diff options
Diffstat (limited to 'sfx2/source/doc/objcont.cxx')
-rw-r--r-- | sfx2/source/doc/objcont.cxx | 663 |
1 files changed, 663 insertions, 0 deletions
diff --git a/sfx2/source/doc/objcont.cxx b/sfx2/source/doc/objcont.cxx new file mode 100644 index 000000000..3055a52cc --- /dev/null +++ b/sfx2/source/doc/objcont.cxx @@ -0,0 +1,663 @@ +/* -*- 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 <com/sun/star/uno/Reference.hxx> + +#include <com/sun/star/document/DocumentProperties.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <comphelper/fileurl.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/window.hxx> +#include <svl/style.hxx> + +#include <svl/intitem.hxx> +#include <svl/ctloptions.hxx> +#include <comphelper/processfactory.hxx> +#include <unotools/securityoptions.hxx> +#include <tools/datetime.hxx> +#include <tools/diagnose_ex.h> +#include <rtl/uri.hxx> + +#include <unotools/useroptions.hxx> +#include <vcl/virdev.hxx> +#include <vcl/settings.hxx> +#include <vcl/gdimtf.hxx> + +#include <sfx2/app.hxx> +#include <sfx2/dinfdlg.hxx> +#include <sfx2/sfxresid.hxx> +#include <appdata.hxx> +#include <sfx2/docfac.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/objsh.hxx> +#include <objshimp.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/doctempl.hxx> +#include <sfx2/sfxsids.hrc> +#include <sfx2/strings.hrc> +#include <sfx2/docfile.hxx> +#include <sfx2/docfilt.hxx> +#include <memory> +#include <helpids.h> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + + +static +bool operator> (const util::DateTime& i_rLeft, const util::DateTime& i_rRight) +{ + if ( i_rLeft.Year != i_rRight.Year ) + return i_rLeft.Year > i_rRight.Year; + + if ( i_rLeft.Month != i_rRight.Month ) + return i_rLeft.Month > i_rRight.Month; + + if ( i_rLeft.Day != i_rRight.Day ) + return i_rLeft.Day > i_rRight.Day; + + if ( i_rLeft.Hours != i_rRight.Hours ) + return i_rLeft.Hours > i_rRight.Hours; + + if ( i_rLeft.Minutes != i_rRight.Minutes ) + return i_rLeft.Minutes > i_rRight.Minutes; + + if ( i_rLeft.Seconds != i_rRight.Seconds ) + return i_rLeft.Seconds > i_rRight.Seconds; + + if ( i_rLeft.NanoSeconds != i_rRight.NanoSeconds ) + return i_rLeft.NanoSeconds > i_rRight.NanoSeconds; + + return false; +} + +std::shared_ptr<GDIMetaFile> +SfxObjectShell::GetPreviewMetaFile( bool bFullContent ) const +{ + return CreatePreviewMetaFile_Impl( bFullContent ); +} + +std::shared_ptr<GDIMetaFile> +SfxObjectShell::CreatePreviewMetaFile_Impl( bool bFullContent ) const +{ + // DoDraw can only be called when no printing is done, otherwise + // the printer may be turned off + SfxViewFrame *pFrame = SfxViewFrame::GetFirst( this ); + if ( pFrame && pFrame->GetViewShell() && + pFrame->GetViewShell()->GetPrinter() && + pFrame->GetViewShell()->GetPrinter()->IsPrinting() ) + return std::shared_ptr<GDIMetaFile>(); + + auto xFile = std::make_shared<GDIMetaFile>(); + + ScopedVclPtrInstance< VirtualDevice > pDevice; + pDevice->EnableOutput( false ); + + MapMode aMode( GetMapUnit() ); + pDevice->SetMapMode( aMode ); + xFile->SetPrefMapMode( aMode ); + + Size aTmpSize; + sal_Int8 nAspect; + if ( bFullContent ) + { + nAspect = ASPECT_CONTENT; + aTmpSize = GetVisArea( nAspect ).GetSize(); + } + else + { + nAspect = ASPECT_THUMBNAIL; + aTmpSize = GetFirstPageSize(); + } + + xFile->SetPrefSize( aTmpSize ); + DBG_ASSERT( !aTmpSize.IsEmpty(), + "size of first page is 0, override GetFirstPageSize or set visible-area!" ); + + xFile->Record( pDevice ); + + LanguageType eLang; + SvtCTLOptions aCTLOptions; + if ( SvtCTLOptions::NUMERALS_HINDI == aCTLOptions.GetCTLTextNumerals() ) + eLang = LANGUAGE_ARABIC_SAUDI_ARABIA; + else if ( SvtCTLOptions::NUMERALS_ARABIC == aCTLOptions.GetCTLTextNumerals() ) + eLang = LANGUAGE_ENGLISH; + else + eLang = Application::GetSettings().GetLanguageTag().getLanguageType(); + + pDevice->SetDigitLanguage( eLang ); + + const_cast<SfxObjectShell*>(this)->DoDraw( pDevice, Point(0,0), aTmpSize, JobSetup(), nAspect ); + + xFile->Stop(); + + return xFile; +} + + +void SfxObjectShell::UpdateDocInfoForSave() +{ + uno::Reference<document::XDocumentProperties> xDocProps(getDocProperties()); + + // clear user data if recommend (see 'Tools - Options - LibreOffice - Security') + if ( SvtSecurityOptions().IsOptionSet( + SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo ) ) + { + xDocProps->resetUserData( OUString() ); + } + else if ( IsModified() ) + { + const OUString aUserName = SvtUserOptions().GetFullName(); + if ( !IsUseUserData() ) + { + // remove all data pointing to the current user + if (xDocProps->getAuthor() == aUserName) { + xDocProps->setAuthor( OUString() ); + } + xDocProps->setModifiedBy( OUString() ); + if (xDocProps->getPrintedBy() == aUserName) { + xDocProps->setPrintedBy( OUString() ); + } + } + else + { + // update ModificationAuthor, revision and editing time + ::DateTime now( ::DateTime::SYSTEM ); + xDocProps->setModificationDate( now.GetUNODateTime() ); + xDocProps->setModifiedBy( aUserName ); + UpdateTime_Impl( xDocProps ); + } + } +} + + +static void +lcl_add(util::Duration & rDur, tools::Time const& rTime) +{ + // here we don't care about overflow: rDur is converted back to seconds + // anyway, and tools::Time cannot store more than ~4000 hours + rDur.Hours += rTime.GetHour(); + rDur.Minutes += rTime.GetMin(); + rDur.Seconds += rTime.GetSec(); +} + +// Update the processing time +void SfxObjectShell::UpdateTime_Impl( + const uno::Reference<document::XDocumentProperties> & i_xDocProps) +{ + // Get old time from documentinfo + const sal_Int32 secs = i_xDocProps->getEditingDuration(); + util::Duration editDuration(false, 0, 0, 0, + secs/3600, (secs%3600)/60, secs%60, 0); + + // Initialize some local member! It's necessary for follow operations! + DateTime aNow( DateTime::SYSTEM ); // Date and time at current moment + tools::Time n24Time (24,0,0,0) ; // Time-value for 24 hours - see follow calculation + sal_Int32 nDays = 0 ; // Count of days between now and last editing + tools::Time nAddTime (0) ; // Value to add on aOldTime + + // Save impossible cases! + // User has changed time to the past between last editing and now... it's not possible!!! + DBG_ASSERT( !(aNow.GetDate()<pImpl->nTime.GetDate()), "Timestamp of last change is in the past!?..." ); + + // Do the follow only, if user has NOT changed time to the past. + // Else add a time of 0 to aOldTime... !!! + if (aNow.GetDate()>=pImpl->nTime.GetDate()) + { + // Get count of days last editing. + nDays = aNow.GetSecFromDateTime(Date(pImpl->nTime.GetDate()))/86400 ; + + if (nDays==0) + { + // If no day between now and last editing - calculate time directly. + nAddTime = static_cast<const tools::Time&>(aNow) - static_cast<const tools::Time&>(pImpl->nTime); + } + else if (nDays<=31) + { + // If time of working without save greater than 1 month (!)... + // we add 0 to aOldTime! + + // If 1 or up to 31 days between now and last editing - calculate time indirectly. + // nAddTime = (24h - nTime) + (nDays * 24h) + aNow + --nDays; + nAddTime = tools::Time( nDays * n24Time.GetTime()); + nAddTime += n24Time-static_cast<const tools::Time&>(pImpl->nTime); + nAddTime += aNow ; + } + + lcl_add(editDuration, nAddTime); + } + + pImpl->nTime = aNow; + try { + const sal_Int32 newSecs( (editDuration.Hours*3600) + + (editDuration.Minutes*60) + editDuration.Seconds); + i_xDocProps->setEditingDuration(newSecs); + i_xDocProps->setEditingCycles(i_xDocProps->getEditingCycles() + 1); + } + catch (const lang::IllegalArgumentException &) + { + // ignore overflow + } +} + +std::shared_ptr<SfxDocumentInfoDialog> SfxObjectShell::CreateDocumentInfoDialog(weld::Window* pParent, + const SfxItemSet& rSet) +{ + return std::make_shared<SfxDocumentInfoDialog>(pParent, rSet); +} + +std::set<Color> SfxObjectShell::GetDocColors() +{ + std::set<Color> empty; + return empty; +} + +sfx::AccessibilityIssueCollection SfxObjectShell::runAccessibilityCheck() +{ + sfx::AccessibilityIssueCollection aCollection; + return aCollection; +} + +SfxStyleSheetBasePool* SfxObjectShell::GetStyleSheetPool() +{ + return nullptr; +} + +namespace { + +struct Styles_Impl +{ + SfxStyleSheetBase *pSource; + SfxStyleSheetBase *pDest; +}; + +} + +void SfxObjectShell::LoadStyles +( + SfxObjectShell &rSource /* the document template from which + the styles are to be loaded */ +) + +/* [Description] + + This method is called by the SFx if styles are to be loaded from a template. + Existing styles are in this case overwritten. The document must then be + re-formatted. Therefore, applications usually override this method + and call the implementation in the base class. +*/ + +{ + SfxStyleSheetBasePool *pSourcePool = rSource.GetStyleSheetPool(); + DBG_ASSERT(pSourcePool, "Source-DocumentShell without StyleSheetPool"); + SfxStyleSheetBasePool *pMyPool = GetStyleSheetPool(); + DBG_ASSERT(pMyPool, "Dest-DocumentShell without StyleSheetPool"); + auto xIter = pSourcePool->CreateIterator(SfxStyleFamily::All); + std::unique_ptr<Styles_Impl[]> pFound(new Styles_Impl[xIter->Count()]); + sal_uInt16 nFound = 0; + + SfxStyleSheetBase *pSource = xIter->First(); + while ( pSource ) + { + SfxStyleSheetBase *pDest = + pMyPool->Find( pSource->GetName(), pSource->GetFamily() ); + if ( !pDest ) + { + pDest = &pMyPool->Make( pSource->GetName(), + pSource->GetFamily(), pSource->GetMask()); + // Setting of parents, the next style + } + pFound[nFound].pSource = pSource; + pFound[nFound].pDest = pDest; + ++nFound; + pSource = xIter->Next(); + } + + for ( sal_uInt16 i = 0; i < nFound; ++i ) + { + pFound[i].pDest->GetItemSet().PutExtended(pFound[i].pSource->GetItemSet(), SfxItemState::DONTCARE, SfxItemState::DEFAULT); + if(pFound[i].pSource->HasParentSupport()) + pFound[i].pDest->SetParent(pFound[i].pSource->GetParent()); + if(pFound[i].pSource->HasFollowSupport()) + pFound[i].pDest->SetFollow(pFound[i].pSource->GetParent()); + } +} + +sfx2::StyleManager* SfxObjectShell::GetStyleManager() +{ + return nullptr; +} + +namespace +{ + class QueryTemplateBox + { + private: + std::unique_ptr<weld::MessageDialog> m_xQueryBox; + public: + QueryTemplateBox(weld::Window* pParent, const OUString& rMessage) + : m_xQueryBox(Application::CreateMessageDialog(pParent, VclMessageType::Question, VclButtonsType::NONE, rMessage)) + { + m_xQueryBox->add_button(SfxResId(STR_QRYTEMPL_UPDATE_BTN), RET_YES); + m_xQueryBox->add_button(SfxResId(STR_QRYTEMPL_KEEP_BTN), RET_NO); + m_xQueryBox->set_default_response(RET_YES); + m_xQueryBox->set_help_id(HID_QUERY_LOAD_TEMPLATE); + } + short run() { return m_xQueryBox->run(); } + }; +} + +void SfxObjectShell::UpdateFromTemplate_Impl( ) + +/* [Description] + + This internal method checks whether the document was created from a + template, and if this is newer than the document. If this is the case, + the user is asked if the Templates (StyleSheets) should be updated. + If this is answered positively, the StyleSheets are updated. +*/ + +{ + // Storage-medium? + SfxMedium *pFile = GetMedium(); + DBG_ASSERT( pFile, "cannot UpdateFromTemplate without medium" ); + if ( !pFile ) + return; + + if ( !comphelper::isFileUrl( pFile->GetName() ) ) + // update only for documents loaded from the local file system + return; + + // tdf#113935 - do not remove this line - somehow, it makes the process + // of switching from viewing a read-only document to opening it in writable + // mode much faster. + uno::Reference< embed::XStorage > xDocStor = pFile->GetStorage(false); + + // only for own storage formats + if ( !pFile->GetFilter() || !pFile->GetFilter()->IsOwnFormat() ) + return; + + const SfxUInt16Item* pUpdateDocItem = SfxItemSet::GetItem<SfxUInt16Item>(pFile->GetItemSet(), SID_UPDATEDOCMODE, false); + sal_Int16 bCanUpdateFromTemplate = pUpdateDocItem ? pUpdateDocItem->GetValue() : document::UpdateDocMode::NO_UPDATE; + + // created from template? + uno::Reference<document::XDocumentProperties> xDocProps(getDocProperties()); + const OUString aTemplName( xDocProps->getTemplateName() ); + OUString aTemplURL( xDocProps->getTemplateURL() ); + OUString aFoundName; + + if ( !aTemplName.isEmpty() || (!aTemplURL.isEmpty() && !IsReadOnly()) ) + { + // try to locate template, first using filename this must be done + // because writer global document uses this "great" idea to manage + // the templates of all parts in the master document but it is NOT + // an error if the template filename points not to a valid file + SfxDocumentTemplates aTempl; + if (!aTemplURL.isEmpty()) + { + try { + aFoundName = ::rtl::Uri::convertRelToAbs(GetMedium()->GetName(), + aTemplURL); + } catch (::rtl::MalformedUriException const&) { + assert(false); // don't think that's supposed to happen? + } + } + + if( aFoundName.isEmpty() && !aTemplName.isEmpty() ) + // if the template filename did not lead to success, + // try to get a file name for the logical template name + aTempl.GetFull( OUString(), aTemplName, aFoundName ); + } + + if ( aFoundName.isEmpty() ) + return; + + // check existence of template storage + aTemplURL = aFoundName; + + // should the document checked against changes in the template ? + if ( !IsQueryLoadTemplate() ) + return; + + bool bLoad = false; + + // load document properties of template + bool bOK = false; + util::DateTime aTemplDate; + try + { + Reference<document::XDocumentProperties> const + xTemplateDocProps( document::DocumentProperties::create( + ::comphelper::getProcessComponentContext())); + xTemplateDocProps->loadFromMedium(aTemplURL, + Sequence<beans::PropertyValue>()); + aTemplDate = xTemplateDocProps->getModificationDate(); + bOK = true; + } + catch (const Exception&) + { + TOOLS_INFO_EXCEPTION("sfx.doc", ""); + } + + // if modify date was read successfully + if ( bOK ) + { + // compare modify data of template with the last check date of the document + const util::DateTime aInfoDate( xDocProps->getTemplateDate() ); + if ( aTemplDate > aInfoDate ) + { + // ask user + if( bCanUpdateFromTemplate == document::UpdateDocMode::QUIET_UPDATE + || bCanUpdateFromTemplate == document::UpdateDocMode::FULL_UPDATE ) + bLoad = true; + else if ( bCanUpdateFromTemplate == document::UpdateDocMode::ACCORDING_TO_CONFIG ) + { + const OUString sMessage( SfxResId(STR_QRYTEMPL_MESSAGE).replaceAll( "$(ARG1)", aTemplName ) ); + vcl::Window *pWin = GetDialogParent(); + QueryTemplateBox aBox(pWin ? pWin->GetFrameWeld() : nullptr, sMessage); + if (RET_YES == aBox.run()) + bLoad = true; + } + + if( !bLoad ) + { + // user refuses, so don't ask again for this document + SetQueryLoadTemplate(false); + SetModified(); + } + } + } + + if ( !bLoad ) + return; + + // styles should be updated, create document in organizer mode to read in the styles + //TODO: testen! + SfxObjectShellLock xTemplDoc = CreateObjectByFactoryName( GetFactory().GetFactoryName(), SfxObjectCreateMode::ORGANIZER ); + xTemplDoc->DoInitNew(); + + // TODO/MBA: do we need a BaseURL? Then LoadFrom must be extended! + //xTemplDoc->SetBaseURL( aFoundName ); + + // TODO/LATER: make sure that we don't use binary templates! + SfxMedium aMedium( aFoundName, StreamMode::STD_READ ); + if ( xTemplDoc->LoadFrom( aMedium ) ) + { + // transfer styles from xTemplDoc to this document + // TODO/MBA: make sure that no BaseURL is needed in *this* document + LoadStyles(*xTemplDoc); + + // remember date/time of check + xDocProps->setTemplateDate(aTemplDate); + // TODO/LATER: new functionality to store document info is required ( didn't work for SO7 XML format ) + } +} + +bool SfxObjectShell::IsHelpDocument() const +{ + std::shared_ptr<const SfxFilter> pFilter = GetMedium()->GetFilter(); + return (pFilter && pFilter->GetFilterName() == "writer_web_HTML_help"); +} + +void SfxObjectShell::ResetFromTemplate( const OUString& rTemplateName, const OUString& rFileName ) +{ + // only care about resetting this data for LibreOffice formats otherwise + if ( !IsOwnStorageFormat( *GetMedium()) ) + return; + + uno::Reference<document::XDocumentProperties> xDocProps(getDocProperties()); + xDocProps->setTemplateURL( OUString() ); + xDocProps->setTemplateName( OUString() ); + xDocProps->setTemplateDate( util::DateTime() ); + xDocProps->resetUserData( OUString() ); + + // TODO/REFACTOR: + // Title? + + if( !comphelper::isFileUrl( rFileName ) ) + return; + + OUString aFoundName; + if( SfxGetpApp()->Get_Impl()->GetDocumentTemplates()->GetFull( OUString(), rTemplateName, aFoundName ) ) + { + INetURLObject aObj( rFileName ); + xDocProps->setTemplateURL( aObj.GetMainURL(INetURLObject::DecodeMechanism::ToIUri) ); + xDocProps->setTemplateName( rTemplateName ); + + ::DateTime now( ::DateTime::SYSTEM ); + xDocProps->setTemplateDate( now.GetUNODateTime() ); + + SetQueryLoadTemplate( true ); + } +} + +bool SfxObjectShell::IsQueryLoadTemplate() const +{ + return pImpl->bQueryLoadTemplate; +} + +bool SfxObjectShell::IsUseUserData() const +{ + return pImpl->bUseUserData; +} + +bool SfxObjectShell::IsUseThumbnailSave() const +{ + return pImpl->bUseThumbnailSave; +} + +void SfxObjectShell::SetQueryLoadTemplate( bool bNew ) +{ + if ( pImpl->bQueryLoadTemplate != bNew ) + SetModified(); + pImpl->bQueryLoadTemplate = bNew; +} + +void SfxObjectShell::SetUseUserData( bool bNew ) +{ + if ( pImpl->bUseUserData != bNew ) + SetModified(); + pImpl->bUseUserData = bNew; +} + +void SfxObjectShell::SetUseThumbnailSave( bool _bNew ) +{ + if ( pImpl->bUseThumbnailSave != _bNew ) + SetModified(); + pImpl->bUseThumbnailSave = _bNew; +} + +bool SfxObjectShell::IsLoadReadonly() const +{ + return pImpl->bLoadReadonly; +} + +bool SfxObjectShell::IsSaveVersionOnClose() const +{ + return pImpl->bSaveVersionOnClose; +} + +void SfxObjectShell::SetLoadReadonly( bool bNew ) +{ + if ( pImpl->bLoadReadonly != bNew ) + SetModified(); + pImpl->bLoadReadonly = bNew; +} + +void SfxObjectShell::SetSaveVersionOnClose( bool bNew ) +{ + if ( pImpl->bSaveVersionOnClose != bNew ) + SetModified(); + pImpl->bSaveVersionOnClose = bNew; +} + +sal_uInt32 SfxObjectShell::GetModifyPasswordHash() const +{ + return pImpl->m_nModifyPasswordHash; +} + +bool SfxObjectShell::SetModifyPasswordHash( sal_uInt32 nHash ) +{ + if ( ( !IsReadOnly() && !IsReadOnlyUI() ) + || !(pImpl->nFlagsInProgress & SfxLoadedFlags::MAINDOCUMENT ) ) + { + // the hash can be changed only in editable documents, + // or during loading of document + pImpl->m_nModifyPasswordHash = nHash; + return true; + } + + return false; +} + +const uno::Sequence< beans::PropertyValue >& SfxObjectShell::GetModifyPasswordInfo() const +{ + return pImpl->m_aModifyPasswordInfo; +} + +bool SfxObjectShell::SetModifyPasswordInfo( const uno::Sequence< beans::PropertyValue >& aInfo ) +{ + if ( ( !IsReadOnly() && !IsReadOnlyUI() ) + || !(pImpl->nFlagsInProgress & SfxLoadedFlags::MAINDOCUMENT ) ) + { + // the hash can be changed only in editable documents, + // or during loading of document + pImpl->m_aModifyPasswordInfo = aInfo; + return true; + } + + return false; +} + +void SfxObjectShell::SetModifyPasswordEntered( bool bEntered ) +{ + pImpl->m_bModifyPasswordEntered = bEntered; +} + +bool SfxObjectShell::IsModifyPasswordEntered() const +{ + return pImpl->m_bModifyPasswordEntered; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |