diff options
Diffstat (limited to '')
-rw-r--r-- | starmath/source/view.cxx | 2269 |
1 files changed, 2269 insertions, 0 deletions
diff --git a/starmath/source/view.cxx b/starmath/source/view.cxx new file mode 100644 index 000000000..2ddb7a230 --- /dev/null +++ b/starmath/source/view.cxx @@ -0,0 +1,2269 @@ +/* -*- 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/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 <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/msg.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/request.hxx> +#include <sfx2/sfxbasecontroller.hxx> +#include <sfx2/sidebar/SidebarChildWindow.hxx> +#include <sfx2/viewfac.hxx> +#include <svl/eitem.hxx> +#include <svl/itemset.hxx> +#include <svl/poolitem.hxx> +#include <svl/stritem.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 <svx/svxdlg.hxx> +#include <sfx2/zoomitem.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/settings.hxx> +#include <vcl/virdev.hxx> +#include <sal/log.hxx> +#include <tools/svborder.hxx> +#include <o3tl/string_view.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> + +#define MINZOOM sal_uInt16(25) +#define MAXZOOM sal_uInt16(800) + +// 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 visiblility 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->GetDrawingArea()->get_ref_device(); + MapMode aMap( rNewMapMode ); + aMap.SetOrigin( aMap.GetOrigin() + rDevice.PixelToLogic( aPixOffset, aMap ) ); + rDevice.SetMapMode( aMap ); + mxGraphic->Invalidate(); +} + +MapMode SmGraphicWindow::GetGraphicMapMode() const +{ + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + MapMode aMap(rDevice.GetMapMode()); + aMap.SetOrigin( aMap.GetOrigin() - rDevice.PixelToLogic( aPixOffset ) ); + return aMap; +} + +void SmGraphicWindow::SetTotalSize( const Size& rNewSize ) +{ + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + aTotPixSz = rDevice.LogicToPixel(rNewSize); + Resize(); +} + +Size SmGraphicWindow::GetTotalSize() const +{ + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + return rDevice.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 = pDrawingArea->get_ref_device(); + + rDevice.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor); + + const Fraction aFraction(1, 1); + rDevice.SetMapMode(MapMode(MapUnit::Map100thMM, 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(); +} + +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 = GetDrawingArea()->get_ref_device(); + // get click position relative to formula + Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos()); + + const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree(); + if (!pTree) + return true; + + if (SmViewShell::IsInlineEditEnabled()) { + mrViewShell.GetDoc()->GetCursor().MoveTo(&rDevice, aPos, !rMEvt.IsShift()); + 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; + + SmEditWindow* pEdit = mrViewShell.GetEditWindow(); + 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 = GetDrawingArea()->get_ref_device(); + Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos()); + mrViewShell.GetDoc()->GetCursor().MoveTo(&rDevice, aPos, false); + + CaretBlinkStop(); + SetIsCursorVisible(true); + CaretBlinkStart(); + RepaintViewShellDoc(); + } + return true; +} + +void SmGraphicWidget::GetFocus() +{ + if (!SmViewShell::IsInlineEditEnabled()) + return; + if (mrViewShell.GetEditWindow()) + mrViewShell.GetEditWindow()->Flush(); + //Let view shell know what insertions should be done in visual editor + mrViewShell.SetInsertIntoEditWindow(false); + 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() +{ + SmDocShell* pDoc = mrViewShell.GetDoc(); + if (pDoc) + pDoc->Repaint(); +} + +IMPL_LINK_NOARG(SmGraphicWidget, CaretBlinkTimerHdl, Timer *, void) +{ + if (IsCursorVisible()) + SetIsCursorVisible(false); + else + SetIsCursorVisible(true); + + RepaintViewShellDoc(); +} + +void SmGraphicWidget::CaretBlinkInit() +{ + aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWidget, CaretBlinkTimerHdl)); + aCaretBlinkTimer.SetTimeout(Application::GetSettings().GetStyleSettings().GetCursorBlinkTime()); +} + +void SmGraphicWidget::CaretBlinkStart() +{ + if (!SmViewShell::IsInlineEditEnabled()) + return; + if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME) + aCaretBlinkTimer.Start(); +} + +void SmGraphicWidget::CaretBlinkStop() +{ + if (!SmViewShell::IsInlineEditEnabled()) + 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) + { + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + InvertFocusRect(rDevice, aCursorRect); + } + + SetIsCursorVisible(bShow); +} + +void SmGraphicWidget::ShowLine(bool bShow) +{ + if (!SmViewShell::IsInlineEditEnabled()) + return; + + bIsLineVisible = bShow; +} + +void SmGraphicWidget::SetCursor(const SmNode *pNode) +{ + if (SmViewShell::IsInlineEditEnabled()) + return; + + const SmNode *pTree = mrViewShell.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 = mrViewShell.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&) +{ + SmDocShell& rDoc = *mrViewShell.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 (mrViewShell.GetDoc()->HasCursor() && IsLineVisible()) + mrViewShell.GetDoc()->GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible()); + } + else + { + SetIsCursorVisible(false); // (old) cursor must be drawn again + + const SmEditWindow* pEdit = mrViewShell.GetEditWindow(); + if (pEdit) + { // 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() +{ + SmDocShell &rDoc = *mrViewShell.GetDoc(); + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + const Size aTmp(rDevice.PixelToLogic(rDevice.LogicToPixel(rDoc.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 (!SmViewShell::IsInlineEditEnabled()) + return mrViewShell.KeyInput(rKEvt); + + bool bConsumed = true; + + SmCursor& rCursor = mrViewShell.GetDoc()->GetCursor(); + KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction(); + if (eFunc == KeyFuncType::COPY) + rCursor.Copy(); + else if (eFunc == KeyFuncType::CUT) + rCursor.Cut(); + else if (eFunc == KeyFuncType::PASTE) + rCursor.Paste(); + else { + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch(nCode) + { + case KEY_LEFT: + { + rCursor.Move(&rDevice, MoveLeft, !rKEvt.GetKeyCode().IsShift()); + }break; + case KEY_RIGHT: + { + rCursor.Move(&rDevice, MoveRight, !rKEvt.GetKeyCode().IsShift()); + }break; + case KEY_UP: + { + rCursor.Move(&rDevice, MoveUp, !rKEvt.GetKeyCode().IsShift()); + }break; + case KEY_DOWN: + { + rCursor.Move(&rDevice, MoveDown, !rKEvt.GetKeyCode().IsShift()); + }break; + case KEY_RETURN: + { + if(!rKEvt.GetKeyCode().IsShift()) + rCursor.InsertRow(); + }break; + case KEY_DELETE: + { + if(!rCursor.HasSelection()){ + rCursor.Move(&rDevice, MoveRight, false); + if(rCursor.HasComplexSelection()) break; + } + rCursor.Delete(); + }break; + case KEY_BACKSPACE: + { + rCursor.DeletePrev(&rDevice); + }break; + default: + { + if (!CharInput(rKEvt.GetCharCode(), rCursor, rDevice)) + bConsumed = mrViewShell.KeyInput(rKEvt); + } + } + } + CaretBlinkStop(); + CaretBlinkStart(); + SetIsCursorVisible(true); + RepaintViewShellDoc(); + + return bConsumed; +} + +bool SmGraphicWidget::Command(const CommandEvent& rCEvt) +{ + bool bCallBase = true; + if (!mrViewShell.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; + + default: break; + } + } + else + { + switch (rCEvt.GetCommand()) + { + case CommandEventId::ExtTextInput: + if (comphelper::LibreOfficeKit::isActive()) + { + const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData(); + assert(pData); + const OUString& rText = pData->GetText(); + SmCursor& rCursor = mrViewShell.GetDoc()->GetCursor(); + OutputDevice& rDevice = GetDrawingArea()->get_ref_device(); + 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) +{ + nZoom = std::clamp(Factor, MINZOOM, MAXZOOM); + Fraction aFraction(nZoom, 100); + SetGraphicMapMode(MapMode(MapUnit::Map100thMM, 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() +{ + SmViewShell& rViewSh = mxGraphic->GetView(); + SmDocShell& rDoc = *rViewSh.GetDoc(); + + // set defined mapmode before calling 'LogicToPixel' below + SetGraphicMapMode(MapMode(MapUnit::Map100thMM)); + + OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device(); + Size aSize(rDevice.LogicToPixel(rDoc.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 LOK inline edit mode + if (!comphelper::LibreOfficeKit::isActive()) + { + 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(SmElementsDockingWindowWrapper::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(MapUnit::Map100thMM)); + 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 ); +} + +Size SmViewShell::GetTextLineSize(OutputDevice const & rDevice, const OUString& rLine) +{ + Size aSize(rDevice.GetTextWidth(rLine), rDevice.GetTextHeight()); + const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8; + + if (nTabPos) + { + aSize.setWidth( 0 ); + sal_Int32 nPos = 0; + do + { + if (nPos > 0) + aSize.setWidth( ((aSize.Width() / nTabPos) + 1) * nTabPos ); + + const OUString aText = rLine.getToken(0, '\t', nPos); + aSize.AdjustWidth(rDevice.GetTextWidth(aText) ); + } + while (nPos >= 0); + } + + return aSize; +} + +Size SmViewShell::GetTextSize(OutputDevice const & rDevice, std::u16string_view rText, tools::Long MaxWidth) +{ + Size aSize; + Size aTextSize; + if (rText.empty()) + return aTextSize; + + sal_Int32 nPos = 0; + do + { + OUString aLine( o3tl::getToken(rText, 0, '\n', nPos) ); + aLine = aLine.replaceAll("\r", ""); + + aSize = GetTextLineSize(rDevice, aLine); + + if (aSize.Width() > MaxWidth) + { + do + { + OUString aText; + sal_Int32 m = aLine.getLength(); + sal_Int32 nLen = m; + + for (sal_Int32 n = 0; n < nLen; n++) + { + sal_Unicode cLineChar = aLine[n]; + if ((cLineChar == ' ') || (cLineChar == '\t')) + { + aText = aLine.copy(0, n); + if (GetTextLineSize(rDevice, aText).Width() < MaxWidth) + m = n; + else + break; + } + } + + aText = aLine.copy(0, m); + aLine = aLine.replaceAt(0, m, u""); + aSize = GetTextLineSize(rDevice, aText); + aTextSize.AdjustHeight(aSize.Height() ); + aTextSize.setWidth( std::clamp(aSize.Width(), aTextSize.Width(), MaxWidth) ); + + aLine = comphelper::string::stripStart(aLine, ' '); + aLine = comphelper::string::stripStart(aLine, '\t'); + aLine = comphelper::string::stripStart(aLine, ' '); + } + while (!aLine.isEmpty()); + } + else + { + aTextSize.AdjustHeight(aSize.Height() ); + aTextSize.setWidth( std::max(aTextSize.Width(), aSize.Width()) ); + } + } + while (nPos >= 0); + + return aTextSize; +} + +void SmViewShell::DrawTextLine(OutputDevice& rDevice, const Point& rPosition, const OUString& rLine) +{ + Point aPoint(rPosition); + const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8; + + if (nTabPos) + { + sal_Int32 nPos = 0; + do + { + if (nPos > 0) + aPoint.setX( ((aPoint.X() / nTabPos) + 1) * nTabPos ); + + OUString aText = rLine.getToken(0, '\t', nPos); + rDevice.DrawText(aPoint, aText); + aPoint.AdjustX(rDevice.GetTextWidth(aText) ); + } + while ( nPos >= 0 ); + } + else + rDevice.DrawText(aPoint, rLine); +} + +void SmViewShell::DrawText(OutputDevice& rDevice, const Point& rPosition, std::u16string_view rText, sal_uInt16 MaxWidth) +{ + if (rText.empty()) + return; + + Point aPoint(rPosition); + Size aSize; + + sal_Int32 nPos = 0; + do + { + OUString aLine( o3tl::getToken(rText, 0, '\n', nPos) ); + aLine = aLine.replaceAll("\r", ""); + aSize = GetTextLineSize(rDevice, aLine); + if (aSize.Width() > MaxWidth) + { + do + { + OUString aText; + sal_Int32 m = aLine.getLength(); + sal_Int32 nLen = m; + + for (sal_Int32 n = 0; n < nLen; n++) + { + sal_Unicode cLineChar = aLine[n]; + if ((cLineChar == ' ') || (cLineChar == '\t')) + { + aText = aLine.copy(0, n); + if (GetTextLineSize(rDevice, aText).Width() < MaxWidth) + m = n; + else + break; + } + } + aText = aLine.copy(0, m); + aLine = aLine.replaceAt(0, m, u""); + + DrawTextLine(rDevice, aPoint, aText); + aPoint.AdjustY(aSize.Height() ); + + aLine = comphelper::string::stripStart(aLine, ' '); + aLine = comphelper::string::stripStart(aLine, '\t'); + aLine = comphelper::string::stripStart(aLine, ' '); + } + while (GetTextLineSize(rDevice, aLine).Width() > MaxWidth); + + // print the remaining text + if (!aLine.isEmpty()) + { + DrawTextLine(rDevice, aPoint, aLine); + aPoint.AdjustY(aSize.Height() ); + } + } + else + { + DrawTextLine(rDevice, aPoint, aLine); + aPoint.AdjustY(aSize.Height() ); + } + } + while ( nPos >= 0 ); +} + +void SmViewShell::Impl_Print(OutputDevice &rOutDev, const SmPrintUIOptions &rPrintUIOptions, tools::Rectangle aOutRect ) +{ + const bool bIsPrintTitle = rPrintUIOptions.getBoolValue( PRTUIOPT_TITLE_ROW, true ); + const bool bIsPrintFrame = rPrintUIOptions.getBoolValue( PRTUIOPT_BORDER, true ); + const bool bIsPrintFormulaText = rPrintUIOptions.getBoolValue( PRTUIOPT_FORMULA_TEXT, true ); + SmPrintSize ePrintSize( static_cast< SmPrintSize >( rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_FORMAT, PRINT_SIZE_NORMAL ) )); + const sal_uInt16 nZoomFactor = static_cast< sal_uInt16 >(rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_SCALE, 100 )); + + rOutDev.Push(); + rOutDev.SetLineColor( COL_BLACK ); + + // output text on top + if (bIsPrintTitle) + { + Size aSize600 (0, 600); + Size aSize650 (0, 650); + vcl::Font aFont(FAMILY_DONTKNOW, aSize600); + + aFont.SetAlignment(ALIGN_TOP); + aFont.SetWeight(WEIGHT_BOLD); + aFont.SetFontSize(aSize650); + aFont.SetColor( COL_BLACK ); + rOutDev.SetFont(aFont); + + Size aTitleSize (GetTextSize(rOutDev, GetDoc()->GetTitle(), aOutRect.GetWidth() - 200)); + + aFont.SetWeight(WEIGHT_NORMAL); + aFont.SetFontSize(aSize600); + rOutDev.SetFont(aFont); + + Size aDescSize (GetTextSize(rOutDev, GetDoc()->GetComment(), aOutRect.GetWidth() - 200)); + + if (bIsPrintFrame) + rOutDev.DrawRect(tools::Rectangle(aOutRect.TopLeft(), + Size(aOutRect.GetWidth(), 100 + aTitleSize.Height() + 200 + aDescSize.Height() + 100))); + aOutRect.AdjustTop(200 ); + + // output title + aFont.SetWeight(WEIGHT_BOLD); + aFont.SetFontSize(aSize650); + rOutDev.SetFont(aFont); + Point aPoint(aOutRect.Left() + (aOutRect.GetWidth() - aTitleSize.Width()) / 2, + aOutRect.Top()); + DrawText(rOutDev, aPoint, GetDoc()->GetTitle(), + sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200)); + aOutRect.AdjustTop(aTitleSize.Height() + 200 ); + + // output description + aFont.SetWeight(WEIGHT_NORMAL); + aFont.SetFontSize(aSize600); + rOutDev.SetFont(aFont); + aPoint.setX( aOutRect.Left() + (aOutRect.GetWidth() - aDescSize.Width()) / 2 ); + aPoint.setY( aOutRect.Top() ); + DrawText(rOutDev, aPoint, GetDoc()->GetComment(), + sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200)); + aOutRect.AdjustTop(aDescSize.Height() + 300 ); + } + + // output text on bottom + if (bIsPrintFormulaText) + { + vcl::Font aFont(FAMILY_DONTKNOW, Size(0, 600)); + aFont.SetAlignment(ALIGN_TOP); + aFont.SetColor( COL_BLACK ); + + // get size + rOutDev.SetFont(aFont); + + Size aSize (GetTextSize(rOutDev, GetDoc()->GetText(), aOutRect.GetWidth() - 200)); + + aOutRect.AdjustBottom( -(aSize.Height() + 600) ); + + if (bIsPrintFrame) + rOutDev.DrawRect(tools::Rectangle(aOutRect.BottomLeft(), + Size(aOutRect.GetWidth(), 200 + aSize.Height() + 200))); + + Point aPoint (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2, + aOutRect.Bottom() + 300); + DrawText(rOutDev, aPoint, GetDoc()->GetText(), + sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200)); + aOutRect.AdjustBottom( -200 ); + } + + if (bIsPrintFrame) + rOutDev.DrawRect(aOutRect); + + aOutRect.AdjustTop(100 ); + aOutRect.AdjustLeft(100 ); + aOutRect.AdjustBottom( -100 ); + aOutRect.AdjustRight( -100 ); + + Size aSize (GetDoc()->GetSize()); + + MapMode OutputMapMode; + // PDF export should always use PRINT_SIZE_NORMAL ... + if (!rPrintUIOptions.getBoolValue( "IsPrinter" ) ) + ePrintSize = PRINT_SIZE_NORMAL; + switch (ePrintSize) + { + case PRINT_SIZE_NORMAL: + OutputMapMode = MapMode(MapUnit::Map100thMM); + break; + + case PRINT_SIZE_SCALED: + if (!aSize.IsEmpty()) + { + Size OutputSize (rOutDev.LogicToPixel(Size(aOutRect.GetWidth(), + aOutRect.GetHeight()), MapMode(MapUnit::Map100thMM))); + Size GraphicSize (rOutDev.LogicToPixel(aSize, MapMode(MapUnit::Map100thMM))); + sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(tools::Long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())), + tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height())))); + nZ -= 10; + Fraction aFraction (std::clamp(nZ, MINZOOM, sal_uInt16(100))); + + OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction); + } + else + OutputMapMode = MapMode(MapUnit::Map100thMM); + break; + + case PRINT_SIZE_ZOOMED: + { + Fraction aFraction( nZoomFactor, 100 ); + + OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction); + break; + } + } + + aSize = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aSize, OutputMapMode), + MapMode(MapUnit::Map100thMM)); + + Point aPos (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2, + aOutRect.Top() + (aOutRect.GetHeight() - aSize.Height()) / 2); + + aPos = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aPos, MapMode(MapUnit::Map100thMM)), + OutputMapMode); + aOutRect = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aOutRect, MapMode(MapUnit::Map100thMM)), + OutputMapMode); + + rOutDev.SetMapMode(OutputMapMode); + rOutDev.SetClipRegion(vcl::Region(aOutRect)); + GetDoc()->DrawFormula(rOutDev, aPos); + rOutDev.SetClipRegion(); + + rOutDev.Pop(); +} + +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; +} + +SmElementsDockingWindow* SmViewShell::GetDockingWindow() +{ + auto eldockwinwrap = GetViewFrame()->GetChildWindow(SmElementsDockingWindowWrapper::GetChildWindowId()); + if(eldockwinwrap) + return dynamic_cast<SmElementsDockingWindow*>(eldockwinwrap->GetWindow()); + else + 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 ); + GetEditWindow()->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 ? + Reference<css::frame::XModel> xModel(pDoc->GetModel()); + SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !! + bRet = ERRCODE_NONE == aEquation.Import(rMedium); + } + } + + if (!bRet) + return; + + OUString aText = pDoc->GetText(); + SmEditWindow *pEditWin = GetEditWindow(); + if (pEditWin) + 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 ) + { + Reference<css::frame::XModel> xModel(pDoc->GetModel()); + SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !! + bSuccess = ERRCODE_NONE == aEquation.Import(rMedium); + } + } + + if (!bSuccess) + return; + + OUString aText = pDoc->GetText(); + SmEditWindow *pEditWin = GetEditWindow(); + if (pEditWin) + 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 = comphelper::getFromUnoTunnel<TransferableHelper>(xTrans); + if (pTrans) + { + SmEditWindow *pEditWin = GetEditWindow(); + pTrans->CopyToClipboard(pEditWin->GetClipboard()); + } + } + } + break; + + case SID_PASTEOBJECT: + { + SmEditWindow *pEditWin = GetEditWindow(); + TransferableDataHelper aData(TransferableDataHelper::CreateFromClipboard(pEditWin->GetClipboard())); + uno::Reference < io::XInputStream > xStrm; + 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 (pWin) + pWin->Cut(); + break; + + case SID_COPY: + if (pWin) + { + if (pWin->IsAllSelected()) + { + GetViewFrame()->GetDispatcher()->ExecuteList( + SID_COPYOBJECT, SfxCallMode::RECORD, + { new SfxVoidItem(SID_COPYOBJECT) }); + } + else + pWin->Copy(); + } + break; + + case SID_PASTE: + { + bool bCallExec = nullptr == pWin; + if( !bCallExec ) + { + SmEditWindow *pEditWin = GetEditWindow(); + TransferableDataHelper aDataHelper( + TransferableDataHelper::CreateFromClipboard( + pEditWin->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 (pWin) + pWin->Delete(); + break; + + case SID_SELECT: + if (pWin) + pWin->SelectAll(); + break; + + case SID_INSERTCOMMANDTEXT: + { + const SfxStringItem& rItem = static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT)); + + if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled())) + { + pWin->InsertText(rItem.GetValue()); + } + if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow)) + { + GetDoc()->GetCursor().InsertCommandText(rItem.GetValue()); + GetGraphicWidget().GrabFocus(); + } + break; + + } + + case SID_INSERTSPECIAL: + { + const SfxStringItem& rItem = + static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTSPECIAL)); + + if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled())) + pWin->InsertText(rItem.GetValue()); + if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow)) + GetDoc()->GetCursor().InsertSpecial(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: + { + SmEditWindow *pEditWin = GetEditWindow(); + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(pEditWin->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 = + static_cast<const SfxStringItem&>(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: + { + GetViewFrame()->ToggleChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() ); + GetViewFrame()->GetBindings().Invalidate( SID_ELEMENTSDOCKINGWINDOW ); + + rReq.Ignore (); + } + 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; + } + 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 (! 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: + { + SmModule *pp = SM_MOD(); + rSet.Put(SfxBoolItem(nWh, pp->GetConfig()->IsShowFormulaCursor())); + } + break; + case SID_ELEMENTSDOCKINGWINDOW: + { + bool bState = false; + SfxChildWindow *pChildWnd = GetViewFrame()-> + GetChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() ); + if (pChildWnd && pChildWnd->GetWindow()->IsVisible()) + bState = true; + rSet.Put(SfxBoolItem(SID_ELEMENTSDOCKINGWINDOW, bState)); + } + break; + } + } +} + +namespace +{ +class SmController : public SfxBaseController +{ +public: + SmController(SfxViewShell& rViewShell) + : SfxBaseController(&rViewShell) + , mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler( + GetContextName, this, vcl::EnumContext::Context::Math)) + { + mpSelectionChangeHandler->Connect(); + rViewShell.SetContextName(GetContextName()); + } + ~SmController() { mpSelectionChangeHandler->Disconnect(); } + + // css::frame::XController + void SAL_CALL attachFrame(const css::uno::Reference<css::frame::XFrame>& xFrame) override + { + SfxBaseController::attachFrame(xFrame); + + mpSelectionChangeHandler->selectionChanged({}); // Installs the correct context + } + +private: + static OUString GetContextName() { return "Math"; } // Static constant for now + + rtl::Reference<svx::sidebar::SelectionChangeHandler> mpSelectionChangeHandler; +}; +} + +SmViewShell::SmViewShell(SfxViewFrame *pFrame_, SfxViewShell *) + : SfxViewShell(pFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS) + , mxGraphicWindow(VclPtr<SmGraphicWindow>::Create(*this)) + , maGraphicController(mxGraphicWindow->GetGraphicWidget(), SID_GRAPHIC_SM, pFrame_->GetBindings()) + , mbPasteState(false) + , mbInsertIntoEditWindow(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 + SmEditWindow *pEditWin = GetEditWindow(); + if (pEditWin) + pEditWin->DeleteEditView(); + mxGraphicWindow.disposeAndClear(); +} + +void SmViewShell::Deactivate( bool bIsMDIActivate ) +{ + SmEditWindow *pEdit = GetEditWindow(); + if ( pEdit ) + pEdit->Flush(); + + SfxViewShell::Deactivate( bIsMDIActivate ); +} + +void SmViewShell::Activate( bool bIsMDIActivate ) +{ + SfxViewShell::Activate( bIsMDIActivate ); + + if (comphelper::LibreOfficeKit::isActive()) + { + // 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() + || officecfg::Office::Common::Misc::ExperimentalMode::get(); +} + +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( MapUnit::Map100thMM ); + 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)); + sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(tools::Long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())), + tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height())))); + mxGraphicWindow->SetZoom(nZ); + break; + } + default: + break; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |