Kaydet (Commit) 83b7bfc0 authored tarafından Tamás Zolnai's avatar Tamás Zolnai

sw lok: View jumps to cursor position even if it is moved by an other view.

Scrolling is done twice. Once in SwCursorShell::UpdateCursor() by
SCROLLWIN flag. Here we can check the actual viewid and avoid scrolling
if the cursor is move by an other user.
The second instance in the LO online code, for it we need to pass the
viewid identifying the view which moved the cursor.

Change-Id: I033274f88ce41acbb632e2aeb0d986ab11cd2d52
Reviewed-on: https://gerrit.libreoffice.org/52220Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarTamás Zolnai <tamas.zolnai@collabora.com>
üst 602774ae
...@@ -29,6 +29,8 @@ static bool g_bTiledAnnotations(true); ...@@ -29,6 +29,8 @@ static bool g_bTiledAnnotations(true);
static bool g_bRangeHeaders(false); static bool g_bRangeHeaders(false);
static bool g_bViewIdForVisCursorInvalidation(false);
static bool g_bLocalRendering(false); static bool g_bLocalRendering(false);
static LanguageTag g_aLanguageTag("en-US", true); static LanguageTag g_aLanguageTag("en-US", true);
...@@ -88,6 +90,16 @@ void setRangeHeaders(bool bRangeHeaders) ...@@ -88,6 +90,16 @@ void setRangeHeaders(bool bRangeHeaders)
g_bRangeHeaders = bRangeHeaders; g_bRangeHeaders = bRangeHeaders;
} }
void setViewIdForVisCursorInvalidation(bool bViewIdForVisCursorInvalidation)
{
g_bViewIdForVisCursorInvalidation = bViewIdForVisCursorInvalidation;
}
bool isViewIdForVisCursorInvalidation()
{
return g_bViewIdForVisCursorInvalidation;
}
bool isRangeHeaders() bool isRangeHeaders()
{ {
return g_bRangeHeaders; return g_bRangeHeaders;
......
...@@ -3514,6 +3514,8 @@ static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long con ...@@ -3514,6 +3514,8 @@ static void lo_setOptionalFeatures(LibreOfficeKit* pThis, unsigned long long con
comphelper::LibreOfficeKit::setTiledAnnotations(false); comphelper::LibreOfficeKit::setTiledAnnotations(false);
if (features & LOK_FEATURE_RANGE_HEADERS) if (features & LOK_FEATURE_RANGE_HEADERS)
comphelper::LibreOfficeKit::setRangeHeaders(true); comphelper::LibreOfficeKit::setRangeHeaders(true);
if (features & LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK)
comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
} }
static void lo_setDocumentPassword(LibreOfficeKit* pThis, static void lo_setDocumentPassword(LibreOfficeKit* pThis,
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <LibreOfficeKit/LibreOfficeKitEnums.h> #include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/string.hxx> #include <comphelper/string.hxx>
#include <comphelper/lok.hxx> #include <comphelper/lok.hxx>
#include <sfx2/lokhelper.hxx>
using namespace ::com::sun::star; using namespace ::com::sun::star;
using namespace ::com::sun::star::uno; using namespace ::com::sun::star::uno;
...@@ -1112,7 +1113,7 @@ void ImpEditView::ShowCursor( bool bGotoCursor, bool bForceVisCursor ) ...@@ -1112,7 +1113,7 @@ void ImpEditView::ShowCursor( bool bGotoCursor, bool bForceVisCursor )
} }
else else
{ {
mpViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, sRect.getStr()); SfxLokHelper::notifyVisCursorInvalidation(mpViewShell, sRect);
mpViewShell->NotifyOtherViews(LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect); mpViewShell->NotifyOtherViews(LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect);
} }
} }
......
...@@ -84,7 +84,13 @@ typedef enum ...@@ -84,7 +84,13 @@ typedef enum
/** /**
* Enable range based header data * Enable range based header data
*/ */
LOK_FEATURE_RANGE_HEADERS = (1ULL << 4) LOK_FEATURE_RANGE_HEADERS = (1ULL << 4),
/**
* Request to have the active view's Id as the 1st value in the
* LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR payload.
*/
LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK = (1ULL << 5)
} }
LibreOfficeKitOptionalFeatures; LibreOfficeKitOptionalFeatures;
......
...@@ -67,6 +67,12 @@ COMPHELPER_DLLPUBLIC void setRangeHeaders(bool bTiledAnnotations); ...@@ -67,6 +67,12 @@ COMPHELPER_DLLPUBLIC void setRangeHeaders(bool bTiledAnnotations);
/// Check if range based header data is enabled /// Check if range based header data is enabled
COMPHELPER_DLLPUBLIC bool isRangeHeaders(); COMPHELPER_DLLPUBLIC bool isRangeHeaders();
/// Check whether clients want viewId in visible cursor invalidation payload.
COMPHELPER_DLLPUBLIC bool isViewIdForVisCursorInvalidation();
/// Set whether clients want viewId in visible cursor invalidation payload.
COMPHELPER_DLLPUBLIC void setViewIdForVisCursorInvalidation(bool bViewIdForVisCursorInvalidation);
/// Update the current LOK's language. /// Update the current LOK's language.
COMPHELPER_DLLPUBLIC void setLanguageTag(const LanguageTag& languageTag); COMPHELPER_DLLPUBLIC void setLanguageTag(const LanguageTag& languageTag);
/// Get the current LOK's language. /// Get the current LOK's language.
......
...@@ -50,6 +50,8 @@ public: ...@@ -50,6 +50,8 @@ public:
const std::vector<vcl::LOKPayloadItem>& rPayload = std::vector<vcl::LOKPayloadItem>()); const std::vector<vcl::LOKPayloadItem>& rPayload = std::vector<vcl::LOKPayloadItem>());
/// Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to setOptionalFeatures() if needed. /// Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to setOptionalFeatures() if needed.
static void notifyInvalidation(SfxViewShell const* pThisView, const OString& rPayload); static void notifyInvalidation(SfxViewShell const* pThisView, const OString& rPayload);
/// Emits a LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, but tweaks it according to setOptionalFeatures() if needed.
static void notifyVisCursorInvalidation(OutlinerViewShell const* pThisView, const OString& rRectangle);
/// Notifies all views with the given type and payload. /// Notifies all views with the given type and payload.
static void notifyAllViews(int nType, const OString& rPayload); static void notifyAllViews(int nType, const OString& rPayload);
/// A special value to signify 'infinity'. /// A special value to signify 'infinity'.
......
...@@ -1163,13 +1163,25 @@ callback (gpointer pData) ...@@ -1163,13 +1163,25 @@ callback (gpointer pData)
break; break;
case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR: case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
{ {
priv->m_aVisibleCursor = payloadToRectangle(pDocView, pCallback->m_aPayload.c_str());
std::stringstream aStream(pCallback->m_aPayload);
boost::property_tree::ptree aTree;
boost::property_tree::read_json(aStream, aTree);
const std::string& rRectangle = aTree.get<std::string>("rectangle");
int nViewId = aTree.get<int>("viewId");
priv->m_aVisibleCursor = payloadToRectangle(pDocView, rRectangle.c_str());
priv->m_bCursorOverlayVisible = true; priv->m_bCursorOverlayVisible = true;
g_signal_emit(pDocView, doc_view_signals[CURSOR_CHANGED], 0, std::cerr << nViewId;
std::cerr << priv->m_nViewId;
if(nViewId == priv->m_nViewId)
{
g_signal_emit(pDocView, doc_view_signals[CURSOR_CHANGED], 0,
priv->m_aVisibleCursor.x, priv->m_aVisibleCursor.x,
priv->m_aVisibleCursor.y, priv->m_aVisibleCursor.y,
priv->m_aVisibleCursor.width, priv->m_aVisibleCursor.width,
priv->m_aVisibleCursor.height); priv->m_aVisibleCursor.height);
}
gtk_widget_queue_draw(GTK_WIDGET(pDocView)); gtk_widget_queue_draw(GTK_WIDGET(pDocView));
} }
break; break;
...@@ -2701,6 +2713,7 @@ static gboolean lok_doc_view_initable_init (GInitable *initable, GCancellable* / ...@@ -2701,6 +2713,7 @@ static gboolean lok_doc_view_initable_init (GInitable *initable, GCancellable* /
return FALSE; return FALSE;
} }
priv->m_nLOKFeatures |= LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK; priv->m_nLOKFeatures |= LOK_FEATURE_PART_IN_INVALIDATION_CALLBACK;
priv->m_nLOKFeatures |= LOK_FEATURE_VIEWID_IN_VISCURSOR_INVALIDATION_CALLBACK;
priv->m_pOffice->pClass->setOptionalFeatures(priv->m_pOffice, priv->m_nLOKFeatures); priv->m_pOffice->pClass->setOptionalFeatures(priv->m_pOffice, priv->m_nLOKFeatures);
return TRUE; return TRUE;
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <sfx2/viewfrm.hxx> #include <sfx2/viewfrm.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h> #include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/lok.hxx> #include <comphelper/lok.hxx>
#include <editeng/outliner.hxx>
#include <shellimpl.hxx> #include <shellimpl.hxx>
...@@ -199,6 +200,21 @@ void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, const OStri ...@@ -199,6 +200,21 @@ void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, const OStri
pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_TILES, aBuf.makeStringAndClear().getStr()); pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_TILES, aBuf.makeStringAndClear().getStr());
} }
void SfxLokHelper::notifyVisCursorInvalidation(OutlinerViewShell const* pThisView, const OString& rRectangle)
{
OString sPayload;
if (comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
{
sPayload = OString("{ \"viewId\": \"") + OString::number(SfxLokHelper::getView()) +
"\", \"rectangle\": \"" + rRectangle + "\" }";
}
else
{
sPayload = rRectangle;
}
pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, sPayload.getStr());
}
void SfxLokHelper::notifyAllViews(int nType, const OString& rPayload) void SfxLokHelper::notifyAllViews(int nType, const OString& rPayload)
{ {
const auto payload = rPayload.getStr(); const auto payload = rPayload.getStr();
......
...@@ -106,6 +106,7 @@ public: ...@@ -106,6 +106,7 @@ public:
void testIMESupport(); void testIMESupport();
void testSplitNodeRedlineCallback(); void testSplitNodeRedlineCallback();
void testDeleteNodeRedlineCallback(); void testDeleteNodeRedlineCallback();
void testVisCursorInvalidation();
CPPUNIT_TEST_SUITE(SwTiledRenderingTest); CPPUNIT_TEST_SUITE(SwTiledRenderingTest);
CPPUNIT_TEST(testRegisterCallback); CPPUNIT_TEST(testRegisterCallback);
...@@ -160,6 +161,7 @@ public: ...@@ -160,6 +161,7 @@ public:
CPPUNIT_TEST(testIMESupport); CPPUNIT_TEST(testIMESupport);
CPPUNIT_TEST(testSplitNodeRedlineCallback); CPPUNIT_TEST(testSplitNodeRedlineCallback);
CPPUNIT_TEST(testDeleteNodeRedlineCallback); CPPUNIT_TEST(testDeleteNodeRedlineCallback);
CPPUNIT_TEST(testVisCursorInvalidation);
CPPUNIT_TEST_SUITE_END(); CPPUNIT_TEST_SUITE_END();
private: private:
...@@ -672,6 +674,7 @@ class ViewCallback ...@@ -672,6 +674,7 @@ class ViewCallback
{ {
public: public:
bool m_bOwnCursorInvalidated; bool m_bOwnCursorInvalidated;
int m_nOwnCursorInvalidatedBy;
bool m_bOwnCursorAtOrigin; bool m_bOwnCursorAtOrigin;
tools::Rectangle m_aOwnCursor; tools::Rectangle m_aOwnCursor;
bool m_bViewCursorInvalidated; bool m_bViewCursorInvalidated;
...@@ -693,6 +696,7 @@ public: ...@@ -693,6 +696,7 @@ public:
ViewCallback() ViewCallback()
: m_bOwnCursorInvalidated(false), : m_bOwnCursorInvalidated(false),
m_nOwnCursorInvalidatedBy(-1),
m_bOwnCursorAtOrigin(false), m_bOwnCursorAtOrigin(false),
m_bViewCursorInvalidated(false), m_bViewCursorInvalidated(false),
m_bOwnSelectionSet(false), m_bOwnSelectionSet(false),
...@@ -726,7 +730,18 @@ public: ...@@ -726,7 +730,18 @@ public:
{ {
m_bOwnCursorInvalidated = true; m_bOwnCursorInvalidated = true;
uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(aPayload)); OString sRect;
if(comphelper::LibreOfficeKit::isViewIdForVisCursorInvalidation())
{
std::stringstream aStream(pPayload);
boost::property_tree::ptree aTree;
boost::property_tree::read_json(aStream, aTree);
sRect = aTree.get_child("rectangle").get_value<std::string>().c_str();
m_nOwnCursorInvalidatedBy = aTree.get_child("viewId").get_value<int>();
}
else
sRect = aPayload;
uno::Sequence<OUString> aSeq = comphelper::string::convertCommaSeparated(OUString::fromUtf8(sRect));
if (OString("EMPTY") == pPayload) if (OString("EMPTY") == pPayload)
return; return;
CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength()); CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(4), aSeq.getLength());
...@@ -2276,6 +2291,86 @@ void SwTiledRenderingTest::testDeleteNodeRedlineCallback() ...@@ -2276,6 +2291,86 @@ void SwTiledRenderingTest::testDeleteNodeRedlineCallback()
comphelper::LibreOfficeKit::setActive(false); comphelper::LibreOfficeKit::setActive(false);
} }
void SwTiledRenderingTest::testVisCursorInvalidation()
{
comphelper::LibreOfficeKit::setActive();
SwXTextDocument* pXTextDocument = createDoc("dummy.fodt");
ViewCallback aView1;
SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView1);
int nView1 = SfxLokHelper::getView();
SfxLokHelper::createView();
int nView2 = SfxLokHelper::getView();
ViewCallback aView2;
SfxViewShell::Current()->registerLibreOfficeKitViewCallback(&ViewCallback::callback, &aView2);
Scheduler::ProcessEventsToIdle();
// Move visible cursor in the first view
SfxLokHelper::setView(nView1);
Scheduler::ProcessEventsToIdle();
aView1.m_bOwnCursorInvalidated = false;
aView1.m_bViewCursorInvalidated = false;
aView2.m_bOwnCursorInvalidated = false;
aView2.m_bViewCursorInvalidated = false;
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, KEY_RIGHT);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, KEY_RIGHT);
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT(!aView1.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(!aView2.m_bOwnCursorInvalidated);
// Insert text in the second view which moves the other view's cursor too
SfxLokHelper::setView(nView2);
aView1.m_bOwnCursorInvalidated = false;
aView1.m_bViewCursorInvalidated = false;
aView2.m_bOwnCursorInvalidated = false;
aView2.m_bViewCursorInvalidated = false;
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
// Do the same as before, but set the related compatibility flag first
SfxLokHelper::setView(nView2);
comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(true);
aView1.m_bOwnCursorInvalidated = false;
aView1.m_bViewCursorInvalidated = false;
aView2.m_bOwnCursorInvalidated = false;
aView2.m_bViewCursorInvalidated = false;
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
Scheduler::ProcessEventsToIdle();
CPPUNIT_ASSERT(aView1.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView1.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT_EQUAL(nView2, aView1.m_nOwnCursorInvalidatedBy);
CPPUNIT_ASSERT(aView2.m_bViewCursorInvalidated);
CPPUNIT_ASSERT(aView2.m_bOwnCursorInvalidated);
CPPUNIT_ASSERT_EQUAL(nView2, aView2.m_nOwnCursorInvalidatedBy);
comphelper::LibreOfficeKit::setViewIdForVisCursorInvalidation(false);
mxComponent->dispose();
mxComponent.clear();
comphelper::LibreOfficeKit::setActive(false);
}
CPPUNIT_TEST_SUITE_REGISTRATION(SwTiledRenderingTest); CPPUNIT_TEST_SUITE_REGISTRATION(SwTiledRenderingTest);
CPPUNIT_PLUGIN_IMPLEMENT(); CPPUNIT_PLUGIN_IMPLEMENT();
......
...@@ -1411,6 +1411,13 @@ void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd ) ...@@ -1411,6 +1411,13 @@ void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd )
SET_CURR_SHELL( this ); SET_CURR_SHELL( this );
ClearUpCursors(); ClearUpCursors();
bool bScrollWin = eFlags & SwCursorShell::SCROLLWIN;
// Don't scroll to the cursor if it's moved by an other view
if(comphelper::LibreOfficeKit::isActive())
{
bScrollWin = SfxLokHelper::getView() != SfxLokHelper::getView(GetSfxViewShell());
}
if (ActionPend()) if (ActionPend())
{ {
if ( eFlags & SwCursorShell::READONLY ) if ( eFlags & SwCursorShell::READONLY )
...@@ -1553,7 +1560,7 @@ void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd ) ...@@ -1553,7 +1560,7 @@ void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd )
m_pVisibleCursor->Hide(); // always hide visible Cursor m_pVisibleCursor->Hide(); // always hide visible Cursor
// scroll Cursor to visible area // scroll Cursor to visible area
if( (eFlags & SwCursorShell::SCROLLWIN) && if( bScrollWin &&
(HasSelection() || eFlags & SwCursorShell::READONLY || (HasSelection() || eFlags & SwCursorShell::READONLY ||
!IsCursorReadonly()) ) !IsCursorReadonly()) )
{ {
...@@ -1811,7 +1818,7 @@ void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd ) ...@@ -1811,7 +1818,7 @@ void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd )
} }
// scroll Cursor to visible area // scroll Cursor to visible area
if( m_bHasFocus && eFlags & SwCursorShell::SCROLLWIN && if( m_bHasFocus && bScrollWin&&
(HasSelection() || eFlags & SwCursorShell::READONLY || (HasSelection() || eFlags & SwCursorShell::READONLY ||
!IsCursorReadonly() || GetViewOptions()->IsSelectionInReadonly()) ) !IsCursorReadonly() || GetViewOptions()->IsSelectionInReadonly()) )
{ {
...@@ -1823,7 +1830,7 @@ void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd ) ...@@ -1823,7 +1830,7 @@ void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd )
m_bSVCursorVis = bSav; m_bSVCursorVis = bSav;
} }
} while( eFlags & SwCursorShell::SCROLLWIN ); } while( bScrollWin );
if( m_pBlockCursor ) if( m_pBlockCursor )
RefreshBlockCursor(); RefreshBlockCursor();
......
...@@ -214,13 +214,15 @@ void SwVisibleCursor::SetPosAndShow(SfxViewShell const * pViewShell) ...@@ -214,13 +214,15 @@ void SwVisibleCursor::SetPosAndShow(SfxViewShell const * pViewShell)
if (pViewShell) if (pViewShell)
{ {
if (pViewShell == m_pCursorShell->GetSfxViewShell()) if (pViewShell == m_pCursorShell->GetSfxViewShell())
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, sRect.getStr()); {
SfxLokHelper::notifyVisCursorInvalidation(pViewShell, sRect);
}
else else
SfxLokHelper::notifyOtherView(m_pCursorShell->GetSfxViewShell(), pViewShell, LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect); SfxLokHelper::notifyOtherView(m_pCursorShell->GetSfxViewShell(), pViewShell, LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect);
} }
else else
{ {
m_pCursorShell->GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, sRect.getStr()); SfxLokHelper::notifyVisCursorInvalidation(m_pCursorShell->GetSfxViewShell(), sRect);
SfxLokHelper::notifyOtherViews(m_pCursorShell->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect); SfxLokHelper::notifyOtherViews(m_pCursorShell->GetSfxViewShell(), LOK_CALLBACK_INVALIDATE_VIEW_CURSOR, "rectangle", sRect);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment