office-gobmx/starmath/source/view.cxx
Caolán McNamara ddec502caa tdf#160459 embedded starmath doesn't fit inside allocated area
much to and fro with MapModes and rounding issue obscured that
fundmentally that the 'etched-out' GtkScrolledWindow frame requires
space which eats into what is available to show the formula, and
in embedded mode we want the editing area to be equal in size
to the area used for the preview.

Change-Id: I1d126ddcc953131cf4add7da58ccd89e7fa86b66
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171964
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
2024-08-17 15:16:08 +02:00

2257 lines
74 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <com/sun/star/accessibility/AccessibleEventId.hpp>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/XFramesSupplier.hpp>
#include <com/sun/star/frame/XModel.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <comphelper/lok.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/servicehelper.hxx>
#include <comphelper/storagehelper.hxx>
#include <comphelper/string.hxx>
#include <i18nutil/unicode.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <officecfg/Office/Common.hxx>
#include <sfx2/dispatch.hxx>
#include <sfx2/docfile.hxx>
#include <sfx2/docfilt.hxx>
#include <sfx2/docinsert.hxx>
#include <sfx2/filedlghelper.hxx>
#include <sfx2/infobar.hxx>
#include <sfx2/lokcomponenthelpers.hxx>
#include <sfx2/lokhelper.hxx>
#include <sfx2/msg.hxx>
#include <sfx2/objface.hxx>
#include <sfx2/printer.hxx>
#include <sfx2/request.hxx>
#include <sfx2/sfxbasecontroller.hxx>
#include <sfx2/sidebar/Sidebar.hxx>
#include <sfx2/sidebar/SidebarChildWindow.hxx>
#include <sfx2/sidebar/SidebarController.hxx>
#include <sfx2/viewfac.hxx>
#include <svl/eitem.hxx>
#include <svl/itemset.hxx>
#include <svl/poolitem.hxx>
#include <svl/stritem.hxx>
#include <svl/voiditem.hxx>
#include <vcl/transfer.hxx>
#include <svtools/colorcfg.hxx>
#include <svl/whiter.hxx>
#include <svx/sidebar/SelectionChangeHandler.hxx>
#include <svx/zoomslideritem.hxx>
#include <editeng/editeng.hxx>
#include <editeng/editview.hxx>
#include <editeng/editund2.hxx>
#include <svx/svxdlg.hxx>
#include <sfx2/zoomitem.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/event.hxx>
#include <vcl/help.hxx>
#include <vcl/settings.hxx>
#include <vcl/virdev.hxx>
#include <sal/log.hxx>
#include <tools/svborder.hxx>
#include <o3tl/string_view.hxx>
#include <o3tl/temporary.hxx>
#include <unotools/streamwrap.hxx>
#include <unomodel.hxx>
#include <view.hxx>
#include <cfgitem.hxx>
#include <dialog.hxx>
#include <document.hxx>
#include <starmath.hrc>
#include <strings.hrc>
#include <smmod.hxx>
#include <mathmlimport.hxx>
#include <cursor.hxx>
#include "accessibility.hxx"
#include <ElementsDockingWindow.hxx>
#include <helpids.h>
// space around the edit window, in pixels
// fdo#69111: Increased border on the top so that the window is
// easier to tear off.
#define CMD_BOX_PADDING 3
#define CMD_BOX_PADDING_TOP 11
#define ShellClass_SmViewShell
#include <smslots.hxx>
using namespace css;
using namespace css::accessibility;
using namespace css::uno;
static OUString GetScrollUIName(const SmViewShell& rShell)
{
const SmDocShell* pShell = rShell.GetDoc();
if (pShell && pShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED)
{
// This one has no border on the scrolledwindow, to maximize the space
// available when in embedded mode and minimize the difference from
// the ole preview to give a more seamless embedded editing experience.
return "modules/smath/ui/embedwindow.ui";
}
return "modules/smath/ui/mathwindow.ui";
}
SmGraphicWindow::SmGraphicWindow(SmViewShell& rShell)
: InterimItemWindow(&rShell.GetViewFrame().GetWindow(), GetScrollUIName(rShell), u"MathWindow"_ustr)
, nLinePixH(GetSettings().GetStyleSettings().GetScrollBarSize())
, nColumnPixW(nLinePixH)
, nZoom(100)
// continue to use user-scrolling to make this work equivalent to how it 'always' worked
, mxScrolledWindow(m_xBuilder->weld_scrolled_window(u"scrolledwindow"_ustr, true))
, mxGraphic(new SmGraphicWidget(rShell, *this))
, mxGraphicWin(new weld::CustomWeld(*m_xBuilder, u"mathview"_ustr, *mxGraphic))
{
InitControlBase(mxGraphic->GetDrawingArea());
mxScrolledWindow->connect_hadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl));
mxScrolledWindow->connect_vadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl));
// docking windows are usually hidden (often already done in the
// resource) and will be shown by the sfx framework.
Hide();
}
void SmGraphicWindow::dispose()
{
InitControlBase(nullptr);
mxGraphicWin.reset();
mxGraphic.reset();
mxScrolledWindow.reset();
InterimItemWindow::dispose();
}
SmGraphicWindow::~SmGraphicWindow()
{
disposeOnce();
}
void SmGraphicWindow::Resize()
{
InterimItemWindow::Resize();
// get the new output-size in pixel
Size aOutPixSz = GetOutputSizePixel();
// determine the size of the output-area and if we need scrollbars
const auto nScrSize = mxScrolledWindow->get_scroll_thickness();
bool bVVisible = false; // by default no vertical-ScrollBar
bool bHVisible = false; // by default no horizontal-ScrollBar
bool bChanged; // determines if a visibility was changed
do
{
bChanged = false;
// does we need a vertical ScrollBar
if ( aOutPixSz.Width() < aTotPixSz.Width() && !bHVisible )
{
bHVisible = true;
aOutPixSz.AdjustHeight( -nScrSize );
bChanged = true;
}
// does we need a horizontal ScrollBar
if ( aOutPixSz.Height() < aTotPixSz.Height() && !bVVisible )
{
bVVisible = true;
aOutPixSz.AdjustWidth( -nScrSize );
bChanged = true;
}
}
while ( bChanged ); // until no visibility has changed
// store the old offset and map-mode
MapMode aMap(GetGraphicMapMode());
Point aOldPixOffset(aPixOffset);
// justify (right/bottom borders should never exceed the virtual window)
Size aPixDelta;
if ( aPixOffset.X() < 0 &&
aPixOffset.X() + aTotPixSz.Width() < aOutPixSz.Width() )
aPixDelta.setWidth(
aOutPixSz.Width() - ( aPixOffset.X() + aTotPixSz.Width() ) );
if ( aPixOffset.Y() < 0 &&
aPixOffset.Y() + aTotPixSz.Height() < aOutPixSz.Height() )
aPixDelta.setHeight(
aOutPixSz.Height() - ( aPixOffset.Y() + aTotPixSz.Height() ) );
if ( aPixDelta.Width() || aPixDelta.Height() )
{
aPixOffset.AdjustX(aPixDelta.Width() );
aPixOffset.AdjustY(aPixDelta.Height() );
}
// for axis without scrollbar restore the origin
if ( !bVVisible || !bHVisible )
{
aPixOffset = Point(
bHVisible
? aPixOffset.X()
: (aOutPixSz.Width()-aTotPixSz.Width()) / 2,
bVVisible
? aPixOffset.Y()
: (aOutPixSz.Height()-aTotPixSz.Height()) / 2 );
}
if (bHVisible && mxScrolledWindow->get_hpolicy() == VclPolicyType::NEVER)
aPixOffset.setX( 0 );
if (bVVisible && mxScrolledWindow->get_vpolicy() == VclPolicyType::NEVER)
aPixOffset.setY( 0 );
// select the shifted map-mode
if (aPixOffset != aOldPixOffset)
SetGraphicMapMode(aMap);
// show or hide scrollbars
mxScrolledWindow->set_vpolicy(bVVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
mxScrolledWindow->set_hpolicy(bHVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
// resize scrollbars and set their ranges
if ( bHVisible )
{
mxScrolledWindow->hadjustment_configure(-aPixOffset.X(), 0, aTotPixSz.Width(), nColumnPixW,
aOutPixSz.Width(), aOutPixSz.Width());
}
if ( bVVisible )
{
mxScrolledWindow->vadjustment_configure(-aPixOffset.Y(), 0, aTotPixSz.Height(), nLinePixH,
aOutPixSz.Height(), aOutPixSz.Height());
}
}
IMPL_LINK_NOARG(SmGraphicWindow, ScrollHdl, weld::ScrolledWindow&, void)
{
MapMode aMap(GetGraphicMapMode());
Point aNewPixOffset(aPixOffset);
// scrolling horizontally?
if (mxScrolledWindow->get_hpolicy() == VclPolicyType::ALWAYS)
aNewPixOffset.setX(-mxScrolledWindow->hadjustment_get_value());
// scrolling vertically?
if (mxScrolledWindow->get_vpolicy() == VclPolicyType::ALWAYS)
aNewPixOffset.setY(-mxScrolledWindow->vadjustment_get_value());
// scrolling?
if (aPixOffset == aNewPixOffset)
return;
// recompute the logical scroll units
aPixOffset = aNewPixOffset;
SetGraphicMapMode(aMap);
}
void SmGraphicWindow::SetGraphicMapMode(const MapMode& rNewMapMode)
{
OutputDevice& rDevice = mxGraphic->GetOutputDevice();
MapMode aMap( rNewMapMode );
aMap.SetOrigin( aMap.GetOrigin() + rDevice.PixelToLogic( aPixOffset, aMap ) );
rDevice.SetMapMode( aMap );
mxGraphic->Invalidate();
}
MapMode SmGraphicWindow::GetGraphicMapMode() const
{
OutputDevice& rDevice = mxGraphic->GetOutputDevice();
MapMode aMap(rDevice.GetMapMode());
aMap.SetOrigin( aMap.GetOrigin() - rDevice.PixelToLogic( aPixOffset ) );
return aMap;
}
void SmGraphicWindow::SetTotalSize( const Size& rNewSize )
{
aTotPixSz = mxGraphic->GetOutputDevice().LogicToPixel(rNewSize);
Resize();
}
Size SmGraphicWindow::GetTotalSize() const
{
return mxGraphic->GetOutputDevice().PixelToLogic(aTotPixSz);
}
void SmGraphicWindow::ShowContextMenu(const CommandEvent& rCEvt)
{
GetParent()->ToTop();
Point aPos(5, 5);
if (rCEvt.IsMouseEvent())
aPos = rCEvt.GetMousePosPixel();
// added for replaceability of context menus
SfxDispatcher::ExecutePopup( this, &aPos );
}
SmGraphicWidget::SmGraphicWidget(SmViewShell& rShell, SmGraphicWindow& rGraphicWindow)
: mrGraphicWindow(rGraphicWindow)
, bIsCursorVisible(false)
, bIsLineVisible(false)
, aCaretBlinkTimer("SmGraphicWidget aCaretBlinkTimer")
, mrViewShell(rShell)
{
}
void SmGraphicWidget::SetDrawingArea(weld::DrawingArea* pDrawingArea)
{
weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
OutputDevice& rDevice = GetOutputDevice();
rDevice.EnableRTL(GetDoc()->GetFormat().IsRightToLeft());
rDevice.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
if (comphelper::LibreOfficeKit::isActive())
{
// Disable map mode, so that it's possible to send mouse event coordinates
// directly in twips.
rDevice.EnableMapMode(false);
}
else
{
const Fraction aFraction(1, 1);
rDevice.SetMapMode(MapMode(SmMapUnit(), Point(), aFraction, aFraction));
}
SetTotalSize();
SetHelpId(HID_SMA_WIN_DOCUMENT);
ShowLine(false);
CaretBlinkInit();
}
SmGraphicWidget::~SmGraphicWidget()
{
if (mxAccessible.is())
mxAccessible->ClearWin(); // make Accessible nonfunctional
mxAccessible.clear();
CaretBlinkStop();
}
SmDocShell* SmGraphicWidget::GetDoc() { return GetView().GetDoc(); }
SmCursor& SmGraphicWidget::GetCursor()
{
assert(GetDoc());
return GetDoc()->GetCursor();
}
bool SmGraphicWidget::MouseButtonDown(const MouseEvent& rMEvt)
{
GrabFocus();
// set formula-cursor and selection of edit window according to the
// position clicked at
SAL_WARN_IF( rMEvt.GetClicks() == 0, "starmath", "0 clicks" );
if ( !rMEvt.IsLeft() )
return true;
OutputDevice& rDevice = GetOutputDevice();
// get click position relative to formula
Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
const SmNode *pTree = GetDoc()->GetFormulaTree();
if (!pTree)
return true;
SmEditWindow* pEdit = GetView().GetEditWindow();
if (SmViewShell::IsInlineEditEnabled()) {
GetCursor().MoveTo(&rDevice, aPos, !rMEvt.IsShift());
GetView().InvalidateSlots();
// 'on grab' window events are missing in lok, do it explicitly
if (comphelper::LibreOfficeKit::isActive())
SetIsCursorVisible(true);
return true;
}
const SmNode *pNode = nullptr;
// if it was clicked inside the formula then get the appropriate node
if (pTree->OrientedDist(aPos) <= 0)
pNode = pTree->FindRectClosestTo(aPos);
if (!pNode)
return true;
if (!pEdit)
return true;
// set selection to the beginning of the token
pEdit->SetSelection(pNode->GetSelection());
SetCursor(pNode);
// allow for immediate editing and
//! implicitly synchronize the cursor position mark in this window
pEdit->GrabFocus();
return true;
}
bool SmGraphicWidget::MouseMove(const MouseEvent &rMEvt)
{
if (rMEvt.IsLeft() && SmViewShell::IsInlineEditEnabled())
{
OutputDevice& rDevice = GetOutputDevice();
Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
GetCursor().MoveTo(&rDevice, aPos, false);
CaretBlinkStop();
SetIsCursorVisible(true);
CaretBlinkStart();
RepaintViewShellDoc();
}
return true;
}
void SmGraphicWidget::GetFocus()
{
if (!SmViewShell::IsInlineEditEnabled())
return;
if (SmEditWindow* pEdit = GetView().GetEditWindow())
pEdit->Flush();
SetIsCursorVisible(true);
ShowLine(true);
CaretBlinkStart();
RepaintViewShellDoc();
}
void SmGraphicWidget::LoseFocus()
{
if (mxAccessible.is())
{
uno::Any aOldValue, aNewValue;
aOldValue <<= AccessibleStateType::FOCUSED;
// aNewValue remains empty
mxAccessible->LaunchEvent( AccessibleEventId::STATE_CHANGED,
aOldValue, aNewValue );
}
if (!SmViewShell::IsInlineEditEnabled())
return;
SetIsCursorVisible(false);
ShowLine(false);
CaretBlinkStop();
RepaintViewShellDoc();
}
void SmGraphicWidget::RepaintViewShellDoc()
{
if (SmDocShell* pDoc = GetDoc())
pDoc->Repaint();
}
IMPL_LINK_NOARG(SmGraphicWidget, CaretBlinkTimerHdl, Timer *, void)
{
if (IsCursorVisible())
SetIsCursorVisible(false);
else
SetIsCursorVisible(true);
RepaintViewShellDoc();
}
void SmGraphicWidget::CaretBlinkInit()
{
if (comphelper::LibreOfficeKit::isActive())
return; // No blinking in lok case
aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWidget, CaretBlinkTimerHdl));
aCaretBlinkTimer.SetTimeout(Application::GetSettings().GetStyleSettings().GetCursorBlinkTime());
}
void SmGraphicWidget::CaretBlinkStart()
{
if (!SmViewShell::IsInlineEditEnabled() || comphelper::LibreOfficeKit::isActive())
return;
if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME)
aCaretBlinkTimer.Start();
}
void SmGraphicWidget::CaretBlinkStop()
{
if (!SmViewShell::IsInlineEditEnabled() || comphelper::LibreOfficeKit::isActive())
return;
aCaretBlinkTimer.Stop();
}
// shows or hides the formula-cursor depending on 'bShow' is true or not
void SmGraphicWidget::ShowCursor(bool bShow)
{
if (SmViewShell::IsInlineEditEnabled())
return;
bool bInvert = bShow != IsCursorVisible();
if (bInvert)
InvertFocusRect(GetOutputDevice(), aCursorRect);
SetIsCursorVisible(bShow);
}
void SmGraphicWidget::ShowLine(bool bShow)
{
if (!SmViewShell::IsInlineEditEnabled())
return;
bIsLineVisible = bShow;
}
void SmGraphicWidget::SetIsCursorVisible(bool bVis)
{
bIsCursorVisible = bVis;
if (comphelper::LibreOfficeKit::isActive())
{
mrViewShell.SendCaretToLOK();
mrViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE,
OString::boolean(bVis));
}
}
void SmGraphicWidget::SetCursor(const SmNode *pNode)
{
if (SmViewShell::IsInlineEditEnabled())
return;
const SmNode *pTree = GetDoc()->GetFormulaTree();
// get appropriate rectangle
Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()),
aTLPos (GetFormulaDrawPos() + aOffset);
aTLPos.AdjustX( -(pNode->GetItalicLeftSpace()) );
Size aSize (pNode->GetItalicSize());
SetCursor(tools::Rectangle(aTLPos, aSize));
}
void SmGraphicWidget::SetCursor(const tools::Rectangle &rRect)
// sets cursor to new position (rectangle) 'rRect'.
// The old cursor will be removed, and the new one will be shown if
// that is activated in the ConfigItem
{
if (SmViewShell::IsInlineEditEnabled())
return;
SmModule *pp = SM_MOD();
if (IsCursorVisible())
ShowCursor(false); // clean up remainings of old cursor
aCursorRect = rRect;
if (pp->GetConfig()->IsShowFormulaCursor())
ShowCursor(true); // draw new cursor
}
const SmNode * SmGraphicWidget::SetCursorPos(sal_uInt16 nRow, sal_uInt16 nCol)
// looks for a VISIBLE node in the formula tree with its token at
// (or around) the position 'nRow', 'nCol' in the edit window
// (row and column numbering starts with 1 there!).
// If there is such a node the formula-cursor is set to cover that nodes
// rectangle. If not the formula-cursor will be hidden.
// In any case the search result is being returned.
{
if (SmViewShell::IsInlineEditEnabled())
return nullptr;
// find visible node with token at nRow, nCol
const SmNode *pTree = GetDoc()->GetFormulaTree(),
*pNode = nullptr;
if (pTree)
pNode = pTree->FindTokenAt(nRow, nCol);
if (pNode)
SetCursor(pNode);
else
ShowCursor(false);
return pNode;
}
void SmGraphicWidget::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
{
assert(GetDoc());
SmDocShell& rDoc = *GetDoc();
Point aPoint;
rDoc.DrawFormula(rRenderContext, aPoint, true); //! modifies aPoint to be the topleft
//! corner of the formula
aFormulaDrawPos = aPoint;
if (SmViewShell::IsInlineEditEnabled())
{
//Draw cursor if any...
if (rDoc.HasCursor() && IsLineVisible())
rDoc.GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible());
}
else
{
SetIsCursorVisible(false); // (old) cursor must be drawn again
if (const SmEditWindow* pEdit = GetView().GetEditWindow())
{ // get new position for formula-cursor (for possible altered formula)
sal_Int32 nRow;
sal_uInt16 nCol;
SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol);
const SmNode *pFound = SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
SmModule *pp = SM_MOD();
if (pFound && pp->GetConfig()->IsShowFormulaCursor())
ShowCursor(true);
}
}
}
void SmGraphicWidget::SetTotalSize()
{
assert(GetDoc());
OutputDevice& rDevice = GetOutputDevice();
const Size aTmp(rDevice.PixelToLogic(rDevice.LogicToPixel(GetDoc()->GetSize())));
if (aTmp != mrGraphicWindow.GetTotalSize())
mrGraphicWindow.SetTotalSize(aTmp);
}
namespace
{
SmBracketType BracketTypeOf(sal_uInt32 c)
{
switch (c)
{
case '(':
case ')':
return SmBracketType::Round;
case '[':
case ']':
return SmBracketType::Square;
case '{':
case '}':
return SmBracketType::Curly;
}
assert(false); // Unreachable
return SmBracketType::Round;
}
bool CharInput(sal_uInt32 c, SmCursor& rCursor, OutputDevice& rDevice)
{
switch (c)
{
case 0:
return false;
case ' ':
rCursor.InsertElement(BlankElement);
break;
case '!':
rCursor.InsertElement(FactorialElement);
break;
case '%':
rCursor.InsertElement(PercentElement);
break;
case '*':
rCursor.InsertElement(CDotElement);
break;
case '+':
rCursor.InsertElement(PlusElement);
break;
case '-':
rCursor.InsertElement(MinusElement);
break;
case '<':
rCursor.InsertElement(LessThanElement);
break;
case '=':
rCursor.InsertElement(EqualElement);
break;
case '>':
rCursor.InsertElement(GreaterThanElement);
break;
case '^':
rCursor.InsertSubSup(RSUP);
break;
case '_':
rCursor.InsertSubSup(RSUB);
break;
case '/':
rCursor.InsertFraction();
break;
case '(':
case '[':
case '{':
rCursor.InsertBrackets(BracketTypeOf(c));
break;
case ')':
case ']':
case '}':
if (rCursor.IsAtTailOfBracket(BracketTypeOf(c)))
{
rCursor.Move(&rDevice, MoveRight);
break;
}
[[fallthrough]];
default:
rCursor.InsertText(OUString(&c, 1));
break;
}
return true;
}
}
bool SmGraphicWidget::KeyInput(const KeyEvent& rKEvt)
{
if (rKEvt.GetKeyCode().GetCode() == KEY_F1)
{
GetView().StartMainHelp();
return true;
}
if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
{
// Terminate possible InPlace mode
return GetView().Escape();
}
if (!SmViewShell::IsInlineEditEnabled())
return GetView().KeyInput(rKEvt);
bool bConsumed = true;
SmCursor& rCursor = GetCursor();
switch (rKEvt.GetKeyCode().GetFunction())
{
case KeyFuncType::COPY:
rCursor.Copy(&mrGraphicWindow);
break;
case KeyFuncType::CUT:
rCursor.Cut(&mrGraphicWindow);
break;
case KeyFuncType::PASTE:
rCursor.Paste(&mrGraphicWindow);
break;
case KeyFuncType::UNDO:
GetDoc()->Execute(o3tl::temporary(SfxRequest(*GetView().GetFrame(), SID_UNDO)));
break;
case KeyFuncType::REDO:
GetDoc()->Execute(o3tl::temporary(SfxRequest(*GetView().GetFrame(), SID_REDO)));
break;
default:
switch (rKEvt.GetKeyCode().GetCode())
{
case KEY_LEFT:
rCursor.Move(&GetOutputDevice(), MoveLeft, !rKEvt.GetKeyCode().IsShift());
break;
case KEY_RIGHT:
rCursor.Move(&GetOutputDevice(), MoveRight, !rKEvt.GetKeyCode().IsShift());
break;
case KEY_UP:
rCursor.Move(&GetOutputDevice(), MoveUp, !rKEvt.GetKeyCode().IsShift());
break;
case KEY_DOWN:
rCursor.Move(&GetOutputDevice(), MoveDown, !rKEvt.GetKeyCode().IsShift());
break;
case KEY_RETURN:
if (!rKEvt.GetKeyCode().IsShift())
rCursor.InsertRow();
break;
case KEY_DELETE:
if (!rCursor.HasSelection())
{
rCursor.Move(&GetOutputDevice(), MoveRight, false);
if (rCursor.HasComplexSelection())
break;
}
rCursor.Delete();
break;
case KEY_BACKSPACE:
rCursor.DeletePrev(&GetOutputDevice());
break;
default:
if (!CharInput(rKEvt.GetCharCode(), rCursor, GetOutputDevice()))
bConsumed = GetView().KeyInput(rKEvt);
}
}
GetView().InvalidateSlots();
CaretBlinkStop();
CaretBlinkStart();
SetIsCursorVisible(true);
RepaintViewShellDoc();
return bConsumed;
}
bool SmGraphicWidget::Command(const CommandEvent& rCEvt)
{
bool bCallBase = true;
if (!GetView().GetViewFrame().GetFrame().IsInPlace())
{
switch ( rCEvt.GetCommand() )
{
case CommandEventId::ContextMenu:
// purely for "ExecutePopup" taking a vcl::Window and
// we assume SmGraphicWindow 0,0 is at SmEditWindow 0,0
mrGraphicWindow.ShowContextMenu(rCEvt);
bCallBase = false;
break;
case CommandEventId::Wheel:
{
const CommandWheelData* pWData = rCEvt.GetWheelData();
if ( pWData && CommandWheelMode::ZOOM == pWData->GetMode() )
{
sal_uInt16 nTmpZoom = mrGraphicWindow.GetZoom();
if( 0 > pWData->GetDelta() )
nTmpZoom -= 10;
else
nTmpZoom += 10;
mrGraphicWindow.SetZoom(nTmpZoom);
bCallBase = false;
}
break;
}
case CommandEventId::GestureZoom:
{
const CommandGestureZoomData* pData = rCEvt.GetGestureZoomData();
if (pData)
{
if (pData->meEventType == GestureEventZoomType::Begin)
{
mfLastZoomScale = pData->mfScaleDelta;
}
else if (pData->meEventType == GestureEventZoomType::Update)
{
double deltaBetweenEvents = (pData->mfScaleDelta - mfLastZoomScale) / mfLastZoomScale;
mfLastZoomScale = pData->mfScaleDelta;
// Accumulate fractional zoom to avoid small zoom changes from being ignored
mfAccumulatedZoom += deltaBetweenEvents;
int nZoomChangePercent = mfAccumulatedZoom * 100;
mfAccumulatedZoom -= nZoomChangePercent / 100.0;
sal_uInt16 nZoom = mrGraphicWindow.GetZoom();
nZoom += nZoomChangePercent;
mrGraphicWindow.SetZoom(nZoom);
}
bCallBase = false;
}
break;
}
default: break;
}
}
switch (rCEvt.GetCommand())
{
case CommandEventId::ExtTextInput:
if (SmViewShell::IsInlineEditEnabled())
{
const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
assert(pData);
const OUString& rText = pData->GetText();
SmCursor& rCursor = GetCursor();
OutputDevice& rDevice = GetOutputDevice();
for (sal_Int32 i = 0; i < rText.getLength();)
CharInput(rText.iterateCodePoints(&i), rCursor, rDevice);
bCallBase = false;
}
break;
default:
break;
}
return !bCallBase;
}
void SmGraphicWindow::SetZoom(sal_uInt16 Factor)
{
if (comphelper::LibreOfficeKit::isActive())
return;
nZoom = std::clamp(Factor, MINZOOM, MAXZOOM);
Fraction aFraction(nZoom, 100);
SetGraphicMapMode(MapMode(SmMapUnit(), Point(), aFraction, aFraction));
mxGraphic->SetTotalSize();
SmViewShell& rViewSh = mxGraphic->GetView();
rViewSh.GetViewFrame().GetBindings().Invalidate(SID_ATTR_ZOOM);
rViewSh.GetViewFrame().GetBindings().Invalidate(SID_ATTR_ZOOMSLIDER);
}
void SmGraphicWindow::ZoomToFitInWindow()
{
// set defined mapmode before calling 'LogicToPixel' below
SetGraphicMapMode(MapMode(SmMapUnit()));
assert(mxGraphic->GetDoc());
Size aSize(mxGraphic->GetOutputDevice().LogicToPixel(mxGraphic->GetDoc()->GetSize()));
Size aWindowSize(GetSizePixel());
if (!aSize.IsEmpty())
{
tools::Long nVal = std::min ((85 * aWindowSize.Width()) / aSize.Width(),
(85 * aWindowSize.Height()) / aSize.Height());
SetZoom ( sal::static_int_cast< sal_uInt16 >(nVal) );
}
}
uno::Reference< XAccessible > SmGraphicWidget::CreateAccessible()
{
if (!mxAccessible.is())
{
mxAccessible = new SmGraphicAccessible( this );
}
return mxAccessible;
}
/**************************************************************************/
SmGraphicController::SmGraphicController(SmGraphicWidget &rSmGraphic,
sal_uInt16 nId_,
SfxBindings &rBindings) :
SfxControllerItem(nId_, rBindings),
rGraphic(rSmGraphic)
{
}
void SmGraphicController::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
{
rGraphic.SetTotalSize();
rGraphic.Invalidate();
SfxControllerItem::StateChangedAtToolBoxControl (nSID, eState, pState);
}
/**************************************************************************/
SmEditController::SmEditController(SmEditWindow &rSmEdit,
sal_uInt16 nId_,
SfxBindings &rBindings) :
SfxControllerItem(nId_, rBindings),
rEdit(rSmEdit)
{
}
void SmEditController::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
{
const SfxStringItem *pItem = dynamic_cast<const SfxStringItem*>( pState);
if ((pItem != nullptr) && (rEdit.GetText() != pItem->GetValue()))
rEdit.SetText(pItem->GetValue());
SfxControllerItem::StateChangedAtToolBoxControl (nSID, eState, pState);
}
/**************************************************************************/
SmCmdBoxWindow::SmCmdBoxWindow(SfxBindings *pBindings_, SfxChildWindow *pChildWindow,
vcl::Window *pParent)
: SfxDockingWindow(pBindings_, pChildWindow, pParent, u"EditWindow"_ustr, u"modules/smath/ui/editwindow.ui"_ustr)
, m_xEdit(new SmEditWindow(*this, *m_xBuilder))
, aController(*m_xEdit, SID_TEXT, *pBindings_)
, bExiting(false)
, aInitialFocusTimer("SmCmdBoxWindow aInitialFocusTimer")
{
set_id(u"math_edit"_ustr);
SetHelpId( HID_SMA_COMMAND_WIN );
SetSizePixel(LogicToPixel(Size(292 , 94), MapMode(MapUnit::MapAppFont)));
SetText(SmResId(STR_CMDBOXWINDOW));
Hide();
// Don't try to grab focus in inline edit mode
if (!SmViewShell::IsInlineEditEnabled())
{
aInitialFocusTimer.SetInvokeHandler(LINK(this, SmCmdBoxWindow, InitialFocusTimerHdl));
aInitialFocusTimer.SetTimeout(100);
}
}
Point SmCmdBoxWindow::WidgetToWindowPos(const weld::Widget& rWidget, const Point& rPos)
{
Point aRet(rPos);
int x(0), y(0), width(0), height(0);
rWidget.get_extents_relative_to(*m_xContainer, x, y, width, height);
aRet.Move(x, y);
aRet.Move(m_xBox->GetPosPixel().X(), m_xBox->GetPosPixel().Y());
return aRet;
}
void SmCmdBoxWindow::ShowContextMenu(const Point& rPos)
{
ToTop();
SmViewShell *pViewSh = GetView();
if (pViewSh)
pViewSh->GetViewFrame().GetDispatcher()->ExecutePopup(u"edit"_ustr, this, &rPos);
}
void SmCmdBoxWindow::Command(const CommandEvent& rCEvt)
{
if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
{
ShowContextMenu(rCEvt.GetMousePosPixel());
return;
}
SfxDockingWindow::Command(rCEvt);
}
SmCmdBoxWindow::~SmCmdBoxWindow ()
{
disposeOnce();
}
void SmCmdBoxWindow::dispose()
{
aInitialFocusTimer.Stop();
bExiting = true;
aController.dispose();
m_xEdit.reset();
SfxDockingWindow::dispose();
}
SmViewShell * SmCmdBoxWindow::GetView()
{
SfxDispatcher *pDispatcher = GetBindings().GetDispatcher();
SfxViewShell *pView = pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr;
return dynamic_cast<SmViewShell*>( pView);
}
Size SmCmdBoxWindow::CalcDockingSize(SfxChildAlignment eAlign)
{
switch (eAlign)
{
case SfxChildAlignment::LEFT:
case SfxChildAlignment::RIGHT:
return Size();
default:
break;
}
return SfxDockingWindow::CalcDockingSize(eAlign);
}
SfxChildAlignment SmCmdBoxWindow::CheckAlignment(SfxChildAlignment eActual,
SfxChildAlignment eWish)
{
switch (eWish)
{
case SfxChildAlignment::TOP:
case SfxChildAlignment::BOTTOM:
case SfxChildAlignment::NOALIGNMENT:
return eWish;
default:
break;
}
return eActual;
}
void SmCmdBoxWindow::StateChanged( StateChangedType nStateChange )
{
if (StateChangedType::InitShow == nStateChange)
{
Resize(); // avoid SmEditWindow not being painted correctly
// set initial position of window in floating mode
if (IsFloatingMode())
AdjustPosition(); //! don't change pos in docking-mode !
aInitialFocusTimer.Start();
}
SfxDockingWindow::StateChanged( nStateChange );
}
IMPL_LINK_NOARG( SmCmdBoxWindow, InitialFocusTimerHdl, Timer *, void )
{
// We want to have the focus in the edit window once Math has been opened
// to allow for immediate typing.
// Problem: There is no proper way to do this
// Thus: this timer based solution has been implemented (see GrabFocus below)
// Follow-up problem (#i114910): grabbing the focus may bust the help system since
// it relies on getting the current frame which conflicts with grabbing the focus.
// Thus aside from the 'GrabFocus' call everything else is to get the
// help reliably working despite using 'GrabFocus'.
try
{
uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( comphelper::getProcessComponentContext() );
m_xEdit->GrabFocus();
SmViewShell* pView = GetView();
assert(pView);
bool bInPlace = pView->GetViewFrame().GetFrame().IsInPlace();
uno::Reference< frame::XFrame > xFrame( GetBindings().GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface());
if ( bInPlace )
{
uno::Reference<container::XChild> xModel(pView->GetDoc()->GetModel(),
uno::UNO_QUERY_THROW);
uno::Reference< frame::XModel > xParent( xModel->getParent(), uno::UNO_QUERY_THROW );
uno::Reference< frame::XController > xParentCtrler( xParent->getCurrentController() );
uno::Reference< frame::XFramesSupplier > xParentFrame( xParentCtrler->getFrame(), uno::UNO_QUERY_THROW );
xParentFrame->setActiveFrame( xFrame );
}
else
{
xDesktop->setActiveFrame( xFrame );
}
}
catch (uno::Exception &)
{
SAL_WARN( "starmath", "failed to properly set initial focus to edit window" );
}
}
void SmCmdBoxWindow::AdjustPosition()
{
const tools::Rectangle aRect( Point(), GetParent()->GetOutputSizePixel() );
Point aTopLeft( Point( aRect.Left(),
aRect.Bottom() - GetSizePixel().Height() ) );
Point aPos( GetParent()->OutputToScreenPixel( aTopLeft ) );
if (aPos.X() < 0)
aPos.setX( 0 );
if (aPos.Y() < 0)
aPos.setY( 0 );
SetPosPixel( aPos );
}
void SmCmdBoxWindow::ToggleFloatingMode()
{
SfxDockingWindow::ToggleFloatingMode();
if (GetFloatingWindow())
GetFloatingWindow()->SetMinOutputSizePixel(Size (200, 50));
}
void SmCmdBoxWindow::GetFocus()
{
if (!bExiting)
m_xEdit->GrabFocus();
}
SFX_IMPL_DOCKINGWINDOW_WITHID(SmCmdBoxWrapper, SID_CMDBOXWINDOW);
SmCmdBoxWrapper::SmCmdBoxWrapper(vcl::Window *pParentWindow, sal_uInt16 nId,
SfxBindings *pBindings,
SfxChildWinInfo *pInfo) :
SfxChildWindow(pParentWindow, nId)
{
VclPtrInstance<SmCmdBoxWindow> pDialog(pBindings, this, pParentWindow);
SetWindow(pDialog);
// make window docked to the bottom initially (after first start)
SetAlignment(SfxChildAlignment::BOTTOM);
pDialog->setDeferredProperties();
pDialog->set_border_width(CMD_BOX_PADDING);
pDialog->set_margin_top(CMD_BOX_PADDING_TOP);
pDialog->Initialize(pInfo);
}
SFX_IMPL_SUPERCLASS_INTERFACE(SmViewShell, SfxViewShell)
void SmViewShell::InitInterface_Impl()
{
GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS,
SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server,
ToolbarId::Math_Toolbox);
//Dummy-Objectbar, to avoid quiver while activating
GetStaticInterface()->RegisterChildWindow(SmCmdBoxWrapper::GetChildWindowId());
GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId());
}
SFX_IMPL_NAMED_VIEWFACTORY(SmViewShell, "Default")
{
SFX_VIEW_REGISTRATION(SmDocShell);
}
void SmViewShell::InnerResizePixel(const Point &rOfs, const Size &rSize, bool)
{
Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
if ( !aObjSize.IsEmpty() )
{
Size aProvidedSize = GetWindow()->PixelToLogic(rSize, MapMode(SmMapUnit()));
Fraction aZoomX(aProvidedSize.Width(), aObjSize.Width());
Fraction aZoomY(aProvidedSize.Height(), aObjSize.Height());
MapMode aMap(mxGraphicWindow->GetGraphicMapMode());
aMap.SetScaleX(aZoomX);
aMap.SetScaleY(aZoomY);
mxGraphicWindow->SetGraphicMapMode(aMap);
}
SetBorderPixel( SvBorder() );
mxGraphicWindow->SetPosSizePixel(rOfs, rSize);
GetGraphicWidget().SetTotalSize();
}
void SmViewShell::OuterResizePixel(const Point &rOfs, const Size &rSize)
{
mxGraphicWindow->SetPosSizePixel(rOfs, rSize);
if (GetDoc()->IsPreview())
mxGraphicWindow->ZoomToFitInWindow();
}
void SmViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
{
rRect.SetSize(mxGraphicWindow->GetSizePixel());
}
void SmViewShell::SetZoomFactor( const Fraction &rX, const Fraction &rY )
{
const Fraction &rFrac = std::min(rX, rY);
mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(tools::Long(rFrac * Fraction( 100, 1 ))));
//To avoid rounding errors base class regulates crooked values too
//if necessary
SfxViewShell::SetZoomFactor( rX, rY );
}
SfxPrinter* SmViewShell::GetPrinter(bool bCreate)
{
SmDocShell* pDoc = GetDoc();
if (pDoc->HasPrinter() || bCreate)
return pDoc->GetPrinter();
return nullptr;
}
sal_uInt16 SmViewShell::SetPrinter(SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
{
SfxPrinter *pOld = GetDoc()->GetPrinter();
if ( pOld && pOld->IsPrinting() )
return SFX_PRINTERROR_BUSY;
if ((nDiffFlags & SfxPrinterChangeFlags::PRINTER) == SfxPrinterChangeFlags::PRINTER)
GetDoc()->SetPrinter( pNewPrinter );
if ((nDiffFlags & SfxPrinterChangeFlags::OPTIONS) == SfxPrinterChangeFlags::OPTIONS)
{
SmModule *pp = SM_MOD();
pp->GetConfig()->ItemSetToConfig(pNewPrinter->GetOptions());
}
return 0;
}
bool SmViewShell::HasPrintOptionsPage() const
{
return true;
}
std::unique_ptr<SfxTabPage> SmViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController,
const SfxItemSet &rOptions)
{
return SmPrintOptionsTabPage::Create(pPage, pController, rOptions);
}
SmEditWindow *SmViewShell::GetEditWindow()
{
SmCmdBoxWrapper* pWrapper = static_cast<SmCmdBoxWrapper*>(
GetViewFrame().GetChildWindow(SmCmdBoxWrapper::GetChildWindowId()));
if (pWrapper != nullptr)
{
SmEditWindow& rEditWin = pWrapper->GetEditWindow();
return &rEditWin;
}
return nullptr;
}
void SmViewShell::SetStatusText(const OUString& rText)
{
maStatusText = rText;
GetViewFrame().GetBindings().Invalidate(SID_TEXTSTATUS);
}
void SmViewShell::ShowError(const SmErrorDesc* pErrorDesc)
{
assert(GetDoc());
if (pErrorDesc || nullptr != (pErrorDesc = GetDoc()->GetParser()->GetError()) )
{
SetStatusText( pErrorDesc->m_aText );
if (SmEditWindow* pEdit = GetEditWindow())
pEdit->MarkError( Point( pErrorDesc->m_pNode->GetColumn(),
pErrorDesc->m_pNode->GetRow()));
}
}
void SmViewShell::NextError()
{
assert(GetDoc());
const SmErrorDesc *pErrorDesc = GetDoc()->GetParser()->NextError();
if (pErrorDesc)
ShowError( pErrorDesc );
}
void SmViewShell::PrevError()
{
assert(GetDoc());
const SmErrorDesc *pErrorDesc = GetDoc()->GetParser()->PrevError();
if (pErrorDesc)
ShowError( pErrorDesc );
}
void SmViewShell::Insert( SfxMedium& rMedium )
{
SmDocShell *pDoc = GetDoc();
bool bRet = false;
uno::Reference <embed::XStorage> xStorage = rMedium.GetStorage();
if (xStorage.is() && xStorage->getElementNames().hasElements())
{
if (xStorage->hasByName(u"content.xml"_ustr))
{
// is this a fabulous math package ?
rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(pDoc->GetModel().get()));
SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
bRet = ERRCODE_NONE == aEquation.Import(rMedium);
}
}
if (!bRet)
return;
OUString aText = pDoc->GetText();
if (SmEditWindow *pEditWin = GetEditWindow())
pEditWin->InsertText( aText );
else
{
SAL_WARN( "starmath", "EditWindow missing" );
}
pDoc->Parse();
pDoc->SetModified();
SfxBindings &rBnd = GetViewFrame().GetBindings();
rBnd.Invalidate(SID_GRAPHIC_SM);
rBnd.Invalidate(SID_TEXT);
}
void SmViewShell::InsertFrom(SfxMedium &rMedium)
{
bool bSuccess = false;
SmDocShell* pDoc = GetDoc();
SvStream* pStream = rMedium.GetInStream();
if (pStream)
{
const OUString& rFltName = rMedium.GetFilter()->GetFilterName();
if ( rFltName == MATHML_XML )
{
rtl::Reference<SmModel> xModel(dynamic_cast<SmModel*>(pDoc->GetModel().get()));
SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
bSuccess = ERRCODE_NONE == aEquation.Import(rMedium);
}
}
if (!bSuccess)
return;
OUString aText = pDoc->GetText();
if (SmEditWindow *pEditWin = GetEditWindow())
pEditWin->InsertText(aText);
else
SAL_WARN( "starmath", "EditWindow missing" );
pDoc->Parse();
pDoc->SetModified();
SfxBindings& rBnd = GetViewFrame().GetBindings();
rBnd.Invalidate(SID_GRAPHIC_SM);
rBnd.Invalidate(SID_TEXT);
}
void SmViewShell::Execute(SfxRequest& rReq)
{
SmEditWindow *pWin = GetEditWindow();
switch (rReq.GetSlot())
{
case SID_FORMULACURSOR:
{
SmModule *pp = SM_MOD();
const SfxItemSet *pArgs = rReq.GetArgs();
const SfxPoolItem *pItem;
bool bVal;
if ( pArgs &&
SfxItemState::SET == pArgs->GetItemState( SID_FORMULACURSOR, false, &pItem))
bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
else
bVal = !pp->GetConfig()->IsShowFormulaCursor();
pp->GetConfig()->SetShowFormulaCursor(bVal);
if (!IsInlineEditEnabled())
GetGraphicWidget().ShowCursor(bVal);
break;
}
case SID_DRAW:
if (pWin)
{
GetDoc()->SetText( pWin->GetText() );
SetStatusText(OUString());
ShowError( nullptr );
GetDoc()->Repaint();
}
break;
case SID_ZOOM_OPTIMAL:
mxGraphicWindow->ZoomToFitInWindow();
break;
case SID_ZOOMIN:
mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() + 25);
break;
case SID_ZOOMOUT:
SAL_WARN_IF( mxGraphicWindow->GetZoom() < 25, "starmath", "incorrect sal_uInt16 argument" );
mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() - 25);
break;
case SID_COPYOBJECT:
{
//TODO/LATER: does not work because of UNO Tunneling - will be fixed later
Reference< datatransfer::XTransferable > xTrans( GetDoc()->GetModel(), uno::UNO_QUERY );
if( xTrans.is() )
{
auto pTrans = dynamic_cast<TransferableHelper*>(xTrans.get());
if (pTrans)
{
if (pWin)
pTrans->CopyToClipboard(pWin->GetClipboard());
}
}
}
break;
case SID_PASTEOBJECT:
{
uno::Reference < io::XInputStream > xStrm;
if (pWin)
{
TransferableDataHelper aData(TransferableDataHelper::CreateFromClipboard(pWin->GetClipboard()));
SotClipboardFormatId nId;
if( aData.GetTransferable().is() &&
( aData.HasFormat( nId = SotClipboardFormatId::EMBEDDED_OBJ ) ||
(aData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) &&
aData.HasFormat( nId = SotClipboardFormatId::EMBED_SOURCE ))))
xStrm = aData.GetInputStream(nId, OUString());
}
if (xStrm.is())
{
try
{
uno::Reference < embed::XStorage > xStorage =
::comphelper::OStorageHelper::GetStorageFromInputStream( xStrm, ::comphelper::getProcessComponentContext() );
SfxMedium aMedium( xStorage, OUString() );
Insert( aMedium );
GetDoc()->UpdateText();
}
catch (uno::Exception &)
{
SAL_WARN( "starmath", "SmViewShell::Execute (SID_PASTEOBJECT): failed to get storage from input stream" );
}
}
}
break;
case SID_CUT:
if (IsInlineEditEnabled())
{
GetDoc()->GetCursor().Cut(&GetGraphicWindow());
GetGraphicWidget().GrabFocus();
}
else if (pWin)
pWin->Cut();
break;
case SID_COPY:
if (IsInlineEditEnabled())
{
GetDoc()->GetCursor().Copy(&GetGraphicWindow());
GetGraphicWidget().GrabFocus();
}
else if (pWin)
{
if (pWin->IsAllSelected())
{
GetViewFrame().GetDispatcher()->ExecuteList(
SID_COPYOBJECT, SfxCallMode::RECORD,
{ new SfxVoidItem(SID_COPYOBJECT) });
}
else
pWin->Copy();
}
break;
case SID_PASTE:
{
if (IsInlineEditEnabled())
{
GetDoc()->GetCursor().Paste(&GetGraphicWindow());
GetGraphicWidget().GrabFocus();
break;
}
bool bCallExec = nullptr == pWin;
if( !bCallExec )
{
if (pWin)
{
TransferableDataHelper aDataHelper(
TransferableDataHelper::CreateFromClipboard(
pWin->GetClipboard()));
if( aDataHelper.GetTransferable().is() &&
aDataHelper.HasFormat( SotClipboardFormatId::STRING ))
pWin->Paste();
else
bCallExec = true;
}
}
if( bCallExec )
{
GetViewFrame().GetDispatcher()->ExecuteList(
SID_PASTEOBJECT, SfxCallMode::RECORD,
{ new SfxVoidItem(SID_PASTEOBJECT) });
}
}
break;
case SID_DELETE:
if (IsInlineEditEnabled())
{
if (!GetDoc()->GetCursor().HasSelection())
{
GetDoc()->GetCursor().Move(&GetGraphicWindow().GetGraphicWidget().GetOutputDevice(), MoveRight, false);
if (!GetDoc()->GetCursor().HasComplexSelection())
GetDoc()->GetCursor().Delete();
}
else
GetDoc()->GetCursor().Delete();
GetGraphicWidget().GrabFocus();
}
else if (pWin)
pWin->Delete();
break;
case SID_SELECT:
if (pWin)
pWin->SelectAll();
break;
case SID_INSERTCOMMANDTEXT:
{
const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT);
if (IsInlineEditEnabled())
{
GetDoc()->GetCursor().InsertCommandText(rItem.GetValue());
GetGraphicWidget().GrabFocus();
}
else if (pWin)
{
pWin->InsertText(rItem.GetValue());
}
break;
}
case SID_INSERTSPECIAL:
{
const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_INSERTSPECIAL);
if (IsInlineEditEnabled())
GetDoc()->GetCursor().InsertSpecial(rItem.GetValue());
else if (pWin)
pWin->InsertText(rItem.GetValue());
break;
}
case SID_IMPORT_FORMULA:
{
mpRequest.reset(new SfxRequest( rReq ));
mpDocInserter.reset(new ::sfx2::DocumentInserter(pWin ? pWin->GetFrameWeld() : nullptr,
GetDoc()->GetFactory().GetFactoryName()));
mpDocInserter->StartExecuteModal( LINK( this, SmViewShell, DialogClosedHdl ) );
break;
}
case SID_IMPORT_MATHML_CLIPBOARD:
{
if (pWin)
{
TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(pWin->GetClipboard()));
uno::Reference < io::XInputStream > xStrm;
if ( aDataHelper.GetTransferable().is() )
{
SotClipboardFormatId nId = SotClipboardFormatId::MATHML;
if (aDataHelper.HasFormat(nId))
{
xStrm = aDataHelper.GetInputStream(nId, u""_ustr);
if (xStrm.is())
{
SfxMedium aClipboardMedium;
aClipboardMedium.GetItemSet(); //generate initial itemset, not sure if necessary
std::shared_ptr<const SfxFilter> pMathFilter =
SfxFilter::GetFilterByName(MATHML_XML);
aClipboardMedium.SetFilter(pMathFilter);
aClipboardMedium.setStreamToLoadFrom(xStrm, true /*bIsReadOnly*/);
InsertFrom(aClipboardMedium);
GetDoc()->UpdateText();
}
}
else
{
nId = SotClipboardFormatId::STRING;
if (aDataHelper.HasFormat(nId))
{
// In case of FORMAT_STRING no stream exists, need to generate one
OUString aString;
if (aDataHelper.GetString( nId, aString))
{
// tdf#117091 force xml declaration to exist
if (!aString.startsWith("<?xml"))
aString = "<?xml version=\"1.0\"?>\n" + aString;
SfxMedium aClipboardMedium;
aClipboardMedium.GetItemSet(); //generates initial itemset, not sure if necessary
std::shared_ptr<const SfxFilter> pMathFilter =
SfxFilter::GetFilterByName(MATHML_XML);
aClipboardMedium.SetFilter(pMathFilter);
SvMemoryStream aStrm( const_cast<sal_Unicode *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode), StreamMode::READ);
uno::Reference<io::XInputStream> xStrm2( new ::utl::OInputStreamWrapper(aStrm) );
aClipboardMedium.setStreamToLoadFrom(xStrm2, true /*bIsReadOnly*/);
InsertFrom(aClipboardMedium);
GetDoc()->UpdateText();
}
}
}
}
}
break;
}
case SID_NEXTERR:
NextError();
if (pWin)
pWin->GrabFocus();
break;
case SID_PREVERR:
PrevError();
if (pWin)
pWin->GrabFocus();
break;
case SID_NEXTMARK:
if (pWin)
{
pWin->SelNextMark();
pWin->GrabFocus();
}
break;
case SID_PREVMARK:
if (pWin)
{
pWin->SelPrevMark();
pWin->GrabFocus();
}
break;
case SID_TEXTSTATUS:
{
if (rReq.GetArgs() != nullptr)
{
const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_TEXTSTATUS);
SetStatusText(rItem.GetValue());
}
break;
}
case SID_GETEDITTEXT:
if (pWin && !pWin->GetText().isEmpty())
GetDoc()->SetText( pWin->GetText() );
break;
case SID_ATTR_ZOOM:
{
if ( !GetViewFrame().GetFrame().IsInPlace() )
{
const SfxItemSet *pSet = rReq.GetArgs();
if ( pSet )
{
ZoomByItemSet(pSet);
}
else
{
SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aSet( SmDocShell::GetPool() );
aSet.Put( SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->GetZoom()));
SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
ScopedVclPtr<AbstractSvxZoomDialog> xDlg(pFact->CreateSvxZoomDialog(GetViewFrame().GetWindow().GetFrameWeld(), aSet));
xDlg->SetLimits( MINZOOM, MAXZOOM );
if (xDlg->Execute() != RET_CANCEL)
ZoomByItemSet(xDlg->GetOutputItemSet());
}
}
}
break;
case SID_ATTR_ZOOMSLIDER:
{
const SfxItemSet *pArgs = rReq.GetArgs();
const SfxPoolItem* pItem;
if ( pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) )
{
const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue();
mxGraphicWindow->SetZoom(nCurrentZoom);
}
}
break;
case SID_ELEMENTSDOCKINGWINDOW:
{
// First make sure that the sidebar is visible
GetViewFrame().ShowChildWindow(SID_SIDEBAR);
sfx2::sidebar::Sidebar::TogglePanel(u"MathElementsPanel",
GetViewFrame().GetFrame().GetFrameInterface());
GetViewFrame().GetBindings().Invalidate( SID_ELEMENTSDOCKINGWINDOW );
rReq.Ignore ();
}
break;
case SID_CMDBOXWINDOW:
{
GetViewFrame().ToggleChildWindow(SID_CMDBOXWINDOW);
GetViewFrame().GetBindings().Invalidate(SID_CMDBOXWINDOW);
}
break;
case SID_UNICODE_NOTATION_TOGGLE:
{
EditEngine* pEditEngine = nullptr;
if( pWin )
pEditEngine = pWin->GetEditEngine();
EditView* pEditView = nullptr;
if( pEditEngine )
pEditView = pEditEngine->GetView();
if( pEditView )
{
const OUString sInput = pEditView->GetSurroundingText();
ESelection aSel( pWin->GetSelection() );
if ( aSel.nStartPos > aSel.nEndPos )
aSel.nEndPos = aSel.nStartPos;
//calculate a valid end-position by reading logical characters
sal_Int32 nUtf16Pos=0;
while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) )
{
sInput.iterateCodePoints(&nUtf16Pos);
if( nUtf16Pos > aSel.nEndPos )
aSel.nEndPos = nUtf16Pos;
}
ToggleUnicodeCodepoint aToggle;
while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
--nUtf16Pos;
const OUString sReplacement = aToggle.ReplacementString();
if( !sReplacement.isEmpty() )
{
pEditView->SetSelection( aSel );
pEditEngine->UndoActionStart(EDITUNDO_REPLACEALL);
aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength();
pWin->SetSelection( aSel );
pEditView->InsertText( sReplacement, true );
pEditEngine->UndoActionEnd();
pWin->Flush();
}
}
}
break;
case SID_SYMBOLS_CATALOGUE:
{
// get device used to retrieve the FontList
SmDocShell *pDoc = GetDoc();
OutputDevice *pDev = pDoc->GetPrinter();
if (!pDev || pDev->GetFontFaceCollectionCount() == 0)
pDev = &SM_MOD()->GetDefaultVirtualDev();
SAL_WARN_IF( !pDev, "starmath", "device for font list missing" );
SmModule *pp = SM_MOD();
SmSymbolDialog aDialog(pWin ? pWin->GetFrameWeld() : nullptr, pDev, pp->GetSymbolManager(), *this);
aDialog.run();
}
break;
case SID_CHARMAP:
{
const SfxItemSet* pArgs = rReq.GetArgs();
const SfxStringItem* pItem = nullptr;
if (pArgs && SfxItemState::SET == pArgs->GetItemState(SID_CHARMAP, true, &pItem))
{
if (IsInlineEditEnabled())
GetDoc()->GetCursor().InsertText(pItem->GetValue());
else if (pWin)
pWin->InsertText(pItem->GetValue());
break;
}
SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
SfxAllItemSet aSet(GetViewFrame().GetObjectShell()->GetPool());
aSet.Put(SfxBoolItem(FN_PARAM_1, false));
aSet.Put(SfxStringItem(SID_FONT_NAME,
GetDoc()->GetFormat().GetFont(FNT_VARIABLE).GetFamilyName()));
VclPtr<SfxAbstractDialog> pDialog(
pFact->CreateCharMapDialog(pWin ? pWin->GetFrameWeld() : nullptr, aSet,
GetViewFrame().GetFrame().GetFrameInterface()));
pDialog->StartExecuteAsync(
[pDialog] (sal_Int32 /*nResult*/)->void
{
pDialog->disposeOnce();
}
);
}
break;
case SID_ATTR_PARA_LEFT_TO_RIGHT:
case SID_ATTR_PARA_RIGHT_TO_LEFT:
{
bool bRTL = rReq.GetSlot() == SID_ATTR_PARA_RIGHT_TO_LEFT;
GetDoc()->SetRightToLeft(bRTL);
GetGraphicWindow().GetGraphicWidget().GetOutputDevice().EnableRTL(bRTL);
GetViewFrame().GetBindings().Invalidate(bRTL ? SID_ATTR_PARA_LEFT_TO_RIGHT : SID_ATTR_PARA_RIGHT_TO_LEFT);
}
break;
}
rReq.Done();
}
void SmViewShell::GetState(SfxItemSet &rSet)
{
SfxWhichIter aIter(rSet);
SmEditWindow *pEditWin = GetEditWindow();
for (sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich())
{
switch (nWh)
{
case SID_CUT:
case SID_COPY:
case SID_DELETE:
if (IsInlineEditEnabled())
{
if (!GetDoc()->GetCursor().HasSelection())
rSet.DisableItem(nWh);
}
else if (! pEditWin || ! pEditWin->IsSelected())
rSet.DisableItem(nWh);
break;
case SID_PASTE:
if (pEditWin)
{
TransferableDataHelper aDataHelper(
TransferableDataHelper::CreateFromClipboard(
pEditWin->GetClipboard()) );
mbPasteState = aDataHelper.GetTransferable().is() &&
( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ||
aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) ||
(aDataHelper.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR )
&& aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )));
}
if( !mbPasteState )
rSet.DisableItem( nWh );
break;
case SID_ATTR_ZOOM:
rSet.Put(SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->GetZoom()));
[[fallthrough]];
case SID_ZOOMIN:
case SID_ZOOMOUT:
case SID_ZOOM_OPTIMAL:
if ( GetViewFrame().GetFrame().IsInPlace() )
rSet.DisableItem( nWh );
break;
case SID_ATTR_ZOOMSLIDER :
{
const sal_uInt16 nCurrentZoom = mxGraphicWindow->GetZoom();
SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM );
aZoomSliderItem.AddSnappingPoint( 100 );
rSet.Put( aZoomSliderItem );
}
break;
case SID_NEXTERR:
case SID_PREVERR:
case SID_NEXTMARK:
case SID_PREVMARK:
case SID_DRAW:
case SID_SELECT:
if (! pEditWin || pEditWin->IsEmpty())
rSet.DisableItem(nWh);
break;
case SID_TEXTSTATUS:
{
rSet.Put(SfxStringItem(nWh, maStatusText));
}
break;
case SID_FORMULACURSOR:
{
if (IsInlineEditEnabled())
rSet.DisableItem(nWh);
else
rSet.Put(SfxBoolItem(nWh, SM_MOD()->GetConfig()->IsShowFormulaCursor()));
}
break;
case SID_ELEMENTSDOCKINGWINDOW:
{
const bool bState = sfx2::sidebar::Sidebar::IsPanelVisible(
u"MathElementsPanel", GetViewFrame().GetFrame().GetFrameInterface());
rSet.Put(SfxBoolItem(SID_ELEMENTSDOCKINGWINDOW, bState));
}
break;
case SID_CMDBOXWINDOW:
{
bool bState = false;
auto pCmdWin = GetViewFrame().GetChildWindow(SID_CMDBOXWINDOW);
if (pCmdWin)
bState = pCmdWin->IsVisible();
rSet.Put(SfxBoolItem(SID_CMDBOXWINDOW, bState));
}
break;
case SID_ATTR_PARA_LEFT_TO_RIGHT:
rSet.Put(SfxBoolItem(nWh, !GetDoc()->GetFormat().IsRightToLeft()));
break;
case SID_ATTR_PARA_RIGHT_TO_LEFT:
rSet.Put(SfxBoolItem(nWh, GetDoc()->GetFormat().IsRightToLeft()));
break;
}
}
}
namespace
{
css::uno::Reference<css::ui::XSidebar>
getSidebarFromModel(const css::uno::Reference<css::frame::XModel>& xModel)
{
css::uno::Reference<css::container::XChild> xChild(xModel, css::uno::UNO_QUERY);
if (!xChild.is())
return nullptr;
css::uno::Reference<css::frame::XModel> xParent(xChild->getParent(), css::uno::UNO_QUERY);
if (!xParent.is())
return nullptr;
css::uno::Reference<css::frame::XController2> xController(xParent->getCurrentController(),
css::uno::UNO_QUERY);
if (!xController.is())
return nullptr;
css::uno::Reference<css::ui::XSidebarProvider> xSidebarProvider = xController->getSidebar();
if (!xSidebarProvider.is())
return nullptr;
return xSidebarProvider->getSidebar();
}
class SmController : public SfxBaseController
{
public:
SmController(SfxViewShell& rViewShell)
: SfxBaseController(&rViewShell)
, mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler(
GetContextName, this, vcl::EnumContext::Context::Math))
{
rViewShell.SetContextName(GetContextName());
}
// No need to call mpSelectionChangeHandler->Disconnect() unless SmController implements XSelectionSupplier
// ~SmController() { mpSelectionChangeHandler->Disconnect(); }
// css::frame::XController
void SAL_CALL attachFrame(const css::uno::Reference<css::frame::XFrame>& xFrame) override
{
SfxBaseController::attachFrame(xFrame);
if (comphelper::LibreOfficeKit::isActive())
{
CopyLokViewCallbackFromFrameCreator();
// In lok mode, DocumentHolder::ShowUI is not called on OLE in-place activation,
// because respective code is skipped in OCommonEmbeddedObject::SwitchStateTo_Impl,
// so sidebar controller does not get registered properly; do it here
if (auto xSidebar = getSidebarFromModel(getModel()))
{
auto pSidebar = dynamic_cast<sfx2::sidebar::SidebarController*>(xSidebar.get());
assert(pSidebar);
pSidebar->registerSidebarForFrame(this);
pSidebar->updateModel(getModel());
}
}
// No need to call mpSelectionChangeHandler->Connect() unless SmController implements XSelectionSupplier
mpSelectionChangeHandler->selectionChanged({}); // Installs the correct context
}
virtual void SAL_CALL dispose() override
{
if (comphelper::LibreOfficeKit::isActive())
if (auto pViewShell = GetViewShell_Impl())
pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE,
OString::boolean(false));
SfxBaseController::dispose();
}
private:
static OUString GetContextName() { return u"Math"_ustr; } // Static constant for now
rtl::Reference<svx::sidebar::SelectionChangeHandler> mpSelectionChangeHandler;
};
}
SmViewShell::SmViewShell(SfxViewFrame& rFrame_, SfxViewShell *)
: SfxViewShell(rFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS)
, mxGraphicWindow(VclPtr<SmGraphicWindow>::Create(*this))
, maGraphicController(mxGraphicWindow->GetGraphicWidget(), SID_GRAPHIC_SM, rFrame_.GetBindings())
, mbPasteState(false)
{
SetStatusText(OUString());
SetWindow(mxGraphicWindow.get());
SfxShell::SetName(u"SmView"_ustr);
SfxShell::SetUndoManager( &GetDoc()->GetEditEngine().GetUndoManager() );
SetController(new SmController(*this));
}
SmViewShell::~SmViewShell()
{
//!! this view shell is not active anymore !!
// Thus 'SmGetActiveView' will give a 0 pointer.
// Thus we need to supply this view as argument
if (SmEditWindow *pEditWin = GetEditWindow())
pEditWin->DeleteEditView();
mxGraphicWindow.disposeAndClear();
}
void SmViewShell::Deactivate( bool bIsMDIActivate )
{
if (SmEditWindow *pEdit = GetEditWindow())
pEdit->Flush();
SfxViewShell::Deactivate( bIsMDIActivate );
}
void SmViewShell::Activate( bool bIsMDIActivate )
{
SfxViewShell::Activate( bIsMDIActivate );
if (IsInlineEditEnabled())
{
// In LOK, activate in-place editing
GetGraphicWidget().GrabFocus();
}
else if (SmEditWindow *pEdit = GetEditWindow())
{
//! Since there is no way to be informed if a "drag and drop"
//! event has taken place, we call SetText here in order to
//! synchronize the GraphicWindow display with the text in the
//! EditEngine.
SmDocShell *pDoc = GetDoc();
pDoc->SetText( pDoc->GetEditEngine().GetText() );
if ( bIsMDIActivate )
pEdit->GrabFocus();
}
}
IMPL_LINK( SmViewShell, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
{
assert(_pFileDlg && "SmViewShell::DialogClosedHdl(): no file dialog");
assert(mpDocInserter && "ScDocShell::DialogClosedHdl(): no document inserter");
if ( ERRCODE_NONE == _pFileDlg->GetError() )
{
std::unique_ptr<SfxMedium> pMedium = mpDocInserter->CreateMedium();
if ( pMedium )
{
if ( pMedium->IsStorage() )
Insert( *pMedium );
else
InsertFrom( *pMedium );
pMedium.reset();
SmDocShell* pDoc = GetDoc();
pDoc->UpdateText();
pDoc->ArrangeFormula();
pDoc->Repaint();
// adjust window, repaint, increment ModifyCount,...
GetViewFrame().GetBindings().Invalidate(SID_GRAPHIC_SM);
}
}
mpRequest->SetReturnValue( SfxBoolItem( mpRequest->GetSlot(), true ) );
mpRequest->Done();
}
void SmViewShell::Notify( SfxBroadcaster& , const SfxHint& rHint )
{
switch( rHint.GetId() )
{
case SfxHintId::ModeChanged:
case SfxHintId::DocChanged:
GetViewFrame().GetBindings().InvalidateAll(false);
break;
default:
break;
}
}
bool SmViewShell::IsInlineEditEnabled()
{
return comphelper::LibreOfficeKit::isActive()
|| SM_MOD()->GetConfig()->IsInlineEditEnable();
}
void SmViewShell::StartMainHelp()
{
Help* pHelp = Application::GetHelp();
if (pHelp)
pHelp->Start(HID_SMA_MAIN_HELP, GetViewFrame().GetFrameWeld());
}
void SmViewShell::ZoomByItemSet(const SfxItemSet *pSet)
{
assert(pSet);
const SvxZoomItem &rZoom = pSet->Get(SID_ATTR_ZOOM);
switch( rZoom.GetType() )
{
case SvxZoomType::PERCENT:
mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(rZoom.GetValue ()));
break;
case SvxZoomType::OPTIMAL:
mxGraphicWindow->ZoomToFitInWindow();
break;
case SvxZoomType::PAGEWIDTH:
case SvxZoomType::WHOLEPAGE:
{
const MapMode aMap( SmMapUnit() );
SfxPrinter *pPrinter = GetPrinter( true );
tools::Rectangle OutputRect(Point(), pPrinter->GetOutputSize());
Size OutputSize(pPrinter->LogicToPixel(Size(OutputRect.GetWidth(),
OutputRect.GetHeight()), aMap));
Size GraphicSize(pPrinter->LogicToPixel(GetDoc()->GetSize(), aMap));
if (GraphicSize.Width() <= 0 || GraphicSize.Height() <= 0)
break;
sal_uInt16 nZ = std::min(o3tl::convert(OutputSize.Width(), 100, GraphicSize.Width()),
o3tl::convert(OutputSize.Height(), 100, GraphicSize.Height()));
mxGraphicWindow->SetZoom(nZ);
break;
}
default:
break;
}
}
std::optional<OString> SmViewShell::getLOKPayload(int nType, int nViewId) const
{
switch (nType)
{
case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
{
OString sRectangle;
if (const SmGraphicWidget& widget = GetGraphicWidget(); widget.IsCursorVisible())
{
SmCursor& rCursor = GetDoc()->GetCursor();
OutputDevice& rOutDev = const_cast<SmGraphicWidget&>(widget).GetOutputDevice();
tools::Rectangle aCaret = rCursor.GetCaretRectangle(rOutDev);
Point aFormulaDrawPos = widget.GetFormulaDrawPos();
aCaret.Move(aFormulaDrawPos.X(), aFormulaDrawPos.Y());
LokStarMathHelper helper(SfxViewShell::Current());
tools::Rectangle aBounds = helper.GetBoundingBox();
aCaret.Move(aBounds.Left(), aBounds.Top());
sRectangle = aCaret.toString();
}
return SfxLokHelper::makeVisCursorInvalidation(nViewId, sRectangle, false, {});
}
case LOK_CALLBACK_TEXT_SELECTION:
{
OString sRectangle;
if (const SmGraphicWidget& widget = GetGraphicWidget(); widget.IsCursorVisible())
{
SmCursor& rCursor = GetDoc()->GetCursor();
OutputDevice& rOutDev = const_cast<SmGraphicWidget&>(widget).GetOutputDevice();
tools::Rectangle aSelection = rCursor.GetSelectionRectangle(rOutDev);
if (!aSelection.IsEmpty())
{
Point aFormulaDrawPos = widget.GetFormulaDrawPos();
aSelection.Move(aFormulaDrawPos.X(), aFormulaDrawPos.Y());
LokStarMathHelper helper(SfxViewShell::Current());
tools::Rectangle aBounds = helper.GetBoundingBox();
aSelection.Move(aBounds.Left(), aBounds.Top());
sRectangle = aSelection.toString();
}
}
return sRectangle;
}
case LOK_CALLBACK_TEXT_SELECTION_START:
case LOK_CALLBACK_TEXT_SELECTION_END:
case LOK_CALLBACK_INVALIDATE_VIEW_CURSOR:
case LOK_CALLBACK_TEXT_VIEW_SELECTION:
return {};
}
return SfxViewShell::getLOKPayload(nType, nViewId); // aborts
}
void SmViewShell::SendCaretToLOK() const
{
const int nViewId = sal_Int32(GetViewShellId());
if (const auto& payload = getLOKPayload(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, nViewId))
{
libreOfficeKitViewCallbackWithViewId(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR,
*payload, nViewId);
}
if (const auto& payload = getLOKPayload(LOK_CALLBACK_TEXT_SELECTION, nViewId))
{
libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, *payload);
}
}
void SmViewShell::InvalidateSlots()
{
auto& rBind = GetViewFrame().GetBindings();
rBind.Invalidate(SID_COPY);
rBind.Invalidate(SID_CUT);
rBind.Invalidate(SID_DELETE);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */