summaryrefslogtreecommitdiff
path: root/framework/source/services/autorecovery.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'framework/source/services/autorecovery.cxx')
-rw-r--r--framework/source/services/autorecovery.cxx3595
1 files changed, 3595 insertions, 0 deletions
diff --git a/framework/source/services/autorecovery.cxx b/framework/source/services/autorecovery.cxx
new file mode 100644
index 000000000000..9b19503f887e
--- /dev/null
+++ b/framework/source/services/autorecovery.cxx
@@ -0,0 +1,3595 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2008 by Sun Microsystems, Inc.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * $RCSfile: autorecovery.cxx,v $
+ *
+ * $Revision: 1.28 $
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_framework.hxx"
+#include "services/autorecovery.hxx"
+
+//_______________________________________________
+// own includes
+#include <loadenv/loaddispatchlistener.hxx>
+#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/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/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 <vcl/svapp.hxx>
+#include <unotools/pathoptions.hxx>
+#include <tools/link.hxx>
+#include <tools/string.hxx>
+#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 <tools/urlobj.hxx>
+
+//_______________________________________________
+// namespaces
+
+#ifndef css
+namespace css = ::com::sun::star;
+#endif
+
+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 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 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 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_actualizeTimer();
+ // 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_actualizeTimer();
+
+ 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);
+
+ // REINTRANT !? -> --------------------------------
+ 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_actualizeModifiedState(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); // 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_actualizeTimer();
+}
+
+//-----------------------------------------------
+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); // 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);
+
+ // REINTRANT -> --------------------------------
+ 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();
+ // <- REINTRANT --------------------------------
+
+ 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;
+
+ // REINTRANT -> --------------------------
+ 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 ;
+ implts_specifyAppModuleAndFactoryURL(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();
+ // <- REINTRANT --------------------------
+ }
+
+ implts_actualizeTimer();
+}
+
+//-----------------------------------------------
+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_specifyAppModuleAndFactoryURL(AutoRecovery::TDocumentInfo& rInfo)
+{
+ if (
+ (!rInfo.AppModule.getLength()) &&
+ (!rInfo.Document.is() )
+ )
+ {
+ throw css::uno::RuntimeException(
+ ::rtl::OUString::createFromAscii("Cant find out the application module nor its factory URL, if no application module (or a suitable) document is known!"),
+ static_cast< css::frame::XDispatch* >(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;
+}
+
+//-----------------------------------------------
+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 ));
+
+ 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_actualizeTimer()
+{
+ 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;
+
+ 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_actualizeTimer();
+ 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_actualizeTimer();
+ 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_actualizeTimer();
+ }
+ 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 shure 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_actualizeModifiedState(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;
+
+ // 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_specifyAppModuleAndFactoryURL(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())
+ )
+ 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;
+ aNew.DocumentState |= AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
+ }
+
+ aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
+
+ // SAFE -> ----------------------------------
+ WriteGuard aWriteLock(m_aLock);
+
+ // create a new cache entry ... this document isnt well 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); // 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;
+ rInfo.DocumentState |= AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
+
+ /* 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_actualizeModifiedState(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 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;
+ rInfo.DocumentState |= AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
+ }
+ else
+ {
+ rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED;
+ rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
+ }
+ }
+
+ 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;
+}
+
+//-----------------------------------------------
+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);
+ css::uno::Reference< css::container::XIndexAccess > xContainer(xDesktop->getFrames() , css::uno::UNO_QUERY);
+ sal_Int32 c = xContainer->getCount();
+ sal_Int32 i = 0;
+
+ for (i=0; i<c; ++i)
+ {
+ css::uno::Reference< css::frame::XFrame > xTask;
+
+ xContainer->getByIndex(i) >>= xTask;
+ if (!xTask.is())
+ continue;
+
+ css::uno::Reference< css::awt::XWindow > xWindow = xTask->getContainerWindow();
+ xWindow->setVisible(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 reseted 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!
+ sal_Bool bModified = ((aInfo.DocumentState & AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE ) == AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE);
+ if (! bModified)
+ {
+ 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.
+ css::uno::Reference< css::frame::XStorable > xStore(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
+ {
+ xStore->storeToURL(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;
+ rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
+ }
+ 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();
+
+ 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!
+
+ // <- 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(sURL, lPatchDescriptor.getAsConstPropertyValueList());
+ }
+
+ css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
+ 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 documentw as recovered ... we must start listening.
+ Otherwhise the first "modify" dont reach us. Because weself 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::util::XURLTransformer > xParser(xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), css::uno::UNO_QUERY_THROW);
+ css::util::URL aURL;
+ aURL.Complete = sURL;
+ xParser->parseStrict(aURL);
+
+ LoadDispatchListener* pLoadListener = new LoadDispatchListener();
+ css::uno::Reference< css::frame::XDispatchResultListener > xLoadListener (static_cast< css::frame::XDispatchResultListener* >(pLoadListener), css::uno::UNO_QUERY_THROW);
+
+ css::uno::Reference< css::frame::XFrame > xDesktop (xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::frame::XFrame > xNewTarget = xDesktop->findFrame(SPECIALTARGET_BLANK, 0);
+ css::uno::Reference< css::frame::XDispatchProvider > xProvider (xNewTarget, css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::frame::XNotifyingDispatch > xDispatcher(
+ xProvider->queryDispatch(aURL, SPECIALTARGET_SELF, 0),
+ css::uno::UNO_QUERY_THROW);
+
+ // load the document and listen for the state of this operation.
+ pLoadListener->setURL(aURL.Complete);
+
+ // make sure the right progress is used always.
+ impl_establishProgress(rInfo, lDescriptor, xNewTarget);
+
+ try
+ {
+ xDispatcher->dispatchWithNotification(
+ aURL,
+ lDescriptor.getAsConstPropertyValueList(),
+ xLoadListener);
+
+ pLoadListener->wait(0); // wait for ever!
+
+ css::frame::DispatchResultEvent aResult = pLoadListener->getResult();
+ if (aResult.State != css::frame::DispatchResultState::SUCCESS)
+ {
+ ::rtl::OUStringBuffer sMsg(256);
+ sMsg.appendAscii("Recovery of \"");
+ sMsg.append (aURL.Complete );
+ sMsg.appendAscii("\" failed." );
+ throw css::uno::Exception(sMsg.makeStringAndClear(), static_cast< css::frame::XDispatch* >(this));
+ }
+
+ rInfo.Document = fpf::extractFrameModel(xNewTarget);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {
+ css::uno::Reference< css::util::XCloseable > xClose(xNewTarget, css::uno::UNO_QUERY);
+ xClose->close(sal_True);
+ xNewTarget.clear();
+ throw;
+ }
+
+ // of course we must forget all references to this temp(!) progress
+ impl_forgetProgress(rInfo, lDescriptor, xNewTarget);
+}
+
+//-----------------------------------------------
+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
+ 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
+ css::uno::Sequence< css::beans::NamedValue > lInfo(8);
+ lInfo[0].Name = CFG_ENTRY_PROP_ID;
+ lInfo[0].Value <<= pInfo->ID;
+
+ lInfo[1].Name = CFG_ENTRY_PROP_ORIGINALURL;
+ lInfo[1].Value <<= pInfo->OrgURL;
+
+ lInfo[2].Name = CFG_ENTRY_PROP_FACTORYURL;
+ lInfo[2].Value <<= pInfo->FactoryURL;
+
+ lInfo[3].Name = CFG_ENTRY_PROP_TEMPLATEURL;
+ lInfo[3].Value <<= pInfo->TemplateURL;
+
+ lInfo[4].Name = CFG_ENTRY_PROP_TEMPURL;
+ if (pInfo->OldTempURL.getLength())
+ lInfo[4].Value <<= pInfo->OldTempURL;
+ else
+ lInfo[4].Value <<= pInfo->NewTempURL;
+
+ lInfo[5].Name = CFG_ENTRY_PROP_MODULE;
+ lInfo[5].Value <<= pInfo->AppModule;
+
+ lInfo[6].Name = CFG_ENTRY_PROP_TITLE;
+ lInfo[6].Value <<= pInfo->Title;
+
+ lInfo[7].Name = CFG_ENTRY_PROP_DOCUMENTSTATE;
+ lInfo[7].Value <<= pInfo->DocumentState;
+
+ aEvent.State <<= lInfo;
+ }
+
+ 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);
+
+ // 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();
+
+ // 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); // 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 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