| /************************************************************** |
| * |
| * 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 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_framework.hxx" |
| |
| #include "services/autorecovery.hxx" |
| #include <loadenv/loadenv.hxx> |
| |
| //_______________________________________________ |
| // own includes |
| #include <loadenv/targethelper.hxx> |
| #include <pattern/frame.hxx> |
| #include <threadhelp/readguard.hxx> |
| #include <threadhelp/writeguard.hxx> |
| |
| #include <classes/resource.hrc> |
| #include <classes/fwkresid.hxx> |
| #include <protocols.h> |
| #include <properties.h> |
| #include <services.h> |
| |
| //_______________________________________________ |
| // interface includes |
| #include <com/sun/star/ucb/NameClash.hpp> |
| #include <com/sun/star/container/XNameAccess.hpp> |
| #include <com/sun/star/frame/XLoadable.hpp> |
| #include <com/sun/star/frame/XModel2.hpp> |
| #include <com/sun/star/frame/XModuleManager.hpp> |
| #include <com/sun/star/frame/XTitle.hpp> |
| #include <com/sun/star/frame/XFrame.hpp> |
| #include <com/sun/star/frame/XDispatchProvider.hpp> |
| #include <com/sun/star/frame/DispatchResultState.hpp> |
| #include <com/sun/star/frame/XNotifyingDispatch.hpp> |
| #include <com/sun/star/frame/XController.hpp> |
| #include <com/sun/star/frame/XModel.hpp> |
| #include <com/sun/star/frame/XStorable.hpp> |
| #include <com/sun/star/util/XModifiable.hpp> |
| #include <com/sun/star/util/XURLTransformer.hpp> |
| #include <com/sun/star/frame/XDesktop.hpp> |
| #include <com/sun/star/container/XHierarchicalNameAccess.hpp> |
| #include <com/sun/star/container/XNameContainer.hpp> |
| #include <com/sun/star/util/XChangesNotifier.hpp> |
| #include <com/sun/star/util/XChangesBatch.hpp> |
| #include <com/sun/star/beans/XPropertySet.hpp> |
| #include <com/sun/star/beans/PropertyAttribute.hpp> |
| #include <com/sun/star/container/XContainerQuery.hpp> |
| #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> |
| #include <com/sun/star/document/XDocumentRecovery.hpp> |
| #include <com/sun/star/util/XCloseable.hpp> |
| #include <com/sun/star/awt/XWindow2.hpp> |
| #include <com/sun/star/task/XStatusIndicatorFactory.hpp> |
| |
| //_______________________________________________ |
| // other includes |
| #include <comphelper/configurationhelper.hxx> |
| #include <comphelper/mediadescriptor.hxx> |
| #include <comphelper/namedvaluecollection.hxx> |
| #include <vcl/svapp.hxx> |
| #include <unotools/pathoptions.hxx> |
| #include <tools/link.hxx> |
| #include <tools/string.hxx> |
| #include <tools/diagnose_ex.h> |
| #include <unotools/tempfile.hxx> |
| #include <ucbhelper/content.hxx> |
| |
| #include <osl/time.h> |
| #include <vcl/msgbox.hxx> |
| #include <osl/file.hxx> |
| #include <unotools/bootstrap.hxx> |
| #include <unotools/configmgr.hxx> |
| #include <svl/documentlockfile.hxx> |
| #include <cppuhelper/exc_hlp.hxx> |
| |
| #include <tools/urlobj.hxx> |
| |
| #include <fwkdllapi.h> |
| |
| //_______________________________________________ |
| // namespaces |
| |
| #ifndef css |
| namespace css = ::com::sun::star; |
| #endif |
| |
| using ::com::sun::star::uno::Sequence; |
| using ::com::sun::star::uno::UNO_QUERY; |
| using ::com::sun::star::uno::UNO_QUERY_THROW; |
| using ::com::sun::star::uno::UNO_SET_THROW; |
| using ::com::sun::star::uno::Reference; |
| using ::com::sun::star::uno::Any; |
| using ::com::sun::star::beans::PropertyValue; |
| using ::com::sun::star::container::XEnumeration; |
| using ::com::sun::star::document::XDocumentRecovery; |
| using ::com::sun::star::frame::XModel2; |
| using ::com::sun::star::frame::XModel; |
| using ::com::sun::star::frame::XFrame; |
| using ::com::sun::star::frame::XController2; |
| using ::com::sun::star::frame::XLoadable; |
| using ::com::sun::star::frame::XStorable; |
| using ::com::sun::star::lang::XComponent; |
| |
| namespace fpf = ::framework::pattern::frame; |
| |
| |
| namespace framework |
| { |
| |
| //----------------------------------------------- |
| // recovery.xcu |
| static const ::rtl::OUString CFG_PACKAGE_RECOVERY = ::rtl::OUString::createFromAscii("org.openoffice.Office.Recovery/"); |
| static const ::rtl::OUString CFG_ENTRY_RECOVERYLIST = ::rtl::OUString::createFromAscii("RecoveryList" ); |
| static const ::rtl::OUString CFG_PATH_RECOVERYINFO = ::rtl::OUString::createFromAscii("RecoveryInfo" ); |
| static const ::rtl::OUString CFG_ENTRY_ENABLED = ::rtl::OUString::createFromAscii("Enabled" ); |
| static const ::rtl::OUString CFG_ENTRY_CRASHED = ::rtl::OUString::createFromAscii("Crashed" ); |
| static const ::rtl::OUString CFG_ENTRY_SESSIONDATA = ::rtl::OUString::createFromAscii("SessionData" ); |
| |
| static const ::rtl::OUString CFG_ENTRY_AUTOSAVE_ENABLED = ::rtl::OUString::createFromAscii("AutoSave/Enabled" ); |
| static const ::rtl::OUString CFG_ENTRY_AUTOSAVE_TIMEINTERVALL = ::rtl::OUString::createFromAscii("AutoSave/TimeIntervall" ); |
| |
| static const ::rtl::OUString CFG_PATH_AUTOSAVE = ::rtl::OUString::createFromAscii("AutoSave" ); |
| static const ::rtl::OUString CFG_ENTRY_MINSPACE_DOCSAVE = ::rtl::OUString::createFromAscii("MinSpaceDocSave" ); |
| static const ::rtl::OUString CFG_ENTRY_MINSPACE_CONFIGSAVE = ::rtl::OUString::createFromAscii("MinSpaceConfigSave" ); |
| |
| static const ::rtl::OUString CFG_PACKAGE_MODULES = ::rtl::OUString::createFromAscii("org.openoffice.Setup/Office/Factories"); |
| static const ::rtl::OUString CFG_ENTRY_REALDEFAULTFILTER = ::rtl::OUString::createFromAscii("ooSetupFactoryActualFilter" ); |
| |
| static const ::rtl::OUString CFG_ENTRY_PROP_TEMPURL = ::rtl::OUString::createFromAscii("TempURL" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_ORIGINALURL = ::rtl::OUString::createFromAscii("OriginalURL" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_TEMPLATEURL = ::rtl::OUString::createFromAscii("TemplateURL" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_FACTORYURL = ::rtl::OUString::createFromAscii("FactoryURL" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_MODULE = ::rtl::OUString::createFromAscii("Module" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_DOCUMENTSTATE = ::rtl::OUString::createFromAscii("DocumentState"); |
| static const ::rtl::OUString CFG_ENTRY_PROP_FILTER = ::rtl::OUString::createFromAscii("Filter" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_TITLE = ::rtl::OUString::createFromAscii("Title" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_ID = ::rtl::OUString::createFromAscii("ID" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_VIEWNAMES = ::rtl::OUString::createFromAscii("ViewNames" ); |
| |
| static const ::rtl::OUString FILTER_PROP_TYPE = ::rtl::OUString::createFromAscii("Type" ); |
| static const ::rtl::OUString FILTER_PROP_NAME = ::rtl::OUString::createFromAscii("Name" ); |
| static const ::rtl::OUString TYPE_PROP_EXTENSIONS = ::rtl::OUString::createFromAscii("Extensions" ); |
| static const ::rtl::OUString DOCINFO_PROP_TEMPLATE = ::rtl::OUString::createFromAscii("TemplateFileName"); |
| |
| // setup.xcu |
| static const ::rtl::OUString CFG_ENTRY_PROP_EMPTYDOCUMENTURL = ::rtl::OUString::createFromAscii("ooSetupFactoryEmptyDocumentURL"); |
| static const ::rtl::OUString CFG_ENTRY_PROP_DEFAULTFILTER = ::rtl::OUString::createFromAscii("ooSetupFactoryDefaultFilter" ); |
| static const ::rtl::OUString CFG_ENTRY_PROP_FACTORYSERVICE = ::rtl::OUString::createFromAscii("ooSetupFactoryDocumentService" ); |
| |
| static const ::rtl::OUString EVENT_ON_NEW = ::rtl::OUString::createFromAscii("OnNew" ); |
| static const ::rtl::OUString EVENT_ON_LOAD = ::rtl::OUString::createFromAscii("OnLoad" ); |
| static const ::rtl::OUString EVENT_ON_UNLOAD = ::rtl::OUString::createFromAscii("OnUnload" ); |
| static const ::rtl::OUString EVENT_ON_MODIFYCHANGED = ::rtl::OUString::createFromAscii("OnModifyChanged"); |
| static const ::rtl::OUString EVENT_ON_SAVE = ::rtl::OUString::createFromAscii("OnSave" ); |
| static const ::rtl::OUString EVENT_ON_SAVEAS = ::rtl::OUString::createFromAscii("OnSaveAs" ); |
| static const ::rtl::OUString EVENT_ON_SAVETO = ::rtl::OUString::createFromAscii("OnCopyTo" ); |
| static const ::rtl::OUString EVENT_ON_SAVEDONE = ::rtl::OUString::createFromAscii("OnSaveDone" ); |
| static const ::rtl::OUString EVENT_ON_SAVEASDONE = ::rtl::OUString::createFromAscii("OnSaveAsDone" ); |
| static const ::rtl::OUString EVENT_ON_SAVETODONE = ::rtl::OUString::createFromAscii("OnCopyToDone" ); |
| static const ::rtl::OUString EVENT_ON_SAVEFAILED = ::rtl::OUString::createFromAscii("OnSaveFailed" ); |
| static const ::rtl::OUString EVENT_ON_SAVEASFAILED = ::rtl::OUString::createFromAscii("OnSaveAsFailed" ); |
| static const ::rtl::OUString EVENT_ON_SAVETOFAILED = ::rtl::OUString::createFromAscii("OnCopyToFailed" ); |
| |
| static const ::rtl::OUString RECOVERY_ITEM_BASE_IDENTIFIER = ::rtl::OUString::createFromAscii("recovery_item_" ); |
| |
| static const ::rtl::OUString CMD_PROTOCOL = ::rtl::OUString::createFromAscii("vnd.sun.star.autorecovery:"); |
| |
| static const ::rtl::OUString CMD_DO_AUTO_SAVE = ::rtl::OUString::createFromAscii("/doAutoSave" ); // force AutoSave ignoring the AutoSave timer |
| static const ::rtl::OUString CMD_DO_PREPARE_EMERGENCY_SAVE = ::rtl::OUString::createFromAscii("/doPrepareEmergencySave" ); // prepare the office for the following EmergencySave step (hide windows etcpp.) |
| static const ::rtl::OUString CMD_DO_EMERGENCY_SAVE = ::rtl::OUString::createFromAscii("/doEmergencySave" ); // do EmergencySave on crash |
| static const ::rtl::OUString CMD_DO_RECOVERY = ::rtl::OUString::createFromAscii("/doAutoRecovery" ); // recover all crashed documents |
| static const ::rtl::OUString CMD_DO_ENTRY_BACKUP = ::rtl::OUString::createFromAscii("/doEntryBackup" ); // try to store a temp or original file to a user defined location |
| static const ::rtl::OUString CMD_DO_ENTRY_CLEANUP = ::rtl::OUString::createFromAscii("/doEntryCleanUp" ); // remove the specified entry from the recovery cache |
| static const ::rtl::OUString CMD_DO_SESSION_SAVE = ::rtl::OUString::createFromAscii("/doSessionSave" ); // save all open documents if e.g. a window manager closes an user session |
| static const ::rtl::OUString CMD_DO_SESSION_QUIET_QUIT = ::rtl::OUString::createFromAscii("/doSessionQuietQuit" ); // let the current session be quietly closed ( the saving should be done using doSessionSave previously ) if e.g. a window manager closes an user session |
| static const ::rtl::OUString CMD_DO_SESSION_RESTORE = ::rtl::OUString::createFromAscii("/doSessionRestore" ); // restore a saved user session from disc |
| static const ::rtl::OUString CMD_DO_DISABLE_RECOVERY = ::rtl::OUString::createFromAscii("/disableRecovery" ); // disable recovery and auto save (!) temp. for this office session |
| static const ::rtl::OUString CMD_DO_SET_AUTOSAVE_STATE = ::rtl::OUString::createFromAscii("/setAutoSaveState" ); // disable/enable auto save (not crash save) for this office session |
| |
| static const ::rtl::OUString REFERRER_USER = ::rtl::OUString::createFromAscii("private:user"); |
| |
| static const ::rtl::OUString PROP_DISPATCH_ASYNCHRON = ::rtl::OUString::createFromAscii("DispatchAsynchron"); |
| static const ::rtl::OUString PROP_PROGRESS = ::rtl::OUString::createFromAscii("StatusIndicator" ); |
| static const ::rtl::OUString PROP_SAVEPATH = ::rtl::OUString::createFromAscii("SavePath" ); |
| static const ::rtl::OUString PROP_ENTRY_ID = ::rtl::OUString::createFromAscii("EntryID" ); |
| static const ::rtl::OUString PROP_DBG_MAKE_IT_FASTER = ::rtl::OUString::createFromAscii("DBGMakeItFaster" ); |
| static const ::rtl::OUString PROP_AUTOSAVE_STATE = ::rtl::OUString::createFromAscii("AutoSaveState" ); |
| |
| static const ::rtl::OUString OPERATION_START = ::rtl::OUString::createFromAscii("start" ); |
| static const ::rtl::OUString OPERATION_STOP = ::rtl::OUString::createFromAscii("stop" ); |
| static const ::rtl::OUString OPERATION_UPDATE = ::rtl::OUString::createFromAscii("update"); |
| |
| static const sal_Int32 MIN_DISCSPACE_DOCSAVE = 5; // [MB] |
| static const sal_Int32 MIN_DISCSPACE_CONFIGSAVE = 1; // [MB] |
| static const sal_Int32 RETRY_STORE_ON_FULL_DISC_FOREVER = 300; // not forever ... but often enough .-) |
| static const sal_Int32 RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL = 3; // in case FULL DISC does not seam the real problem |
| static const sal_Int32 GIVE_UP_RETRY = 1; // in case FULL DISC does not seam the real problem |
| |
| #define SAVE_IN_PROGRESS sal_True |
| #define SAVE_FINISHED sal_False |
| |
| #define LOCK_FOR_CACHE_ADD_REMOVE sal_True |
| #define LOCK_FOR_CACHE_USE sal_False |
| |
| #define MIN_TIME_FOR_USER_IDLE 10000 // 10s user idle |
| |
| // enable the following defines in case you whish to simulate a full disc for debug purposes .-) |
| |
| // this define throws everytime a document is stored or a configuration change |
| // should be flushed an exception ... so the special error handler for this scenario is triggered |
| // #define TRIGGER_FULL_DISC_CHECK |
| |
| // force "return sal_False" for the method impl_enoughDiscSpace(). |
| // #define SIMULATE_FULL_DISC |
| |
| //----------------------------------------------- |
| // #define ENABLE_RECOVERY_LOGGING |
| #undef ENABLE_RECOVERY_LOGGING |
| #ifdef ENABLE_RECOVERY_LOGGING |
| #define LOGFILE_RECOVERY "recovery.log" |
| |
| #define LOG_RECOVERY(MSG) \ |
| { \ |
| WRITE_LOGFILE(LOGFILE_RECOVERY, MSG) \ |
| WRITE_LOGFILE(LOGFILE_RECOVERY, "\n") \ |
| } |
| #else |
| #undef LOGFILE_RECOVERY |
| #define LOG_RECOVERY(MSG) |
| #endif |
| |
| //----------------------------------------------- |
| // TODO debug - remove it! |
| class DbgListener : private ThreadHelpBase |
| , public ::cppu::OWeakObject |
| , public css::frame::XStatusListener |
| { |
| public: |
| |
| FWK_DECLARE_XINTERFACE |
| |
| DbgListener() |
| { |
| WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::ctor()\n\n") |
| } |
| |
| virtual ~DbgListener() |
| { |
| WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::dtor()\n\n") |
| } |
| |
| void startListening(const css::uno::Reference< css::frame::XDispatch >& xBroadcaster) |
| { |
| ::rtl::OUStringBuffer sMsg1(256); |
| sMsg1.appendAscii("//**********************************************************************************\n"); |
| sMsg1.appendAscii("start listening\n{\n"); |
| WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg1.makeStringAndClear())) |
| |
| ++m_refCount; |
| |
| css::util::URL aURL; |
| aURL.Complete = ::rtl::OUString(); |
| xBroadcaster->addStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL); |
| |
| --m_refCount; |
| |
| ::rtl::OUStringBuffer sMsg2(256); |
| sMsg2.appendAscii("}\nstart listening\n"); |
| sMsg2.appendAscii("//**********************************************************************************\n"); |
| WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg2.makeStringAndClear())) |
| } |
| |
| virtual void SAL_CALL disposing(const css::lang::EventObject&) |
| throw(css::uno::RuntimeException) |
| { |
| WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::dtor()\n\n") |
| } |
| |
| virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& aEvent) |
| throw(css::uno::RuntimeException) |
| { |
| ::rtl::OUStringBuffer sMsg(256); |
| |
| sMsg.appendAscii("//**********************************************************************************\n"); |
| |
| sMsg.appendAscii("FeatureURL = \""); |
| sMsg.append (aEvent.FeatureURL.Complete); |
| sMsg.appendAscii("\"\n"); |
| |
| sMsg.appendAscii("State = ["); |
| sal_Int32 nState = -1; |
| aEvent.State >>= nState; |
| if (nState==-1) |
| { |
| sMsg.appendAscii("?-"); |
| sMsg.append (::rtl::OUString::valueOf(nState)); |
| sMsg.appendAscii("-? "); |
| } |
| if (nState==0) |
| sMsg.appendAscii("UNKNOWN "); |
| if ((nState & 1)==1) |
| sMsg.appendAscii("MODIFIED "); |
| if ((nState & 2)==2) |
| sMsg.appendAscii("TRYIT "); |
| if ((nState & 4)==4) |
| sMsg.appendAscii("HANDLED "); |
| if ((nState & 8)==8) |
| sMsg.appendAscii("POSTPONED "); |
| if ((nState & 16)==16) |
| sMsg.appendAscii("INCOMPLETE "); |
| if ((nState & 32)==32) |
| sMsg.appendAscii("DAMAGED "); |
| sMsg.appendAscii("]\n"); |
| /* |
| sMsg.appendAscii("IsEnabled = \""); |
| sMsg.append (::rtl::OUString::valueOf(aEvent.IsEnabled)); |
| sMsg.appendAscii("\"\n"); |
| |
| sMsg.appendAscii("Requery = \""); |
| sMsg.append (::rtl::OUString::valueOf(aEvent.Requery)); |
| sMsg.appendAscii("\"\n"); |
| */ |
| sMsg.appendAscii("\n"); |
| |
| WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg.makeStringAndClear())) |
| } |
| }; |
| |
| //----------------------------------------------- |
| class CacheLockGuard |
| { |
| private: |
| |
| // holds the outside calli alive, so it's shared resources |
| // are valid everytimes |
| css::uno::Reference< css::uno::XInterface > m_xOwner; |
| |
| // mutex shared with outside calli ! |
| LockHelper& m_rSharedMutex; |
| |
| // this variable knows the state of the "cache lock" |
| sal_Int32& m_rCacheLock; |
| |
| // to prevent increasing/decreasing of m_rCacheLock more then ones |
| // we must know if THIS guard has an actual lock set there ! |
| sal_Bool m_bLockedByThisGuard; |
| |
| public: |
| |
| CacheLockGuard(AutoRecovery* pOwner , |
| LockHelper& rMutex , |
| sal_Int32& rCacheLock , |
| sal_Bool bLockForAddRemoveVectorItems); |
| ~CacheLockGuard(); |
| |
| void lock(sal_Bool bLockForAddRemoveVectorItems); |
| void unlock(); |
| }; |
| |
| //----------------------------------------------- |
| CacheLockGuard::CacheLockGuard(AutoRecovery* pOwner , |
| LockHelper& rMutex , |
| sal_Int32& rCacheLock , |
| sal_Bool bLockForAddRemoveVectorItems) |
| : m_xOwner (static_cast< css::frame::XDispatch* >(pOwner)) |
| , m_rSharedMutex (rMutex ) |
| , m_rCacheLock (rCacheLock ) |
| , m_bLockedByThisGuard(sal_False ) |
| { |
| lock(bLockForAddRemoveVectorItems); |
| } |
| |
| //----------------------------------------------- |
| CacheLockGuard::~CacheLockGuard() |
| { |
| unlock(); |
| m_xOwner.clear(); |
| } |
| |
| //----------------------------------------------- |
| void CacheLockGuard::lock(sal_Bool bLockForAddRemoveVectorItems) |
| { |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_rSharedMutex); |
| |
| if (m_bLockedByThisGuard) |
| return; |
| |
| // This cache lock is needed only to prevent us from removing/adding |
| // items from/into the recovery cache ... during it's used at another code place |
| // for iterating .-) |
| |
| // Modifying of item properties is allowed and sometimes needed! |
| // So we should detect only the dangerous state of concurrent add/remove |
| // requests and throw an exception then ... which can of course break the whole |
| // operation. On the other side a crash reasoned by an invalid stl iterator |
| // will have the same effect .-) |
| |
| if ( |
| (m_rCacheLock > 0 ) && |
| (bLockForAddRemoveVectorItems) |
| ) |
| { |
| OSL_ENSURE(sal_False, "Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp."); |
| throw css::uno::RuntimeException( |
| ::rtl::OUString::createFromAscii("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp."), |
| m_xOwner); |
| } |
| |
| ++m_rCacheLock; |
| m_bLockedByThisGuard = sal_True; |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| void CacheLockGuard::unlock() |
| { |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_rSharedMutex); |
| |
| if ( ! m_bLockedByThisGuard) |
| return; |
| |
| --m_rCacheLock; |
| m_bLockedByThisGuard = sal_False; |
| |
| if (m_rCacheLock < 0) |
| { |
| OSL_ENSURE(sal_False, "Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)"); |
| throw css::uno::RuntimeException( |
| ::rtl::OUString::createFromAscii("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)"), |
| m_xOwner); |
| } |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| DispatchParams::DispatchParams() |
| : m_nWorkingEntryID(-1) |
| { |
| }; |
| |
| //----------------------------------------------- |
| DispatchParams::DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs , |
| const css::uno::Reference< css::uno::XInterface >& xOwner) |
| { |
| m_nWorkingEntryID = lArgs.getUnpackedValueOrDefault(PROP_ENTRY_ID, (sal_Int32)-1 ); |
| m_xProgress = lArgs.getUnpackedValueOrDefault(PROP_PROGRESS, css::uno::Reference< css::task::XStatusIndicator >()); |
| m_sSavePath = lArgs.getUnpackedValueOrDefault(PROP_SAVEPATH, ::rtl::OUString() ); |
| m_xHoldRefForAsyncOpAlive = xOwner; |
| }; |
| |
| //----------------------------------------------- |
| DispatchParams::DispatchParams(const DispatchParams& rCopy) |
| { |
| m_xProgress = rCopy.m_xProgress; |
| m_sSavePath = rCopy.m_sSavePath; |
| m_nWorkingEntryID = rCopy.m_nWorkingEntryID; |
| m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive; |
| }; |
| |
| //----------------------------------------------- |
| DispatchParams::~DispatchParams() |
| {}; |
| |
| //----------------------------------------------- |
| DispatchParams& DispatchParams::operator=(const DispatchParams& rCopy) |
| { |
| m_xProgress = rCopy.m_xProgress; |
| m_sSavePath = rCopy.m_sSavePath; |
| m_nWorkingEntryID = rCopy.m_nWorkingEntryID; |
| m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive; |
| return *this; |
| } |
| |
| //----------------------------------------------- |
| void DispatchParams::forget() |
| { |
| m_sSavePath = ::rtl::OUString(); |
| m_nWorkingEntryID = -1; |
| m_xProgress.clear(); |
| m_xHoldRefForAsyncOpAlive.clear(); |
| }; |
| |
| //----------------------------------------------- |
| DEFINE_XINTERFACE_1(DbgListener , |
| OWeakObject , |
| DIRECT_INTERFACE(css::frame::XStatusListener)) |
| |
| //----------------------------------------------- |
| DEFINE_XINTERFACE_10(AutoRecovery , |
| OWeakObject , |
| DIRECT_INTERFACE (css::lang::XTypeProvider ), |
| DIRECT_INTERFACE (css::lang::XServiceInfo ), |
| DIRECT_INTERFACE (css::frame::XDispatch ), |
| DIRECT_INTERFACE (css::beans::XMultiPropertySet ), |
| DIRECT_INTERFACE (css::beans::XFastPropertySet ), |
| DIRECT_INTERFACE (css::beans::XPropertySet ), |
| DIRECT_INTERFACE (css::document::XEventListener ), |
| DIRECT_INTERFACE (css::util::XChangesListener ), |
| DIRECT_INTERFACE (css::util::XModifyListener ), |
| DERIVED_INTERFACE(css::lang::XEventListener, css::document::XEventListener)) |
| |
| //----------------------------------------------- |
| DEFINE_XTYPEPROVIDER_6(AutoRecovery , |
| css::lang::XTypeProvider , |
| css::lang::XServiceInfo , |
| css::frame::XDispatch , |
| css::beans::XMultiPropertySet, |
| css::beans::XFastPropertySet , |
| css::beans::XPropertySet ) |
| |
| //----------------------------------------------- |
| DEFINE_XSERVICEINFO_ONEINSTANCESERVICE(AutoRecovery , |
| ::cppu::OWeakObject , |
| SERVICENAME_AUTORECOVERY , |
| IMPLEMENTATIONNAME_AUTORECOVERY) |
| |
| //----------------------------------------------- |
| DEFINE_INIT_SERVICE( |
| AutoRecovery, |
| { |
| /*Attention |
| I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() |
| to create a new instance of this class by our own supported service factory. |
| see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! |
| */ |
| |
| // read configuration to know if autosave/recovery is on/off etcpp... |
| implts_readConfig(); |
| |
| implts_startListening(); |
| |
| // establish callback for our internal used timer. |
| // Note: Its only active, if the timer will be started ... |
| m_aTimer.SetTimeoutHdl(LINK(this, AutoRecovery, implts_timerExpired)); |
| /* |
| DbgListener* pListener = new DbgListener(); |
| pListener->startListening(this); |
| */ |
| } |
| ) |
| |
| //----------------------------------------------- |
| AutoRecovery::AutoRecovery(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR) |
| : ThreadHelpBase (&Application::GetSolarMutex() ) |
| , ::cppu::OBroadcastHelper ( m_aLock.getShareableOslMutex() ) |
| , ::cppu::OPropertySetHelper( *(static_cast< ::cppu::OBroadcastHelper* >(this)) ) |
| , ::cppu::OWeakObject ( ) |
| , m_xSMGR (xSMGR ) |
| , m_bListenForDocEvents (sal_False ) |
| , m_bListenForConfigChanges (sal_False ) |
| , m_nAutoSaveTimeIntervall (0 ) |
| , m_eJob (AutoRecovery::E_NO_JOB ) |
| , m_aAsyncDispatcher ( LINK( this, AutoRecovery, implts_asyncDispatch ) ) |
| , m_eTimerType (E_DONT_START_TIMER ) |
| , m_nIdPool (0 ) |
| , m_lListener (m_aLock.getShareableOslMutex() ) |
| , m_nDocCacheLock (0 ) |
| , m_nMinSpaceDocSave (MIN_DISCSPACE_DOCSAVE ) |
| , m_nMinSpaceConfigSave (MIN_DISCSPACE_CONFIGSAVE ) |
| |
| #if OSL_DEBUG_LEVEL > 1 |
| , m_dbg_bMakeItFaster (sal_False ) |
| #endif |
| { |
| } |
| |
| //----------------------------------------------- |
| AutoRecovery::~AutoRecovery() |
| { |
| implts_stopTimer(); |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::dispatch(const css::util::URL& aURL , |
| const css::uno::Sequence< css::beans::PropertyValue >& lArguments) |
| throw(css::uno::RuntimeException) |
| { |
| LOG_RECOVERY("AutoRecovery::dispatch() starts ...") |
| LOG_RECOVERY(U2B(aURL.Complete).getStr()) |
| |
| // valid request ? |
| sal_Int32 eNewJob = AutoRecovery::implst_classifyJob(aURL); |
| if (eNewJob == AutoRecovery::E_NO_JOB) |
| return; |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| // still running operation ... ignoring AUTO_SAVE. |
| // All other requests has higher prio! |
| if ( |
| ( m_eJob != AutoRecovery::E_NO_JOB ) && |
| ((m_eJob & AutoRecovery::E_AUTO_SAVE ) != AutoRecovery::E_AUTO_SAVE) |
| ) |
| { |
| LOG_WARNING("AutoRecovery::dispatch()", "There is already an asynchronous dispatch() running. New request will be ignored!") |
| return; |
| } |
| |
| ::comphelper::SequenceAsHashMap lArgs(lArguments); |
| |
| // check if somewhere wish to disable recovery temp. for this office session |
| // This can be done immediatly ... must not been done asynchronous. |
| if ((eNewJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY) |
| { |
| // it's important to set a flag internaly, so AutoRecovery will be supressed - even if it's requested. |
| m_eJob |= eNewJob; |
| implts_stopTimer(); |
| implts_stopListening(); |
| return; |
| } |
| |
| // disable/enable AutoSave for this office session only |
| // independend from the configuration entry. |
| if ((eNewJob & AutoRecovery::E_SET_AUTOSAVE_STATE) == AutoRecovery::E_SET_AUTOSAVE_STATE) |
| { |
| sal_Bool bOn = lArgs.getUnpackedValueOrDefault(PROP_AUTOSAVE_STATE, (sal_Bool)sal_True); |
| if (bOn) |
| { |
| // dont enable AutoSave hardly ! |
| // reload configuration to know the current state. |
| implts_readAutoSaveConfig(); |
| implts_updateTimer(); |
| // can it happen that might be the listener was stopped ? .-) |
| // make sure it runs always ... even if AutoSave itself was disabled temporarly. |
| implts_startListening(); |
| } |
| else |
| { |
| implts_stopTimer(); |
| m_eJob &= ~AutoRecovery::E_AUTO_SAVE; |
| m_eTimerType = AutoRecovery::E_DONT_START_TIMER; |
| } |
| return; |
| } |
| |
| m_eJob |= eNewJob; |
| |
| sal_Bool bAsync = lArgs.getUnpackedValueOrDefault(PROP_DISPATCH_ASYNCHRON, (sal_Bool)sal_False); |
| DispatchParams aParams (lArgs, static_cast< css::frame::XDispatch* >(this)); |
| |
| // Hold this instance alive till the asynchronous operation will be finished. |
| if (bAsync) |
| m_aDispatchParams = aParams; |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| if (bAsync) |
| m_aAsyncDispatcher.Post(0); |
| else |
| implts_dispatch(aParams); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_dispatch(const DispatchParams& aParams) |
| { |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| sal_Int32 eJob = m_eJob; |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| // in case a new dispatch overwrites a may ba active AutoSave session |
| // we must restore this session later. see below ... |
| sal_Bool bWasAutoSaveActive = ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE); |
| |
| // On the other side it make no sense to reactivate the AutoSave operation |
| // if the new dispatch indicates a final decision ... |
| // E.g. an EmergencySave/SessionSave indicates the end of life of the current office session. |
| // It make no sense to reactivate an AutoSave then. |
| // But a Recovery or SessionRestore should reactivate a may be already active AutoSave. |
| sal_Bool bAllowAutoSaveReactivation = sal_True; |
| |
| implts_stopTimer(); |
| implts_stopListening(); |
| |
| implts_informListener(eJob, |
| AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_START, NULL)); |
| |
| try |
| { |
| // if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE) |
| // Auto save is called from our internal timer ... not via dispatch() API ! |
| // else |
| if ( |
| ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE) && |
| ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY ) != AutoRecovery::E_DISABLE_AUTORECOVERY ) |
| ) |
| { |
| LOG_RECOVERY("... prepare emergency save ...") |
| bAllowAutoSaveReactivation = sal_False; |
| implts_prepareEmergencySave(); |
| } |
| else |
| if ( |
| ((eJob & AutoRecovery::E_EMERGENCY_SAVE ) == AutoRecovery::E_EMERGENCY_SAVE ) && |
| ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) |
| ) |
| { |
| LOG_RECOVERY("... do emergency save ...") |
| bAllowAutoSaveReactivation = sal_False; |
| implts_doEmergencySave(aParams); |
| } |
| else |
| if ( |
| ((eJob & AutoRecovery::E_RECOVERY ) == AutoRecovery::E_RECOVERY ) && |
| ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) |
| ) |
| { |
| LOG_RECOVERY("... do recovery ...") |
| implts_doRecovery(aParams); |
| } |
| else |
| if ( |
| ((eJob & AutoRecovery::E_SESSION_SAVE ) == AutoRecovery::E_SESSION_SAVE ) && |
| ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) |
| ) |
| { |
| LOG_RECOVERY("... do session save ...") |
| bAllowAutoSaveReactivation = sal_False; |
| implts_doSessionSave(aParams); |
| } |
| else |
| if ( |
| ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT ) == AutoRecovery::E_SESSION_QUIET_QUIT ) && |
| ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) |
| ) |
| { |
| LOG_RECOVERY("... do session quiet quit ...") |
| bAllowAutoSaveReactivation = sal_False; |
| implts_doSessionQuietQuit(aParams); |
| } |
| else |
| if ( |
| ((eJob & AutoRecovery::E_SESSION_RESTORE ) == AutoRecovery::E_SESSION_RESTORE ) && |
| ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) |
| ) |
| { |
| LOG_RECOVERY("... do session restore ...") |
| implts_doSessionRestore(aParams); |
| } |
| else |
| if ( |
| ((eJob & AutoRecovery::E_ENTRY_BACKUP ) == AutoRecovery::E_ENTRY_BACKUP ) && |
| ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) |
| ) |
| implts_backupWorkingEntry(aParams); |
| else |
| if ( |
| ((eJob & AutoRecovery::E_ENTRY_CLEANUP ) == AutoRecovery::E_ENTRY_CLEANUP ) && |
| ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) |
| ) |
| implts_cleanUpWorkingEntry(aParams); |
| } |
| catch(const css::uno::RuntimeException& exRun) |
| { throw exRun; } |
| catch(const css::uno::Exception&) |
| {} // TODO better error handling |
| |
| implts_informListener(eJob, |
| AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_STOP, NULL)); |
| |
| // SAFE -> ---------------------------------- |
| aWriteLock.lock(); |
| m_eJob = E_NO_JOB; |
| if ( |
| (bAllowAutoSaveReactivation) && |
| (bWasAutoSaveActive ) |
| ) |
| { |
| m_eJob |= AutoRecovery::E_AUTO_SAVE; |
| } |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| // depends on bAllowAutoSaveReactivation implicitly by looking on m_eJob=E_AUTO_SAVE! see before ... |
| implts_updateTimer(); |
| |
| if (bAllowAutoSaveReactivation) |
| implts_startListening(); |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener, |
| const css::util::URL& aURL ) |
| throw(css::uno::RuntimeException) |
| { |
| if (!xListener.is()) |
| throw css::uno::RuntimeException(::rtl::OUString::createFromAscii("Invalid listener reference."), static_cast< css::frame::XDispatch* >(this)); |
| // container is threadsafe by using a shared mutex! |
| m_lListener.addInterface(aURL.Complete, xListener); |
| |
| // REENTRANT !? -> -------------------------------- |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // THREAD SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| |
| AutoRecovery::TDocumentList::iterator pIt; |
| for( pIt = m_lDocCache.begin(); |
| pIt != m_lDocCache.end() ; |
| ++pIt ) |
| { |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| css::frame::FeatureStateEvent aEvent = AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_UPDATE, &rInfo); |
| |
| // <- SAFE ------------------------------ |
| aReadLock.unlock(); |
| xListener->statusChanged(aEvent); |
| aReadLock.lock(); |
| // SAFE -> ------------------------------ |
| } |
| |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener, |
| const css::util::URL& aURL ) |
| throw(css::uno::RuntimeException) |
| { |
| if (!xListener.is()) |
| throw css::uno::RuntimeException(::rtl::OUString::createFromAscii("Invalid listener reference."), static_cast< css::frame::XDispatch* >(this)); |
| // container is threadsafe by using a shared mutex! |
| m_lListener.removeInterface(aURL.Complete, xListener); |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::notifyEvent(const css::document::EventObject& aEvent) |
| throw(css::uno::RuntimeException) |
| { |
| css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY); |
| |
| // new document => put it into the internal list |
| if ( |
| (aEvent.EventName.equals(EVENT_ON_NEW )) || |
| (aEvent.EventName.equals(EVENT_ON_LOAD)) |
| ) |
| { |
| implts_registerDocument(xDocument); |
| } |
| // document modified => set its modify state new (means modified against the original file!) |
| else |
| if (aEvent.EventName.equals(EVENT_ON_MODIFYCHANGED)) |
| { |
| implts_updateModifiedState(xDocument); |
| } |
| /* at least one document starts saving process => |
| Our application code isnt ready for multiple save requests |
| at the same time. So we have to supress our AutoSave feature |
| for the moment, till this other save requests will be finished. |
| */ |
| else |
| if ( |
| (aEvent.EventName.equals(EVENT_ON_SAVE )) || |
| (aEvent.EventName.equals(EVENT_ON_SAVEAS)) || |
| (aEvent.EventName.equals(EVENT_ON_SAVETO)) |
| ) |
| { |
| implts_updateDocumentUsedForSavingState(xDocument, SAVE_IN_PROGRESS); |
| } |
| // document saved => remove tmp. files - but hold config entries alive! |
| else |
| if ( |
| (aEvent.EventName.equals(EVENT_ON_SAVEDONE )) || |
| (aEvent.EventName.equals(EVENT_ON_SAVEASDONE)) |
| ) |
| { |
| implts_markDocumentAsSaved(xDocument); |
| implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); |
| } |
| /* document saved as copy => mark it as "non used by concurrent save operation". |
| so we can try to create a backup copy if next time AutoSave is started too. |
| Dont remove temp. files or change the modified state of the document! |
| It was not realy saved to the original file ... |
| */ |
| else |
| if (aEvent.EventName.equals(EVENT_ON_SAVETODONE)) |
| { |
| implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); |
| } |
| // If saving of a document failed by an error ... we have to save this document |
| // by ourself next time AutoSave or EmergencySave is triggered. |
| // But we can reset the state "used for other save requests". Otherwhise |
| // these documents will never be saved! |
| else |
| if ( |
| (aEvent.EventName.equals(EVENT_ON_SAVEFAILED )) || |
| (aEvent.EventName.equals(EVENT_ON_SAVEASFAILED)) || |
| (aEvent.EventName.equals(EVENT_ON_SAVETOFAILED)) |
| ) |
| { |
| implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); |
| } |
| // document closed => remove temp. files and configuration entries |
| else |
| if (aEvent.EventName.equals(EVENT_ON_UNLOAD)) |
| { |
| implts_deregisterDocument(xDocument, sal_True); // sal_True => stop listening for disposing() ! |
| } |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::changesOccurred(const css::util::ChangesEvent& aEvent) |
| throw(css::uno::RuntimeException) |
| { |
| const css::uno::Sequence< css::util::ElementChange > lChanges (aEvent.Changes); |
| const css::util::ElementChange* pChanges = lChanges.getConstArray(); |
| |
| sal_Int32 c = lChanges.getLength(); |
| sal_Int32 i = 0; |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| // Changes of the configuration must be ignored if AutoSave/Recovery was disabled for this |
| // office session. That can happen if e.g. the command line arguments "-norestore" or "-headless" |
| // was set. |
| if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY) |
| return; |
| |
| for (i=0; i<c; ++i) |
| { |
| ::rtl::OUString sPath; |
| pChanges[i].Accessor >>= sPath; |
| |
| if (sPath.equals(CFG_ENTRY_AUTOSAVE_ENABLED)) |
| { |
| sal_Bool bEnabled = sal_False; |
| if (pChanges[i].Element >>= bEnabled) |
| { |
| if (bEnabled) |
| { |
| m_eJob |= AutoRecovery::E_AUTO_SAVE; |
| m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; |
| } |
| else |
| { |
| m_eJob &= ~AutoRecovery::E_AUTO_SAVE; |
| m_eTimerType = AutoRecovery::E_DONT_START_TIMER; |
| } |
| } |
| } |
| else |
| if (sPath.equals(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL)) |
| pChanges[i].Element >>= m_nAutoSaveTimeIntervall; |
| } |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| // Note: This call stops the timer and starts it again. |
| // But it checks the different timer states internaly and |
| // may be supress the restart! |
| implts_updateTimer(); |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::modified(const css::lang::EventObject& aEvent) |
| throw(css::uno::RuntimeException) |
| { |
| css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY); |
| if (! xDocument.is()) |
| return; |
| |
| implts_markDocumentModifiedAgainstLastBackup(xDocument); |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::disposing(const css::lang::EventObject& aEvent) |
| throw(css::uno::RuntimeException) |
| { |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| if (aEvent.Source == m_xNewDocBroadcaster) |
| { |
| m_xNewDocBroadcaster.clear(); |
| return; |
| } |
| |
| if (aEvent.Source == m_xRecoveryCFG) |
| { |
| m_xRecoveryCFG.clear(); |
| return; |
| } |
| |
| // dispose from one of our cached documents ? |
| // Normaly they should send a OnUnload message ... |
| // But some stacktraces shows another possible use case .-) |
| css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY); |
| if (xDocument.is()) |
| { |
| implts_deregisterDocument(xDocument, sal_False); // sal_False => dont call removeEventListener() .. because it's not needed here |
| return; |
| } |
| |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| css::uno::Reference< css::container::XNameAccess > AutoRecovery::implts_openConfig() |
| { |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| if (m_xRecoveryCFG.is()) |
| return m_xRecoveryCFG; |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| // throws a RuntimeException if an error occure! |
| css::uno::Reference< css::container::XNameAccess > xCFG( |
| ::comphelper::ConfigurationHelper::openConfig(xSMGR, CFG_PACKAGE_RECOVERY, ::comphelper::ConfigurationHelper::E_STANDARD), |
| css::uno::UNO_QUERY); |
| |
| sal_Int32 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE; |
| sal_Int32 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE; |
| |
| try |
| { |
| ::comphelper::ConfigurationHelper::readDirectKey(xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_AUTOSAVE, |
| CFG_ENTRY_MINSPACE_DOCSAVE, |
| ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceDocSave; |
| |
| ::comphelper::ConfigurationHelper::readDirectKey(xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_AUTOSAVE, |
| CFG_ENTRY_MINSPACE_CONFIGSAVE, |
| ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceConfigSave; |
| } |
| catch(const css::uno::Exception&) |
| { |
| // These config keys are not sooooo important, that |
| // we are interested on errors here realy .-) |
| nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE; |
| nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE; |
| } |
| |
| // SAFE -> ---------------------------------- |
| aWriteLock.lock(); |
| m_xRecoveryCFG = xCFG; |
| m_nMinSpaceDocSave = nMinSpaceDocSave; |
| m_nMinSpaceConfigSave = nMinSpaceConfigSave; |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| return xCFG; |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_readAutoSaveConfig() |
| { |
| css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY); |
| |
| // AutoSave [bool] |
| sal_Bool bEnabled = sal_False; |
| xCommonRegistry->getByHierarchicalName(CFG_ENTRY_AUTOSAVE_ENABLED) >>= bEnabled; |
| |
| // SAFE -> ------------------------------ |
| WriteGuard aWriteLock(m_aLock); |
| if (bEnabled) |
| { |
| m_eJob |= AutoRecovery::E_AUTO_SAVE; |
| m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; |
| } |
| else |
| { |
| m_eJob &= ~AutoRecovery::E_AUTO_SAVE; |
| m_eTimerType = AutoRecovery::E_DONT_START_TIMER; |
| } |
| aWriteLock.unlock(); |
| // <- SAFE ------------------------------ |
| |
| // AutoSaveTimeIntervall [int] in min |
| sal_Int32 nTimeIntervall = 15; |
| xCommonRegistry->getByHierarchicalName(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL) >>= nTimeIntervall; |
| |
| // SAFE -> ---------------------------------- |
| aWriteLock.lock(); |
| m_nAutoSaveTimeIntervall = nTimeIntervall; |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_readConfig() |
| { |
| implts_readAutoSaveConfig(); |
| |
| css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY); |
| |
| // REENTRANT -> -------------------------------- |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); |
| |
| // THREADSAFE -> ------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| // reset current cache load cache |
| m_lDocCache.clear(); |
| m_nIdPool = 0; |
| aWriteLock.unlock(); |
| // <- THREADSAFE ------------------------------- |
| |
| aCacheLock.unlock(); |
| // <- REENTRANT -------------------------------- |
| |
| css::uno::Any aValue; |
| |
| // RecoveryList [set] |
| aValue = xCommonRegistry->getByHierarchicalName(CFG_ENTRY_RECOVERYLIST); |
| css::uno::Reference< css::container::XNameAccess > xList; |
| aValue >>= xList; |
| if (xList.is()) |
| { |
| const css::uno::Sequence< ::rtl::OUString > lItems = xList->getElementNames(); |
| const ::rtl::OUString* pItems = lItems.getConstArray(); |
| sal_Int32 c = lItems.getLength(); |
| sal_Int32 i = 0; |
| |
| // REENTRANT -> -------------------------- |
| aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE); |
| |
| for (i=0; i<c; ++i) |
| { |
| css::uno::Reference< css::beans::XPropertySet > xItem; |
| xList->getByName(pItems[i]) >>= xItem; |
| if (!xItem.is()) |
| continue; |
| |
| AutoRecovery::TDocumentInfo aInfo; |
| aInfo.NewTempURL = ::rtl::OUString(); |
| aInfo.Document = css::uno::Reference< css::frame::XModel >(); |
| xItem->getPropertyValue(CFG_ENTRY_PROP_ORIGINALURL ) >>= aInfo.OrgURL ; |
| xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPURL ) >>= aInfo.OldTempURL ; |
| xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL ) >>= aInfo.TemplateURL ; |
| xItem->getPropertyValue(CFG_ENTRY_PROP_FILTER ) >>= aInfo.RealFilter ; |
| xItem->getPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE) >>= aInfo.DocumentState; |
| xItem->getPropertyValue(CFG_ENTRY_PROP_MODULE ) >>= aInfo.AppModule ; |
| xItem->getPropertyValue(CFG_ENTRY_PROP_TITLE ) >>= aInfo.Title ; |
| xItem->getPropertyValue(CFG_ENTRY_PROP_VIEWNAMES ) >>= aInfo.ViewNames ; |
| implts_specifyAppModuleAndFactory(aInfo); |
| implts_specifyDefaultFilterAndExtension(aInfo); |
| |
| if (pItems[i].indexOf(RECOVERY_ITEM_BASE_IDENTIFIER)==0) |
| { |
| ::rtl::OUString sID = pItems[i].copy(RECOVERY_ITEM_BASE_IDENTIFIER.getLength()); |
| aInfo.ID = sID.toInt32(); |
| // SAFE -> ---------------------- |
| aWriteLock.lock(); |
| if (aInfo.ID > m_nIdPool) |
| { |
| m_nIdPool = aInfo.ID+1; |
| LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_readConfig()\nOverflow of IDPool detected!") |
| } |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------- |
| } |
| #ifdef ENABLE_WARNINGS |
| else |
| LOG_WARNING("AutoRecovery::implts_readConfig()", "Who changed numbering of recovery items? Cache will be inconsistent then! I do not know, what will happen next time .-)") |
| #endif |
| |
| // THREADSAFE -> -------------------------- |
| aWriteLock.lock(); |
| m_lDocCache.push_back(aInfo); |
| aWriteLock.unlock(); |
| // <- THREADSAFE -------------------------- |
| } |
| |
| aCacheLock.unlock(); |
| // <- REENTRANT -------------------------- |
| } |
| |
| implts_updateTimer(); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo) |
| { |
| if (!rInfo.AppModule.getLength()) |
| { |
| throw css::uno::RuntimeException( |
| ::rtl::OUString::createFromAscii("Cant find out the default filter and its extension, if no application module is known!"), |
| static_cast< css::frame::XDispatch* >(this)); |
| } |
| |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| css::uno::Reference< css::container::XNameAccess> xCFG = m_xModuleCFG; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| try |
| { |
| if (! xCFG.is()) |
| { |
| // open module config on demand and cache the update access |
| xCFG = css::uno::Reference< css::container::XNameAccess >( |
| ::comphelper::ConfigurationHelper::openConfig(xSMGR, CFG_PACKAGE_MODULES, ::comphelper::ConfigurationHelper::E_STANDARD), |
| css::uno::UNO_QUERY_THROW); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| m_xModuleCFG = xCFG; |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| css::uno::Reference< css::container::XNameAccess > xModuleProps( |
| xCFG->getByName(rInfo.AppModule), |
| css::uno::UNO_QUERY_THROW); |
| |
| xModuleProps->getByName(CFG_ENTRY_REALDEFAULTFILTER) >>= rInfo.DefaultFilter; |
| |
| css::uno::Reference< css::container::XNameAccess > xFilterCFG(xSMGR->createInstance(SERVICENAME_FILTERFACTORY), css::uno::UNO_QUERY_THROW); |
| css::uno::Reference< css::container::XNameAccess > xTypeCFG (xSMGR->createInstance(SERVICENAME_TYPEDETECTION), css::uno::UNO_QUERY_THROW); |
| |
| ::comphelper::SequenceAsHashMap lFilterProps (xFilterCFG->getByName(rInfo.DefaultFilter)); |
| ::rtl::OUString sTypeRegistration = lFilterProps.getUnpackedValueOrDefault(FILTER_PROP_TYPE, ::rtl::OUString()); |
| ::comphelper::SequenceAsHashMap lTypeProps (xTypeCFG->getByName(sTypeRegistration)); |
| css::uno::Sequence< ::rtl::OUString > lExtensions = lTypeProps.getUnpackedValueOrDefault(TYPE_PROP_EXTENSIONS, css::uno::Sequence< ::rtl::OUString >()); |
| if (lExtensions.getLength()) |
| { |
| rInfo.Extension = ::rtl::OUString::createFromAscii("."); |
| rInfo.Extension += lExtensions[0]; |
| } |
| else |
| rInfo.Extension = ::rtl::OUString::createFromAscii(".unknown"); |
| } |
| catch(const css::uno::Exception&) |
| { |
| rInfo.DefaultFilter = ::rtl::OUString(); |
| rInfo.Extension = ::rtl::OUString(); |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo) |
| { |
| ENSURE_OR_THROW2( |
| rInfo.AppModule.getLength() || rInfo.Document.is(), |
| "Cant find out the application module nor its factory URL, if no application module (or a suitable) document is known!", |
| *this ); |
| |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| css::uno::Reference< css::frame::XModuleManager > xManager (xSMGR->createInstance(SERVICENAME_MODULEMANAGER), css::uno::UNO_QUERY_THROW); |
| css::uno::Reference< css::container::XNameAccess > xModuleConfig(xManager , css::uno::UNO_QUERY_THROW); |
| |
| if (!rInfo.AppModule.getLength()) |
| rInfo.AppModule = xManager->identify(rInfo.Document); |
| |
| ::comphelper::SequenceAsHashMap lModuleDescription(xModuleConfig->getByName(rInfo.AppModule)); |
| lModuleDescription[CFG_ENTRY_PROP_EMPTYDOCUMENTURL] >>= rInfo.FactoryURL; |
| lModuleDescription[CFG_ENTRY_PROP_FACTORYSERVICE] >>= rInfo.FactoryService; |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& i_rInfo ) |
| { |
| ENSURE_OR_THROW2( i_rInfo.Document.is(), "need at document, at the very least", *this ); |
| |
| i_rInfo.ViewNames.realloc(0); |
| |
| // obtain list of controllers of this document |
| ::std::vector< ::rtl::OUString > aViewNames; |
| const Reference< XModel2 > xModel( i_rInfo.Document, UNO_QUERY ); |
| if ( xModel.is() ) |
| { |
| const Reference< XEnumeration > xEnumControllers( xModel->getControllers() ); |
| while ( xEnumControllers->hasMoreElements() ) |
| { |
| const Reference< XController2 > xController( xEnumControllers->nextElement(), UNO_QUERY ); |
| ::rtl::OUString sViewName; |
| if ( xController.is() ) |
| sViewName = xController->getViewControllerName(); |
| OSL_ENSURE( sViewName.getLength(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" ); |
| |
| if ( sViewName.getLength() ) |
| aViewNames.push_back( sViewName ); |
| } |
| } |
| else |
| { |
| const Reference< XController2 > xController( xModel->getCurrentController(), UNO_QUERY ); |
| ::rtl::OUString sViewName; |
| if ( xController.is() ) |
| sViewName = xController->getViewControllerName(); |
| OSL_ENSURE( sViewName.getLength(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" ); |
| |
| if ( sViewName.getLength() ) |
| aViewNames.push_back( sViewName ); |
| } |
| |
| i_rInfo.ViewNames.realloc( aViewNames.size() ); |
| ::std::copy( aViewNames.begin(), aViewNames.end(), i_rInfo.ViewNames.getArray() ); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_persistAllActiveViewNames() |
| { |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| // This list will be filled with every document |
| AutoRecovery::TDocumentList::iterator pIt; |
| for ( pIt = m_lDocCache.begin(); |
| pIt != m_lDocCache.end() ; |
| ++pIt ) |
| { |
| implts_collectActiveViewNames( *pIt ); |
| implts_flushConfigItem( *pIt ); |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_flushConfigItem(const AutoRecovery::TDocumentInfo& rInfo, sal_Bool bRemoveIt) |
| { |
| css::uno::Reference< css::container::XHierarchicalNameAccess > xCFG; |
| |
| try |
| { |
| xCFG = css::uno::Reference< css::container::XHierarchicalNameAccess >(implts_openConfig(), css::uno::UNO_QUERY_THROW); |
| |
| css::uno::Reference< css::container::XNameAccess > xCheck; |
| xCFG->getByHierarchicalName(CFG_ENTRY_RECOVERYLIST) >>= xCheck; |
| |
| css::uno::Reference< css::container::XNameContainer > xModify(xCheck, css::uno::UNO_QUERY_THROW); |
| css::uno::Reference< css::lang::XSingleServiceFactory > xCreate(xCheck, css::uno::UNO_QUERY_THROW); |
| |
| ::rtl::OUStringBuffer sIDBuf; |
| sIDBuf.append(RECOVERY_ITEM_BASE_IDENTIFIER); |
| sIDBuf.append((sal_Int32)rInfo.ID); |
| ::rtl::OUString sID = sIDBuf.makeStringAndClear(); |
| |
| // remove |
| if (bRemoveIt) |
| { |
| // Catch NoSuchElementException. |
| // Its not a good idea inside multithreaded environments to call hasElement - removeElement. |
| // DO IT! |
| try |
| { |
| xModify->removeByName(sID); |
| } |
| catch(const css::container::NoSuchElementException&) |
| { return; } |
| } |
| else |
| { |
| // new/modify |
| css::uno::Reference< css::beans::XPropertySet > xSet; |
| sal_Bool bNew = (!xCheck->hasByName(sID)); |
| if (bNew) |
| xSet = css::uno::Reference< css::beans::XPropertySet >(xCreate->createInstance(), css::uno::UNO_QUERY_THROW); |
| else |
| xCheck->getByName(sID) >>= xSet; |
| |
| xSet->setPropertyValue(CFG_ENTRY_PROP_ORIGINALURL , css::uno::makeAny(rInfo.OrgURL )); |
| xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPURL , css::uno::makeAny(rInfo.OldTempURL )); |
| xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL , css::uno::makeAny(rInfo.TemplateURL )); |
| xSet->setPropertyValue(CFG_ENTRY_PROP_FILTER , css::uno::makeAny(rInfo.RealFilter )); |
| xSet->setPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE, css::uno::makeAny(rInfo.DocumentState)); |
| xSet->setPropertyValue(CFG_ENTRY_PROP_MODULE , css::uno::makeAny(rInfo.AppModule )); |
| xSet->setPropertyValue(CFG_ENTRY_PROP_TITLE , css::uno::makeAny(rInfo.Title )); |
| xSet->setPropertyValue(CFG_ENTRY_PROP_VIEWNAMES , css::uno::makeAny(rInfo.ViewNames )); |
| |
| if (bNew) |
| xModify->insertByName(sID, css::uno::makeAny(xSet)); |
| } |
| } |
| catch(const css::uno::RuntimeException& exRun) |
| { throw exRun; } |
| catch(const css::uno::Exception&) |
| {} // ??? can it happen that a full disc let these set of operations fail too ??? |
| |
| sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER; |
| do |
| { |
| try |
| { |
| css::uno::Reference< css::util::XChangesBatch > xFlush(xCFG, css::uno::UNO_QUERY_THROW); |
| xFlush->commitChanges(); |
| |
| #ifdef TRIGGER_FULL_DISC_CHECK |
| throw css::uno::Exception(); |
| #endif |
| |
| nRetry = 0; |
| } |
| catch(const css::uno::Exception& ex) |
| { |
| // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300) |
| // b) unknown problem (may be locking problem) => reset RETRY value to more usefull value(!) (e.g. retry=3) |
| // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace ! |
| |
| // SAFE -> |
| ReadGuard aReadLock(m_aLock); |
| sal_Int32 nMinSpaceConfigSave = m_nMinSpaceConfigSave; |
| aReadLock.unlock(); |
| // <- SAFE |
| |
| if (! impl_enoughDiscSpace(nMinSpaceConfigSave)) |
| AutoRecovery::impl_showFullDiscError(); |
| else |
| if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL) |
| nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL; |
| else |
| if (nRetry <= GIVE_UP_RETRY) |
| throw ex; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!! |
| |
| --nRetry; |
| } |
| } |
| while(nRetry>0); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_startListening() |
| { |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG, css::uno::UNO_QUERY); |
| css::uno::Reference< css::document::XEventBroadcaster > xBroadcaster = m_xNewDocBroadcaster; |
| sal_Bool bListenForDocEvents = m_bListenForDocEvents; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| if ( |
| ( xCFG.is() ) && |
| (! m_bListenForConfigChanges) |
| ) |
| { |
| xCFG->addChangesListener(static_cast< css::util::XChangesListener* >(this)); |
| m_bListenForConfigChanges = sal_True; |
| } |
| |
| if (!xBroadcaster.is()) |
| { |
| xBroadcaster = css::uno::Reference< css::document::XEventBroadcaster >(xSMGR->createInstance(SERVICENAME_GLOBALEVENTBROADCASTER), css::uno::UNO_QUERY_THROW); |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| m_xNewDocBroadcaster = xBroadcaster; |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| if ( |
| ( xBroadcaster.is() ) && |
| (! bListenForDocEvents) |
| ) |
| { |
| xBroadcaster->addEventListener(static_cast< css::document::XEventListener* >(this)); |
| // SAFE -> |
| WriteGuard aWriteLock(m_aLock); |
| m_bListenForDocEvents = sal_True; |
| aWriteLock.unlock(); |
| // <- SAFE |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_stopListening() |
| { |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| // Attention: Dont reset our internal members here too. |
| // May be we must work with our configuration, but dont wish to be informed |
| // about changes any longer. Needed e.g. during EMERGENCY_SAVE! |
| css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG , css::uno::UNO_QUERY); |
| css::uno::Reference< css::document::XEventBroadcaster > xGlobalEventBroadcaster(m_xNewDocBroadcaster, css::uno::UNO_QUERY); |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| if ( |
| (xGlobalEventBroadcaster.is()) && |
| (m_bListenForDocEvents ) |
| ) |
| { |
| xGlobalEventBroadcaster->removeEventListener(static_cast< css::document::XEventListener* >(this)); |
| m_bListenForDocEvents = sal_False; |
| } |
| |
| if ( |
| (xCFG.is() ) && |
| (m_bListenForConfigChanges) |
| ) |
| { |
| xCFG->removeChangesListener(static_cast< css::util::XChangesListener* >(this)); |
| m_bListenForConfigChanges = sal_False; |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo) |
| { |
| if (rInfo.ListenForModify) |
| return; |
| |
| css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY); |
| if (xBroadcaster.is()) |
| { |
| css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY); |
| xBroadcaster->addModifyListener(xThis); |
| rInfo.ListenForModify = sal_True; |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo) |
| { |
| if (! rInfo.ListenForModify) |
| return; |
| |
| css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY); |
| if (xBroadcaster.is()) |
| { |
| css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY); |
| xBroadcaster->removeModifyListener(xThis); |
| rInfo.ListenForModify = sal_False; |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_updateTimer() |
| { |
| implts_stopTimer(); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| if ( |
| (m_eJob == AutoRecovery::E_NO_JOB ) || // TODO may be superflous - E_DONT_START_TIMER should be used only |
| (m_eTimerType == AutoRecovery::E_DONT_START_TIMER) |
| ) |
| return; |
| |
| sal_uLong nMilliSeconds = 0; |
| if (m_eTimerType == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL) |
| { |
| nMilliSeconds = (m_nAutoSaveTimeIntervall*60000); // [min] => 60.000 ms |
| #if OSL_DEBUG_LEVEL > 1 |
| if (m_dbg_bMakeItFaster) |
| nMilliSeconds = m_nAutoSaveTimeIntervall; // [ms] |
| #endif |
| } |
| else |
| if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE) |
| { |
| nMilliSeconds = MIN_TIME_FOR_USER_IDLE; |
| #if OSL_DEBUG_LEVEL > 1 |
| if (m_dbg_bMakeItFaster) |
| nMilliSeconds = 300; // let us some time, to finish this method .-) |
| #endif |
| } |
| else |
| if (m_eTimerType == AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED) |
| nMilliSeconds = 300; // there is a minimum time frame, where the user can loose some key input data! |
| |
| m_aTimer.SetTimeout(nMilliSeconds); |
| m_aTimer.Start(); |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_stopTimer() |
| { |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| if (!m_aTimer.IsActive()) |
| return; |
| m_aTimer.Stop(); |
| |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| IMPL_LINK(AutoRecovery, implts_timerExpired, void*, EMPTYARG) |
| { |
| try |
| { |
| // This method is called by using a pointer to us. |
| // But we must be aware that we can be destroyed hardly |
| // if our uno reference will be gone! |
| // => Hold this object alive till this method finish its work. |
| css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XTypeProvider* >(this)); |
| |
| // Needed! Otherwise every reschedule request allow a new triggered timer event :-( |
| implts_stopTimer(); |
| |
| // The timer must be ignored if AutoSave/Recovery was disabled for this |
| // office session. That can happen if e.g. the command line arguments "-norestore" or "-headless" |
| // was set. But normaly the timer was disabled if recovery was disabled ... |
| // But so we are more "safe" .-) |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY) |
| return 0; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| // check some "states", where its not allowed (better: not a good idea) to |
| // start an AutoSave. (e.g. if the user makes drag & drop ...) |
| // Then we poll till this "disallowed" state is gone. |
| sal_Bool bAutoSaveNotAllowed = Application::IsUICaptured(); |
| if (bAutoSaveNotAllowed) |
| { |
| // SAFE -> ------------------------------ |
| WriteGuard aWriteLock(m_aLock); |
| m_eTimerType = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED; |
| aWriteLock.unlock(); |
| // <- SAFE ------------------------------ |
| implts_updateTimer(); |
| return 0; |
| } |
| |
| // analyze timer type. |
| // If we poll for an user idle period, may be we must |
| // do nothing here and start the timer again. |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE) |
| { |
| sal_Bool bUserIdle = (Application::GetLastInputInterval()>MIN_TIME_FOR_USER_IDLE); |
| if (!bUserIdle) |
| { |
| implts_updateTimer(); |
| return 0; |
| } |
| } |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| implts_informListener(AutoRecovery::E_AUTO_SAVE, |
| AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_START, NULL)); |
| |
| // force save of all currently open documents |
| // The called method returns an info, if and how this |
| // timer must be restarted. |
| sal_Bool bAllowUserIdleLoop = sal_True; |
| AutoRecovery::ETimerType eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False); |
| |
| // If timer isnt used for "short callbacks" (means polling |
| // for special states) ... reset the handle state of all |
| // cache items. Such handle state indicates, that a document |
| // was already saved during the THIS(!) AutoSave session. |
| // Of course NEXT AutoSave session must be started without |
| // any "handle" state ... |
| if ( |
| (eSuggestedTimer == AutoRecovery::E_DONT_START_TIMER ) || |
| (eSuggestedTimer == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL) |
| ) |
| { |
| implts_resetHandleStates(sal_False); |
| } |
| |
| implts_informListener(AutoRecovery::E_AUTO_SAVE, |
| AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_STOP, NULL)); |
| |
| // restart timer - because it was disabled before ... |
| // SAFE -> ---------------------------------- |
| aWriteLock.lock(); |
| m_eTimerType = eSuggestedTimer; |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| implts_updateTimer(); |
| } |
| catch(const css::uno::Exception&) |
| { |
| LOG_ASSERT(sal_False, "May be you found the reason for bug #125528#. Please report a test scenario to the right developer. THX."); |
| } |
| |
| return 0; |
| } |
| |
| //----------------------------------------------- |
| IMPL_LINK(AutoRecovery, implts_asyncDispatch, void*, EMPTYARG) |
| { |
| // SAFE -> |
| WriteGuard aWriteLock(m_aLock); |
| DispatchParams aParams = m_aDispatchParams; |
| css::uno::Reference< css::uno::XInterface > xHoldRefForMethodAlive = aParams.m_xHoldRefForAsyncOpAlive; |
| m_aDispatchParams.forget(); // clears all members ... including the ref-hold object .-) |
| aWriteLock.unlock(); |
| // <- SAFE |
| |
| implts_dispatch(aParams); |
| return 0; |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_registerDocument(const css::uno::Reference< css::frame::XModel >& xDocument) |
| { |
| // ignore corrupted events, where no document is given ... Runtime Error ?! |
| if (!xDocument.is()) |
| return; |
| |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // notification for already existing document ! |
| // Can happen if events came in asynchronous on recovery time. |
| // Then our cache was filled from the configuration ... but now we get some |
| // asynchronous events from the global event broadcaster. We must be sure that |
| // we dont add the same document more then once. |
| AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); |
| if (pIt != m_lDocCache.end()) |
| { |
| // Normaly nothing must be done for this "late" notification. |
| // But may be the modified state was changed inbetween. |
| // Check it ... |
| implts_updateModifiedState(xDocument); |
| return; |
| } |
| |
| aCacheLock.unlock(); |
| |
| ::comphelper::MediaDescriptor lDescriptor(xDocument->getArgs()); |
| |
| // check if this document must be ignored for recovery ! |
| // Some use cases dont wish support for AutoSave/Recovery ... as e.g. OLE-Server / ActiveX Control etcpp. |
| sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False)); |
| if (bNoAutoSave) |
| return; |
| |
| // Check if doc is well known on the desktop. Otherwhise ignore it! |
| // Other frames mostly are used from external programs - e.g. the bean ... |
| css::uno::Reference< css::frame::XController > xController = xDocument->getCurrentController(); |
| if (!xController.is()) |
| return; |
| |
| css::uno::Reference< css::frame::XFrame > xFrame = xController->getFrame(); |
| css::uno::Reference< css::frame::XDesktop > xDesktop (xFrame->getCreator(), css::uno::UNO_QUERY); |
| if (!xDesktop.is()) |
| return; |
| |
| // if the document doesn't support the XDocumentRecovery interface, we're not interested in it. |
| Reference< XDocumentRecovery > xDocRecovery( xDocument, UNO_QUERY ); |
| if ( !xDocRecovery.is() ) |
| return; |
| |
| // get all needed informations of this document |
| // We need it to update our cache or to locate already existing elements there! |
| AutoRecovery::TDocumentInfo aNew; |
| aNew.Document = xDocument; |
| |
| // TODO replace getLocation() with getURL() ... its a workaround currently only! |
| css::uno::Reference< css::frame::XStorable > xDoc(aNew.Document, css::uno::UNO_QUERY_THROW); |
| aNew.OrgURL = xDoc->getLocation(); |
| |
| css::uno::Reference< css::frame::XTitle > xTitle(aNew.Document, css::uno::UNO_QUERY_THROW); |
| aNew.Title = xTitle->getTitle (); |
| |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| // classify the used application module, which is used by this document. |
| implts_specifyAppModuleAndFactory(aNew); |
| |
| // Hack! Check for "illegal office documents" ... as e.g. the Basic IDE |
| // Its not realy a full featured office document. It doesnt provide an URL, any filter, a factory URL etcpp. |
| // TODO file bug to Basci IDE developers. They must remove the office document API from its service. |
| if ( |
| (!aNew.OrgURL.getLength() ) && |
| (!aNew.FactoryURL.getLength()) |
| ) |
| { |
| OSL_ENSURE( false, "AutoRecovery::implts_registerDocument: this should not happen anymore!" ); |
| // nowadays, the Basic IDE should already die on the "supports XDocumentRecovery" check. And no other known |
| // document type fits in here ... |
| return; |
| } |
| |
| // By the way - get some information about the default format for saving! |
| // and save an information about the real used filter by this document. |
| // We save this document with DefaultFilter ... and load it with the RealFilter. |
| implts_specifyDefaultFilterAndExtension(aNew); |
| aNew.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME() , ::rtl::OUString()); |
| |
| // Further we must know, if this document base on a template. |
| // Then we must load it in a different way. |
| css::uno::Reference< css::document::XDocumentPropertiesSupplier > xSupplier(aNew.Document, css::uno::UNO_QUERY); |
| if (xSupplier.is()) // optional interface! |
| { |
| css::uno::Reference< css::document::XDocumentProperties > xDocProps(xSupplier->getDocumentProperties(), css::uno::UNO_QUERY_THROW); |
| aNew.TemplateURL = xDocProps->getTemplateURL(); |
| } |
| |
| css::uno::Reference< css::util::XModifiable > xModifyCheck(xDocument, css::uno::UNO_QUERY_THROW); |
| if (xModifyCheck->isModified()) |
| { |
| aNew.DocumentState |= AutoRecovery::E_MODIFIED; |
| } |
| |
| aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| // create a new cache entry ... this document isn't known. |
| ++m_nIdPool; |
| aNew.ID = m_nIdPool; |
| LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_registerDocument()\nOverflow of ID pool detected.") |
| m_lDocCache.push_back(aNew); |
| |
| AutoRecovery::TDocumentList::iterator pIt1 = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); |
| AutoRecovery::TDocumentInfo& rInfo = *pIt1; |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| implts_flushConfigItem(rInfo); |
| implts_startModifyListeningOnDoc(rInfo); |
| |
| aCacheLock.unlock(); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument , |
| sal_Bool bStopListening) |
| { |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| // Attention: Dont leave SAFE section, if you work with pIt! |
| // Because it points directly into the m_lDocCache list ... |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); |
| if (pIt == m_lDocCache.end()) |
| return; // unknown document => not a runtime error! Because we register only a few documents. see registration ... |
| |
| AutoRecovery::TDocumentInfo aInfo = *pIt; |
| |
| aCacheLock.unlock(); |
| |
| // Sometimes we close documents by ourself. |
| // And these documents cant be deregistered. |
| // Otherwhise we loos our configuration data ... but need it ! |
| // see SessionSave ! |
| if (aInfo.IgnoreClosing) |
| return; |
| |
| CacheLockGuard aCacheLock2(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); |
| pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); |
| if (pIt != m_lDocCache.end()) |
| m_lDocCache.erase(pIt); |
| pIt = m_lDocCache.end(); // otherwhise its not specified what pIt means! |
| aCacheLock2.unlock(); |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| /* This method is called within disposing() of the document too. But there it's not a good idea to |
| deregister us as listener. Furter it make no sense - because the broadcaster dies. |
| So we supress deregistration in such case ... |
| */ |
| if (bStopListening) |
| implts_stopModifyListeningOnDoc(aInfo); |
| |
| AutoRecovery::st_impl_removeFile(aInfo.OldTempURL); |
| AutoRecovery::st_impl_removeFile(aInfo.NewTempURL); |
| implts_flushConfigItem(aInfo, sal_True); // sal_True => remove it from config |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument) |
| { |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); |
| if (pIt != m_lDocCache.end()) |
| { |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| |
| /* Now we know, that this document was modified again and must be saved next time. |
| But we dont need this information for every e.g. key input of the user. |
| So we stop listening here. |
| But if the document was saved as temp. file we start listening for this event again. |
| */ |
| implts_stopModifyListeningOnDoc(rInfo); |
| } |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument) |
| { |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); |
| if (pIt != m_lDocCache.end()) |
| { |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| |
| // use sal_True as fallback ... so we recognize every document on EmergencySave/AutoRecovery! |
| sal_Bool bModified = sal_True; |
| css::uno::Reference< css::util::XModifiable > xModify(xDocument, css::uno::UNO_QUERY); |
| if (xModify.is()) |
| bModified = xModify->isModified(); |
| if (bModified) |
| { |
| rInfo.DocumentState |= AutoRecovery::E_MODIFIED; |
| } |
| else |
| { |
| rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED; |
| } |
| } |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument , |
| sal_Bool bSaveInProgress) |
| { |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); |
| if (pIt == m_lDocCache.end()) |
| return; |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| rInfo.UsedForSaving = bSaveInProgress; |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument) |
| { |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); |
| if (pIt == m_lDocCache.end()) |
| return; |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| |
| rInfo.DocumentState = AutoRecovery::E_UNKNOWN; |
| // TODO replace getLocation() with getURL() ... its a workaround currently only! |
| css::uno::Reference< css::frame::XStorable > xDoc(rInfo.Document, css::uno::UNO_QUERY); |
| rInfo.OrgURL = xDoc->getLocation(); |
| |
| ::rtl::OUString sRemoveURL1 = rInfo.OldTempURL; |
| ::rtl::OUString sRemoveURL2 = rInfo.NewTempURL; |
| rInfo.OldTempURL = ::rtl::OUString(); |
| rInfo.NewTempURL = ::rtl::OUString(); |
| |
| ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs()); |
| rInfo.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME(), ::rtl::OUString()); |
| |
| css::uno::Reference< css::frame::XTitle > xDocTitle(xDocument, css::uno::UNO_QUERY); |
| if (xDocTitle.is ()) |
| rInfo.Title = xDocTitle->getTitle (); |
| else |
| { |
| rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_TITLE() , ::rtl::OUString()); |
| if (!rInfo.Title.getLength()) |
| rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_DOCUMENTTITLE(), ::rtl::OUString()); |
| } |
| |
| rInfo.UsedForSaving = sal_False; |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| implts_flushConfigItem(rInfo); |
| |
| aCacheLock.unlock(); |
| |
| AutoRecovery::st_impl_removeFile(sRemoveURL1); |
| AutoRecovery::st_impl_removeFile(sRemoveURL2); |
| } |
| |
| //----------------------------------------------- |
| AutoRecovery::TDocumentList::iterator AutoRecovery::impl_searchDocument( AutoRecovery::TDocumentList& rList , |
| const css::uno::Reference< css::frame::XModel >& xDocument) |
| { |
| AutoRecovery::TDocumentList::iterator pIt; |
| for ( pIt = rList.begin(); |
| pIt != rList.end() ; |
| ++pIt ) |
| { |
| const AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| if (rInfo.Document == xDocument) |
| break; |
| } |
| return pIt; |
| } |
| |
| //----------------------------------------------- |
| namespace |
| { |
| void lcl_changeVisibility( const css::uno::Reference< css::frame::XFramesSupplier >& i_rFrames, sal_Bool i_bVisible ) |
| { |
| css::uno::Reference< css::container::XIndexAccess > xFramesContainer( i_rFrames->getFrames(), css::uno::UNO_QUERY ); |
| const sal_Int32 count = xFramesContainer->getCount(); |
| |
| Any aElement; |
| for ( sal_Int32 i=0; i < count; ++i ) |
| { |
| aElement = xFramesContainer->getByIndex(i); |
| // check for sub frames |
| css::uno::Reference< css::frame::XFramesSupplier > xFramesSupp( aElement, css::uno::UNO_QUERY ); |
| if ( xFramesSupp.is() ) |
| lcl_changeVisibility( xFramesSupp, i_bVisible ); |
| |
| css::uno::Reference< css::frame::XFrame > xFrame( aElement, css::uno::UNO_QUERY ); |
| if ( !xFrame.is() ) |
| continue; |
| |
| css::uno::Reference< css::awt::XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW ); |
| xWindow->setVisible( i_bVisible ); |
| } |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_changeAllDocVisibility(sal_Bool bVisible) |
| { |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| css::uno::Reference< css::frame::XFramesSupplier > xDesktop(xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY); |
| lcl_changeVisibility( xDesktop, bVisible ); |
| |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| /* Currently the document is not closed in case of crash, |
| so the lock file must be removed explicitly |
| */ |
| void lc_removeLockFile(AutoRecovery::TDocumentInfo& rInfo) |
| { |
| if ( rInfo.Document.is() ) |
| { |
| try |
| { |
| css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW); |
| ::rtl::OUString aURL = xStore->getLocation(); |
| if ( aURL.getLength() ) |
| { |
| ::svt::DocumentLockFile aLockFile( aURL ); |
| aLockFile.RemoveFile(); |
| } |
| } |
| catch( const css::uno::Exception& ) |
| {} |
| } |
| } |
| |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_prepareSessionShutdown() |
| { |
| LOG_RECOVERY("AutoRecovery::implts_prepareSessionShutdown() starts ...") |
| |
| // a) reset modified documents (of course the must be saved before this method is called!) |
| // b) close it without showing any UI! |
| |
| // SAFE -> |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| AutoRecovery::TDocumentList::iterator pIt; |
| for ( pIt = m_lDocCache.begin(); |
| pIt != m_lDocCache.end() ; |
| ++pIt ) |
| { |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| |
| // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly |
| // it is not done on documents saving since shutdown can be cancelled |
| lc_removeLockFile( rInfo ); |
| |
| // Prevent us from deregistration of these documents. |
| // Because we close these documents by ourself (see XClosable below) ... |
| // it's fact, that we reach our deregistration method. There we |
| // must not(!) update our configuration ... Otherwhise all |
| // session data are lost !!! |
| rInfo.IgnoreClosing = sal_True; |
| |
| // reset modified flag of these documents (ignoring the notification about it!) |
| // Otherwise a message box is shown on closing these models. |
| implts_stopModifyListeningOnDoc(rInfo); |
| |
| // if the session save is still running the documents should not be thrown away, |
| // actually that would be a bad sign, that means that the SessionManager tryes |
| // to kill the session before the saving is ready |
| if ((m_eJob & AutoRecovery::E_SESSION_SAVE) != AutoRecovery::E_SESSION_SAVE) |
| { |
| css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY); |
| if (xModify.is()) |
| xModify->setModified(sal_False); |
| |
| // close the model. |
| css::uno::Reference< css::util::XCloseable > xClose(rInfo.Document, css::uno::UNO_QUERY); |
| if (xClose.is()) |
| { |
| try |
| { |
| xClose->close(sal_False); |
| } |
| /* |
| catch(const css::lang::DisposedException&) |
| { |
| // closed ... disposed ... always the same .-) |
| } |
| */ |
| catch(const css::uno::Exception&) |
| { |
| // At least it's only a try to close these documents before anybody else it does. |
| // So it seams to be possible to ignore any error here .-) |
| } |
| |
| rInfo.Document.clear(); |
| } |
| } |
| } |
| |
| aCacheLock.unlock(); |
| // <- SAFE |
| } |
| |
| //----------------------------------------------- |
| /* TODO WORKAROUND: |
| |
| #i64599# |
| |
| Normaly the MediaDescriptor argument NoAutoSave indicates, |
| that a document must be ignored for AutoSave and Recovery. |
| But sometimes XModel->getArgs() does not contained this information |
| if implts_registerDocument() was called. |
| So we have to check a second time, if this property is set .... |
| Best place doing so is to check it immeditaly before saving |
| and supressingd saving the document then. |
| Of course removing the corresponding cache entry isnt an option. |
| Because it would disturb iteration over the cache ! |
| So we ignore such documents only ... |
| Hopefully next time they are not inserted in our cache. |
| */ |
| sal_Bool lc_checkIfSaveForbiddenByArguments(AutoRecovery::TDocumentInfo& rInfo) |
| { |
| if (! rInfo.Document.is()) |
| return sal_True; |
| |
| ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs()); |
| sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False)); |
| |
| return bNoAutoSave; |
| } |
| |
| //----------------------------------------------- |
| AutoRecovery::ETimerType AutoRecovery::implts_saveDocs( sal_Bool bAllowUserIdleLoop, |
| sal_Bool bRemoveLockFiles, |
| const DispatchParams* pParams ) |
| { |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| css::uno::Reference< css::task::XStatusIndicator > xExternalProgress; |
| if (pParams) |
| xExternalProgress = pParams->m_xProgress; |
| |
| css::uno::Reference< css::frame::XFramesSupplier > xDesktop (xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY); |
| ::rtl::OUString sBackupPath (SvtPathOptions().GetBackupPath()); |
| |
| css::uno::Reference< css::frame::XController > xActiveController; |
| css::uno::Reference< css::frame::XModel > xActiveModel ; |
| css::uno::Reference< css::frame::XFrame > xActiveFrame = xDesktop->getActiveFrame(); |
| if (xActiveFrame.is()) |
| xActiveController = xActiveFrame->getController(); |
| if (xActiveController.is()) |
| xActiveModel = xActiveController->getModel(); |
| |
| // Set the default timer action for our calli. |
| // Default = NORMAL_AUTOSAVE |
| // We return a suggestion for an active timer only. |
| // It will be ignored if the timer was disabled by the user ... |
| // Further this state can be set to USER_IDLE only later in this method. |
| // Its not allowed to reset such state then. Because we must know, if |
| // there exists POSTPONED documents. see below ... |
| AutoRecovery::ETimerType eTimer = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; |
| |
| sal_Int32 eJob = m_eJob; |
| |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| // This list will be filled with every document |
| // which should be saved as last one. E.g. if it was used |
| // already for an UI save operation => crashed ... and |
| // now we try to save it again ... which can fail again ( of course .-) ). |
| ::std::vector< AutoRecovery::TDocumentList::iterator > lDangerousDocs; |
| |
| AutoRecovery::TDocumentList::iterator pIt; |
| for ( pIt = m_lDocCache.begin(); |
| pIt != m_lDocCache.end() ; |
| ++pIt ) |
| { |
| AutoRecovery::TDocumentInfo aInfo = *pIt; |
| |
| // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly |
| if ( bRemoveLockFiles ) |
| lc_removeLockFile( aInfo ); |
| |
| // WORKAROUND ... see comment of this method |
| if (lc_checkIfSaveForbiddenByArguments(aInfo)) |
| continue; |
| |
| // already auto saved during this session :-) |
| // This state must be reset for all documents |
| // if timer is started with normnal AutoSaveTimerIntervall! |
| if ((aInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED) |
| continue; |
| |
| // Not modified documents are not saved. |
| // We safe an information about the URL only! |
| Reference< XDocumentRecovery > xDocRecover( aInfo.Document, UNO_QUERY_THROW ); |
| if ( !xDocRecover->wasModifiedSinceLastSave() ) |
| { |
| aInfo.DocumentState |= AutoRecovery::E_HANDLED; |
| continue; |
| } |
| |
| // check if this document is still used by a concurrent save operation |
| // e.g. if the user tried to save via UI. |
| // Handle it in the following way: |
| // i) For an AutoSave ... ignore this document! It will be saved and next time we will (hopefully) |
| // get a notification about the state of this operation. |
| // And if a document was saved by the user we can remove our temp. file. But that will be done inside |
| // our callback for SaveDone notification. |
| // ii) For a CrashSave ... add it to the list of dangerous documents and |
| // save it after all other documents was saved successfully. That decrease |
| // the chance for a crash inside a crash. |
| // On the other side it's not neccessary for documents, which are not modified. |
| // They can be handled normaly - means we patch the corresponding configuration entry only. |
| // iii) For a SessionSave ... ignore it! There is no time to wait for this save operation. |
| // Because the WindowManager will kill the process if it doesnt react immediatly. |
| // On the other side we cant risk a concurrent save request ... because we know |
| // that it will produce a crash. |
| |
| // Attention: Because eJob is used as a flag field, you have to check for the worst case first. |
| // E.g. a CrashSave can overwrite an AutoSave. So you have to check for a CrashSave before an AutoSave! |
| if (aInfo.UsedForSaving) |
| { |
| if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE) |
| { |
| lDangerousDocs.push_back(pIt); |
| continue; |
| } |
| else |
| if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE) |
| { |
| continue; |
| } |
| else |
| if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE) |
| { |
| eTimer = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED; |
| aInfo.DocumentState |= AutoRecovery::E_POSTPONED; |
| continue; |
| } |
| } |
| |
| // a) Document was not postponed - and is active now. => postpone it (restart timer, restart loop) |
| // b) Document was not postponed - and is not active now. => save it |
| // c) Document was postponed - and is not active now. => save it |
| // d) Document was postponed - and is active now. => save it (because user idle was checked already) |
| sal_Bool bActive = (xActiveModel == aInfo.Document); |
| sal_Bool bWasPostponed = ((aInfo.DocumentState & AutoRecovery::E_POSTPONED) == AutoRecovery::E_POSTPONED); |
| |
| if ( |
| ! bWasPostponed && |
| bActive |
| ) |
| { |
| aInfo.DocumentState |= AutoRecovery::E_POSTPONED; |
| *pIt = aInfo; |
| // postponed documents will be saved if this method is called again! |
| // That can be done by an outside started timer => E_POLL_FOR_USER_IDLE (if normal AutoSave is active) |
| // or it must be done directly without starting any timer => E_CALL_ME_BACK (if Emergency- or SessionSave is active and must be finished ASAP!) |
| eTimer = AutoRecovery::E_POLL_FOR_USER_IDLE; |
| if (!bAllowUserIdleLoop) |
| eTimer = AutoRecovery::E_CALL_ME_BACK; |
| continue; |
| } |
| |
| // b, c, d) |
| // <- SAFE -------------------------- |
| aWriteLock.unlock(); |
| // changing of aInfo and flushing it is done inside implts_saveOneDoc! |
| implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress); |
| implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo)); |
| aWriteLock.lock(); |
| // SAFE -> -------------------------- |
| |
| *pIt = aInfo; |
| } |
| |
| // Did we have some "dangerous candidates" ? |
| // Try to save it ... but may be it will fail ! |
| ::std::vector< AutoRecovery::TDocumentList::iterator >::iterator pIt2; |
| for ( pIt2 = lDangerousDocs.begin(); |
| pIt2 != lDangerousDocs.end() ; |
| ++pIt2 ) |
| { |
| pIt = *pIt2; |
| AutoRecovery::TDocumentInfo aInfo = *pIt; |
| |
| // <- SAFE -------------------------- |
| aWriteLock.unlock(); |
| // changing of aInfo and flushing it is done inside implts_saveOneDoc! |
| implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress); |
| implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo)); |
| aWriteLock.lock(); |
| // SAFE -> -------------------------- |
| |
| *pIt = aInfo; |
| } |
| |
| return eTimer; |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_saveOneDoc(const ::rtl::OUString& sBackupPath , |
| AutoRecovery::TDocumentInfo& rInfo , |
| const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress) |
| { |
| // no document? => can occure if we loaded our configuration with files, |
| // which couldnt be recovered successfully. In such case we have all needed informations |
| // excepting the real document instance! |
| |
| // TODO: search right place, where such "dead files" can be removed from the configuration! |
| if (!rInfo.Document.is()) |
| return; |
| |
| ::comphelper::MediaDescriptor lOldArgs(rInfo.Document->getArgs()); |
| implts_generateNewTempURL(sBackupPath, lOldArgs, rInfo); |
| |
| // if the document was loaded with a password, it should be |
| // stored with password |
| ::comphelper::MediaDescriptor lNewArgs; |
| ::rtl::OUString sPassword = lOldArgs.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_PASSWORD(), ::rtl::OUString()); |
| if (sPassword.getLength()) |
| lNewArgs[::comphelper::MediaDescriptor::PROP_PASSWORD()] <<= sPassword; |
| |
| // Further it must be saved using the default file format of that application. |
| // Otherwhise we will some data lost. |
| if (rInfo.DefaultFilter.getLength()) |
| lNewArgs[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.DefaultFilter; |
| |
| // prepare frame/document/mediadescriptor in a way, that it uses OUR progress .-) |
| if (xExternalProgress.is()) |
| lNewArgs[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= xExternalProgress; |
| impl_establishProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >()); |
| |
| // #i66598# use special handling of property "DocumentBaseURL" (it must be an empty string!) |
| // for make hyperlinks working |
| lNewArgs[::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL()] <<= ::rtl::OUString(); |
| |
| // try to save this document as a new temp file everytimes. |
| // Mark AutoSave state as "INCOMPLETE" if it failed. |
| // Because the last temp file is to old and does not include all changes. |
| Reference< XDocumentRecovery > xDocRecover(rInfo.Document, css::uno::UNO_QUERY_THROW); |
| |
| // safe the state about "trying to save" |
| // ... we need it for recovery if e.g. a crash occures inside next line! |
| rInfo.DocumentState |= AutoRecovery::E_TRY_SAVE; |
| implts_flushConfigItem(rInfo); |
| |
| sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER; |
| sal_Bool bError = sal_False; |
| do |
| { |
| try |
| { |
| xDocRecover->storeToRecoveryFile( rInfo.NewTempURL, lNewArgs.getAsConstPropertyValueList() ); |
| |
| #ifdef TRIGGER_FULL_DISC_CHECK |
| throw css::uno::Exception(); |
| #endif |
| |
| bError = sal_False; |
| nRetry = 0; |
| } |
| catch(const css::uno::Exception& ex) |
| { |
| bError = sal_True; |
| |
| // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300) |
| // b) unknown problem (may be locking problem) => reset RETRY value to more usefull value(!) (e.g. retry=3) |
| // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace ! |
| |
| // SAFE -> |
| ReadGuard aReadLock2(m_aLock); |
| sal_Int32 nMinSpaceDocSave = m_nMinSpaceDocSave; |
| aReadLock2.unlock(); |
| // <- SAFE |
| |
| if (! impl_enoughDiscSpace(nMinSpaceDocSave)) |
| AutoRecovery::impl_showFullDiscError(); |
| else |
| if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL) |
| nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL; |
| else |
| if (nRetry <= GIVE_UP_RETRY) |
| throw ex; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!! |
| |
| --nRetry; |
| } |
| } |
| while(nRetry>0); |
| |
| if (! bError) |
| { |
| // safe the state about success |
| // ... you know the reason: to know it on recovery time if next line crash .-) |
| rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE; |
| rInfo.DocumentState |= AutoRecovery::E_HANDLED; |
| rInfo.DocumentState |= AutoRecovery::E_SUCCEDED; |
| } |
| else |
| { |
| // safe the state about error ... |
| rInfo.NewTempURL = ::rtl::OUString(); |
| rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE; |
| rInfo.DocumentState |= AutoRecovery::E_HANDLED; |
| rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE; |
| } |
| |
| // make sure the progress isnt referred any longer |
| impl_forgetProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >()); |
| |
| // try to remove the old temp file. |
| // Ignore any error here. We have a new temp file, which is up to date. |
| // The only thing is: we fill the disk with temp files, if we cant remove old ones :-) |
| ::rtl::OUString sRemoveFile = rInfo.OldTempURL; |
| rInfo.OldTempURL = rInfo.NewTempURL; |
| rInfo.NewTempURL = ::rtl::OUString(); |
| |
| implts_flushConfigItem(rInfo); |
| |
| // We must know if the user modifies the document again ... |
| implts_startModifyListeningOnDoc(rInfo); |
| |
| AutoRecovery::st_impl_removeFile(sRemoveFile); |
| } |
| |
| //----------------------------------------------- |
| AutoRecovery::ETimerType AutoRecovery::implts_openDocs(const DispatchParams& aParams) |
| { |
| AutoRecovery::ETimerType eTimer = AutoRecovery::E_DONT_START_TIMER; |
| |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| |
| sal_Int32 eJob = m_eJob; |
| AutoRecovery::TDocumentList::iterator pIt; |
| for ( pIt = m_lDocCache.begin(); |
| pIt != m_lDocCache.end() ; |
| ++pIt ) |
| { |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| |
| // Such documents are already loaded by the last loop. |
| // Dont check E_SUCCEDED here! Its may be the final state of an AutoSave |
| // operation before!!! |
| if ((rInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED) |
| continue; |
| |
| // a1,b1,c1,d2,e2,f2) |
| if ((rInfo.DocumentState & AutoRecovery::E_DAMAGED) == AutoRecovery::E_DAMAGED) |
| { |
| // dont forget to inform listener! May be this document was |
| // damaged on last saving time ... |
| // Then our listener need this notification. |
| // If it was damaged during last "try to open" ... |
| // it will be notified more then once. SH.. HAPPENS ... |
| // <- SAFE -------------------------- |
| aWriteLock.unlock(); |
| implts_informListener(eJob, |
| AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo)); |
| aWriteLock.lock(); |
| // SAFE -> -------------------------- |
| continue; |
| } |
| |
| ::comphelper::MediaDescriptor lDescriptor; |
| |
| // its an UI feature - so the "USER" itself must be set as referer |
| lDescriptor[::comphelper::MediaDescriptor::PROP_REFERRER()] <<= REFERRER_USER; |
| lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= ::rtl::OUString(); |
| |
| // recovered documents are loaded hidden, and shown all at once, later |
| lDescriptor[::comphelper::MediaDescriptor::PROP_HIDDEN()] <<= true; |
| |
| if (aParams.m_xProgress.is()) |
| lDescriptor[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= aParams.m_xProgress; |
| |
| sal_Bool bBackupWasTried = ( |
| ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_BACKUP ) == AutoRecovery::E_TRY_LOAD_BACKUP) || // temp. state! |
| ((rInfo.DocumentState & AutoRecovery::E_INCOMPLETE ) == AutoRecovery::E_INCOMPLETE ) // transport TRY_LOAD_BACKUP from last loop to this new one! |
| ); |
| sal_Bool bOriginalWasTried = ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL); |
| |
| if (bBackupWasTried) |
| { |
| if (!bOriginalWasTried) |
| { |
| rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE; |
| // try original URL ... ! dont continue with next item here ... |
| } |
| else |
| { |
| rInfo.DocumentState |= AutoRecovery::E_DAMAGED; |
| continue; |
| } |
| } |
| |
| ::rtl::OUString sLoadOriginalURL; |
| ::rtl::OUString sLoadBackupURL ; |
| |
| if (!bBackupWasTried) |
| sLoadBackupURL = rInfo.OldTempURL; |
| |
| if (rInfo.OrgURL.getLength()) |
| { |
| sLoadOriginalURL = rInfo.OrgURL; |
| } |
| else |
| if (rInfo.TemplateURL.getLength()) |
| { |
| sLoadOriginalURL = rInfo.TemplateURL; |
| lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True; |
| lDescriptor[::comphelper::MediaDescriptor::PROP_TEMPLATENAME()] <<= rInfo.TemplateURL; |
| } |
| else |
| if (rInfo.FactoryURL.getLength()) |
| { |
| sLoadOriginalURL = rInfo.FactoryURL; |
| lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True; |
| } |
| |
| // A "Salvaged" item must exists every time. The core can make something special then for recovery. |
| // Of course it should be the real file name of the original file, in case we load the temp. backup here. |
| ::rtl::OUString sURL; |
| if (sLoadBackupURL.getLength()) |
| { |
| sURL = sLoadBackupURL; |
| rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_BACKUP; |
| lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= sLoadOriginalURL; |
| } |
| else |
| if (sLoadOriginalURL.getLength()) |
| { |
| sURL = sLoadOriginalURL; |
| rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_ORIGINAL; |
| } |
| else |
| continue; // TODO ERROR! |
| |
| LoadEnv::initializeUIDefaults( m_xSMGR, lDescriptor, true, NULL ); |
| |
| // <- SAFE ------------------------------ |
| aWriteLock.unlock(); |
| |
| implts_flushConfigItem(rInfo); |
| implts_informListener(eJob, |
| AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo)); |
| |
| try |
| { |
| implts_openOneDoc(sURL, lDescriptor, rInfo); |
| } |
| catch(const css::uno::Exception&) |
| { |
| rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP; |
| rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL; |
| if (sLoadBackupURL.getLength()) |
| { |
| rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE; |
| eTimer = AutoRecovery::E_CALL_ME_BACK; |
| } |
| else |
| { |
| rInfo.DocumentState |= AutoRecovery::E_HANDLED; |
| rInfo.DocumentState |= AutoRecovery::E_DAMAGED; |
| } |
| |
| implts_flushConfigItem(rInfo, sal_True); |
| implts_informListener(eJob, |
| AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo)); |
| |
| // SAFE -> ------------------------------ |
| // Needed for next loop! |
| aWriteLock.lock(); |
| continue; |
| } |
| |
| if (rInfo.RealFilter.getLength()) |
| { |
| ::comphelper::MediaDescriptor lPatchDescriptor(rInfo.Document->getArgs()); |
| lPatchDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.RealFilter; |
| rInfo.Document->attachResource(rInfo.Document->getURL(), lPatchDescriptor.getAsConstPropertyValueList()); |
| // do *not* use sURL here. In case this points to the recovery file, it has already been passed |
| // to recoverFromFile. Also, passing it here is logically wrong, as attachResource is intended |
| // to take the logical file URL. |
| } |
| |
| css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY); |
| if ( xModify.is() ) |
| { |
| sal_Bool bModified = ((rInfo.DocumentState & AutoRecovery::E_MODIFIED) == AutoRecovery::E_MODIFIED); |
| xModify->setModified(bModified); |
| } |
| |
| rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP; |
| rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL; |
| rInfo.DocumentState |= AutoRecovery::E_HANDLED; |
| rInfo.DocumentState |= AutoRecovery::E_SUCCEDED; |
| |
| implts_flushConfigItem(rInfo); |
| implts_informListener(eJob, |
| AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo)); |
| |
| /* Normaly we listen as XModifyListener on a document to know if a document was changed |
| since our last AutoSave. And we deregister us in case we know this state. |
| But directly after one document as recovered ... we must start listening. |
| Otherwhise the first "modify" doesnt reach us. Because we ourself called setModified() |
| on the document via API. And currently we dont listen for any events (not at the GlobalEventBroadcaster |
| nor at any document!). |
| */ |
| implts_startModifyListeningOnDoc(rInfo); |
| |
| // SAFE -> ------------------------------ |
| // Needed for next loop. Dont unlock it again! |
| aWriteLock.lock(); |
| } |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| return eTimer; |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_openOneDoc(const ::rtl::OUString& sURL , |
| ::comphelper::MediaDescriptor& lDescriptor, |
| AutoRecovery::TDocumentInfo& rInfo ) |
| { |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| css::uno::Reference< css::frame::XFrame > xDesktop( xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY_THROW ); |
| |
| ::std::vector< Reference< XComponent > > aCleanup; |
| try |
| { |
| // create a new document of the desired type |
| Reference< XModel2 > xModel( xSMGR->createInstance( rInfo.FactoryService ), UNO_QUERY_THROW ); |
| aCleanup.push_back( xModel.get() ); |
| |
| // put the filter name into the descriptor - we're not going to involve any type detection, so |
| // the document might be lost without the FilterName property |
| lDescriptor[ ::comphelper::MediaDescriptor::PROP_FILTERNAME() ] <<= rInfo.RealFilter; |
| |
| if ( sURL == rInfo.FactoryURL ) |
| { |
| // if the document was a new, unmodified document, then there's nothing to recover, just to init |
| ENSURE_OR_THROW( ( rInfo.DocumentState & AutoRecovery::E_MODIFIED ) == 0, |
| "unexpected document state" ); |
| Reference< XLoadable > xModelLoad( xModel, UNO_QUERY_THROW ); |
| xModelLoad->initNew(); |
| |
| // TODO: remove load-process specific arguments from the descriptor, e.g. the status indicator |
| xModel->attachResource( sURL, lDescriptor.getAsConstPropertyValueList() ); |
| } |
| else |
| { |
| // let it recover itself |
| Reference< XDocumentRecovery > xDocRecover( xModel, UNO_QUERY_THROW ); |
| xDocRecover->recoverFromFile( |
| sURL, |
| lDescriptor.getUnpackedValueOrDefault( ::comphelper::MediaDescriptor::PROP_SALVAGEDFILE(), ::rtl::OUString() ), |
| lDescriptor.getAsConstPropertyValueList() |
| ); |
| |
| // No attachResource needed here. By definition (of XDocumentRecovery), the implementation is responsible |
| // for completely initializing the model, which includes attachResource (or equivalent), if required. |
| } |
| |
| // re-create all the views |
| ::std::vector< ::rtl::OUString > aViewsToRestore( rInfo.ViewNames.getLength() ); |
| if ( rInfo.ViewNames.getLength() ) |
| ::std::copy( rInfo.ViewNames.getConstArray(), rInfo.ViewNames.getConstArray() + rInfo.ViewNames.getLength(), aViewsToRestore.begin() ); |
| // if we don't have views for whatever reason, then create a default-view, at least |
| if ( aViewsToRestore.empty() ) |
| aViewsToRestore.push_back( ::rtl::OUString() ); |
| |
| for ( ::std::vector< ::rtl::OUString >::const_iterator viewName = aViewsToRestore.begin(); |
| viewName != aViewsToRestore.end(); |
| ++viewName |
| ) |
| { |
| // create a frame |
| Reference< XFrame > xTargetFrame = xDesktop->findFrame( SPECIALTARGET_BLANK, 0 ); |
| aCleanup.push_back( xTargetFrame.get() ); |
| |
| // create a view to the document |
| Reference< XController2 > xController; |
| if ( viewName->getLength() ) |
| { |
| xController.set( xModel->createViewController( *viewName, Sequence< PropertyValue >(), xTargetFrame ), UNO_SET_THROW ); |
| } |
| else |
| { |
| xController.set( xModel->createDefaultViewController( xTargetFrame ), UNO_SET_THROW ); |
| } |
| |
| // introduce model/view/controller to each other |
| xController->attachModel( xModel.get() ); |
| xModel->connectController( xController.get() ); |
| xTargetFrame->setComponent( xController->getComponentWindow(), xController.get() ); |
| xController->attachFrame( xTargetFrame ); |
| xModel->setCurrentController( xController.get() ); |
| } |
| |
| rInfo.Document = xModel.get(); |
| } |
| catch(const css::uno::RuntimeException&) |
| { throw; } |
| catch(const css::uno::Exception&) |
| { |
| Any aCaughtException( ::cppu::getCaughtException() ); |
| |
| // clean up |
| for ( ::std::vector< Reference< XComponent > >::const_iterator component = aCleanup.begin(); |
| component != aCleanup.end(); |
| ++component |
| ) |
| { |
| css::uno::Reference< css::util::XCloseable > xClose( *component, css::uno::UNO_QUERY ); |
| if ( xClose.is() ) |
| xClose->close( sal_True ); |
| else |
| (*component)->dispose(); |
| } |
| |
| // re-throw |
| ::rtl::OUStringBuffer sMsg(256); |
| sMsg.appendAscii("Recovery of \""); |
| sMsg.append (sURL ); |
| sMsg.appendAscii("\" failed." ); |
| |
| throw css::lang::WrappedTargetException( |
| sMsg.makeStringAndClear(), |
| static_cast< css::frame::XDispatch* >(this), |
| aCaughtException |
| ); |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_generateNewTempURL(const ::rtl::OUString& sBackupPath , |
| ::comphelper::MediaDescriptor& /*rMediaDescriptor*/, |
| AutoRecovery::TDocumentInfo& rInfo ) |
| { |
| // SAFE -> ---------------------------------- |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| aReadLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| // specify URL for saving (which points to a temp file inside backup directory) |
| // and define an unique name, so we can locate it later. |
| // This unique name must solve an optimization problem too! |
| // In case we are asked to save unmodified documents too - and one of them |
| // is an empty one (because it was new created using e.g. an URL private:factory/...) |
| // we should not save it realy. Then we put the information about such "empty document" |
| // into the configuration and dont create any recovery file on disk. |
| // We use the title of the document to make it unique. |
| ::rtl::OUStringBuffer sUniqueName; |
| if (rInfo.OrgURL.getLength()) |
| { |
| css::uno::Reference< css::util::XURLTransformer > xParser(xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), css::uno::UNO_QUERY); |
| css::util::URL aURL; |
| aURL.Complete = rInfo.OrgURL; |
| xParser->parseStrict(aURL); |
| sUniqueName.append(aURL.Name); |
| } |
| else |
| if (rInfo.FactoryURL.getLength()) |
| sUniqueName.appendAscii("untitled"); |
| sUniqueName.appendAscii("_"); |
| |
| // TODO: Must we strip some illegal signes - if we use the title? |
| |
| String sName (sUniqueName.makeStringAndClear()); |
| String sExtension(rInfo.Extension ); |
| String sPath (sBackupPath ); |
| ::utl::TempFile aTempFile(sName, &sExtension, &sPath); |
| |
| rInfo.NewTempURL = aTempFile.GetURL(); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_informListener( sal_Int32 eJob , |
| const css::frame::FeatureStateEvent& aEvent) |
| { |
| // Helper shares mutex with us -> threadsafe! |
| ::cppu::OInterfaceContainerHelper* pListenerForURL = 0; |
| ::rtl::OUString sJob = AutoRecovery::implst_getJobDescription(eJob); |
| |
| // inform listener, which are registered for any URLs(!) |
| pListenerForURL = m_lListener.getContainer(sJob); |
| if(pListenerForURL != 0) |
| { |
| ::cppu::OInterfaceIteratorHelper pIt(*pListenerForURL); |
| while(pIt.hasMoreElements()) |
| { |
| try |
| { |
| css::uno::Reference< css::frame::XStatusListener > xListener(((css::frame::XStatusListener*)pIt.next()), css::uno::UNO_QUERY); |
| xListener->statusChanged(aEvent); |
| } |
| catch(const css::uno::RuntimeException&) |
| { pIt.remove(); } |
| } |
| } |
| } |
| |
| //----------------------------------------------- |
| ::rtl::OUString AutoRecovery::implst_getJobDescription(sal_Int32 eJob) |
| { |
| // describe the current running operation |
| ::rtl::OUStringBuffer sFeature(256); |
| sFeature.append(CMD_PROTOCOL); |
| |
| // Attention: Because "eJob" is used as a flag field the order of checking these |
| // flags is importent. We must preferr job with higher priorities! |
| // E.g. EmergencySave has an higher prio then AutoSave ... |
| // On the other side there exist a well defined order between two different jobs. |
| // e.g. PrepareEmergencySave must be done before EmergencySave is started of course. |
| |
| if ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE) |
| sFeature.append(CMD_DO_PREPARE_EMERGENCY_SAVE); |
| else |
| if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE) |
| sFeature.append(CMD_DO_EMERGENCY_SAVE); |
| else |
| if ((eJob & AutoRecovery::E_RECOVERY) == AutoRecovery::E_RECOVERY) |
| sFeature.append(CMD_DO_RECOVERY); |
| else |
| if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE) |
| sFeature.append(CMD_DO_SESSION_SAVE); |
| else |
| if ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT) == AutoRecovery::E_SESSION_QUIET_QUIT) |
| sFeature.append(CMD_DO_SESSION_QUIET_QUIT); |
| else |
| if ((eJob & AutoRecovery::E_SESSION_RESTORE) == AutoRecovery::E_SESSION_RESTORE) |
| sFeature.append(CMD_DO_SESSION_RESTORE); |
| else |
| if ((eJob & AutoRecovery::E_ENTRY_BACKUP) == AutoRecovery::E_ENTRY_BACKUP) |
| sFeature.append(CMD_DO_ENTRY_BACKUP); |
| else |
| if ((eJob & AutoRecovery::E_ENTRY_CLEANUP) == AutoRecovery::E_ENTRY_CLEANUP) |
| sFeature.append(CMD_DO_ENTRY_CLEANUP); |
| else |
| if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE) |
| sFeature.append(CMD_DO_AUTO_SAVE); |
| #ifdef ENABLE_WARNINGS |
| else if ( eJob != AutoRecovery::E_NO_JOB ) |
| LOG_WARNING("AutoRecovery::implst_getJobDescription()", "Invalid job identifier detected.") |
| #endif |
| |
| return sFeature.makeStringAndClear(); |
| } |
| |
| //----------------------------------------------- |
| sal_Int32 AutoRecovery::implst_classifyJob(const css::util::URL& aURL) |
| { |
| if (aURL.Protocol.equals(CMD_PROTOCOL)) |
| { |
| if (aURL.Path.equals(CMD_DO_PREPARE_EMERGENCY_SAVE)) |
| return AutoRecovery::E_PREPARE_EMERGENCY_SAVE; |
| else |
| if (aURL.Path.equals(CMD_DO_EMERGENCY_SAVE)) |
| return AutoRecovery::E_EMERGENCY_SAVE; |
| else |
| if (aURL.Path.equals(CMD_DO_RECOVERY)) |
| return AutoRecovery::E_RECOVERY; |
| else |
| if (aURL.Path.equals(CMD_DO_ENTRY_BACKUP)) |
| return AutoRecovery::E_ENTRY_BACKUP; |
| else |
| if (aURL.Path.equals(CMD_DO_ENTRY_CLEANUP)) |
| return AutoRecovery::E_ENTRY_CLEANUP; |
| else |
| if (aURL.Path.equals(CMD_DO_SESSION_SAVE)) |
| return AutoRecovery::E_SESSION_SAVE; |
| else |
| if (aURL.Path.equals(CMD_DO_SESSION_QUIET_QUIT)) |
| return AutoRecovery::E_SESSION_QUIET_QUIT; |
| else |
| if (aURL.Path.equals(CMD_DO_SESSION_RESTORE)) |
| return AutoRecovery::E_SESSION_RESTORE; |
| else |
| if (aURL.Path.equals(CMD_DO_DISABLE_RECOVERY)) |
| return AutoRecovery::E_DISABLE_AUTORECOVERY; |
| else |
| if (aURL.Path.equals(CMD_DO_SET_AUTOSAVE_STATE)) |
| return AutoRecovery::E_SET_AUTOSAVE_STATE; |
| } |
| |
| LOG_WARNING("AutoRecovery::implts_classifyJob()", "Invalid URL (protocol).") |
| return AutoRecovery::E_NO_JOB; |
| } |
| |
| //----------------------------------------------- |
| css::frame::FeatureStateEvent AutoRecovery::implst_createFeatureStateEvent( sal_Int32 eJob , |
| const ::rtl::OUString& sEventType, |
| AutoRecovery::TDocumentInfo* pInfo ) |
| { |
| css::frame::FeatureStateEvent aEvent; |
| aEvent.FeatureURL.Complete = AutoRecovery::implst_getJobDescription(eJob); |
| aEvent.FeatureDescriptor = sEventType; |
| |
| if (sEventType.equals(OPERATION_UPDATE) && pInfo) |
| { |
| // pack rInfo for transport via UNO |
| ::comphelper::NamedValueCollection aInfo; |
| aInfo.put( CFG_ENTRY_PROP_ID, pInfo->ID ); |
| aInfo.put( CFG_ENTRY_PROP_ORIGINALURL, pInfo->OrgURL ); |
| aInfo.put( CFG_ENTRY_PROP_FACTORYURL, pInfo->FactoryURL ); |
| aInfo.put( CFG_ENTRY_PROP_TEMPLATEURL, pInfo->TemplateURL ); |
| aInfo.put( CFG_ENTRY_PROP_TEMPURL, pInfo->OldTempURL.getLength() ? pInfo->OldTempURL : pInfo->NewTempURL ); |
| aInfo.put( CFG_ENTRY_PROP_MODULE, pInfo->AppModule ); |
| aInfo.put( CFG_ENTRY_PROP_TITLE, pInfo->Title ); |
| aInfo.put( CFG_ENTRY_PROP_VIEWNAMES, pInfo->ViewNames ); |
| aInfo.put( CFG_ENTRY_PROP_DOCUMENTSTATE, pInfo->DocumentState ); |
| |
| aEvent.State <<= aInfo.getPropertyValues(); |
| } |
| |
| return aEvent; |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_resetHandleStates(sal_Bool /*bLoadCache*/) |
| { |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| // SAFE -> ------------------------------ |
| WriteGuard aWriteLock(m_aLock); |
| |
| AutoRecovery::TDocumentList::iterator pIt; |
| for ( pIt = m_lDocCache.begin(); |
| pIt != m_lDocCache.end() ; |
| ++pIt ) |
| { |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| rInfo.DocumentState &= ~AutoRecovery::E_HANDLED ; |
| rInfo.DocumentState &= ~AutoRecovery::E_POSTPONED; |
| |
| // SAFE -> ------------------------------ |
| aWriteLock.unlock(); |
| implts_flushConfigItem(rInfo); |
| aWriteLock.lock(); |
| // <- SAFE ------------------------------ |
| } |
| |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_prepareEmergencySave() |
| { |
| // Be sure to know all open documents realy .-) |
| implts_verifyCacheAgainstDesktopDocumentList(); |
| |
| // hide all docs, so the user cant disturb our emergency save .-) |
| implts_changeAllDocVisibility(sal_False); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_doEmergencySave(const DispatchParams& aParams) |
| { |
| // Write a hint "we chrashed" into the configuration, so |
| // the error report tool is started too in case no recovery |
| // documents exists and was saved. |
| ::comphelper::ConfigurationHelper::writeDirectKey( |
| m_xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_RECOVERYINFO, |
| CFG_ENTRY_CRASHED, |
| css::uno::makeAny(sal_True), |
| ::comphelper::ConfigurationHelper::E_STANDARD); |
| |
| // for all docs, store their current view/names in the configurtion |
| implts_persistAllActiveViewNames(); |
| |
| // The called method for saving documents runs |
| // during normal AutoSave more then once. Because |
| // it postpone active documents and save it later. |
| // That is normaly done by recalling it from a timer. |
| // Here we must do it immediatly! |
| // Of course this method returns the right state - |
| // because it knows, that we are running in ERMERGENCY SAVE mode .-) |
| |
| sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-) |
| AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; |
| do |
| { |
| eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_True, &aParams); |
| } |
| while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); |
| |
| // reset the handle state of all |
| // cache items. Such handle state indicates, that a document |
| // was already saved during the THIS(!) EmergencySave session. |
| // Of course following recovery session must be started without |
| // any "handle" state ... |
| implts_resetHandleStates(sal_False); |
| |
| // flush config cached back to disc. |
| impl_flushALLConfigChanges(); |
| |
| // try to make sure next time office will be started user wont be |
| // notified about any other might be running office instance |
| // remove ".lock" file from disc ! |
| AutoRecovery::st_impl_removeLockFile(); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_doRecovery(const DispatchParams& aParams) |
| { |
| AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; |
| do |
| { |
| eSuggestedTimer = implts_openDocs(aParams); |
| } |
| while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); |
| |
| // reset the handle state of all |
| // cache items. Such handle state indicates, that a document |
| // was already saved during the THIS(!) Recovery session. |
| // Of course a may be following EmergencySave session must be started without |
| // any "handle" state ... |
| implts_resetHandleStates(sal_True); |
| |
| // Reset the configuration hint "we was crashed"! |
| ::comphelper::ConfigurationHelper::writeDirectKey( |
| m_xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_RECOVERYINFO, |
| CFG_ENTRY_CRASHED, |
| css::uno::makeAny(sal_False), |
| ::comphelper::ConfigurationHelper::E_STANDARD); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_doSessionSave(const DispatchParams& aParams) |
| { |
| LOG_RECOVERY("AutoRecovery::implts_doSessionSave()") |
| |
| // Be sure to know all open documents realy .-) |
| implts_verifyCacheAgainstDesktopDocumentList(); |
| |
| // for all docs, store their current view/names in the configurtion |
| implts_persistAllActiveViewNames(); |
| |
| // The called method for saving documents runs |
| // during normal AutoSave more then once. Because |
| // it postpone active documents and save it later. |
| // That is normaly done by recalling it from a timer. |
| // Here we must do it immediatly! |
| // Of course this method returns the right state - |
| // because it knows, that we are running in SESSION SAVE mode .-) |
| |
| sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-) |
| AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; |
| do |
| { |
| // do not remove lock files of the documents, it will be done on session quit |
| eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False, &aParams); |
| } |
| while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); |
| |
| // reset the handle state of all |
| // cache items. Such handle state indicates, that a document |
| // was already saved during the THIS(!) save session. |
| // Of course following restore session must be started without |
| // any "handle" state ... |
| implts_resetHandleStates(sal_False); |
| |
| // flush config cached back to disc. |
| impl_flushALLConfigChanges(); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_doSessionQuietQuit(const DispatchParams& /*aParams*/) |
| { |
| LOG_RECOVERY("AutoRecovery::implts_doSessionQuietQuit()") |
| |
| // try to make sure next time office will be started user wont be |
| // notified about any other might be running office instance |
| // remove ".lock" file from disc ! |
| // it is done as a first action for session save since Gnome sessions |
| // do not provide enough time for shutdown, and the dialog looks to be |
| // confusing for the user |
| AutoRecovery::st_impl_removeLockFile(); |
| |
| // reset all modified documents, so the dont show any UI on closing ... |
| // and close all documents, so we can shutdown the OS! |
| implts_prepareSessionShutdown(); |
| |
| // Write a hint for "stored session data" into the configuration, so |
| // the on next startup we know what's happen last time |
| ::comphelper::ConfigurationHelper::writeDirectKey( |
| m_xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_RECOVERYINFO, |
| CFG_ENTRY_SESSIONDATA, |
| css::uno::makeAny(sal_True), |
| ::comphelper::ConfigurationHelper::E_STANDARD); |
| |
| // flush config cached back to disc. |
| impl_flushALLConfigChanges(); |
| } |
| |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_doSessionRestore(const DispatchParams& aParams) |
| { |
| LOG_RECOVERY("AutoRecovery::implts_doSessionRestore() ...") |
| |
| AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; |
| do |
| { |
| eSuggestedTimer = implts_openDocs(aParams); |
| } |
| while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); |
| |
| // reset the handle state of all |
| // cache items. Such handle state indicates, that a document |
| // was already saved during the THIS(!) Restore session. |
| // Of course a may be following save session must be started without |
| // any "handle" state ... |
| implts_resetHandleStates(sal_True); |
| |
| // make all opened documents visible |
| implts_changeAllDocVisibility(sal_True); |
| |
| // Reset the configuration hint for "session save"! |
| LOG_RECOVERY("... reset config key 'SessionData'") |
| ::comphelper::ConfigurationHelper::writeDirectKey( |
| m_xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_RECOVERYINFO, |
| CFG_ENTRY_SESSIONDATA, |
| css::uno::makeAny(sal_False), |
| ::comphelper::ConfigurationHelper::E_STANDARD); |
| |
| LOG_RECOVERY("... AutoRecovery::implts_doSessionRestore()") |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_backupWorkingEntry(const DispatchParams& aParams) |
| { |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); |
| |
| AutoRecovery::TDocumentList::iterator pIt; |
| for ( pIt = m_lDocCache.begin(); |
| pIt != m_lDocCache.end() ; |
| ++pIt ) |
| { |
| const AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| if (rInfo.ID != aParams.m_nWorkingEntryID) |
| continue; |
| |
| ::rtl::OUString sSourceURL; |
| // Prefer temp file. It contains the changes against the original document! |
| if (rInfo.OldTempURL.getLength()) |
| sSourceURL = rInfo.OldTempURL; |
| else |
| if (rInfo.NewTempURL.getLength()) |
| sSourceURL = rInfo.NewTempURL; |
| else |
| if (rInfo.OrgURL.getLength()) |
| sSourceURL = rInfo.OrgURL; |
| else |
| continue; // nothing real to save! An unmodified but new created document. |
| |
| INetURLObject aParser(sSourceURL); |
| // AutoRecovery::EFailureSafeResult eResult = |
| implts_copyFile(sSourceURL, aParams.m_sSavePath, aParser.getName()); |
| |
| // TODO: Check eResult and react for errors (InteractionHandler!?) |
| // Currently we ignore it ... |
| // DONT UPDATE THE CACHE OR REMOVE ANY TEMP. FILES FROM DISK. |
| // That has to be forced from outside explicitly. |
| // See implts_cleanUpWorkingEntry() for further details. |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_cleanUpWorkingEntry(const DispatchParams& aParams) |
| { |
| CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); |
| |
| AutoRecovery::TDocumentList::iterator pIt; |
| for ( pIt = m_lDocCache.begin(); |
| pIt != m_lDocCache.end() ; |
| ++pIt ) |
| { |
| AutoRecovery::TDocumentInfo& rInfo = *pIt; |
| if (rInfo.ID != aParams.m_nWorkingEntryID) |
| continue; |
| |
| AutoRecovery::st_impl_removeFile(rInfo.OldTempURL); |
| AutoRecovery::st_impl_removeFile(rInfo.NewTempURL); |
| implts_flushConfigItem(rInfo, sal_True); // sal_True => remove it from xml config! |
| |
| m_lDocCache.erase(pIt); |
| break; /// !!! pIt is not defined any longer ... further this function has finished it's work |
| } |
| } |
| |
| //----------------------------------------------- |
| AutoRecovery::EFailureSafeResult AutoRecovery::implts_copyFile(const ::rtl::OUString& sSource , |
| const ::rtl::OUString& sTargetPath, |
| const ::rtl::OUString& sTargetName) |
| { |
| // create content for the parent folder and call transfer on that content with the source content |
| // and the destination file name as parameters |
| |
| css::uno::Reference< css::ucb::XCommandEnvironment > xEnvironment; |
| |
| ::ucbhelper::Content aSourceContent; |
| ::ucbhelper::Content aTargetContent; |
| |
| try |
| { |
| aTargetContent = ::ucbhelper::Content(sTargetPath, xEnvironment); |
| } |
| catch(const css::uno::Exception&) |
| { return AutoRecovery::E_WRONG_TARGET_PATH; } |
| |
| sal_Int32 nNameClash; |
| // nNameClash = css::ucb::NameClash::ERROR; |
| nNameClash = css::ucb::NameClash::RENAME; |
| // nNameClash = css::ucb::NameClash::OVERWRITE; |
| |
| try |
| { |
| ::ucbhelper::Content::create(sSource, xEnvironment, aSourceContent); |
| aTargetContent.transferContent(aSourceContent, ::ucbhelper::InsertOperation_COPY, sTargetName, nNameClash); |
| } |
| catch(const css::uno::Exception&) |
| { return AutoRecovery::E_ORIGINAL_FILE_MISSING; } |
| |
| return AutoRecovery::E_COPIED; |
| } |
| |
| //----------------------------------------------- |
| sal_Bool SAL_CALL AutoRecovery::convertFastPropertyValue( css::uno::Any& /*aConvertedValue*/, |
| css::uno::Any& /*aOldValue*/ , |
| sal_Int32 /*nHandle*/ , |
| const css::uno::Any& /*aValue*/ ) |
| throw(css::lang::IllegalArgumentException) |
| { |
| // not needed currently |
| return sal_False; |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, |
| const css::uno::Any& /*aValue*/ ) |
| throw(css::uno::Exception) |
| { |
| // not needed currently |
| } |
| |
| //----------------------------------------------- |
| void SAL_CALL AutoRecovery::getFastPropertyValue(css::uno::Any& aValue , |
| sal_Int32 nHandle) const |
| { |
| switch(nHandle) |
| { |
| case AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA : |
| { |
| sal_Bool bSessionData = sal_False; |
| ::comphelper::ConfigurationHelper::readDirectKey( |
| m_xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_RECOVERYINFO, |
| CFG_ENTRY_SESSIONDATA, |
| ::comphelper::ConfigurationHelper::E_READONLY) >>= bSessionData; |
| |
| sal_Bool bRecoveryData = ((sal_Bool)(m_lDocCache.size()>0)); |
| |
| // exists session data ... => then we cant say, that these |
| // data are valid for recovery. So we have to return sal_False then! |
| if (bSessionData) |
| bRecoveryData = sal_False; |
| |
| aValue <<= bRecoveryData; |
| } |
| break; |
| |
| case AUTORECOVERY_PROPHANDLE_CRASHED : |
| aValue = ::comphelper::ConfigurationHelper::readDirectKey( |
| m_xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_RECOVERYINFO, |
| CFG_ENTRY_CRASHED, |
| ::comphelper::ConfigurationHelper::E_READONLY); |
| break; |
| |
| case AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA : |
| aValue = ::comphelper::ConfigurationHelper::readDirectKey( |
| m_xSMGR, |
| CFG_PACKAGE_RECOVERY, |
| CFG_PATH_RECOVERYINFO, |
| CFG_ENTRY_SESSIONDATA, |
| ::comphelper::ConfigurationHelper::E_READONLY); |
| break; |
| } |
| } |
| |
| //----------------------------------------------- |
| const css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor() |
| { |
| static const css::beans::Property pPropertys[] = |
| { |
| css::beans::Property( AUTORECOVERY_PROPNAME_CRASHED , AUTORECOVERY_PROPHANDLE_CRASHED , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), |
| css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA, AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA, ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), |
| css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA , AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), |
| }; |
| static const css::uno::Sequence< css::beans::Property > lPropertyDescriptor(pPropertys, AUTORECOVERY_PROPCOUNT); |
| return lPropertyDescriptor; |
| } |
| |
| //----------------------------------------------- |
| ::cppu::IPropertyArrayHelper& SAL_CALL AutoRecovery::getInfoHelper() |
| { |
| static ::cppu::OPropertyArrayHelper* pInfoHelper = 0; |
| if(!pInfoHelper) |
| { |
| ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() ); |
| if(!pInfoHelper) |
| { |
| static ::cppu::OPropertyArrayHelper aInfoHelper(impl_getStaticPropertyDescriptor(), sal_True); |
| pInfoHelper = &aInfoHelper; |
| } |
| } |
| |
| return (*pInfoHelper); |
| } |
| |
| //----------------------------------------------- |
| css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL AutoRecovery::getPropertySetInfo() |
| throw(css::uno::RuntimeException) |
| { |
| static css::uno::Reference< css::beans::XPropertySetInfo >* pInfo = 0; |
| if(!pInfo) |
| { |
| ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() ); |
| if(!pInfo) |
| { |
| static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(createPropertySetInfo(getInfoHelper())); |
| pInfo = &xInfo; |
| } |
| } |
| |
| return (*pInfo); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() |
| { |
| LOG_RECOVERY("AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() ...") |
| |
| // SAFE -> ---------------------------------- |
| WriteGuard aWriteLock(m_aLock); |
| css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; |
| aWriteLock.unlock(); |
| // <- SAFE ---------------------------------- |
| |
| try |
| { |
| css::uno::Reference< css::frame::XFramesSupplier > xDesktop( |
| xSMGR->createInstance(SERVICENAME_DESKTOP), |
| css::uno::UNO_QUERY_THROW); |
| |
| css::uno::Reference< css::container::XIndexAccess > xContainer( |
| xDesktop->getFrames(), |
| css::uno::UNO_QUERY_THROW); |
| |
| sal_Int32 i = 0; |
| sal_Int32 c = xContainer->getCount(); |
| |
| for (i=0; i<c; ++i) |
| { |
| css::uno::Reference< css::frame::XFrame > xFrame; |
| try |
| { |
| xContainer->getByIndex(i) >>= xFrame; |
| if (!xFrame.is()) |
| continue; |
| } |
| // can happen in multithreaded environments, that frames was removed from the container during this loop runs! |
| // Ignore it. |
| catch(const css::lang::IndexOutOfBoundsException&) |
| { continue; } |
| |
| // We are interested on visible documents only. |
| // Note: It's n optional interface .-( |
| css::uno::Reference< css::awt::XWindow2 > xVisibleCheck( |
| xFrame->getContainerWindow(), |
| css::uno::UNO_QUERY); |
| if ( |
| (!xVisibleCheck.is() ) || |
| (!xVisibleCheck->isVisible()) |
| ) |
| { |
| continue; |
| } |
| |
| // extract the model from the frame. |
| // Ignore "view only" frames, which does not have a model. |
| css::uno::Reference< css::frame::XController > xController; |
| css::uno::Reference< css::frame::XModel > xModel; |
| |
| xController = xFrame->getController(); |
| if (xController.is()) |
| xModel = xController->getModel(); |
| if (!xModel.is()) |
| continue; |
| |
| // insert model into cache ... |
| // If the model is already well known inside cache |
| // it's information set will be updated by asking the |
| // model again for it's new states. |
| implts_registerDocument(xModel); |
| } |
| } |
| catch(const css::uno::RuntimeException& exRun) |
| { throw exRun; } |
| catch(const css::uno::Exception&) |
| {} |
| |
| LOG_RECOVERY("... AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()") |
| } |
| |
| //----------------------------------------------- |
| sal_Bool AutoRecovery::impl_enoughDiscSpace(sal_Int32 nRequiredSpace) |
| { |
| #ifdef SIMULATE_FULL_DISC |
| return sal_False; |
| #endif |
| |
| // In case an error occures and we are not able to retrieve the needed information |
| // it's better to "disable" the feature ShowErrorOnFullDisc ! |
| // Otherwhise we start a confusing process of error handling ... |
| |
| sal_uInt64 nFreeSpace = SAL_MAX_UINT64; |
| |
| ::rtl::OUString sBackupPath(SvtPathOptions().GetBackupPath()); |
| ::osl::VolumeInfo aInfo (VolumeInfoMask_FreeSpace); |
| ::osl::FileBase::RC aRC = ::osl::Directory::getVolumeInfo(sBackupPath, aInfo); |
| |
| if ( |
| (aInfo.isValid(VolumeInfoMask_FreeSpace)) && |
| (aRC == ::osl::FileBase::E_None ) |
| ) |
| { |
| nFreeSpace = aInfo.getFreeSpace(); |
| } |
| |
| sal_uInt64 nFreeMB = (nFreeSpace/1048576); |
| return (nFreeMB >= (sal_uInt64)nRequiredSpace); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::impl_showFullDiscError() |
| { |
| static String PLACEHOLDER_PATH = String::CreateFromAscii("%PATH"); |
| |
| String sBtn(FwkResId(STR_FULL_DISC_RETRY_BUTTON)); |
| String sMsg(FwkResId(STR_FULL_DISC_MSG )); |
| |
| String sBackupURL(SvtPathOptions().GetBackupPath()); |
| INetURLObject aConverter(sBackupURL); |
| sal_Unicode aDelimiter; |
| String sBackupPath = aConverter.getFSysPath(INetURLObject::FSYS_DETECT, &aDelimiter); |
| if (sBackupPath.Len()<1) |
| sBackupPath = sBackupURL; |
| sMsg.SearchAndReplace(PLACEHOLDER_PATH, sBackupPath); |
| |
| ErrorBox dlgError(0, WB_OK, sMsg); |
| dlgError.SetButtonText(dlgError.GetButtonId(0), sBtn); |
| dlgError.Execute(); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo , |
| ::comphelper::MediaDescriptor& rArgs , |
| const css::uno::Reference< css::frame::XFrame >& xNewFrame) |
| { |
| // external well known frame must be preferred (because it was created by ourself |
| // for loading documents into this frame)! |
| // But if no frame exists ... we can try to locate it using any frame bound to the provided |
| // document. Of course we must live without any frame in case the document does not exists at this |
| // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-) |
| css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame; |
| if ( |
| (!xFrame.is() ) && |
| (rInfo.Document.is()) |
| ) |
| { |
| css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController(); |
| if (xController.is()) |
| xFrame = xController->getFrame(); |
| } |
| |
| // Any outside progress must be used ... |
| // Only if there is no progress, we can create our own one. |
| css::uno::Reference< css::task::XStatusIndicator > xInternalProgress; |
| css::uno::Reference< css::task::XStatusIndicator > xExternalProgress = rArgs.getUnpackedValueOrDefault( |
| ::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(), |
| css::uno::Reference< css::task::XStatusIndicator >() ); |
| |
| // Normaly a progress is set from outside (e.g. by the CrashSave/Recovery dialog, which uses our dispatch API). |
| // But for a normal auto save we dont have such "external progress"... because this function is triggered by our own timer then. |
| // In such case we must create our own progress ! |
| if ( |
| (! xExternalProgress.is()) && |
| (xFrame.is() ) |
| ) |
| { |
| css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xFrame, css::uno::UNO_QUERY); |
| if (xProgressFactory.is()) |
| xInternalProgress = xProgressFactory->createStatusIndicator(); |
| } |
| |
| // HACK |
| // An external provided progress (most given by the CrashSave/Recovery dialog) |
| // must be preferred. But we know that some application filters query it's own progress instance |
| // at the frame method Frame::createStatusIndicator(). |
| // So we use a two step mechanism: |
| // 1) we set the progress inside the MediaDescriptor, which will be provided to the filter |
| // 2) and we set a special Frame property, which overwrites the normal behaviour of Frame::createStatusIndicator .-) |
| // But we supress 2) in case we uses an internal progress. Because then it doesnt matter |
| // if our applications make it wrong. In such case the internal progress resists at the same frame |
| // and there is no need to forward progress activities to e.g. an outside dialog .-) |
| if ( |
| (xExternalProgress.is()) && |
| (xFrame.is() ) |
| ) |
| { |
| css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY); |
| if (xFrameProps.is()) |
| xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(xExternalProgress)); |
| } |
| |
| // But inside the MediaDescriptor we must set our own create progress ... |
| // in case there is not already anothe rprogress set. |
| rArgs.createItemIfMissing(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(), xInternalProgress); |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo , |
| ::comphelper::MediaDescriptor& rArgs , |
| const css::uno::Reference< css::frame::XFrame >& xNewFrame) |
| { |
| // external well known frame must be preferred (because it was created by ourself |
| // for loading documents into this frame)! |
| // But if no frame exists ... we can try to locate it using any frame bound to the provided |
| // document. Of course we must live without any frame in case the document does not exists at this |
| // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-) |
| css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame; |
| if ( |
| (!xFrame.is() ) && |
| (rInfo.Document.is()) |
| ) |
| { |
| css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController(); |
| if (xController.is()) |
| xFrame = xController->getFrame(); |
| } |
| |
| // stop progress interception on corresponding frame. |
| css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY); |
| if (xFrameProps.is()) |
| xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(css::uno::Reference< css::task::XStatusIndicator >())); |
| |
| // forget progress inside list of arguments. |
| ::comphelper::MediaDescriptor::iterator pArg = rArgs.find(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()); |
| if (pArg != rArgs.end()) |
| { |
| rArgs.erase(pArg); |
| pArg = rArgs.end(); |
| } |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::impl_flushALLConfigChanges() |
| { |
| try |
| { |
| // SAFE -> |
| ReadGuard aReadLock(m_aLock); |
| css::uno::Reference< css::uno::XInterface > xRecoveryCfg(m_xRecoveryCFG, css::uno::UNO_QUERY); |
| aReadLock.unlock(); |
| // <- SAFE |
| |
| if (xRecoveryCfg.is()) |
| ::comphelper::ConfigurationHelper::flush(xRecoveryCfg); |
| |
| // SOLAR SAFE -> |
| ::vos::OGuard aGuard( Application::GetSolarMutex() ); |
| ::utl::ConfigManager* pCfgMgr = ::utl::ConfigManager::GetConfigManager(); |
| if (pCfgMgr) |
| pCfgMgr->StoreConfigItems(); |
| } |
| catch(const css::uno::Exception&) |
| {} |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::st_impl_removeFile(const ::rtl::OUString& sURL) |
| { |
| if ( ! sURL.getLength()) |
| return; |
| |
| try |
| { |
| ::ucbhelper::Content aContent = ::ucbhelper::Content(sURL, css::uno::Reference< css::ucb::XCommandEnvironment >()); |
| aContent.executeCommand(::rtl::OUString::createFromAscii("delete"), css::uno::makeAny(sal_True)); |
| } |
| catch(const css::uno::Exception&) |
| {} |
| } |
| |
| //----------------------------------------------- |
| void AutoRecovery::st_impl_removeLockFile() |
| { |
| try |
| { |
| ::rtl::OUString sUserURL; |
| ::utl::Bootstrap::locateUserInstallation( sUserURL ); |
| |
| ::rtl::OUStringBuffer sLockURLBuf; |
| sLockURLBuf.append (sUserURL); |
| sLockURLBuf.appendAscii("/.lock"); |
| ::rtl::OUString sLockURL = sLockURLBuf.makeStringAndClear(); |
| |
| AutoRecovery::st_impl_removeFile(sLockURL); |
| } |
| catch(const css::uno::Exception&) |
| {} |
| } |
| |
| } // namespace framework |