/* -*- 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 #include #include "mathtype.hxx" #include "ooxmlexport.hxx" #include "ooxmlimport.hxx" #include "rtfexport.hxx" #include #include #include #include #include #include #include "accessibility.hxx" #include #include #include #include #include #include #define ShellClass_SmDocShell #include using namespace ::com::sun::star; using namespace ::com::sun::star::accessibility; using namespace ::com::sun::star::uno; SFX_IMPL_SUPERCLASS_INTERFACE(SmDocShell, SfxObjectShell) void SmDocShell::InitInterface_Impl() { GetStaticInterface()->RegisterPopupMenu("view"); } void SmDocShell::SetSmSyntaxVersion(sal_Int16 nSmSyntaxVersion) { mnSmSyntaxVersion = nSmSyntaxVersion; maParser.reset(starmathdatabase::GetVersionSmParser(mnSmSyntaxVersion)); } SFX_IMPL_OBJECTFACTORY(SmDocShell, SvGlobalName(SO3_SM_CLASSID), "smath" ) void SmDocShell::Notify(SfxBroadcaster&, const SfxHint& rHint) { if (rHint.GetId() == SfxHintId::MathFormatChanged) { SetFormulaArranged(false); mnModifyCount++; //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState Repaint(); } } void SmDocShell::LoadSymbols() { SmModule *pp = SM_MOD(); pp->GetSymbolManager().Load(); } OUString SmDocShell::GetComment() const { uno::Reference xDPS( GetModel(), uno::UNO_QUERY_THROW); uno::Reference xDocProps( xDPS->getDocumentProperties()); return xDocProps->getDescription(); } void SmDocShell::SetText(const OUString& rBuffer) { if (rBuffer == maText) return; bool bIsEnabled = IsEnableSetModified(); if( bIsEnabled ) EnableSetModified( false ); maText = rBuffer; SetFormulaArranged( false ); Parse(); SmViewShell *pViewSh = SmGetActiveView(); if (pViewSh) { pViewSh->GetViewFrame().GetBindings().Invalidate(SID_TEXT); if ( SfxObjectCreateMode::EMBEDDED == GetCreateMode() ) { // have SwOleClient::FormatChanged() to align the modified formula properly // even if the visible area does not change (e.g. when formula text changes from // "{a over b + c} over d" to "d over {a over b + c}" SfxGetpApp()->NotifyEvent(SfxEventHint( SfxEventHintId::VisAreaChanged, GlobalEventConfig::GetEventName(GlobalEventId::VISAREACHANGED), this)); Repaint(); } else pViewSh->GetGraphicWidget().Invalidate(); } if ( bIsEnabled ) EnableSetModified( bIsEnabled ); SetModified(); // launch accessible event if necessary SmGraphicAccessible *pAcc = pViewSh ? pViewSh->GetGraphicWidget().GetAccessible_Impl() : nullptr; if (pAcc) { Any aOldValue, aNewValue; if ( comphelper::OCommonAccessibleText::implInitTextChangedEvent( maText, rBuffer, aOldValue, aNewValue ) ) { pAcc->LaunchEvent( AccessibleEventId::TEXT_CHANGED, aOldValue, aNewValue ); } } if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) OnDocumentPrinterChanged(nullptr); } void SmDocShell::SetFormat(SmFormat const & rFormat) { maFormat = rFormat; SetFormulaArranged( false ); SetModified(); mnModifyCount++; //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState // don't use SmGetActiveView since the view shell might not be active (0 pointer) // if for example the Basic Macro dialog currently has the focus. Thus: SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this ); while (pFrm) { pFrm->GetBindings().Invalidate(SID_GRAPHIC_SM); pFrm = SfxViewFrame::GetNext( *pFrm, this ); } } OUString const & SmDocShell::GetAccessibleText() { ArrangeFormula(); if (maAccText.isEmpty()) { OSL_ENSURE( mpTree, "Tree missing" ); if (mpTree) { OUStringBuffer aBuf; mpTree->GetAccessibleText(aBuf); maAccText = aBuf.makeStringAndClear(); } } return maAccText; } void SmDocShell::Parse() { mpTree.reset(); ReplaceBadChars(); mpTree = maParser->Parse(maText); mnModifyCount++; //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState SetFormulaArranged( false ); InvalidateCursor(); maUsedSymbols = maParser->GetUsedSymbols(); } void SmDocShell::ArrangeFormula() { if (mbFormulaArranged) return; // Only for the duration of the existence of this object the correct settings // at the printer are guaranteed! SmPrinterAccess aPrtAcc(*this); OutputDevice* pOutDev = aPrtAcc.GetRefDev(); SAL_WARN_IF( !pOutDev, "starmath", "!! SmDocShell::ArrangeFormula: reference device missing !!"); // if necessary get another OutputDevice for which we format if (!pOutDev) { if (SmViewShell *pView = SmGetActiveView()) pOutDev = &pView->GetGraphicWidget().GetDrawingArea()->get_ref_device(); else { pOutDev = &SM_MOD()->GetDefaultVirtualDev(); pOutDev->SetMapMode( MapMode(SmMapUnit()) ); } } OSL_ENSURE(pOutDev->GetMapMode().GetMapUnit() == SmMapUnit(), "Sm : wrong MapMode"); const SmFormat &rFormat = GetFormat(); mpTree->Prepare(rFormat, *this, 0); pOutDev->Push(vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::TEXTLANGUAGE); // We want the device to always be LTR, we handle RTL formulas ourselves. bool bOldRTL = pOutDev->IsRTLEnabled(); pOutDev->EnableRTL(false); // For RTL formulas, we want the brackets to be mirrored. bool bRTL = GetFormat().IsRightToLeft(); pOutDev->SetLayoutMode(bRTL ? vcl::text::ComplexTextLayoutFlags::BiDiRtl : vcl::text::ComplexTextLayoutFlags::Default); // Numbers should not be converted, for now. pOutDev->SetDigitLanguage( LANGUAGE_ENGLISH ); mpTree->Arrange(*pOutDev, rFormat); pOutDev->EnableRTL(bOldRTL); pOutDev->Pop(); SetFormulaArranged(true); // invalidate accessible text maAccText.clear(); } void SmDocShell::UpdateEditEngineDefaultFonts() { SmEditEngine::setSmItemPool(mpEditEngineItemPool.get(), maLinguOptions); } EditEngine& SmDocShell::GetEditEngine() { if (!mpEditEngine) { //! //! see also SmEditWindow::DataChanged ! //! mpEditEngineItemPool = EditEngine::CreatePool(); SmEditEngine::setSmItemPool(mpEditEngineItemPool.get(), maLinguOptions); mpEditEngine.reset( new SmEditEngine( mpEditEngineItemPool.get() ) ); mpEditEngine->EraseVirtualDevice(); // set initial text if the document already has some... // (may be the case when reloading a doc) OUString aTxt( GetText() ); if (!aTxt.isEmpty()) mpEditEngine->SetText( aTxt ); mpEditEngine->ClearModifyFlag(); } return *mpEditEngine; } void SmDocShell::DrawFormula(OutputDevice &rDev, Point &rPosition, bool bDrawSelection) { if (!mpTree) Parse(); OSL_ENSURE(mpTree, "Sm : NULL pointer"); ArrangeFormula(); bool bRTL = GetFormat().IsRightToLeft(); // Problem: What happens to WYSIWYG? While we're active inplace, we don't have a reference // device and aren't aligned to that either. So now there can be a difference between the // VisArea (i.e. the size within the client) and the current size. // Idea: The difference could be adapted with SmNod::SetSize (no long-term solution) rPosition.AdjustX(maFormat.GetDistance( DIS_LEFTSPACE ) ); rPosition.AdjustY(maFormat.GetDistance( DIS_TOPSPACE ) ); Point aPosition(rPosition); if (bRTL && rDev.GetOutDevType() != OUTDEV_WINDOW) aPosition.AdjustX(GetSize().Width() - maFormat.GetDistance(DIS_LEFTSPACE) - maFormat.GetDistance(DIS_RIGHTSPACE)); //! in case of high contrast-mode (accessibility option!) //! the draw mode needs to be set to default, because when embedding //! Math for example in Calc in "a over b" the fraction bar may not //! be visible else. More generally: the FillColor may have been changed. DrawModeFlags nOldDrawMode = DrawModeFlags::Default; bool bRestoreDrawMode = false; if (OUTDEV_WINDOW == rDev.GetOutDevType() && rDev.GetOwnerWindow()->GetSettings().GetStyleSettings().GetHighContrastMode()) { nOldDrawMode = rDev.GetDrawMode(); rDev.SetDrawMode( DrawModeFlags::Default ); bRestoreDrawMode = true; } rDev.Push(vcl::PushFlags::TEXTLAYOUTMODE | vcl::PushFlags::TEXTLANGUAGE); // We want the device to always be LTR, we handle RTL formulas ourselves. bool bOldRTL = rDev.IsRTLEnabled(); if (rDev.GetOutDevType() == OUTDEV_WINDOW) rDev.EnableRTL(bRTL); else rDev.EnableRTL(false); auto nLayoutFlags = vcl::text::ComplexTextLayoutFlags::Default; if (bRTL) { // For RTL formulas, we want the brackets to be mirrored. nLayoutFlags |= vcl::text::ComplexTextLayoutFlags::BiDiRtl; if (rDev.GetOutDevType() == OUTDEV_WINDOW) nLayoutFlags |= vcl::text::ComplexTextLayoutFlags::TextOriginLeft; } rDev.SetLayoutMode(nLayoutFlags); // Numbers should not be converted, for now. rDev.SetDigitLanguage( LANGUAGE_ENGLISH ); //Set selection if any if(mpCursor && bDrawSelection){ mpCursor->AnnotateSelection(); SmSelectionDrawingVisitor(rDev, mpTree.get(), aPosition); } //Drawing using visitor SmDrawingVisitor(rDev, aPosition, mpTree.get(), GetFormat()); rDev.EnableRTL(bOldRTL); rDev.Pop(); if (bRestoreDrawMode) rDev.SetDrawMode( nOldDrawMode ); } Size SmDocShell::GetSize() { Size aRet; if (!mpTree) Parse(); if (mpTree) { ArrangeFormula(); aRet = mpTree->GetSize(); if ( !aRet.Width() || aRet.Width() == 1 ) aRet.setWidth( 2000 ); else aRet.AdjustWidth(maFormat.GetDistance( DIS_LEFTSPACE ) + maFormat.GetDistance( DIS_RIGHTSPACE ) ); if ( !aRet.Height() ) aRet.setHeight( 1000 ); else aRet.AdjustHeight(maFormat.GetDistance( DIS_TOPSPACE ) + maFormat.GetDistance( DIS_BOTTOMSPACE ) ); } return aRet; } void SmDocShell::InvalidateCursor(){ mpCursor.reset(); } SmCursor& SmDocShell::GetCursor(){ if(!mpCursor) mpCursor.reset(new SmCursor(mpTree.get(), this)); return *mpCursor; } bool SmDocShell::HasCursor() const { return mpCursor != nullptr; } SmPrinterAccess::SmPrinterAccess( SmDocShell &rDocShell ) { pPrinter = rDocShell.GetPrt(); if ( pPrinter ) { pPrinter->Push( vcl::PushFlags::MAPMODE ); if ( SfxObjectCreateMode::EMBEDDED == rDocShell.GetCreateMode() ) { // if it is an embedded object (without its own printer) // we change the MapMode temporarily. //!If it is a document with its own printer the MapMode should //!be set correct (once) elsewhere(!), in order to avoid numerous //!superfluous pushing and popping of the MapMode when using //!this class. const MapUnit eOld = pPrinter->GetMapMode().GetMapUnit(); if ( SmMapUnit() != eOld ) { MapMode aMap( pPrinter->GetMapMode() ); aMap.SetMapUnit( SmMapUnit() ); Point aTmp( aMap.GetOrigin() ); aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, SmMapUnit() ) ); aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, SmMapUnit() ) ); aMap.SetOrigin( aTmp ); pPrinter->SetMapMode( aMap ); } } } pRefDev = rDocShell.GetRefDev(); if ( !pRefDev || pPrinter.get() == pRefDev.get() ) return; pRefDev->Push( vcl::PushFlags::MAPMODE ); if ( SfxObjectCreateMode::EMBEDDED != rDocShell.GetCreateMode() ) return; // if it is an embedded object (without its own printer) // we change the MapMode temporarily. //!If it is a document with its own printer the MapMode should //!be set correct (once) elsewhere(!), in order to avoid numerous //!superfluous pushing and popping of the MapMode when using //!this class. const MapUnit eOld = pRefDev->GetMapMode().GetMapUnit(); if ( SmMapUnit() != eOld ) { MapMode aMap( pRefDev->GetMapMode() ); aMap.SetMapUnit( SmMapUnit() ); Point aTmp( aMap.GetOrigin() ); aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, SmMapUnit() ) ); aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, SmMapUnit() ) ); aMap.SetOrigin( aTmp ); pRefDev->SetMapMode( aMap ); } } SmPrinterAccess::~SmPrinterAccess() { if ( pPrinter ) pPrinter->Pop(); if ( pRefDev && pRefDev != pPrinter ) pRefDev->Pop(); } Printer* SmDocShell::GetPrt() { if (SfxObjectCreateMode::EMBEDDED == GetCreateMode()) { // Normally the server provides the printer. But if it doesn't provide one (e.g. because // there is no connection) it still can be the case that we know the printer because it // has been passed on by the server in OnDocumentPrinterChanged and being kept temporarily. Printer* pPrt = GetDocumentPrinter(); if (!pPrt && mpTmpPrinter) pPrt = mpTmpPrinter; return pPrt; } else if (!mpPrinter) { auto pOptions = std::make_unique>(GetPool()); SmModule *pp = SM_MOD(); pp->GetConfig()->ConfigToItemSet(*pOptions); mpPrinter = VclPtr::Create(std::move(pOptions)); mpPrinter->SetMapMode(MapMode(SmMapUnit())); } return mpPrinter; } OutputDevice* SmDocShell::GetRefDev() { if (SfxObjectCreateMode::EMBEDDED == GetCreateMode()) { OutputDevice* pOutDev = GetDocumentRefDev(); if (pOutDev) return pOutDev; } return GetPrt(); } void SmDocShell::SetPrinter( SfxPrinter *pNew ) { mpPrinter.disposeAndClear(); mpPrinter = pNew; //Transfer ownership mpPrinter->SetMapMode( MapMode(SmMapUnit()) ); SetFormulaArranged(false); Repaint(); } void SmDocShell::OnDocumentPrinterChanged( Printer *pPrt ) { mpTmpPrinter = pPrt; SetFormulaArranged(false); Size aOldSize = GetVisArea().GetSize(); Repaint(); if( aOldSize != GetVisArea().GetSize() && !maText.isEmpty() ) SetModified(); mpTmpPrinter = nullptr; } void SmDocShell::Repaint() { bool bIsEnabled = IsEnableSetModified(); if (bIsEnabled) EnableSetModified( false ); SetFormulaArranged(false); Size aVisSize = GetSize(); SetVisAreaSize(aVisSize); if (SmViewShell* pViewSh = SmGetActiveView()) pViewSh->GetGraphicWidget().Invalidate(); if (bIsEnabled) EnableSetModified(bIsEnabled); } SmDocShell::SmDocShell( SfxModelFlags i_nSfxCreationFlags ) : SfxObjectShell(i_nSfxCreationFlags) , m_pMlElementTree(nullptr) , mpPrinter(nullptr) , mpTmpPrinter(nullptr) , mnModifyCount(0) , mbFormulaArranged(false) , mnSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion()) { SvtLinguConfig().GetOptions(maLinguOptions); SetPool(&SfxGetpApp()->GetPool()); SmModule *pp = SM_MOD(); maFormat = pp->GetConfig()->GetStandardFormat(); StartListening(maFormat); StartListening(*pp->GetConfig()); SetBaseModel(new SmModel(this)); SetSmSyntaxVersion(mnSmSyntaxVersion); SetMapUnit(SmMapUnit()); } SmDocShell::~SmDocShell() { SmModule *pp = SM_MOD(); EndListening(maFormat); EndListening(*pp->GetConfig()); mpCursor.reset(); mpEditEngine.reset(); mpEditEngineItemPool.clear(); mpPrinter.disposeAndClear(); mathml::SmMlIteratorFree(m_pMlElementTree); } bool SmDocShell::ConvertFrom(SfxMedium &rMedium) { bool bSuccess = false; const OUString& rFltName = rMedium.GetFilter()->GetFilterName(); OSL_ENSURE( rFltName != STAROFFICE_XML, "Wrong filter!"); if ( rFltName == MATHML_XML ) { if (mpTree) { mpTree.reset(); InvalidateCursor(); } rtl::Reference xModel(dynamic_cast(GetModel().get())); SmXMLImportWrapper aEquation(xModel); aEquation.useHTMLMLEntities(true); bSuccess = ( ERRCODE_NONE == aEquation.Import(rMedium) ); } else { SvStream *pStream = rMedium.GetInStream(); if ( pStream ) { if ( SotStorage::IsStorageFile( pStream ) ) { tools::SvRef aStorage = new SotStorage( pStream, false ); if ( aStorage->IsStream("Equation Native") ) { // is this a MathType Storage? OUStringBuffer aBuffer; MathType aEquation(aBuffer); bSuccess = aEquation.Parse( aStorage.get() ); if ( bSuccess ) { maText = aBuffer.makeStringAndClear(); Parse(); } } } } } if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) { SetFormulaArranged( false ); Repaint(); } FinishedLoading(); return bSuccess; } bool SmDocShell::InitNew( const uno::Reference < embed::XStorage >& xStorage ) { bool bRet = false; if ( SfxObjectShell::InitNew( xStorage ) ) { bRet = true; SetVisArea(tools::Rectangle(Point(0, 0), Size(2000, 1000))); } return bRet; } bool SmDocShell::Load( SfxMedium& rMedium ) { bool bRet = false; if( SfxObjectShell::Load( rMedium )) { uno::Reference < embed::XStorage > xStorage = GetMedium()->GetStorage(); if (xStorage->hasByName("content.xml") && xStorage->isStreamElement("content.xml")) { // is this a fabulous math package ? rtl::Reference xModel(dynamic_cast(GetModel().get())); SmXMLImportWrapper aEquation(xModel); auto nError = aEquation.Import(rMedium); bRet = ERRCODE_NONE == nError; SetError(nError); } } if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) { SetFormulaArranged( false ); Repaint(); } FinishedLoading(); return bRet; } bool SmDocShell::Save() { //! apply latest changes if necessary UpdateText(); if ( SfxObjectShell::Save() ) { if (!mpTree) Parse(); if( mpTree ) ArrangeFormula(); Reference xModel(GetModel()); SmXMLExportWrapper aEquation(xModel); aEquation.SetFlat(false); return aEquation.Export(*GetMedium()); } return false; } /* * replace bad characters that can not be saved. (#i74144) * */ void SmDocShell::ReplaceBadChars() { bool bReplace = false; if (!mpEditEngine) return; OUStringBuffer aBuf( mpEditEngine->GetText() ); for (sal_Int32 i = 0; i < aBuf.getLength(); ++i) { if (aBuf[i] < ' ' && aBuf[i] != '\r' && aBuf[i] != '\n' && aBuf[i] != '\t') { aBuf[i] = ' '; bReplace = true; } } if (bReplace) maText = aBuf.makeStringAndClear(); } void SmDocShell::UpdateText() { if (mpEditEngine && mpEditEngine->IsModified()) { OUString aEngTxt( mpEditEngine->GetText() ); if (GetText() != aEngTxt) SetText( aEngTxt ); } } bool SmDocShell::SaveAs( SfxMedium& rMedium ) { bool bRet = false; //! apply latest changes if necessary UpdateText(); if ( SfxObjectShell::SaveAs( rMedium ) ) { if (!mpTree) Parse(); if( mpTree ) ArrangeFormula(); Reference xModel(GetModel()); SmXMLExportWrapper aEquation(xModel); aEquation.SetFlat(false); bRet = aEquation.Export(rMedium); } return bRet; } bool SmDocShell::ConvertTo( SfxMedium &rMedium ) { bool bRet = false; std::shared_ptr pFlt = rMedium.GetFilter(); if( pFlt ) { if( !mpTree ) Parse(); if( mpTree ) ArrangeFormula(); const OUString& rFltName = pFlt->GetFilterName(); if(rFltName == STAROFFICE_XML) { Reference xModel(GetModel()); SmXMLExportWrapper aEquation(xModel); aEquation.SetFlat(false); bRet = aEquation.Export(rMedium); } else if(rFltName == MATHML_XML) { Reference xModel(GetModel()); SmXMLExportWrapper aEquation(xModel); aEquation.SetFlat(true); aEquation.SetUseHTMLMLEntities(true); bRet = aEquation.Export(rMedium); } else if (pFlt->GetFilterName() == "MathType 3.x") bRet = WriteAsMathType3( rMedium ); } return bRet; } void SmDocShell::writeFormulaOoxml( ::sax_fastparser::FSHelperPtr const& pSerializer, oox::core::OoxmlVersion const version, oox::drawingml::DocumentType const documentType, const sal_Int8 nAlign) { if( !mpTree ) Parse(); if( mpTree ) ArrangeFormula(); SmOoxmlExport aEquation(mpTree.get(), version, documentType); if(documentType == oox::drawingml::DOCUMENT_DOCX) aEquation.ConvertFromStarMath( pSerializer, nAlign); else aEquation.ConvertFromStarMath(pSerializer, oox::FormulaImExportBase::eFormulaAlign::INLINE); } void SmDocShell::writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding) { if (!mpTree) Parse(); if (mpTree) ArrangeFormula(); SmRtfExport aEquation(mpTree.get()); aEquation.ConvertFromStarMath(rBuffer, nEncoding); } void SmDocShell::readFormulaOoxml( oox::formulaimport::XmlStream& stream ) { SmOoxmlImport aEquation( stream ); SetText( aEquation.ConvertToStarMath()); } void SmDocShell::Execute(SfxRequest& rReq) { switch (rReq.GetSlot()) { case SID_TEXTMODE: { SmFormat aOldFormat = GetFormat(); SmFormat aNewFormat( aOldFormat ); aNewFormat.SetTextmode(!aOldFormat.IsTextmode()); SfxUndoManager *pTmpUndoMgr = GetUndoManager(); if (pTmpUndoMgr) pTmpUndoMgr->AddUndoAction( std::make_unique(this, aOldFormat, aNewFormat)); SetFormat( aNewFormat ); Repaint(); } break; case SID_AUTO_REDRAW : { SmModule *pp = SM_MOD(); bool bRedraw = pp->GetConfig()->IsAutoRedraw(); pp->GetConfig()->SetAutoRedraw(!bRedraw); } break; case SID_LOADSYMBOLS: LoadSymbols(); break; case SID_SAVESYMBOLS: SaveSymbols(); break; case SID_FONT: { // get device used to retrieve the FontList OutputDevice *pDev = GetPrinter(); if (!pDev || pDev->GetFontFaceCollectionCount() == 0) pDev = &SM_MOD()->GetDefaultVirtualDev(); OSL_ENSURE (pDev, "device for font list missing" ); SmFontTypeDialog aFontTypeDialog(rReq.GetFrameWeld(), pDev); SmFormat aOldFormat = GetFormat(); aFontTypeDialog.ReadFrom( aOldFormat ); if (aFontTypeDialog.run() == RET_OK) { SmFormat aNewFormat( aOldFormat ); aFontTypeDialog.WriteTo(aNewFormat); SfxUndoManager *pTmpUndoMgr = GetUndoManager(); if (pTmpUndoMgr) pTmpUndoMgr->AddUndoAction( std::make_unique(this, aOldFormat, aNewFormat)); SetFormat( aNewFormat ); Repaint(); } } break; case SID_FONTSIZE: { SmFontSizeDialog aFontSizeDialog(rReq.GetFrameWeld()); SmFormat aOldFormat = GetFormat(); aFontSizeDialog.ReadFrom( aOldFormat ); if (aFontSizeDialog.run() == RET_OK) { SmFormat aNewFormat( aOldFormat ); aFontSizeDialog.WriteTo(aNewFormat); SfxUndoManager *pTmpUndoMgr = GetUndoManager(); if (pTmpUndoMgr) pTmpUndoMgr->AddUndoAction( std::make_unique(this, aOldFormat, aNewFormat)); SetFormat( aNewFormat ); Repaint(); } } break; case SID_DISTANCE: { SmDistanceDialog aDistanceDialog(rReq.GetFrameWeld()); SmFormat aOldFormat = GetFormat(); aDistanceDialog.ReadFrom( aOldFormat ); if (aDistanceDialog.run() == RET_OK) { SmFormat aNewFormat( aOldFormat ); aDistanceDialog.WriteTo(aNewFormat); SfxUndoManager *pTmpUndoMgr = GetUndoManager(); if (pTmpUndoMgr) pTmpUndoMgr->AddUndoAction( std::make_unique(this, aOldFormat, aNewFormat)); SetFormat( aNewFormat ); Repaint(); } } break; case SID_ALIGN: { SmAlignDialog aAlignDialog(rReq.GetFrameWeld()); SmFormat aOldFormat = GetFormat(); aAlignDialog.ReadFrom( aOldFormat ); if (aAlignDialog.run() == RET_OK) { SmFormat aNewFormat( aOldFormat ); aAlignDialog.WriteTo(aNewFormat); SmModule *pp = SM_MOD(); SmFormat aFmt( pp->GetConfig()->GetStandardFormat() ); aAlignDialog.WriteTo( aFmt ); pp->GetConfig()->SetStandardFormat( aFmt ); SfxUndoManager *pTmpUndoMgr = GetUndoManager(); if (pTmpUndoMgr) pTmpUndoMgr->AddUndoAction( std::make_unique(this, aOldFormat, aNewFormat)); SetFormat( aNewFormat ); Repaint(); } } break; case SID_TEXT: { const SfxStringItem& rItem = rReq.GetArgs()->Get(SID_TEXT); if (GetText() != rItem.GetValue()) SetText(rItem.GetValue()); } break; case SID_UNDO: case SID_REDO: { SfxUndoManager* pTmpUndoMgr = GetUndoManager(); if( pTmpUndoMgr ) { sal_uInt16 nId = rReq.GetSlot(), nCnt = 1; const SfxItemSet* pArgs = rReq.GetArgs(); const SfxPoolItem* pItem; if( pArgs && SfxItemState::SET == pArgs->GetItemState( nId, false, &pItem )) nCnt = static_cast(pItem)->GetValue(); bool (SfxUndoManager::*fnDo)(); size_t nCount; if( SID_UNDO == rReq.GetSlot() ) { nCount = pTmpUndoMgr->GetUndoActionCount(); fnDo = &SfxUndoManager::Undo; } else { nCount = pTmpUndoMgr->GetRedoActionCount(); fnDo = &SfxUndoManager::Redo; } try { for( ; nCnt && nCount; --nCnt, --nCount ) (pTmpUndoMgr->*fnDo)(); } catch( const Exception& ) { DBG_UNHANDLED_EXCEPTION("starmath"); } } Repaint(); UpdateText(); SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this ); while( pFrm ) { SfxBindings& rBind = pFrm->GetBindings(); rBind.Invalidate(SID_UNDO); rBind.Invalidate(SID_REDO); rBind.Invalidate(SID_REPEAT); rBind.Invalidate(SID_CLEARHISTORY); pFrm = SfxViewFrame::GetNext( *pFrm, this ); } } break; } rReq.Done(); } void SmDocShell::GetState(SfxItemSet &rSet) { SfxWhichIter aIter(rSet); for (sal_uInt16 nWh = aIter.FirstWhich(); 0 != nWh; nWh = aIter.NextWhich()) { switch (nWh) { case SID_TEXTMODE: rSet.Put(SfxBoolItem(SID_TEXTMODE, GetFormat().IsTextmode())); break; case SID_DOCTEMPLATE : rSet.DisableItem(SID_DOCTEMPLATE); break; case SID_AUTO_REDRAW : { SmModule *pp = SM_MOD(); bool bRedraw = pp->GetConfig()->IsAutoRedraw(); rSet.Put(SfxBoolItem(SID_AUTO_REDRAW, bRedraw)); } break; case SID_MODIFYSTATUS: { sal_Unicode cMod = ' '; if (IsModified()) cMod = '*'; rSet.Put(SfxStringItem(SID_MODIFYSTATUS, OUString(cMod))); } break; case SID_TEXT: rSet.Put(SfxStringItem(SID_TEXT, GetText())); break; case SID_GRAPHIC_SM: //! very old (pre UNO) and ugly hack to invalidate the SmGraphicWidget. //! If mnModifyCount gets changed then the call below will implicitly notify //! SmGraphicController::StateChanged and there the window gets invalidated. //! Thus all the 'mnModifyCount++' before invalidating this slot. rSet.Put(SfxInt16Item(SID_GRAPHIC_SM, mnModifyCount)); break; case SID_UNDO: case SID_REDO: { SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this ); if( pFrm ) pFrm->GetSlotState( nWh, nullptr, &rSet ); else rSet.DisableItem( nWh ); } break; case SID_GETUNDOSTRINGS: case SID_GETREDOSTRINGS: { SfxUndoManager* pTmpUndoMgr = GetUndoManager(); if( pTmpUndoMgr ) { OUString(SfxUndoManager::*fnGetComment)( size_t, bool const ) const; size_t nCount; if( SID_GETUNDOSTRINGS == nWh ) { nCount = pTmpUndoMgr->GetUndoActionCount(); fnGetComment = &SfxUndoManager::GetUndoActionComment; } else { nCount = pTmpUndoMgr->GetRedoActionCount(); fnGetComment = &SfxUndoManager::GetRedoActionComment; } if (nCount) { OUStringBuffer aBuf; for (size_t n = 0; n < nCount; ++n) { aBuf.append((pTmpUndoMgr->*fnGetComment)( n, SfxUndoManager::TopLevel )); aBuf.append('\n'); } SfxStringListItem aItem( nWh ); aItem.SetString( aBuf.makeStringAndClear() ); rSet.Put( aItem ); } } else rSet.DisableItem( nWh ); } break; } } } SfxUndoManager *SmDocShell::GetUndoManager() { if (!mpEditEngine) GetEditEngine(); return &mpEditEngine->GetUndoManager(); } void SmDocShell::SaveSymbols() { SmModule *pp = SM_MOD(); pp->GetSymbolManager().Save(); } void SmDocShell::Draw(OutputDevice *pDevice, const JobSetup &, sal_uInt16 /*nAspect*/, bool /*bOutputForScreen*/) { pDevice->IntersectClipRegion(GetVisArea()); Point atmppoint; DrawFormula(*pDevice, atmppoint); } SfxItemPool& SmDocShell::GetPool() { return SfxGetpApp()->GetPool(); } void SmDocShell::SetVisArea(const tools::Rectangle & rVisArea) { tools::Rectangle aNewRect(rVisArea); aNewRect.SetPos(Point()); if (aNewRect.IsWidthEmpty()) aNewRect.SetRight( 2000 ); if (aNewRect.IsHeightEmpty()) aNewRect.SetBottom( 1000 ); bool bIsEnabled = IsEnableSetModified(); if ( bIsEnabled ) EnableSetModified( false ); //TODO/LATER: it's unclear how this interacts with the SFX code // If outplace editing, then don't resize the OutplaceWindow. But the // ObjectShell has to resize. bool bUnLockFrame; if( GetCreateMode() == SfxObjectCreateMode::EMBEDDED && !IsInPlaceActive() && GetFrame() ) { GetFrame()->LockAdjustPosSizePixel(); bUnLockFrame = true; } else bUnLockFrame = false; SfxObjectShell::SetVisArea( aNewRect ); if( bUnLockFrame ) GetFrame()->UnlockAdjustPosSizePixel(); if ( bIsEnabled ) EnableSetModified( bIsEnabled ); } void SmDocShell::FillClass(SvGlobalName* pClassName, SotClipboardFormatId* pFormat, OUString* pFullTypeName, sal_Int32 nFileFormat, bool bTemplate /* = false */) const { if (nFileFormat == SOFFICE_FILEFORMAT_60 ) { *pClassName = SvGlobalName(SO3_SM_CLASSID_60); *pFormat = SotClipboardFormatId::STARMATH_60; *pFullTypeName = SmResId(STR_MATH_DOCUMENT_FULLTYPE_CURRENT); } else if (nFileFormat == SOFFICE_FILEFORMAT_8 ) { *pClassName = SvGlobalName(SO3_SM_CLASSID_60); *pFormat = bTemplate ? SotClipboardFormatId::STARMATH_8_TEMPLATE : SotClipboardFormatId::STARMATH_8; *pFullTypeName = SmResId(STR_MATH_DOCUMENT_FULLTYPE_CURRENT); } } void SmDocShell::SetModified(bool bModified) { if( IsEnableSetModified() ) { SfxObjectShell::SetModified( bModified ); Broadcast(SfxHint(SfxHintId::DocChanged)); } } bool SmDocShell::WriteAsMathType3( SfxMedium& rMedium ) { OUStringBuffer aTextAsBuffer(maText); MathType aEquation(aTextAsBuffer, mpTree.get()); return aEquation.ConvertFromStarMath( rMedium ); } void SmDocShell::SetRightToLeft(bool bRTL) { SmFormat aOldFormat = GetFormat(); if (aOldFormat.IsRightToLeft() == bRTL) return; SmFormat aNewFormat(aOldFormat); aNewFormat.SetRightToLeft(bRTL); SfxUndoManager* pTmpUndoMgr = GetUndoManager(); if (pTmpUndoMgr) pTmpUndoMgr->AddUndoAction( std::make_unique(this, aOldFormat, aNewFormat)); SetFormat(aNewFormat); Repaint(); } static Size 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; } static Size 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; } static void 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); } static void 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 SmDocShell::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( rPrintUIOptions.getIntValue(PRTUIOPT_PRINT_FORMAT, PRINT_SIZE_NORMAL))); const sal_uInt16 nZoomFactor = static_cast(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, GetTitle(), aOutRect.GetWidth() - 200)); aFont.SetWeight(WEIGHT_NORMAL); aFont.SetFontSize(aSize600); rOutDev.SetFont(aFont); Size aDescSize(GetTextSize(rOutDev, 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, GetTitle(), sal::static_int_cast(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, GetComment(), sal::static_int_cast(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, 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, GetText(), sal::static_int_cast(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(GetSize()); MapMode OutputMapMode; switch (ePrintSize) { case PRINT_SIZE_NORMAL: OutputMapMode = MapMode(SmMapUnit()); break; case PRINT_SIZE_SCALED: if (!aSize.IsEmpty()) { sal_uInt16 nZ = std::min(o3tl::convert(aOutRect.GetWidth(), 100, aSize.Width()), o3tl::convert(aOutRect.GetHeight(), 100, aSize.Height())); if (bIsPrintFrame && nZ > MINZOOM) nZ -= 10; Fraction aFraction(std::clamp(nZ, MINZOOM, MAXZOOM), 100); OutputMapMode = MapMode(SmMapUnit(), Point(), aFraction, aFraction); } else OutputMapMode = MapMode(SmMapUnit()); break; case PRINT_SIZE_ZOOMED: { Fraction aFraction(nZoomFactor, 100); OutputMapMode = MapMode(SmMapUnit(), Point(), aFraction, aFraction); break; } } aSize = OutputDevice::LogicToLogic(aSize, OutputMapMode, MapMode(SmMapUnit())); Point aPos(aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2, aOutRect.Top() + (aOutRect.GetHeight() - aSize.Height()) / 2); aPos = OutputDevice::LogicToLogic(aPos, MapMode(SmMapUnit()), OutputMapMode); aOutRect = OutputDevice::LogicToLogic(aOutRect, MapMode(SmMapUnit()), OutputMapMode); rOutDev.SetMapMode(OutputMapMode); rOutDev.SetClipRegion(vcl::Region(aOutRect)); DrawFormula(rOutDev, aPos); rOutDev.SetClipRegion(); rOutDev.Pop(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */