/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cfgitem.hxx" #include #include #include #include #include #include "mathmlimport.hxx" #include #include "accessibility.hxx" #include #include #include #include #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 4 #define CMD_BOX_PADDING_TOP 10 #define ShellClass_SmViewShell #include using namespace css; using namespace css::accessibility; using namespace css::uno; SmGraphicWindow::SmGraphicWindow(SmViewShell* pShell) : ScrollableWindow(&pShell->GetViewFrame()->GetWindow()) , pViewShell(pShell) , nZoom(100) { assert(pViewShell); // docking windows are usually hidden (often already done in the // resource) and will be shown by the sfx framework. Hide(); const Fraction aFraction(1, 1); SetMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction)); SetTotalSize(); SetHelpId(HID_SMA_WIN_DOCUMENT); ShowLine(false); CaretBlinkInit(); } SmGraphicWindow::~SmGraphicWindow() { disposeOnce(); } void SmGraphicWindow::dispose() { if (mxAccessible.is()) mxAccessible->ClearWin(); // make Accessible nonfunctional mxAccessible.clear(); CaretBlinkStop(); ScrollableWindow::dispose(); } void SmGraphicWindow::StateChanged(StateChangedType eType) { if (eType == StateChangedType::InitShow) Show(); ScrollableWindow::StateChanged(eType); } void SmGraphicWindow::MouseButtonDown(const MouseEvent& rMEvt) { ScrollableWindow::MouseButtonDown(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; // get click position relative to formula Point aPos (PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos()); const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(); if (!pTree) return; if (IsInlineEditEnabled()) { pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, !rMEvt.IsShift()); return; } 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; SmEditWindow *pEdit = pViewShell->GetEditWindow(); if (!pEdit) return; const SmToken aToken (pNode->GetToken()); // set selection to the beginning of the token ESelection aSel (aToken.nRow - 1, aToken.nCol - 1); if (rMEvt.GetClicks() != 1 || aToken.eType == TPLACE) aSel.nEndPos = aSel.nEndPos + sal::static_int_cast< sal_uInt16 >(aToken.aText.getLength()); pEdit->SetSelection(aSel); SetCursor(pNode); // allow for immediate editing and //! implicitly synchronize the cursor position mark in this window pEdit->GrabFocus(); } void SmGraphicWindow::MouseMove(const MouseEvent &rMEvt) { ScrollableWindow::MouseMove(rMEvt); if (rMEvt.IsLeft() && IsInlineEditEnabled()) { Point aPos(PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos()); pViewShell->GetDoc()->GetCursor().MoveTo(this, aPos, false); CaretBlinkStop(); SetIsCursorVisible(true); CaretBlinkStart(); RepaintViewShellDoc(); } } bool SmGraphicWindow::IsInlineEditEnabled() const { return pViewShell->IsInlineEditEnabled(); } void SmGraphicWindow::GetFocus() { if (!IsInlineEditEnabled()) return; if (pViewShell->GetEditWindow()) pViewShell->GetEditWindow()->Flush(); //Let view shell know what insertions should be done in visual editor pViewShell->SetInsertIntoEditWindow(false); SetIsCursorVisible(true); ShowLine(true); CaretBlinkStart(); RepaintViewShellDoc(); } void SmGraphicWindow::LoseFocus() { ScrollableWindow::LoseFocus(); if (mxAccessible.is()) { uno::Any aOldValue, aNewValue; aOldValue <<= AccessibleStateType::FOCUSED; // aNewValue remains empty mxAccessible->LaunchEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); } if (!IsInlineEditEnabled()) return; SetIsCursorVisible(false); ShowLine(false); CaretBlinkStop(); RepaintViewShellDoc(); } void SmGraphicWindow::RepaintViewShellDoc() { SmDocShell* pDoc = pViewShell->GetDoc(); if (pDoc) pDoc->Repaint(); } IMPL_LINK_NOARG(SmGraphicWindow, CaretBlinkTimerHdl, Timer *, void) { if (IsCursorVisible()) SetIsCursorVisible(false); else SetIsCursorVisible(true); RepaintViewShellDoc(); } void SmGraphicWindow::CaretBlinkInit() { aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWindow, CaretBlinkTimerHdl)); aCaretBlinkTimer.SetTimeout( ScrollableWindow::GetSettings().GetStyleSettings().GetCursorBlinkTime() ); } void SmGraphicWindow::CaretBlinkStart() { if (!IsInlineEditEnabled()) return; if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME) aCaretBlinkTimer.Start(); } void SmGraphicWindow::CaretBlinkStop() { if (!IsInlineEditEnabled()) return; aCaretBlinkTimer.Stop(); } void SmGraphicWindow::ShowCursor(bool bShow) // shows or hides the formula-cursor depending on 'bShow' is true or not { if (IsInlineEditEnabled()) return; bool bInvert = bShow != IsCursorVisible(); if (bInvert) InvertTracking(aCursorRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow); SetIsCursorVisible(bShow); } void SmGraphicWindow::ShowLine(bool bShow) { if (!IsInlineEditEnabled()) return; bIsLineVisible = bShow; } void SmGraphicWindow::SetCursor(const SmNode *pNode) { if (IsInlineEditEnabled()) return; const SmNode *pTree = pViewShell->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 SmGraphicWindow::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 (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 * SmGraphicWindow::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 (IsInlineEditEnabled()) return nullptr; // find visible node with token at nRow, nCol const SmNode *pTree = pViewShell->GetDoc()->GetFormulaTree(), *pNode = nullptr; if (pTree) pNode = pTree->FindTokenAt(nRow, nCol); if (pNode) SetCursor(pNode); else ShowCursor(false); return pNode; } void SmGraphicWindow::ApplySettings(vcl::RenderContext& rRenderContext) { rRenderContext.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor); } void SmGraphicWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&) { SmDocShell& rDoc = *pViewShell->GetDoc(); Point aPoint; rDoc.DrawFormula(rRenderContext, aPoint, true); //! modifies aPoint to be the topleft //! corner of the formula aFormulaDrawPos = aPoint; if (IsInlineEditEnabled()) { //Draw cursor if any... if (pViewShell->GetDoc()->HasCursor() && IsLineVisible()) pViewShell->GetDoc()->GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible()); } else { SetIsCursorVisible(false); // (old) cursor must be drawn again const SmEditWindow* pEdit = pViewShell->GetEditWindow(); if (pEdit) { // get new position for formula-cursor (for possible altered formula) sal_Int32 nRow; sal_uInt16 nCol; SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol); nRow++; nCol++; const SmNode *pFound = SetCursorPos(static_cast(nRow), nCol); SmModule *pp = SM_MOD(); if (pFound && pp->GetConfig()->IsShowFormulaCursor()) ShowCursor(true); } } } void SmGraphicWindow::SetTotalSize () { SmDocShell &rDoc = *pViewShell->GetDoc(); const Size aTmp( PixelToLogic( LogicToPixel( rDoc.GetSize() ))); if ( aTmp != ScrollableWindow::GetTotalSize() ) ScrollableWindow::SetTotalSize( aTmp ); } void SmGraphicWindow::KeyInput(const KeyEvent& rKEvt) { if (!IsInlineEditEnabled()) { if (! (GetView() && GetView()->KeyInput(rKEvt)) ) ScrollableWindow::KeyInput(rKEvt); return; } SmCursor& rCursor = pViewShell->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 { sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); switch(nCode) { case KEY_LEFT: { rCursor.Move(this, MoveLeft, !rKEvt.GetKeyCode().IsShift()); }break; case KEY_RIGHT: { rCursor.Move(this, MoveRight, !rKEvt.GetKeyCode().IsShift()); }break; case KEY_UP: { rCursor.Move(this, MoveUp, !rKEvt.GetKeyCode().IsShift()); }break; case KEY_DOWN: { rCursor.Move(this, MoveDown, !rKEvt.GetKeyCode().IsShift()); }break; case KEY_RETURN: { if(!rKEvt.GetKeyCode().IsShift()) rCursor.InsertRow(); }break; case KEY_DELETE: { if(!rCursor.HasSelection()){ rCursor.Move(this, MoveRight, false); if(rCursor.HasComplexSelection()) break; } rCursor.Delete(); }break; case KEY_BACKSPACE: { rCursor.DeletePrev(this); }break; case KEY_ADD: rCursor.InsertElement(PlusElement); break; case KEY_SUBTRACT: if(rKEvt.GetKeyCode().IsShift()) rCursor.InsertSubSup(RSUB); else rCursor.InsertElement(MinusElement); break; case KEY_MULTIPLY: rCursor.InsertElement(CDotElement); break; case KEY_DIVIDE: rCursor.InsertFraction(); break; case KEY_LESS: rCursor.InsertElement(LessThanElement); break; case KEY_GREATER: rCursor.InsertElement(GreaterThanElement); break; case KEY_EQUAL: rCursor.InsertElement(EqualElement); break; default: { sal_Unicode code = rKEvt.GetCharCode(); SmBraceNode* pBraceNode = nullptr; if(code == ' ') { rCursor.InsertElement(BlankElement); }else if(code == '^') { rCursor.InsertSubSup(RSUP); }else if(code == '(') { rCursor.InsertBrackets(SmBracketType::Round); }else if(code == '[') { rCursor.InsertBrackets(SmBracketType::Square); }else if(code == '{') { rCursor.InsertBrackets(SmBracketType::Curly); }else if(code == '!') { rCursor.InsertElement(FactorialElement); }else if(code == '%') { rCursor.InsertElement(PercentElement); }else if(code == ')' && rCursor.IsAtTailOfBracket(SmBracketType::Round, &pBraceNode)) { rCursor.MoveAfterBracket(pBraceNode); }else if(code == ']' && rCursor.IsAtTailOfBracket(SmBracketType::Square, &pBraceNode)) { rCursor.MoveAfterBracket(pBraceNode); }else if(code == '}' && rCursor.IsAtTailOfBracket(SmBracketType::Curly, &pBraceNode)) { rCursor.MoveAfterBracket(pBraceNode); }else{ if(code != 0){ rCursor.InsertText(OUString(code)); }else if (! (GetView() && GetView()->KeyInput(rKEvt)) ) ScrollableWindow::KeyInput(rKEvt); } } } } CaretBlinkStop(); CaretBlinkStart(); SetIsCursorVisible(true); RepaintViewShellDoc(); } void SmGraphicWindow::Command(const CommandEvent& rCEvt) { bool bCallBase = true; if ( !pViewShell->GetViewFrame()->GetFrame().IsInPlace() ) { switch ( rCEvt.GetCommand() ) { case CommandEventId::ContextMenu: { GetParent()->ToTop(); Point aPos(5, 5); if (rCEvt.IsMouseEvent()) aPos = rCEvt.GetMousePosPixel(); // added for replaceability of context menus SfxDispatcher::ExecutePopup( this, &aPos ); bCallBase = false; } break; case CommandEventId::Wheel: { const CommandWheelData* pWData = rCEvt.GetWheelData(); if ( pWData && CommandWheelMode::ZOOM == pWData->GetMode() ) { sal_uInt16 nTmpZoom = GetZoom(); if( 0 > pWData->GetDelta() ) nTmpZoom -= 10; else nTmpZoom += 10; SetZoom( nTmpZoom ); bCallBase = false; } } break; default: break; } } if ( bCallBase ) ScrollableWindow::Command (rCEvt); } void SmGraphicWindow::SetZoom(sal_uInt16 Factor) { nZoom = std::min(std::max(Factor, MINZOOM), MAXZOOM); Fraction aFraction (nZoom, 100); SetMapMode( MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction) ); SetTotalSize(); SmViewShell *pViewSh = GetView(); if (pViewSh) { pViewSh->GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOM); pViewSh->GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOMSLIDER); } Invalidate(); } void SmGraphicWindow::ZoomToFitInWindow() { SmDocShell &rDoc = *pViewShell->GetDoc(); // set defined mapmode before calling 'LogicToPixel' below SetMapMode(MapMode(MapUnit::Map100thMM)); Size aSize (LogicToPixel(rDoc.GetSize())); Size aWindowSize (GetSizePixel()); if (!aSize.IsEmpty()) { 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 > SmGraphicWindow::CreateAccessible() { if (!mxAccessible.is()) { mxAccessible = new SmGraphicAccessible( this ); } return mxAccessible.get(); } /**************************************************************************/ SmGraphicController::SmGraphicController(SmGraphicWindow &rSmGraphic, sal_uInt16 nId_, SfxBindings &rBindings) : SfxControllerItem(nId_, rBindings), rGraphic(rSmGraphic) { } void SmGraphicController::StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState) { rGraphic.SetTotalSize(); rGraphic.Invalidate(); SfxControllerItem::StateChanged (nSID, eState, pState); } /**************************************************************************/ SmEditController::SmEditController(SmEditWindow &rSmEdit, sal_uInt16 nId_, SfxBindings &rBindings) : SfxControllerItem(nId_, rBindings), rEdit(rSmEdit) { } void SmEditController::StateChanged(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState) { const SfxStringItem *pItem = dynamic_cast( pState); if ((pItem != nullptr) && (rEdit.GetText() != pItem->GetValue())) rEdit.SetText(pItem->GetValue()); SfxControllerItem::StateChanged (nSID, eState, pState); } /**************************************************************************/ SmCmdBoxWindow::SmCmdBoxWindow(SfxBindings *pBindings_, SfxChildWindow *pChildWindow, vcl::Window *pParent) : SfxDockingWindow(pBindings_, pChildWindow, pParent, WB_MOVEABLE|WB_CLOSEABLE|WB_SIZEABLE|WB_DOCKABLE), aEdit (VclPtr::Create(*this)), aController (*aEdit, SID_TEXT, *pBindings_), bExiting (false) { SetHelpId( HID_SMA_COMMAND_WIN ); SetSizePixel(LogicToPixel(Size(292 , 94), MapMode(MapUnit::MapAppFont))); SetText(SmResId(STR_CMDBOXWINDOW)); Hide(); aInitialFocusTimer.SetInvokeHandler(LINK(this, SmCmdBoxWindow, InitialFocusTimerHdl)); aInitialFocusTimer.SetTimeout(100); } SmCmdBoxWindow::~SmCmdBoxWindow () { disposeOnce(); } void SmCmdBoxWindow::dispose() { aInitialFocusTimer.Stop(); bExiting = true; aController.dispose(); aEdit.disposeAndClear(); SfxDockingWindow::dispose(); } SmViewShell * SmCmdBoxWindow::GetView() { SfxDispatcher *pDispatcher = GetBindings().GetDispatcher(); SfxViewShell *pView = pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr; return dynamic_cast( pView); } void SmCmdBoxWindow::Resize() { tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel()); aRect.AdjustLeft(CMD_BOX_PADDING ); aRect.AdjustTop(CMD_BOX_PADDING_TOP ); aRect.AdjustRight( -(CMD_BOX_PADDING) ); aRect.AdjustBottom( -(CMD_BOX_PADDING) ); DecorationView aView(this); aRect = aView.DrawFrame(aRect, DrawFrameStyle::In, DrawFrameFlags::NoDraw); aEdit->SetPosSizePixel(aRect.TopLeft(), aRect.GetSize()); SfxDockingWindow::Resize(); Invalidate(); } void SmCmdBoxWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) { tools::Rectangle aRect(Point(0, 0), GetOutputSizePixel()); aRect.AdjustLeft(CMD_BOX_PADDING ); aRect.AdjustTop(CMD_BOX_PADDING_TOP ); aRect.AdjustRight( -(CMD_BOX_PADDING) ); aRect.AdjustBottom( -(CMD_BOX_PADDING) ); aEdit->SetPosSizePixel(aRect.TopLeft(), aRect.GetSize()); DecorationView aView(&rRenderContext); aView.DrawFrame( aRect, DrawFrameStyle::In ); } 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() ); aEdit->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 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) aEdit->GrabFocus(); } SFX_IMPL_DOCKINGWINDOW_WITHID(SmCmdBoxWrapper, SID_CMDBOXWINDOW); SmCmdBoxWrapper::SmCmdBoxWrapper(vcl::Window *pParentWindow, sal_uInt16 nId, SfxBindings *pBindings, SfxChildWinInfo *pInfo) : SfxChildWindow(pParentWindow, nId) { SetWindow(VclPtr::Create(pBindings, this, pParentWindow)); // make window docked to the bottom initially (after first start) SetAlignment(SfxChildAlignment::BOTTOM); static_cast(GetWindow())->Initialize(pInfo); } struct SmViewShell_Impl { std::unique_ptr pDocInserter; std::unique_ptr pRequest; SvtMiscOptions aOpts; }; 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()); } 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)); SfxViewShell::SetZoomFactor( Fraction( aProvidedSize.Width(), aObjSize.Width() ), Fraction( aProvidedSize.Height(), aObjSize.Height() ) ); } SetBorderPixel( SvBorder() ); GetGraphicWindow().SetPosSizePixel(rOfs, rSize); GetGraphicWindow().SetTotalSize(); } void SmViewShell::OuterResizePixel(const Point &rOfs, const Size &rSize) { SmGraphicWindow &rWin = GetGraphicWindow(); rWin.SetPosSizePixel(rOfs, rSize); if (GetDoc()->IsPreview()) rWin.ZoomToFitInWindow(); rWin.PaintImmediately(); } void SmViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const { rRect.SetSize( GetGraphicWindow().GetSizePixel() ); } void SmViewShell::SetZoomFactor( const Fraction &rX, const Fraction &rY ) { const Fraction &rFrac = std::min(rX, rY); GetGraphicWindow().SetZoom(sal::static_int_cast(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 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, const OUString& rText, long MaxWidth) { Size aSize; Size aTextSize; if (rText.isEmpty()) return aTextSize; sal_Int32 nPos = 0; do { OUString aLine = rText.getToken(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, ""); aSize = GetTextLineSize(rDevice, aText); aTextSize.AdjustHeight(aSize.Height() ); aTextSize.setWidth( std::max(aTextSize.Width(), std::min(aSize.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 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, const OUString& rText, sal_uInt16 MaxWidth) { if (rText.isEmpty()) return; Point aPoint(rPosition); Size aSize; sal_Int32 nPos = 0; do { OUString aLine = rText.getToken(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, ""); 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(std::min(long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())), long(Fraction(OutputSize.Height() * 100, GraphicSize.Height())))); nZ -= 10; Fraction aFraction (std::max(MINZOOM, std::min(MAXZOOM, nZ)), 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 SmViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet &rOptions) { return SmPrintOptionsTabPage::Create(pPage, pController, rOptions); } SmEditWindow *SmViewShell::GetEditWindow() { SmCmdBoxWrapper* pWrapper = static_cast( 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 ); 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 xStorage = rMedium.GetStorage(); if (xStorage.is() && xStorage->getElementNames().hasElements()) { if (xStorage->hasByName("content.xml")) { // is this a fabulous math package ? Reference 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_GAPHIC_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 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_GAPHIC_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(pItem)->GetValue(); else bVal = !pp->GetConfig()->IsShowFormulaCursor(); pp->GetConfig()->SetShowFormulaCursor(bVal); if (!IsInlineEditEnabled()) GetGraphicWindow().ShowCursor(bVal); break; } case SID_DRAW: if (pWin) { GetDoc()->SetText( pWin->GetText() ); SetStatusText(OUString()); ShowError( nullptr ); GetDoc()->Repaint(); } break; case SID_ZOOM_OPTIMAL: mpGraphic->ZoomToFitInWindow(); break; case SID_ZOOMIN: mpGraphic->SetZoom(mpGraphic->GetZoom() + 25); break; case SID_ZOOMOUT: SAL_WARN_IF( mpGraphic->GetZoom() < 25, "starmath", "incorrect sal_uInt16 argument" ); mpGraphic->SetZoom(mpGraphic->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::getUnoTunnelImplementation(xTrans); if( pTrans ) pTrans->CopyToClipboard(GetEditWindow()); } } break; case SID_PASTEOBJECT: { TransferableDataHelper aData( TransferableDataHelper::CreateFromSystemClipboard(GetEditWindow()) ); 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 ) { TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetEditWindow()) ); 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(rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT)); if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled())) { pWin->InsertText(rItem.GetValue()); } if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow)) { GetDoc()->GetCursor().InsertCommandText(rItem.GetValue()); GetGraphicWindow().GrabFocus(); } break; } case SID_INSERTSPECIAL: { const SfxStringItem& rItem = static_cast(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: { mpImpl->pRequest.reset(new SfxRequest( rReq )); mpImpl->pDocInserter.reset(new ::sfx2::DocumentInserter(pWin ? pWin->GetFrameWeld() : nullptr, GetDoc()->GetFactory().GetFactoryName())); mpImpl->pDocInserter->StartExecuteModal( LINK( this, SmViewShell, DialogClosedHdl ) ); break; } case SID_IMPORT_MATHML_CLIPBOARD: { TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard(GetEditWindow()) ); uno::Reference < io::XInputStream > xStrm; SotClipboardFormatId nId = SOT_FORMAT_SYSTEM_START; //dummy initialize to avoid warning if ( aDataHelper.GetTransferable().is() ) { nId = SotClipboardFormatId::MATHML; if (aDataHelper.HasFormat(nId)) { xStrm = aDataHelper.GetInputStream(nId, ""); if (xStrm.is()) { std::unique_ptr pClipboardMedium(new SfxMedium()); pClipboardMedium->GetItemSet(); //generate initial itemset, not sure if necessary std::shared_ptr pMathFilter = SfxFilter::GetFilterByName(MATHML_XML); pClipboardMedium->SetFilter(pMathFilter); pClipboardMedium->setStreamToLoadFrom(xStrm, true /*bIsReadOnly*/); InsertFrom(*pClipboardMedium); 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("\n" + aString; std::unique_ptr pClipboardMedium(new SfxMedium()); pClipboardMedium->GetItemSet(); //generates initial itemset, not sure if necessary std::shared_ptr pMathFilter = SfxFilter::GetFilterByName(MATHML_XML); pClipboardMedium->SetFilter(pMathFilter); std::unique_ptr pStrm; // The text to be imported might asserts encoding like 'encoding="utf-8"' but FORMAT_STRING is UTF-16. // Force encoding to UTF-16, if encoding exists. bool bForceUTF16 = false; sal_Int32 nPosL = aString.indexOf("encoding=\""); sal_Int32 nPosU = -1; if ( nPosL >= 0 && nPosL +10 < aString.getLength() ) { nPosL += 10; nPosU = aString.indexOf( '"',nPosL); if (nPosU > nPosL) { bForceUTF16 = true; } } if ( bForceUTF16 ) { OUString aNewString = aString.replaceAt( nPosL,nPosU-nPosL,"UTF-16"); pStrm.reset(new SvMemoryStream( const_cast(aNewString.getStr()), aNewString.getLength() * sizeof(sal_Unicode), StreamMode::READ)); } else { pStrm.reset(new SvMemoryStream( const_cast(aString.getStr()), aString.getLength() * sizeof(sal_Unicode), StreamMode::READ)); } uno::Reference xStrm2( new ::utl::OInputStreamWrapper(*pStrm) ); pClipboardMedium->setStreamToLoadFrom(xStrm2, true /*bIsReadOnly*/); InsertFrom(*pClipboardMedium); 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(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 { SfxItemSet aSet( SmDocShell::GetPool(), svl::Items{}); aSet.Put( SvxZoomItem( SvxZoomType::PERCENT, mpGraphic->GetZoom())); SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); ScopedVclPtr 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(pItem)->GetValue(); mpGraphic->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->GetDevFontCount() == 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::CreateFromSystemClipboard( pEditWin) ); 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, mpGraphic->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 = mpGraphic->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; } } } SmViewShell::SmViewShell(SfxViewFrame *pFrame_, SfxViewShell *) : SfxViewShell(pFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS) , mpImpl(new SmViewShell_Impl) , mpGraphic(VclPtr::Create(this)) , maGraphicController(*mpGraphic, SID_GAPHIC_SM, pFrame_->GetBindings()) , mbPasteState(false) , mbInsertIntoEditWindow(false) { SetStatusText(OUString()); SetWindow(mpGraphic.get()); SfxShell::SetName("SmView"); SfxShell::SetUndoManager( &GetDoc()->GetEditEngine().GetUndoManager() ); } 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(); mpGraphic.disposeAndClear(); } void SmViewShell::Deactivate( bool bIsMDIActivate ) { SmEditWindow *pEdit = GetEditWindow(); if ( pEdit ) pEdit->Flush(); SfxViewShell::Deactivate( bIsMDIActivate ); } void SmViewShell::Activate( bool bIsMDIActivate ) { SfxViewShell::Activate( bIsMDIActivate ); SmEditWindow *pEdit = GetEditWindow(); if ( pEdit ) { //! 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(mpImpl->pDocInserter && "ScDocShell::DialogClosedHdl(): no document inserter"); if ( ERRCODE_NONE == _pFileDlg->GetError() ) { std::unique_ptr pMedium = mpImpl->pDocInserter->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_GAPHIC_SM); } } mpImpl->pRequest->SetReturnValue( SfxBoolItem( mpImpl->pRequest->GetSlot(), true ) ); mpImpl->pRequest->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() const { return mpImpl->aOpts.IsExperimentalMode(); } void SmViewShell::ZoomByItemSet(const SfxItemSet *pSet) { assert(pSet); const SvxZoomItem &rZoom = pSet->Get(SID_ATTR_ZOOM); switch( rZoom.GetType() ) { case SvxZoomType::PERCENT: mpGraphic->SetZoom(sal::static_int_cast(rZoom.GetValue ())); break; case SvxZoomType::OPTIMAL: mpGraphic->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(std::min(long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())), long(Fraction(OutputSize.Height() * 100, GraphicSize.Height())))); mpGraphic->SetZoom (nZ); break; } default: break; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */