diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /starmath/source/view.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'starmath/source/view.cxx')
-rw-r--r-- | starmath/source/view.cxx | 2239 |
1 files changed, 2239 insertions, 0 deletions
diff --git a/starmath/source/view.cxx b/starmath/source/view.cxx new file mode 100644 index 0000000000..8f88b5ced6 --- /dev/null +++ b/starmath/source/view.cxx @@ -0,0 +1,2239 @@ +/* -*- 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; + +SmGraphicWindow::SmGraphicWindow(SmViewShell& rShell) + : InterimItemWindow(&rShell.GetViewFrame().GetWindow(), "modules/smath/ui/mathwindow.ui", "MathWindow") + , 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("scrolledwindow", true)) + , mxGraphic(new SmGraphicWidget(rShell, *this)) + , mxGraphicWin(new weld::CustomWeld(*m_xBuilder, "mathview", *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, "EditWindow", "modules/smath/ui/editwindow.ui") + , m_xEdit(new SmEditWindow(*this, *m_xBuilder)) + , aController(*m_xEdit, SID_TEXT, *pBindings_) + , bExiting(false) + , aInitialFocusTimer("SmCmdBoxWindow aInitialFocusTimer") +{ + set_id("math_edit"); + + 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("edit", 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("content.xml")) + { + // 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, ""); + 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())); + ScopedVclPtr<SfxAbstractDialog> pDialog( + pFact->CreateCharMapDialog(pWin ? pWin->GetFrameWeld() : nullptr, aSet, + GetViewFrame().GetFrame().GetFrameInterface())); + pDialog->Execute(); + } + 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 "Math"; } // 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("SmView"); + 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: */ |