update dev300-m58
[ooovba.git] / framework / source / services / autorecovery.cxx
blobca7b94f2c528ebc88071ef67f5710fce669c4d9a
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: autorecovery.cxx,v $
11 * $Revision: 1.28 $
13 * This file is part of OpenOffice.org.
15 * OpenOffice.org is free software: you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License version 3
17 * only, as published by the Free Software Foundation.
19 * OpenOffice.org is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Lesser General Public License version 3 for more details
23 * (a copy is included in the LICENSE file that accompanied this code).
25 * You should have received a copy of the GNU Lesser General Public License
26 * version 3 along with OpenOffice.org. If not, see
27 * <http://www.openoffice.org/license.html>
28 * for a copy of the LGPLv3 License.
30 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_framework.hxx"
34 #include "services/autorecovery.hxx"
36 //_______________________________________________
37 // own includes
38 #include <loadenv/loaddispatchlistener.hxx>
39 #include <loadenv/targethelper.hxx>
40 #include <pattern/frame.hxx>
41 #include <threadhelp/readguard.hxx>
42 #include <threadhelp/writeguard.hxx>
44 #include <classes/resource.hrc>
45 #include <classes/fwkresid.hxx>
46 #include <protocols.h>
47 #include <properties.h>
48 #include <services.h>
50 //_______________________________________________
51 // interface includes
52 #include <com/sun/star/ucb/NameClash.hpp>
53 #include <com/sun/star/container/XNameAccess.hpp>
54 #include <com/sun/star/frame/XModuleManager.hpp>
55 #include <com/sun/star/frame/XTitle.hpp>
56 #include <com/sun/star/frame/XFrame.hpp>
57 #include <com/sun/star/frame/XDispatchProvider.hpp>
58 #include <com/sun/star/frame/DispatchResultState.hpp>
59 #include <com/sun/star/frame/XNotifyingDispatch.hpp>
60 #include <com/sun/star/frame/XController.hpp>
61 #include <com/sun/star/frame/XModel.hpp>
62 #include <com/sun/star/frame/XStorable.hpp>
63 #include <com/sun/star/util/XModifiable.hpp>
64 #include <com/sun/star/util/XURLTransformer.hpp>
65 #include <com/sun/star/frame/XDesktop.hpp>
66 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
67 #include <com/sun/star/container/XNameContainer.hpp>
68 #include <com/sun/star/util/XChangesNotifier.hpp>
69 #include <com/sun/star/util/XChangesBatch.hpp>
70 #include <com/sun/star/beans/XPropertySet.hpp>
71 #include <com/sun/star/beans/PropertyAttribute.hpp>
72 #include <com/sun/star/container/XContainerQuery.hpp>
73 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
74 #include <com/sun/star/util/XCloseable.hpp>
75 #include <com/sun/star/awt/XWindow2.hpp>
76 #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
78 //_______________________________________________
79 // other includes
80 #include <comphelper/configurationhelper.hxx>
81 #include <comphelper/mediadescriptor.hxx>
82 #include <vcl/svapp.hxx>
83 #include <svtools/pathoptions.hxx>
84 #include <tools/link.hxx>
85 #include <tools/string.hxx>
86 #include <unotools/tempfile.hxx>
87 #include <ucbhelper/content.hxx>
89 #include <osl/time.h>
90 #include <vcl/msgbox.hxx>
91 #include <osl/file.hxx>
92 #include <unotools/bootstrap.hxx>
93 #include <unotools/configmgr.hxx>
94 #include <svtools/documentlockfile.hxx>
96 #include <tools/urlobj.hxx>
98 //_______________________________________________
99 // namespaces
101 #ifndef css
102 namespace css = ::com::sun::star;
103 #endif
105 namespace fpf = ::framework::pattern::frame;
107 namespace framework
110 //-----------------------------------------------
111 // recovery.xcu
112 static const ::rtl::OUString CFG_PACKAGE_RECOVERY = ::rtl::OUString::createFromAscii("org.openoffice.Office.Recovery/");
113 static const ::rtl::OUString CFG_ENTRY_RECOVERYLIST = ::rtl::OUString::createFromAscii("RecoveryList" );
114 static const ::rtl::OUString CFG_PATH_RECOVERYINFO = ::rtl::OUString::createFromAscii("RecoveryInfo" );
115 static const ::rtl::OUString CFG_ENTRY_ENABLED = ::rtl::OUString::createFromAscii("Enabled" );
116 static const ::rtl::OUString CFG_ENTRY_CRASHED = ::rtl::OUString::createFromAscii("Crashed" );
117 static const ::rtl::OUString CFG_ENTRY_SESSIONDATA = ::rtl::OUString::createFromAscii("SessionData" );
119 static const ::rtl::OUString CFG_ENTRY_AUTOSAVE_ENABLED = ::rtl::OUString::createFromAscii("AutoSave/Enabled" );
120 static const ::rtl::OUString CFG_ENTRY_AUTOSAVE_TIMEINTERVALL = ::rtl::OUString::createFromAscii("AutoSave/TimeIntervall" );
122 static const ::rtl::OUString CFG_PATH_AUTOSAVE = ::rtl::OUString::createFromAscii("AutoSave" );
123 static const ::rtl::OUString CFG_ENTRY_MINSPACE_DOCSAVE = ::rtl::OUString::createFromAscii("MinSpaceDocSave" );
124 static const ::rtl::OUString CFG_ENTRY_MINSPACE_CONFIGSAVE = ::rtl::OUString::createFromAscii("MinSpaceConfigSave" );
126 static const ::rtl::OUString CFG_PACKAGE_MODULES = ::rtl::OUString::createFromAscii("org.openoffice.Setup/Office/Factories");
127 static const ::rtl::OUString CFG_ENTRY_REALDEFAULTFILTER = ::rtl::OUString::createFromAscii("ooSetupFactoryActualFilter" );
129 static const ::rtl::OUString CFG_ENTRY_PROP_TEMPURL = ::rtl::OUString::createFromAscii("TempURL" );
130 static const ::rtl::OUString CFG_ENTRY_PROP_ORIGINALURL = ::rtl::OUString::createFromAscii("OriginalURL" );
131 static const ::rtl::OUString CFG_ENTRY_PROP_TEMPLATEURL = ::rtl::OUString::createFromAscii("TemplateURL" );
132 static const ::rtl::OUString CFG_ENTRY_PROP_FACTORYURL = ::rtl::OUString::createFromAscii("FactoryURL" );
133 static const ::rtl::OUString CFG_ENTRY_PROP_MODULE = ::rtl::OUString::createFromAscii("Module" );
134 static const ::rtl::OUString CFG_ENTRY_PROP_DOCUMENTSTATE = ::rtl::OUString::createFromAscii("DocumentState");
135 static const ::rtl::OUString CFG_ENTRY_PROP_FILTER = ::rtl::OUString::createFromAscii("Filter" );
136 static const ::rtl::OUString CFG_ENTRY_PROP_TITLE = ::rtl::OUString::createFromAscii("Title" );
137 static const ::rtl::OUString CFG_ENTRY_PROP_ID = ::rtl::OUString::createFromAscii("ID" );
139 static const ::rtl::OUString FILTER_PROP_TYPE = ::rtl::OUString::createFromAscii("Type" );
140 static const ::rtl::OUString FILTER_PROP_NAME = ::rtl::OUString::createFromAscii("Name" );
141 static const ::rtl::OUString TYPE_PROP_EXTENSIONS = ::rtl::OUString::createFromAscii("Extensions" );
142 static const ::rtl::OUString DOCINFO_PROP_TEMPLATE = ::rtl::OUString::createFromAscii("TemplateFileName");
144 // setup.xcu
145 static const ::rtl::OUString CFG_ENTRY_PROP_EMPTYDOCUMENTURL = ::rtl::OUString::createFromAscii("ooSetupFactoryEmptyDocumentURL");
146 static const ::rtl::OUString CFG_ENTRY_PROP_DEFAULTFILTER = ::rtl::OUString::createFromAscii("ooSetupFactoryDefaultFilter" );
148 static const ::rtl::OUString EVENT_ON_NEW = ::rtl::OUString::createFromAscii("OnNew" );
149 static const ::rtl::OUString EVENT_ON_LOAD = ::rtl::OUString::createFromAscii("OnLoad" );
150 static const ::rtl::OUString EVENT_ON_UNLOAD = ::rtl::OUString::createFromAscii("OnUnload" );
151 static const ::rtl::OUString EVENT_ON_MODIFYCHANGED = ::rtl::OUString::createFromAscii("OnModifyChanged");
152 static const ::rtl::OUString EVENT_ON_SAVE = ::rtl::OUString::createFromAscii("OnSave" );
153 static const ::rtl::OUString EVENT_ON_SAVEAS = ::rtl::OUString::createFromAscii("OnSaveAs" );
154 static const ::rtl::OUString EVENT_ON_SAVETO = ::rtl::OUString::createFromAscii("OnCopyTo" );
155 static const ::rtl::OUString EVENT_ON_SAVEDONE = ::rtl::OUString::createFromAscii("OnSaveDone" );
156 static const ::rtl::OUString EVENT_ON_SAVEASDONE = ::rtl::OUString::createFromAscii("OnSaveAsDone" );
157 static const ::rtl::OUString EVENT_ON_SAVETODONE = ::rtl::OUString::createFromAscii("OnCopyToDone" );
158 static const ::rtl::OUString EVENT_ON_SAVEFAILED = ::rtl::OUString::createFromAscii("OnSaveFailed" );
159 static const ::rtl::OUString EVENT_ON_SAVEASFAILED = ::rtl::OUString::createFromAscii("OnSaveAsFailed" );
160 static const ::rtl::OUString EVENT_ON_SAVETOFAILED = ::rtl::OUString::createFromAscii("OnCopyToFailed" );
162 static const ::rtl::OUString RECOVERY_ITEM_BASE_IDENTIFIER = ::rtl::OUString::createFromAscii("recovery_item_" );
164 static const ::rtl::OUString CMD_PROTOCOL = ::rtl::OUString::createFromAscii("vnd.sun.star.autorecovery:");
166 static const ::rtl::OUString CMD_DO_AUTO_SAVE = ::rtl::OUString::createFromAscii("/doAutoSave" ); // force AutoSave ignoring the AutoSave timer
167 static const ::rtl::OUString CMD_DO_PREPARE_EMERGENCY_SAVE = ::rtl::OUString::createFromAscii("/doPrepareEmergencySave" ); // prepare the office for the following EmergencySave step (hide windows etcpp.)
168 static const ::rtl::OUString CMD_DO_EMERGENCY_SAVE = ::rtl::OUString::createFromAscii("/doEmergencySave" ); // do EmergencySave on crash
169 static const ::rtl::OUString CMD_DO_RECOVERY = ::rtl::OUString::createFromAscii("/doAutoRecovery" ); // recover all crashed documents
170 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
171 static const ::rtl::OUString CMD_DO_ENTRY_CLEANUP = ::rtl::OUString::createFromAscii("/doEntryCleanUp" ); // remove the specified entry from the recovery cache
172 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
173 static const ::rtl::OUString CMD_DO_SESSION_RESTORE = ::rtl::OUString::createFromAscii("/doSessionRestore" ); // restore a saved user session from disc
174 static const ::rtl::OUString CMD_DO_DISABLE_RECOVERY = ::rtl::OUString::createFromAscii("/disableRecovery" ); // disable recovery and auto save (!) temp. for this office session
175 static const ::rtl::OUString CMD_DO_SET_AUTOSAVE_STATE = ::rtl::OUString::createFromAscii("/setAutoSaveState" ); // disable/enable auto save (not crash save) for this office session
177 static const ::rtl::OUString REFERRER_USER = ::rtl::OUString::createFromAscii("private:user");
179 static const ::rtl::OUString PROP_DISPATCH_ASYNCHRON = ::rtl::OUString::createFromAscii("DispatchAsynchron");
180 static const ::rtl::OUString PROP_PROGRESS = ::rtl::OUString::createFromAscii("StatusIndicator" );
181 static const ::rtl::OUString PROP_SAVEPATH = ::rtl::OUString::createFromAscii("SavePath" );
182 static const ::rtl::OUString PROP_ENTRY_ID = ::rtl::OUString::createFromAscii("EntryID" );
183 static const ::rtl::OUString PROP_DBG_MAKE_IT_FASTER = ::rtl::OUString::createFromAscii("DBGMakeItFaster" );
184 static const ::rtl::OUString PROP_AUTOSAVE_STATE = ::rtl::OUString::createFromAscii("AutoSaveState" );
186 static const ::rtl::OUString OPERATION_START = ::rtl::OUString::createFromAscii("start" );
187 static const ::rtl::OUString OPERATION_STOP = ::rtl::OUString::createFromAscii("stop" );
188 static const ::rtl::OUString OPERATION_UPDATE = ::rtl::OUString::createFromAscii("update");
190 static const sal_Int32 MIN_DISCSPACE_DOCSAVE = 5; // [MB]
191 static const sal_Int32 MIN_DISCSPACE_CONFIGSAVE = 1; // [MB]
192 static const sal_Int32 RETRY_STORE_ON_FULL_DISC_FOREVER = 300; // not forever ... but often enough .-)
193 static const sal_Int32 RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL = 3; // in case FULL DISC does not seam the real problem
194 static const sal_Int32 GIVE_UP_RETRY = 1; // in case FULL DISC does not seam the real problem
196 #define SAVE_IN_PROGRESS sal_True
197 #define SAVE_FINISHED sal_False
199 #define LOCK_FOR_CACHE_ADD_REMOVE sal_True
200 #define LOCK_FOR_CACHE_USE sal_False
202 #define MIN_TIME_FOR_USER_IDLE 10000 // 10s user idle
204 // enable the following defines in case you whish to simulate a full disc for debug purposes .-)
206 // this define throws everytime a document is stored or a configuration change
207 // should be flushed an exception ... so the special error handler for this scenario is triggered
208 // #define TRIGGER_FULL_DISC_CHECK
210 // force "return FALSE" for the method impl_enoughDiscSpace().
211 // #define SIMULATE_FULL_DISC
213 //-----------------------------------------------
214 // #define ENABLE_RECOVERY_LOGGING
215 #undef ENABLE_RECOVERY_LOGGING
216 #ifdef ENABLE_RECOVERY_LOGGING
217 #define LOGFILE_RECOVERY "recovery.log"
219 #define LOG_RECOVERY(MSG) \
221 WRITE_LOGFILE(LOGFILE_RECOVERY, MSG) \
222 WRITE_LOGFILE(LOGFILE_RECOVERY, "\n") \
224 #else
225 #undef LOGFILE_RECOVERY
226 #define LOG_RECOVERY(MSG)
227 #endif
229 //-----------------------------------------------
230 // TODO debug - remove it!
231 class DbgListener : private ThreadHelpBase
232 , public ::cppu::OWeakObject
233 , public css::frame::XStatusListener
235 public:
237 FWK_DECLARE_XINTERFACE
239 DbgListener()
241 WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::ctor()\n\n")
244 virtual ~DbgListener()
246 WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::dtor()\n\n")
249 void startListening(const css::uno::Reference< css::frame::XDispatch >& xBroadcaster)
251 ::rtl::OUStringBuffer sMsg1(256);
252 sMsg1.appendAscii("//**********************************************************************************\n");
253 sMsg1.appendAscii("start listening\n{\n");
254 WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg1.makeStringAndClear()))
256 ++m_refCount;
258 css::util::URL aURL;
259 aURL.Complete = ::rtl::OUString();
260 xBroadcaster->addStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL);
262 --m_refCount;
264 ::rtl::OUStringBuffer sMsg2(256);
265 sMsg2.appendAscii("}\nstart listening\n");
266 sMsg2.appendAscii("//**********************************************************************************\n");
267 WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg2.makeStringAndClear()))
270 virtual void SAL_CALL disposing(const css::lang::EventObject&)
271 throw(css::uno::RuntimeException)
273 WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::dtor()\n\n")
276 virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& aEvent)
277 throw(css::uno::RuntimeException)
279 ::rtl::OUStringBuffer sMsg(256);
281 sMsg.appendAscii("//**********************************************************************************\n");
283 sMsg.appendAscii("FeatureURL = \"");
284 sMsg.append (aEvent.FeatureURL.Complete);
285 sMsg.appendAscii("\"\n");
287 sMsg.appendAscii("State = [");
288 sal_Int32 nState = -1;
289 aEvent.State >>= nState;
290 if (nState==-1)
292 sMsg.appendAscii("?-");
293 sMsg.append (::rtl::OUString::valueOf(nState));
294 sMsg.appendAscii("-? ");
296 if (nState==0)
297 sMsg.appendAscii("UNKNOWN ");
298 if ((nState & 1)==1)
299 sMsg.appendAscii("MODIFIED ");
300 if ((nState & 2)==2)
301 sMsg.appendAscii("TRYIT ");
302 if ((nState & 4)==4)
303 sMsg.appendAscii("HANDLED ");
304 if ((nState & 8)==8)
305 sMsg.appendAscii("POSTPONED ");
306 if ((nState & 16)==16)
307 sMsg.appendAscii("INCOMPLETE ");
308 if ((nState & 32)==32)
309 sMsg.appendAscii("DAMAGED ");
310 sMsg.appendAscii("]\n");
312 sMsg.appendAscii("IsEnabled = \"");
313 sMsg.append (::rtl::OUString::valueOf(aEvent.IsEnabled));
314 sMsg.appendAscii("\"\n");
316 sMsg.appendAscii("Requery = \"");
317 sMsg.append (::rtl::OUString::valueOf(aEvent.Requery));
318 sMsg.appendAscii("\"\n");
320 sMsg.appendAscii("\n");
322 WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg.makeStringAndClear()))
326 //-----------------------------------------------
327 class CacheLockGuard
329 private:
331 // holds the outside calli alive, so it's shared resources
332 // are valid everytimes
333 css::uno::Reference< css::uno::XInterface > m_xOwner;
335 // mutex shared with outside calli !
336 LockHelper& m_rSharedMutex;
338 // this variable knows the state of the "cache lock"
339 sal_Int32& m_rCacheLock;
341 // to prevent increasing/decreasing of m_rCacheLock more then ones
342 // we must know if THIS guard has an actual lock set there !
343 sal_Bool m_bLockedByThisGuard;
345 public:
347 CacheLockGuard(AutoRecovery* pOwner ,
348 LockHelper& rMutex ,
349 sal_Int32& rCacheLock ,
350 sal_Bool bLockForAddRemoveVectorItems);
351 ~CacheLockGuard();
353 void lock(sal_Bool bLockForAddRemoveVectorItems);
354 void unlock();
357 //-----------------------------------------------
358 CacheLockGuard::CacheLockGuard(AutoRecovery* pOwner ,
359 LockHelper& rMutex ,
360 sal_Int32& rCacheLock ,
361 sal_Bool bLockForAddRemoveVectorItems)
362 : m_xOwner (static_cast< css::frame::XDispatch* >(pOwner))
363 , m_rSharedMutex (rMutex )
364 , m_rCacheLock (rCacheLock )
365 , m_bLockedByThisGuard(sal_False )
367 lock(bLockForAddRemoveVectorItems);
370 //-----------------------------------------------
371 CacheLockGuard::~CacheLockGuard()
373 unlock();
374 m_xOwner.clear();
377 //-----------------------------------------------
378 void CacheLockGuard::lock(sal_Bool bLockForAddRemoveVectorItems)
380 // SAFE -> ----------------------------------
381 WriteGuard aWriteLock(m_rSharedMutex);
383 if (m_bLockedByThisGuard)
384 return;
386 // This cache lock is needed only to prevent us from removing/adding
387 // items from/into the recovery cache ... during it's used at another code place
388 // for iterating .-)
390 // Modifying of item properties is allowed and sometimes needed!
391 // So we should detect only the dangerous state of concurrent add/remove
392 // requests and throw an exception then ... which can of course break the whole
393 // operation. On the other side a crash reasoned by an invalid stl iterator
394 // will have the same effect .-)
396 if (
397 (m_rCacheLock > 0 ) &&
398 (bLockForAddRemoveVectorItems)
401 OSL_ENSURE(sal_False, "Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp.");
402 throw css::uno::RuntimeException(
403 ::rtl::OUString::createFromAscii("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp."),
404 m_xOwner);
407 ++m_rCacheLock;
408 m_bLockedByThisGuard = sal_True;
410 aWriteLock.unlock();
411 // <- SAFE ----------------------------------
414 //-----------------------------------------------
415 void CacheLockGuard::unlock()
417 // SAFE -> ----------------------------------
418 WriteGuard aWriteLock(m_rSharedMutex);
420 if ( ! m_bLockedByThisGuard)
421 return;
423 --m_rCacheLock;
424 m_bLockedByThisGuard = sal_False;
426 if (m_rCacheLock < 0)
428 OSL_ENSURE(sal_False, "Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)");
429 throw css::uno::RuntimeException(
430 ::rtl::OUString::createFromAscii("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)"),
431 m_xOwner);
433 aWriteLock.unlock();
434 // <- SAFE ----------------------------------
437 //-----------------------------------------------
438 DispatchParams::DispatchParams()
439 : m_nWorkingEntryID(-1)
443 //-----------------------------------------------
444 DispatchParams::DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs ,
445 const css::uno::Reference< css::uno::XInterface >& xOwner)
447 m_nWorkingEntryID = lArgs.getUnpackedValueOrDefault(PROP_ENTRY_ID, (sal_Int32)-1 );
448 m_xProgress = lArgs.getUnpackedValueOrDefault(PROP_PROGRESS, css::uno::Reference< css::task::XStatusIndicator >());
449 m_sSavePath = lArgs.getUnpackedValueOrDefault(PROP_SAVEPATH, ::rtl::OUString() );
450 m_xHoldRefForAsyncOpAlive = xOwner;
453 //-----------------------------------------------
454 DispatchParams::DispatchParams(const DispatchParams& rCopy)
456 m_xProgress = rCopy.m_xProgress;
457 m_sSavePath = rCopy.m_sSavePath;
458 m_nWorkingEntryID = rCopy.m_nWorkingEntryID;
459 m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive;
462 //-----------------------------------------------
463 DispatchParams::~DispatchParams()
466 //-----------------------------------------------
467 DispatchParams& DispatchParams::operator=(const DispatchParams& rCopy)
469 m_xProgress = rCopy.m_xProgress;
470 m_sSavePath = rCopy.m_sSavePath;
471 m_nWorkingEntryID = rCopy.m_nWorkingEntryID;
472 m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive;
473 return *this;
476 //-----------------------------------------------
477 void DispatchParams::forget()
479 m_sSavePath = ::rtl::OUString();
480 m_nWorkingEntryID = -1;
481 m_xProgress.clear();
482 m_xHoldRefForAsyncOpAlive.clear();
485 //-----------------------------------------------
486 DEFINE_XINTERFACE_1(DbgListener ,
487 OWeakObject ,
488 DIRECT_INTERFACE(css::frame::XStatusListener))
490 //-----------------------------------------------
491 DEFINE_XINTERFACE_10(AutoRecovery ,
492 OWeakObject ,
493 DIRECT_INTERFACE (css::lang::XTypeProvider ),
494 DIRECT_INTERFACE (css::lang::XServiceInfo ),
495 DIRECT_INTERFACE (css::frame::XDispatch ),
496 DIRECT_INTERFACE (css::beans::XMultiPropertySet ),
497 DIRECT_INTERFACE (css::beans::XFastPropertySet ),
498 DIRECT_INTERFACE (css::beans::XPropertySet ),
499 DIRECT_INTERFACE (css::document::XEventListener ),
500 DIRECT_INTERFACE (css::util::XChangesListener ),
501 DIRECT_INTERFACE (css::util::XModifyListener ),
502 DERIVED_INTERFACE(css::lang::XEventListener, css::document::XEventListener))
504 //-----------------------------------------------
505 DEFINE_XTYPEPROVIDER_6(AutoRecovery ,
506 css::lang::XTypeProvider ,
507 css::lang::XServiceInfo ,
508 css::frame::XDispatch ,
509 css::beans::XMultiPropertySet,
510 css::beans::XFastPropertySet ,
511 css::beans::XPropertySet )
513 //-----------------------------------------------
514 DEFINE_XSERVICEINFO_ONEINSTANCESERVICE(AutoRecovery ,
515 ::cppu::OWeakObject ,
516 SERVICENAME_AUTORECOVERY ,
517 IMPLEMENTATIONNAME_AUTORECOVERY)
519 //-----------------------------------------------
520 DEFINE_INIT_SERVICE(
521 AutoRecovery,
523 /*Attention
524 I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
525 to create a new instance of this class by our own supported service factory.
526 see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations!
529 // read configuration to know if autosave/recovery is on/off etcpp...
530 implts_readConfig();
532 implts_startListening();
534 // establish callback for our internal used timer.
535 // Note: Its only active, if the timer will be started ...
536 m_aTimer.SetTimeoutHdl(LINK(this, AutoRecovery, implts_timerExpired));
538 DbgListener* pListener = new DbgListener();
539 pListener->startListening(this);
544 //-----------------------------------------------
545 AutoRecovery::AutoRecovery(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
546 : ThreadHelpBase (&Application::GetSolarMutex() )
547 , ::cppu::OBroadcastHelper ( m_aLock.getShareableOslMutex() )
548 , ::cppu::OPropertySetHelper( *(static_cast< ::cppu::OBroadcastHelper* >(this)) )
549 , ::cppu::OWeakObject ( )
550 , m_xSMGR (xSMGR )
551 , m_bListenForDocEvents (sal_False )
552 , m_bListenForConfigChanges (sal_False )
553 , m_nAutoSaveTimeIntervall (0 )
554 , m_eJob (AutoRecovery::E_NO_JOB )
555 , m_aAsyncDispatcher ( LINK( this, AutoRecovery, implts_asyncDispatch ) )
556 , m_eTimerType (E_DONT_START_TIMER )
557 , m_nIdPool (0 )
558 , m_lListener (m_aLock.getShareableOslMutex() )
559 , m_nDocCacheLock (0 )
560 , m_nMinSpaceDocSave (MIN_DISCSPACE_DOCSAVE )
561 , m_nMinSpaceConfigSave (MIN_DISCSPACE_CONFIGSAVE )
563 #if OSL_DEBUG_LEVEL > 1
564 , m_dbg_bMakeItFaster (sal_False )
565 #endif
569 //-----------------------------------------------
570 AutoRecovery::~AutoRecovery()
572 implts_stopTimer();
575 //-----------------------------------------------
576 void SAL_CALL AutoRecovery::dispatch(const css::util::URL& aURL ,
577 const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
578 throw(css::uno::RuntimeException)
580 LOG_RECOVERY("AutoRecovery::dispatch() starts ...")
581 LOG_RECOVERY(U2B(aURL.Complete).getStr())
583 // valid request ?
584 sal_Int32 eNewJob = AutoRecovery::implst_classifyJob(aURL);
585 if (eNewJob == AutoRecovery::E_NO_JOB)
586 return;
588 // SAFE -> ----------------------------------
589 WriteGuard aWriteLock(m_aLock);
591 // still running operation ... ignoring AUTO_SAVE.
592 // All other requests has higher prio!
593 if (
594 ( m_eJob != AutoRecovery::E_NO_JOB ) &&
595 ((m_eJob & AutoRecovery::E_AUTO_SAVE ) != AutoRecovery::E_AUTO_SAVE)
598 LOG_WARNING("AutoRecovery::dispatch()", "There is already an asynchronous dispatch() running. New request will be ignored!")
599 return;
602 ::comphelper::SequenceAsHashMap lArgs(lArguments);
604 // check if somewhere wish to disable recovery temp. for this office session
605 // This can be done immediatly ... must not been done asynchronous.
606 if ((eNewJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
608 // it's important to set a flag internaly, so AutoRecovery will be supressed - even if it's requested.
609 m_eJob |= eNewJob;
610 implts_stopTimer();
611 implts_stopListening();
612 return;
615 // disable/enable AutoSave for this office session only
616 // independend from the configuration entry.
617 if ((eNewJob & AutoRecovery::E_SET_AUTOSAVE_STATE) == AutoRecovery::E_SET_AUTOSAVE_STATE)
619 sal_Bool bOn = lArgs.getUnpackedValueOrDefault(PROP_AUTOSAVE_STATE, (sal_Bool)sal_True);
620 if (bOn)
622 // dont enable AutoSave hardly !
623 // reload configuration to know the current state.
624 implts_readAutoSaveConfig();
625 implts_actualizeTimer();
626 // can it happen that might be the listener was stopped ? .-)
627 // make sure it runs always ... even if AutoSave itself was disabled temporarly.
628 implts_startListening();
630 else
632 implts_stopTimer();
633 m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
634 m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
636 return;
639 m_eJob |= eNewJob;
641 sal_Bool bAsync = lArgs.getUnpackedValueOrDefault(PROP_DISPATCH_ASYNCHRON, (sal_Bool)sal_False);
642 DispatchParams aParams (lArgs, static_cast< css::frame::XDispatch* >(this));
644 // Hold this instance alive till the asynchronous operation will be finished.
645 if (bAsync)
646 m_aDispatchParams = aParams;
648 aWriteLock.unlock();
649 // <- SAFE ----------------------------------
651 if (bAsync)
652 m_aAsyncDispatcher.Post(0);
653 else
654 implts_dispatch(aParams);
657 //-----------------------------------------------
658 void AutoRecovery::implts_dispatch(const DispatchParams& aParams)
660 // SAFE -> ----------------------------------
661 WriteGuard aWriteLock(m_aLock);
662 sal_Int32 eJob = m_eJob;
663 aWriteLock.unlock();
664 // <- SAFE ----------------------------------
666 // in case a new dispatch overwrites a may ba active AutoSave session
667 // we must restore this session later. see below ...
668 sal_Bool bWasAutoSaveActive = ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE);
670 // On the other side it make no sense to reactivate the AutoSave operation
671 // if the new dispatch indicates a final decision ...
672 // E.g. an EmergencySave/SessionSave indicates the end of life of the current office session.
673 // It make no sense to reactivate an AutoSave then.
674 // But a Recovery or SessionRestore should reactivate a may be already active AutoSave.
675 sal_Bool bAllowAutoSaveReactivation = sal_True;
677 implts_stopTimer();
678 implts_stopListening();
680 implts_informListener(eJob,
681 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_START, NULL));
685 // if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
686 // Auto save is called from our internal timer ... not via dispatch() API !
687 // else
688 if (
689 ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE) &&
690 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY ) != AutoRecovery::E_DISABLE_AUTORECOVERY )
693 LOG_RECOVERY("... prepare emergency save ...")
694 bAllowAutoSaveReactivation = sal_False;
695 implts_prepareEmergencySave();
697 else
698 if (
699 ((eJob & AutoRecovery::E_EMERGENCY_SAVE ) == AutoRecovery::E_EMERGENCY_SAVE ) &&
700 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
703 LOG_RECOVERY("... do emergency save ...")
704 bAllowAutoSaveReactivation = sal_False;
705 implts_doEmergencySave(aParams);
707 else
708 if (
709 ((eJob & AutoRecovery::E_RECOVERY ) == AutoRecovery::E_RECOVERY ) &&
710 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
713 LOG_RECOVERY("... do recovery ...")
714 implts_doRecovery(aParams);
716 else
717 if (
718 ((eJob & AutoRecovery::E_SESSION_SAVE ) == AutoRecovery::E_SESSION_SAVE ) &&
719 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
722 LOG_RECOVERY("... do session save ...")
723 bAllowAutoSaveReactivation = sal_False;
724 implts_doSessionSave(aParams);
726 else
727 if (
728 ((eJob & AutoRecovery::E_SESSION_RESTORE ) == AutoRecovery::E_SESSION_RESTORE ) &&
729 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
732 LOG_RECOVERY("... do session restore ...")
733 implts_doSessionRestore(aParams);
735 else
736 if (
737 ((eJob & AutoRecovery::E_ENTRY_BACKUP ) == AutoRecovery::E_ENTRY_BACKUP ) &&
738 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
740 implts_backupWorkingEntry(aParams);
741 else
742 if (
743 ((eJob & AutoRecovery::E_ENTRY_CLEANUP ) == AutoRecovery::E_ENTRY_CLEANUP ) &&
744 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
746 implts_cleanUpWorkingEntry(aParams);
748 catch(const css::uno::RuntimeException& exRun)
749 { throw exRun; }
750 catch(const css::uno::Exception&)
751 {} // TODO better error handling
753 implts_informListener(eJob,
754 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_STOP, NULL));
756 // SAFE -> ----------------------------------
757 aWriteLock.lock();
758 m_eJob = E_NO_JOB;
759 if (
760 (bAllowAutoSaveReactivation) &&
761 (bWasAutoSaveActive )
764 m_eJob |= AutoRecovery::E_AUTO_SAVE;
767 aWriteLock.unlock();
768 // <- SAFE ----------------------------------
770 // depends on bAllowAutoSaveReactivation implicitly by looking on m_eJob=E_AUTO_SAVE! see before ...
771 implts_actualizeTimer();
773 if (bAllowAutoSaveReactivation)
774 implts_startListening();
777 //-----------------------------------------------
778 void SAL_CALL AutoRecovery::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
779 const css::util::URL& aURL )
780 throw(css::uno::RuntimeException)
782 if (!xListener.is())
783 throw css::uno::RuntimeException(::rtl::OUString::createFromAscii("Invalid listener reference."), static_cast< css::frame::XDispatch* >(this));
784 // container is threadsafe by using a shared mutex!
785 m_lListener.addInterface(aURL.Complete, xListener);
787 // REINTRANT !? -> --------------------------------
788 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
790 // THREAD SAFE -> ----------------------------------
791 ReadGuard aReadLock(m_aLock);
793 AutoRecovery::TDocumentList::iterator pIt;
794 for( pIt = m_lDocCache.begin();
795 pIt != m_lDocCache.end() ;
796 ++pIt )
798 AutoRecovery::TDocumentInfo& rInfo = *pIt;
799 css::frame::FeatureStateEvent aEvent = AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_UPDATE, &rInfo);
801 // <- SAFE ------------------------------
802 aReadLock.unlock();
803 xListener->statusChanged(aEvent);
804 aReadLock.lock();
805 // SAFE -> ------------------------------
808 aReadLock.unlock();
809 // <- SAFE ----------------------------------
812 //-----------------------------------------------
813 void SAL_CALL AutoRecovery::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
814 const css::util::URL& aURL )
815 throw(css::uno::RuntimeException)
817 if (!xListener.is())
818 throw css::uno::RuntimeException(::rtl::OUString::createFromAscii("Invalid listener reference."), static_cast< css::frame::XDispatch* >(this));
819 // container is threadsafe by using a shared mutex!
820 m_lListener.removeInterface(aURL.Complete, xListener);
823 //-----------------------------------------------
824 void SAL_CALL AutoRecovery::notifyEvent(const css::document::EventObject& aEvent)
825 throw(css::uno::RuntimeException)
827 css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
829 // new document => put it into the internal list
830 if (
831 (aEvent.EventName.equals(EVENT_ON_NEW )) ||
832 (aEvent.EventName.equals(EVENT_ON_LOAD))
835 implts_registerDocument(xDocument);
837 // document modified => set its modify state new (means modified against the original file!)
838 else
839 if (aEvent.EventName.equals(EVENT_ON_MODIFYCHANGED))
841 implts_actualizeModifiedState(xDocument);
843 /* at least one document starts saving process =>
844 Our application code isnt ready for multiple save requests
845 at the same time. So we have to supress our AutoSave feature
846 for the moment, till this other save requests will be finished.
848 else
849 if (
850 (aEvent.EventName.equals(EVENT_ON_SAVE )) ||
851 (aEvent.EventName.equals(EVENT_ON_SAVEAS)) ||
852 (aEvent.EventName.equals(EVENT_ON_SAVETO))
855 implts_updateDocumentUsedForSavingState(xDocument, SAVE_IN_PROGRESS);
857 // document saved => remove tmp. files - but hold config entries alive!
858 else
859 if (
860 (aEvent.EventName.equals(EVENT_ON_SAVEDONE )) ||
861 (aEvent.EventName.equals(EVENT_ON_SAVEASDONE))
864 implts_markDocumentAsSaved(xDocument);
865 implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
867 /* document saved as copy => mark it as "non used by concurrent save operation".
868 so we can try to create a backup copy if next time AutoSave is started too.
869 Dont remove temp. files or change the modified state of the document!
870 It was not realy saved to the original file ...
872 else
873 if (aEvent.EventName.equals(EVENT_ON_SAVETODONE))
875 implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
877 // If saving of a document failed by an error ... we have to save this document
878 // by ourself next time AutoSave or EmergencySave is triggered.
879 // But we can reset the state "used for other save requests". Otherwhise
880 // these documents will never be saved!
881 else
882 if (
883 (aEvent.EventName.equals(EVENT_ON_SAVEFAILED )) ||
884 (aEvent.EventName.equals(EVENT_ON_SAVEASFAILED)) ||
885 (aEvent.EventName.equals(EVENT_ON_SAVETOFAILED))
888 implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
890 // document closed => remove temp. files and configuration entries
891 else
892 if (aEvent.EventName.equals(EVENT_ON_UNLOAD))
894 implts_deregisterDocument(xDocument, sal_True); // TRUE => stop listening for disposing() !
898 //-----------------------------------------------
899 void SAL_CALL AutoRecovery::changesOccurred(const css::util::ChangesEvent& aEvent)
900 throw(css::uno::RuntimeException)
902 const css::uno::Sequence< css::util::ElementChange > lChanges (aEvent.Changes);
903 const css::util::ElementChange* pChanges = lChanges.getConstArray();
905 sal_Int32 c = lChanges.getLength();
906 sal_Int32 i = 0;
908 // SAFE -> ----------------------------------
909 WriteGuard aWriteLock(m_aLock);
911 // Changes of the configuration must be ignored if AutoSave/Recovery was disabled for this
912 // office session. That can happen if e.g. the command line arguments "-norestore" or "-headless"
913 // was set.
914 if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
915 return;
917 for (i=0; i<c; ++i)
919 ::rtl::OUString sPath;
920 pChanges[i].Accessor >>= sPath;
922 if (sPath.equals(CFG_ENTRY_AUTOSAVE_ENABLED))
924 sal_Bool bEnabled = sal_False;
925 if (pChanges[i].Element >>= bEnabled)
927 if (bEnabled)
929 m_eJob |= AutoRecovery::E_AUTO_SAVE;
930 m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
932 else
934 m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
935 m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
939 else
940 if (sPath.equals(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL))
941 pChanges[i].Element >>= m_nAutoSaveTimeIntervall;
944 aWriteLock.unlock();
945 // <- SAFE ----------------------------------
947 // Note: This call stops the timer and starts it again.
948 // But it checks the different timer states internaly and
949 // may be supress the restart!
950 implts_actualizeTimer();
953 //-----------------------------------------------
954 void SAL_CALL AutoRecovery::modified(const css::lang::EventObject& aEvent)
955 throw(css::uno::RuntimeException)
957 css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
958 if (! xDocument.is())
959 return;
961 implts_markDocumentModifiedAgainstLastBackup(xDocument);
964 //-----------------------------------------------
965 void SAL_CALL AutoRecovery::disposing(const css::lang::EventObject& aEvent)
966 throw(css::uno::RuntimeException)
968 // SAFE -> ----------------------------------
969 WriteGuard aWriteLock(m_aLock);
971 if (aEvent.Source == m_xNewDocBroadcaster)
973 m_xNewDocBroadcaster.clear();
974 return;
977 if (aEvent.Source == m_xRecoveryCFG)
979 m_xRecoveryCFG.clear();
980 return;
983 // dispose from one of our cached documents ?
984 // Normaly they should send a OnUnload message ...
985 // But some stacktraces shows another possible use case .-)
986 css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
987 if (xDocument.is())
989 implts_deregisterDocument(xDocument, sal_False); // FALSE => dont call removeEventListener() .. because it's not needed here
990 return;
993 // <- SAFE ----------------------------------
996 //-----------------------------------------------
997 css::uno::Reference< css::container::XNameAccess > AutoRecovery::implts_openConfig()
999 // SAFE -> ----------------------------------
1000 WriteGuard aWriteLock(m_aLock);
1002 if (m_xRecoveryCFG.is())
1003 return m_xRecoveryCFG;
1004 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1006 aWriteLock.unlock();
1007 // <- SAFE ----------------------------------
1009 // throws a RuntimeException if an error occure!
1010 css::uno::Reference< css::container::XNameAccess > xCFG(
1011 ::comphelper::ConfigurationHelper::openConfig(xSMGR, CFG_PACKAGE_RECOVERY, ::comphelper::ConfigurationHelper::E_STANDARD),
1012 css::uno::UNO_QUERY);
1014 sal_Int32 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
1015 sal_Int32 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
1019 ::comphelper::ConfigurationHelper::readDirectKey(xSMGR,
1020 CFG_PACKAGE_RECOVERY,
1021 CFG_PATH_AUTOSAVE,
1022 CFG_ENTRY_MINSPACE_DOCSAVE,
1023 ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceDocSave;
1025 ::comphelper::ConfigurationHelper::readDirectKey(xSMGR,
1026 CFG_PACKAGE_RECOVERY,
1027 CFG_PATH_AUTOSAVE,
1028 CFG_ENTRY_MINSPACE_CONFIGSAVE,
1029 ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceConfigSave;
1031 catch(const css::uno::Exception&)
1033 // These config keys are not sooooo important, that
1034 // we are interested on errors here realy .-)
1035 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
1036 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
1039 // SAFE -> ----------------------------------
1040 aWriteLock.lock();
1041 m_xRecoveryCFG = xCFG;
1042 m_nMinSpaceDocSave = nMinSpaceDocSave;
1043 m_nMinSpaceConfigSave = nMinSpaceConfigSave;
1044 aWriteLock.unlock();
1045 // <- SAFE ----------------------------------
1047 return xCFG;
1050 //-----------------------------------------------
1051 void AutoRecovery::implts_readAutoSaveConfig()
1053 css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY);
1055 // AutoSave [bool]
1056 sal_Bool bEnabled = sal_False;
1057 xCommonRegistry->getByHierarchicalName(CFG_ENTRY_AUTOSAVE_ENABLED) >>= bEnabled;
1059 // SAFE -> ------------------------------
1060 WriteGuard aWriteLock(m_aLock);
1061 if (bEnabled)
1063 m_eJob |= AutoRecovery::E_AUTO_SAVE;
1064 m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
1066 else
1068 m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
1069 m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
1071 aWriteLock.unlock();
1072 // <- SAFE ------------------------------
1074 // AutoSaveTimeIntervall [int] in min
1075 sal_Int32 nTimeIntervall = 15;
1076 xCommonRegistry->getByHierarchicalName(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL) >>= nTimeIntervall;
1078 // SAFE -> ----------------------------------
1079 aWriteLock.lock();
1080 m_nAutoSaveTimeIntervall = nTimeIntervall;
1081 aWriteLock.unlock();
1082 // <- SAFE ----------------------------------
1085 //-----------------------------------------------
1086 void AutoRecovery::implts_readConfig()
1088 implts_readAutoSaveConfig();
1090 css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY);
1092 // REINTRANT -> --------------------------------
1093 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
1095 // THREADSAFE -> -------------------------------
1096 WriteGuard aWriteLock(m_aLock);
1097 // reset current cache load cache
1098 m_lDocCache.clear();
1099 m_nIdPool = 0;
1100 aWriteLock.unlock();
1101 // <- THREADSAFE -------------------------------
1103 aCacheLock.unlock();
1104 // <- REINTRANT --------------------------------
1106 css::uno::Any aValue;
1108 // RecoveryList [set]
1109 aValue = xCommonRegistry->getByHierarchicalName(CFG_ENTRY_RECOVERYLIST);
1110 css::uno::Reference< css::container::XNameAccess > xList;
1111 aValue >>= xList;
1112 if (xList.is())
1114 const css::uno::Sequence< ::rtl::OUString > lItems = xList->getElementNames();
1115 const ::rtl::OUString* pItems = lItems.getConstArray();
1116 sal_Int32 c = lItems.getLength();
1117 sal_Int32 i = 0;
1119 // REINTRANT -> --------------------------
1120 aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
1122 for (i=0; i<c; ++i)
1124 css::uno::Reference< css::beans::XPropertySet > xItem;
1125 xList->getByName(pItems[i]) >>= xItem;
1126 if (!xItem.is())
1127 continue;
1129 AutoRecovery::TDocumentInfo aInfo;
1130 aInfo.NewTempURL = ::rtl::OUString();
1131 aInfo.Document = css::uno::Reference< css::frame::XModel >();
1132 xItem->getPropertyValue(CFG_ENTRY_PROP_ORIGINALURL ) >>= aInfo.OrgURL ;
1133 xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPURL ) >>= aInfo.OldTempURL ;
1134 xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL ) >>= aInfo.TemplateURL ;
1135 xItem->getPropertyValue(CFG_ENTRY_PROP_FILTER ) >>= aInfo.RealFilter ;
1136 xItem->getPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE) >>= aInfo.DocumentState;
1137 xItem->getPropertyValue(CFG_ENTRY_PROP_MODULE ) >>= aInfo.AppModule ;
1138 xItem->getPropertyValue(CFG_ENTRY_PROP_TITLE ) >>= aInfo.Title ;
1139 implts_specifyAppModuleAndFactoryURL(aInfo);
1140 implts_specifyDefaultFilterAndExtension(aInfo);
1142 if (pItems[i].indexOf(RECOVERY_ITEM_BASE_IDENTIFIER)==0)
1144 ::rtl::OUString sID = pItems[i].copy(RECOVERY_ITEM_BASE_IDENTIFIER.getLength());
1145 aInfo.ID = sID.toInt32();
1146 // SAFE -> ----------------------
1147 aWriteLock.lock();
1148 if (aInfo.ID > m_nIdPool)
1150 m_nIdPool = aInfo.ID+1;
1151 LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_readConfig()\nOverflow of IDPool detected!")
1153 aWriteLock.unlock();
1154 // <- SAFE ----------------------
1156 #ifdef ENABLE_WARNINGS
1157 else
1158 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 .-)")
1159 #endif
1161 // THREADSAFE -> --------------------------
1162 aWriteLock.lock();
1163 m_lDocCache.push_back(aInfo);
1164 aWriteLock.unlock();
1165 // <- THREADSAFE --------------------------
1168 aCacheLock.unlock();
1169 // <- REINTRANT --------------------------
1172 implts_actualizeTimer();
1175 //-----------------------------------------------
1176 void AutoRecovery::implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo)
1178 if (!rInfo.AppModule.getLength())
1180 throw css::uno::RuntimeException(
1181 ::rtl::OUString::createFromAscii("Cant find out the default filter and its extension, if no application module is known!"),
1182 static_cast< css::frame::XDispatch* >(this));
1185 // SAFE -> ----------------------------------
1186 ReadGuard aReadLock(m_aLock);
1187 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1188 css::uno::Reference< css::container::XNameAccess> xCFG = m_xModuleCFG;
1189 aReadLock.unlock();
1190 // <- SAFE ----------------------------------
1194 if (! xCFG.is())
1196 // open module config on demand and cache the update access
1197 xCFG = css::uno::Reference< css::container::XNameAccess >(
1198 ::comphelper::ConfigurationHelper::openConfig(xSMGR, CFG_PACKAGE_MODULES, ::comphelper::ConfigurationHelper::E_STANDARD),
1199 css::uno::UNO_QUERY_THROW);
1201 // SAFE -> ----------------------------------
1202 WriteGuard aWriteLock(m_aLock);
1203 m_xModuleCFG = xCFG;
1204 aWriteLock.unlock();
1205 // <- SAFE ----------------------------------
1208 css::uno::Reference< css::container::XNameAccess > xModuleProps(
1209 xCFG->getByName(rInfo.AppModule),
1210 css::uno::UNO_QUERY_THROW);
1212 xModuleProps->getByName(CFG_ENTRY_REALDEFAULTFILTER) >>= rInfo.DefaultFilter;
1214 css::uno::Reference< css::container::XNameAccess > xFilterCFG(xSMGR->createInstance(SERVICENAME_FILTERFACTORY), css::uno::UNO_QUERY_THROW);
1215 css::uno::Reference< css::container::XNameAccess > xTypeCFG (xSMGR->createInstance(SERVICENAME_TYPEDETECTION), css::uno::UNO_QUERY_THROW);
1217 ::comphelper::SequenceAsHashMap lFilterProps (xFilterCFG->getByName(rInfo.DefaultFilter));
1218 ::rtl::OUString sTypeRegistration = lFilterProps.getUnpackedValueOrDefault(FILTER_PROP_TYPE, ::rtl::OUString());
1219 ::comphelper::SequenceAsHashMap lTypeProps (xTypeCFG->getByName(sTypeRegistration));
1220 css::uno::Sequence< ::rtl::OUString > lExtensions = lTypeProps.getUnpackedValueOrDefault(TYPE_PROP_EXTENSIONS, css::uno::Sequence< ::rtl::OUString >());
1221 if (lExtensions.getLength())
1223 rInfo.Extension = ::rtl::OUString::createFromAscii(".");
1224 rInfo.Extension += lExtensions[0];
1226 else
1227 rInfo.Extension = ::rtl::OUString::createFromAscii(".unknown");
1229 catch(const css::uno::Exception&)
1231 rInfo.DefaultFilter = ::rtl::OUString();
1232 rInfo.Extension = ::rtl::OUString();
1236 //-----------------------------------------------
1237 void AutoRecovery::implts_specifyAppModuleAndFactoryURL(AutoRecovery::TDocumentInfo& rInfo)
1239 if (
1240 (!rInfo.AppModule.getLength()) &&
1241 (!rInfo.Document.is() )
1244 throw css::uno::RuntimeException(
1245 ::rtl::OUString::createFromAscii("Cant find out the application module nor its factory URL, if no application module (or a suitable) document is known!"),
1246 static_cast< css::frame::XDispatch* >(this));
1249 // SAFE -> ----------------------------------
1250 ReadGuard aReadLock(m_aLock);
1251 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1252 aReadLock.unlock();
1253 // <- SAFE ----------------------------------
1255 css::uno::Reference< css::frame::XModuleManager > xManager (xSMGR->createInstance(SERVICENAME_MODULEMANAGER), css::uno::UNO_QUERY_THROW);
1256 css::uno::Reference< css::container::XNameAccess > xModuleConfig(xManager , css::uno::UNO_QUERY_THROW);
1258 if (!rInfo.AppModule.getLength())
1259 rInfo.AppModule = xManager->identify(rInfo.Document);
1261 ::comphelper::SequenceAsHashMap lModuleDescription(xModuleConfig->getByName(rInfo.AppModule));
1262 lModuleDescription[CFG_ENTRY_PROP_EMPTYDOCUMENTURL] >>= rInfo.FactoryURL;
1265 //-----------------------------------------------
1266 void AutoRecovery::implts_flushConfigItem(const AutoRecovery::TDocumentInfo& rInfo, sal_Bool bRemoveIt)
1268 css::uno::Reference< css::container::XHierarchicalNameAccess > xCFG;
1272 xCFG = css::uno::Reference< css::container::XHierarchicalNameAccess >(implts_openConfig(), css::uno::UNO_QUERY_THROW);
1274 css::uno::Reference< css::container::XNameAccess > xCheck;
1275 xCFG->getByHierarchicalName(CFG_ENTRY_RECOVERYLIST) >>= xCheck;
1277 css::uno::Reference< css::container::XNameContainer > xModify(xCheck, css::uno::UNO_QUERY_THROW);
1278 css::uno::Reference< css::lang::XSingleServiceFactory > xCreate(xCheck, css::uno::UNO_QUERY_THROW);
1280 ::rtl::OUStringBuffer sIDBuf;
1281 sIDBuf.append(RECOVERY_ITEM_BASE_IDENTIFIER);
1282 sIDBuf.append((sal_Int32)rInfo.ID);
1283 ::rtl::OUString sID = sIDBuf.makeStringAndClear();
1285 // remove
1286 if (bRemoveIt)
1288 // Catch NoSuchElementException.
1289 // Its not a good idea inside multithreaded environments to call hasElement - removeElement.
1290 // DO IT!
1293 xModify->removeByName(sID);
1295 catch(const css::container::NoSuchElementException&)
1296 { return; }
1298 else
1300 // new/modify
1301 css::uno::Reference< css::beans::XPropertySet > xSet;
1302 sal_Bool bNew = (!xCheck->hasByName(sID));
1303 if (bNew)
1304 xSet = css::uno::Reference< css::beans::XPropertySet >(xCreate->createInstance(), css::uno::UNO_QUERY_THROW);
1305 else
1306 xCheck->getByName(sID) >>= xSet;
1308 xSet->setPropertyValue(CFG_ENTRY_PROP_ORIGINALURL , css::uno::makeAny(rInfo.OrgURL ));
1309 xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPURL , css::uno::makeAny(rInfo.OldTempURL ));
1310 xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL , css::uno::makeAny(rInfo.TemplateURL ));
1311 xSet->setPropertyValue(CFG_ENTRY_PROP_FILTER , css::uno::makeAny(rInfo.RealFilter ));
1312 xSet->setPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE, css::uno::makeAny(rInfo.DocumentState));
1313 xSet->setPropertyValue(CFG_ENTRY_PROP_MODULE , css::uno::makeAny(rInfo.AppModule ));
1314 xSet->setPropertyValue(CFG_ENTRY_PROP_TITLE , css::uno::makeAny(rInfo.Title ));
1316 if (bNew)
1317 xModify->insertByName(sID, css::uno::makeAny(xSet));
1320 catch(const css::uno::RuntimeException& exRun)
1321 { throw exRun; }
1322 catch(const css::uno::Exception&)
1323 {} // ??? can it happen that a full disc let these set of operations fail too ???
1325 sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
1330 css::uno::Reference< css::util::XChangesBatch > xFlush(xCFG, css::uno::UNO_QUERY_THROW);
1331 xFlush->commitChanges();
1333 #ifdef TRIGGER_FULL_DISC_CHECK
1334 throw css::uno::Exception();
1335 #endif
1337 nRetry = 0;
1339 catch(const css::uno::Exception& ex)
1341 // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300)
1342 // b) unknown problem (may be locking problem) => reset RETRY value to more usefull value(!) (e.g. retry=3)
1343 // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
1345 // SAFE ->
1346 ReadGuard aReadLock(m_aLock);
1347 sal_Int32 nMinSpaceConfigSave = m_nMinSpaceConfigSave;
1348 aReadLock.unlock();
1349 // <- SAFE
1351 if (! impl_enoughDiscSpace(nMinSpaceConfigSave))
1352 AutoRecovery::impl_showFullDiscError();
1353 else
1354 if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
1355 nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
1356 else
1357 if (nRetry <= GIVE_UP_RETRY)
1358 throw ex; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
1360 --nRetry;
1363 while(nRetry>0);
1366 //-----------------------------------------------
1367 void AutoRecovery::implts_startListening()
1369 // SAFE -> ----------------------------------
1370 ReadGuard aReadLock(m_aLock);
1371 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1372 css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG, css::uno::UNO_QUERY);
1373 css::uno::Reference< css::document::XEventBroadcaster > xBroadcaster = m_xNewDocBroadcaster;
1374 sal_Bool bListenForDocEvents = m_bListenForDocEvents;
1375 aReadLock.unlock();
1376 // <- SAFE ----------------------------------
1378 if (
1379 ( xCFG.is() ) &&
1380 (! m_bListenForConfigChanges)
1383 xCFG->addChangesListener(static_cast< css::util::XChangesListener* >(this));
1384 m_bListenForConfigChanges = sal_True;
1387 if (!xBroadcaster.is())
1389 xBroadcaster = css::uno::Reference< css::document::XEventBroadcaster >(xSMGR->createInstance(SERVICENAME_GLOBALEVENTBROADCASTER), css::uno::UNO_QUERY_THROW);
1390 // SAFE -> ----------------------------------
1391 WriteGuard aWriteLock(m_aLock);
1392 m_xNewDocBroadcaster = xBroadcaster;
1393 aWriteLock.unlock();
1394 // <- SAFE ----------------------------------
1397 if (
1398 ( xBroadcaster.is() ) &&
1399 (! bListenForDocEvents)
1402 xBroadcaster->addEventListener(static_cast< css::document::XEventListener* >(this));
1403 // SAFE ->
1404 WriteGuard aWriteLock(m_aLock);
1405 m_bListenForDocEvents = sal_True;
1406 aWriteLock.unlock();
1407 // <- SAFE
1411 //-----------------------------------------------
1412 void AutoRecovery::implts_stopListening()
1414 // SAFE -> ----------------------------------
1415 ReadGuard aReadLock(m_aLock);
1416 // Attention: Dont reset our internal members here too.
1417 // May be we must work with our configuration, but dont wish to be informed
1418 // about changes any longer. Needed e.g. during EMERGENCY_SAVE!
1419 css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG , css::uno::UNO_QUERY);
1420 css::uno::Reference< css::document::XEventBroadcaster > xGlobalEventBroadcaster(m_xNewDocBroadcaster, css::uno::UNO_QUERY);
1421 aReadLock.unlock();
1422 // <- SAFE ----------------------------------
1424 if (
1425 (xGlobalEventBroadcaster.is()) &&
1426 (m_bListenForDocEvents )
1429 xGlobalEventBroadcaster->removeEventListener(static_cast< css::document::XEventListener* >(this));
1430 m_bListenForDocEvents = sal_False;
1433 if (
1434 (xCFG.is() ) &&
1435 (m_bListenForConfigChanges)
1438 xCFG->removeChangesListener(static_cast< css::util::XChangesListener* >(this));
1439 m_bListenForConfigChanges = sal_False;
1443 //-----------------------------------------------
1444 void AutoRecovery::implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
1446 if (rInfo.ListenForModify)
1447 return;
1449 css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
1450 if (xBroadcaster.is())
1452 css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY);
1453 xBroadcaster->addModifyListener(xThis);
1454 rInfo.ListenForModify = sal_True;
1458 //-----------------------------------------------
1459 void AutoRecovery::implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
1461 if (! rInfo.ListenForModify)
1462 return;
1464 css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
1465 if (xBroadcaster.is())
1467 css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY);
1468 xBroadcaster->removeModifyListener(xThis);
1469 rInfo.ListenForModify = sal_False;
1473 //-----------------------------------------------
1474 void AutoRecovery::implts_actualizeTimer()
1476 implts_stopTimer();
1478 // SAFE -> ----------------------------------
1479 WriteGuard aWriteLock(m_aLock);
1481 if (
1482 (m_eJob == AutoRecovery::E_NO_JOB ) || // TODO may be superflous - E_DONT_START_TIMER should be used only
1483 (m_eTimerType == AutoRecovery::E_DONT_START_TIMER)
1485 return;
1487 ULONG nMilliSeconds = 0;
1488 if (m_eTimerType == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
1490 nMilliSeconds = (m_nAutoSaveTimeIntervall*60000); // [min] => 60.000 ms
1491 #if OSL_DEBUG_LEVEL > 1
1492 if (m_dbg_bMakeItFaster)
1493 nMilliSeconds = m_nAutoSaveTimeIntervall; // [ms]
1494 #endif
1496 else
1497 if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
1499 nMilliSeconds = MIN_TIME_FOR_USER_IDLE;
1500 #if OSL_DEBUG_LEVEL > 1
1501 if (m_dbg_bMakeItFaster)
1502 nMilliSeconds = 300; // let us some time, to finish this method .-)
1503 #endif
1505 else
1506 if (m_eTimerType == AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED)
1507 nMilliSeconds = 300; // there is a minimum time frame, where the user can loose some key input data!
1509 m_aTimer.SetTimeout(nMilliSeconds);
1510 m_aTimer.Start();
1512 aWriteLock.unlock();
1513 // <- SAFE ----------------------------------
1516 //-----------------------------------------------
1517 void AutoRecovery::implts_stopTimer()
1519 // SAFE -> ----------------------------------
1520 WriteGuard aWriteLock(m_aLock);
1522 if (!m_aTimer.IsActive())
1523 return;
1524 m_aTimer.Stop();
1526 // <- SAFE ----------------------------------
1529 //-----------------------------------------------
1530 IMPL_LINK(AutoRecovery, implts_timerExpired, void*, EMPTYARG)
1534 // This method is called by using a pointer to us.
1535 // But we must be aware that we can be destroyed hardly
1536 // if our uno reference will be gone!
1537 // => Hold this object alive till this method finish its work.
1538 css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XTypeProvider* >(this));
1540 // Needed! Otherwise every reschedule request allow a new triggered timer event :-(
1541 implts_stopTimer();
1543 // The timer must be ignored if AutoSave/Recovery was disabled for this
1544 // office session. That can happen if e.g. the command line arguments "-norestore" or "-headless"
1545 // was set. But normaly the timer was disabled if recovery was disabled ...
1546 // But so we are more "safe" .-)
1547 // SAFE -> ----------------------------------
1548 ReadGuard aReadLock(m_aLock);
1549 if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
1550 return 0;
1551 aReadLock.unlock();
1552 // <- SAFE ----------------------------------
1554 // check some "states", where its not allowed (better: not a good idea) to
1555 // start an AutoSave. (e.g. if the user makes drag & drop ...)
1556 // Then we poll till this "disallowed" state is gone.
1557 sal_Bool bAutoSaveNotAllowed = Application::IsUICaptured();
1558 if (bAutoSaveNotAllowed)
1560 // SAFE -> ------------------------------
1561 WriteGuard aWriteLock(m_aLock);
1562 m_eTimerType = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
1563 aWriteLock.unlock();
1564 // <- SAFE ------------------------------
1565 implts_actualizeTimer();
1566 return 0;
1569 // analyze timer type.
1570 // If we poll for an user idle period, may be we must
1571 // do nothing here and start the timer again.
1572 // SAFE -> ----------------------------------
1573 WriteGuard aWriteLock(m_aLock);
1575 if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
1577 sal_Bool bUserIdle = (Application::GetLastInputInterval()>MIN_TIME_FOR_USER_IDLE);
1578 if (!bUserIdle)
1580 implts_actualizeTimer();
1581 return 0;
1585 aWriteLock.unlock();
1586 // <- SAFE ----------------------------------
1588 implts_informListener(AutoRecovery::E_AUTO_SAVE,
1589 AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_START, NULL));
1591 // force save of all currently open documents
1592 // The called method returns an info, if and how this
1593 // timer must be restarted.
1594 sal_Bool bAllowUserIdleLoop = sal_True;
1595 AutoRecovery::ETimerType eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False);
1597 // If timer isnt used for "short callbacks" (means polling
1598 // for special states) ... reset the handle state of all
1599 // cache items. Such handle state indicates, that a document
1600 // was already saved during the THIS(!) AutoSave session.
1601 // Of course NEXT AutoSave session must be started without
1602 // any "handle" state ...
1603 if (
1604 (eSuggestedTimer == AutoRecovery::E_DONT_START_TIMER ) ||
1605 (eSuggestedTimer == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
1608 implts_resetHandleStates(sal_False);
1611 implts_informListener(AutoRecovery::E_AUTO_SAVE,
1612 AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_STOP, NULL));
1614 // restart timer - because it was disabled before ...
1615 // SAFE -> ----------------------------------
1616 aWriteLock.lock();
1617 m_eTimerType = eSuggestedTimer;
1618 aWriteLock.unlock();
1619 // <- SAFE ----------------------------------
1621 implts_actualizeTimer();
1623 catch(const css::uno::Exception&)
1625 LOG_ASSERT(sal_False, "May be you found the reason for bug #125528#. Please report a test scenario to the right developer. THX.");
1628 return 0;
1631 //-----------------------------------------------
1632 IMPL_LINK(AutoRecovery, implts_asyncDispatch, void*, EMPTYARG)
1634 // SAFE ->
1635 WriteGuard aWriteLock(m_aLock);
1636 DispatchParams aParams = m_aDispatchParams;
1637 css::uno::Reference< css::uno::XInterface > xHoldRefForMethodAlive = aParams.m_xHoldRefForAsyncOpAlive;
1638 m_aDispatchParams.forget(); // clears all members ... including the ref-hold object .-)
1639 aWriteLock.unlock();
1640 // <- SAFE
1642 implts_dispatch(aParams);
1643 return 0;
1646 //-----------------------------------------------
1647 void AutoRecovery::implts_registerDocument(const css::uno::Reference< css::frame::XModel >& xDocument)
1649 // ignore corrupted events, where no document is given ... Runtime Error ?!
1650 if (!xDocument.is())
1651 return;
1653 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1655 // notification for already existing document !
1656 // Can happen if events came in asynchronous on recovery time.
1657 // Then our cache was filled from the configuration ... but now we get some
1658 // asynchronous events from the global event broadcaster. We must be shure that
1659 // we dont add the same document more then once.
1660 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1661 if (pIt != m_lDocCache.end())
1663 // Normaly nothing must be done for this "late" notification.
1664 // But may be the modified state was changed inbetween.
1665 // Check it ...
1666 implts_actualizeModifiedState(xDocument);
1667 return;
1670 aCacheLock.unlock();
1672 ::comphelper::MediaDescriptor lDescriptor(xDocument->getArgs());
1674 // check if this document must be ignored for recovery !
1675 // Some use cases dont wish support for AutoSave/Recovery ... as e.g. OLE-Server / ActiveX Control etcpp.
1676 sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False));
1677 if (bNoAutoSave)
1678 return;
1680 // Check if doc is well known on the desktop. Otherwhise ignore it!
1681 // Other frames mostly are used from external programs - e.g. the bean ...
1682 css::uno::Reference< css::frame::XController > xController = xDocument->getCurrentController();
1683 if (!xController.is())
1684 return;
1686 css::uno::Reference< css::frame::XFrame > xFrame = xController->getFrame();
1687 css::uno::Reference< css::frame::XDesktop > xDesktop (xFrame->getCreator(), css::uno::UNO_QUERY);
1688 if (!xDesktop.is())
1689 return;
1691 // get all needed informations of this document
1692 // We need it to update our cache or to locate already existing elements there!
1693 AutoRecovery::TDocumentInfo aNew;
1694 aNew.Document = xDocument;
1696 // TODO replace getLocation() with getURL() ... its a workaround currently only!
1697 css::uno::Reference< css::frame::XStorable > xDoc(aNew.Document, css::uno::UNO_QUERY_THROW);
1698 aNew.OrgURL = xDoc->getLocation();
1700 css::uno::Reference< css::frame::XTitle > xTitle(aNew.Document, css::uno::UNO_QUERY_THROW);
1701 aNew.Title = xTitle->getTitle ();
1703 // SAFE -> ----------------------------------
1704 ReadGuard aReadLock(m_aLock);
1705 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1706 aReadLock.unlock();
1707 // <- SAFE ----------------------------------
1709 // classify the used application module, which is used by this document.
1710 implts_specifyAppModuleAndFactoryURL(aNew);
1712 // Hack! Check for "illegal office documents" ... as e.g. the Basic IDE
1713 // Its not realy a full featured office document. It doesnt provide an URL, any filter, a factory URL etcpp.
1714 // TODO file bug to Basci IDE developers. They must remove the office document API from its service.
1715 if (
1716 (!aNew.OrgURL.getLength() ) &&
1717 (!aNew.FactoryURL.getLength())
1719 return;
1721 // By the way - get some information about the default format for saving!
1722 // and save an information about the real used filter by this document.
1723 // We save this document with DefaultFilter ... and load it with the RealFilter.
1724 implts_specifyDefaultFilterAndExtension(aNew);
1725 aNew.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME() , ::rtl::OUString());
1727 // Further we must know, if this document base on a template.
1728 // Then we must load it in a different way.
1729 css::uno::Reference< css::document::XDocumentPropertiesSupplier > xSupplier(aNew.Document, css::uno::UNO_QUERY);
1730 if (xSupplier.is()) // optional interface!
1732 css::uno::Reference< css::document::XDocumentProperties > xDocProps(xSupplier->getDocumentProperties(), css::uno::UNO_QUERY_THROW);
1733 aNew.TemplateURL = xDocProps->getTemplateURL();
1736 css::uno::Reference< css::util::XModifiable > xModifyCheck(xDocument, css::uno::UNO_QUERY_THROW);
1737 if (xModifyCheck->isModified())
1739 aNew.DocumentState |= AutoRecovery::E_MODIFIED;
1740 aNew.DocumentState |= AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
1743 aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
1745 // SAFE -> ----------------------------------
1746 WriteGuard aWriteLock(m_aLock);
1748 // create a new cache entry ... this document isnt well known.
1749 ++m_nIdPool;
1750 aNew.ID = m_nIdPool;
1751 LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_registerDocument()\nOverflow of ID pool detected.")
1752 m_lDocCache.push_back(aNew);
1754 AutoRecovery::TDocumentList::iterator pIt1 = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1755 AutoRecovery::TDocumentInfo& rInfo = *pIt1;
1757 aWriteLock.unlock();
1758 // <- SAFE ----------------------------------
1760 implts_flushConfigItem(rInfo);
1761 implts_startModifyListeningOnDoc(rInfo);
1763 aCacheLock.unlock();
1766 //-----------------------------------------------
1767 void AutoRecovery::implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument ,
1768 sal_Bool bStopListening)
1771 // SAFE -> ----------------------------------
1772 WriteGuard aWriteLock(m_aLock);
1774 // Attention: Dont leave SAFE section, if you work with pIt!
1775 // Because it points directly into the m_lDocCache list ...
1776 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1778 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1779 if (pIt == m_lDocCache.end())
1780 return; // unknown document => not a runtime error! Because we register only a few documents. see registration ...
1782 AutoRecovery::TDocumentInfo aInfo = *pIt;
1784 aCacheLock.unlock();
1786 // Sometimes we close documents by ourself.
1787 // And these documents cant be deregistered.
1788 // Otherwhise we loos our configuration data ... but need it !
1789 // see SessionSave !
1790 if (aInfo.IgnoreClosing)
1791 return;
1793 CacheLockGuard aCacheLock2(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
1794 pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1795 if (pIt != m_lDocCache.end())
1796 m_lDocCache.erase(pIt);
1797 pIt = m_lDocCache.end(); // otherwhise its not specified what pIt means!
1798 aCacheLock2.unlock();
1800 aWriteLock.unlock();
1801 // <- SAFE ----------------------------------
1803 /* This method is called within disposing() of the document too. But there it's not a good idea to
1804 deregister us as listener. Furter it make no sense - because the broadcaster dies.
1805 So we supress deregistration in such case ...
1807 if (bStopListening)
1808 implts_stopModifyListeningOnDoc(aInfo);
1810 AutoRecovery::st_impl_removeFile(aInfo.OldTempURL);
1811 AutoRecovery::st_impl_removeFile(aInfo.NewTempURL);
1812 implts_flushConfigItem(aInfo, sal_True); // TRUE => remove it from config
1815 //-----------------------------------------------
1816 void AutoRecovery::implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument)
1818 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1820 // SAFE -> ----------------------------------
1821 WriteGuard aWriteLock(m_aLock);
1823 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1824 if (pIt != m_lDocCache.end())
1826 AutoRecovery::TDocumentInfo& rInfo = *pIt;
1827 rInfo.DocumentState |= AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
1829 /* Now we know, that this document was modified again and must be saved next time.
1830 But we dont need this information for every e.g. key input of the user.
1831 So we stop listening here.
1832 But if the document was saved as temp. file we start listening for this event again.
1834 implts_stopModifyListeningOnDoc(rInfo);
1837 aWriteLock.unlock();
1838 // <- SAFE ----------------------------------
1841 //-----------------------------------------------
1842 void AutoRecovery::implts_actualizeModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument)
1844 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1846 // SAFE -> ----------------------------------
1847 WriteGuard aWriteLock(m_aLock);
1849 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1850 if (pIt != m_lDocCache.end())
1852 AutoRecovery::TDocumentInfo& rInfo = *pIt;
1854 // use TRUE as fallback ... so we recognize every document on EmergencySave/AutoRecovery!
1855 sal_Bool bModified = sal_True;
1856 css::uno::Reference< css::util::XModifiable > xModify(xDocument, css::uno::UNO_QUERY);
1857 if (xModify.is())
1858 bModified = xModify->isModified();
1859 if (bModified)
1861 rInfo.DocumentState |= AutoRecovery::E_MODIFIED;
1862 rInfo.DocumentState |= AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
1864 else
1866 rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED;
1867 rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
1871 aWriteLock.unlock();
1872 // <- SAFE ----------------------------------
1875 //-----------------------------------------------
1876 void AutoRecovery::implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument ,
1877 sal_Bool bSaveInProgress)
1879 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1881 // SAFE -> ----------------------------------
1882 WriteGuard aWriteLock(m_aLock);
1884 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1885 if (pIt == m_lDocCache.end())
1886 return;
1887 AutoRecovery::TDocumentInfo& rInfo = *pIt;
1888 rInfo.UsedForSaving = bSaveInProgress;
1890 aWriteLock.unlock();
1891 // <- SAFE ----------------------------------
1894 //-----------------------------------------------
1895 void AutoRecovery::implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument)
1897 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1899 // SAFE -> ----------------------------------
1900 WriteGuard aWriteLock(m_aLock);
1902 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1903 if (pIt == m_lDocCache.end())
1904 return;
1905 AutoRecovery::TDocumentInfo& rInfo = *pIt;
1907 rInfo.DocumentState = AutoRecovery::E_UNKNOWN;
1908 // TODO replace getLocation() with getURL() ... its a workaround currently only!
1909 css::uno::Reference< css::frame::XStorable > xDoc(rInfo.Document, css::uno::UNO_QUERY);
1910 rInfo.OrgURL = xDoc->getLocation();
1912 ::rtl::OUString sRemoveURL1 = rInfo.OldTempURL;
1913 ::rtl::OUString sRemoveURL2 = rInfo.NewTempURL;
1914 rInfo.OldTempURL = ::rtl::OUString();
1915 rInfo.NewTempURL = ::rtl::OUString();
1917 ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs());
1918 rInfo.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME(), ::rtl::OUString());
1920 css::uno::Reference< css::frame::XTitle > xDocTitle(xDocument, css::uno::UNO_QUERY);
1921 if (xDocTitle.is ())
1922 rInfo.Title = xDocTitle->getTitle ();
1923 else
1925 rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_TITLE() , ::rtl::OUString());
1926 if (!rInfo.Title.getLength())
1927 rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_DOCUMENTTITLE(), ::rtl::OUString());
1930 rInfo.UsedForSaving = sal_False;
1932 aWriteLock.unlock();
1933 // <- SAFE ----------------------------------
1935 implts_flushConfigItem(rInfo);
1937 aCacheLock.unlock();
1939 AutoRecovery::st_impl_removeFile(sRemoveURL1);
1940 AutoRecovery::st_impl_removeFile(sRemoveURL2);
1943 //-----------------------------------------------
1944 AutoRecovery::TDocumentList::iterator AutoRecovery::impl_searchDocument( AutoRecovery::TDocumentList& rList ,
1945 const css::uno::Reference< css::frame::XModel >& xDocument)
1947 AutoRecovery::TDocumentList::iterator pIt;
1948 for ( pIt = rList.begin();
1949 pIt != rList.end() ;
1950 ++pIt )
1952 const AutoRecovery::TDocumentInfo& rInfo = *pIt;
1953 if (rInfo.Document == xDocument)
1954 break;
1956 return pIt;
1959 //-----------------------------------------------
1960 void AutoRecovery::implts_changeAllDocVisibility(sal_Bool bVisible)
1962 // SAFE -> ----------------------------------
1963 ReadGuard aReadLock(m_aLock);
1964 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1965 aReadLock.unlock();
1966 // <- SAFE ----------------------------------
1968 css::uno::Reference< css::frame::XFramesSupplier > xDesktop (xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY);
1969 css::uno::Reference< css::container::XIndexAccess > xContainer(xDesktop->getFrames() , css::uno::UNO_QUERY);
1970 sal_Int32 c = xContainer->getCount();
1971 sal_Int32 i = 0;
1973 for (i=0; i<c; ++i)
1975 css::uno::Reference< css::frame::XFrame > xTask;
1977 xContainer->getByIndex(i) >>= xTask;
1978 if (!xTask.is())
1979 continue;
1981 css::uno::Reference< css::awt::XWindow > xWindow = xTask->getContainerWindow();
1982 xWindow->setVisible(bVisible);
1985 aReadLock.unlock();
1986 // <- SAFE ----------------------------------
1989 //-----------------------------------------------
1990 void AutoRecovery::implts_prepareSessionShutdown()
1992 LOG_RECOVERY("AutoRecovery::implts_prepareSessionShutdown() starts ...")
1994 // a) reset modified documents (of course the must be saved before this method is called!)
1995 // b) close it without showing any UI!
1997 // SAFE ->
1998 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2000 AutoRecovery::TDocumentList::iterator pIt;
2001 for ( pIt = m_lDocCache.begin();
2002 pIt != m_lDocCache.end() ;
2003 ++pIt )
2005 AutoRecovery::TDocumentInfo& rInfo = *pIt;
2007 // Prevent us from deregistration of these documents.
2008 // Because we close these documents by ourself (see XClosable below) ...
2009 // it's fact, that we reach our deregistration method. There we
2010 // must not(!) update our configuration ... Otherwhise all
2011 // session data are lost !!!
2012 rInfo.IgnoreClosing = sal_True;
2014 // reset modified flag of these documents (ignoring the notification about it!)
2015 // Otherwise a message box is shown on closing these models.
2016 implts_stopModifyListeningOnDoc(rInfo);
2017 css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
2018 if (xModify.is())
2019 xModify->setModified(sal_False);
2021 // close the model.
2022 css::uno::Reference< css::util::XCloseable > xClose(rInfo.Document, css::uno::UNO_QUERY);
2023 if (xClose.is())
2027 xClose->close(sal_False);
2030 catch(const css::lang::DisposedException&)
2032 // closed ... disposed ... always the same .-)
2035 catch(const css::uno::Exception&)
2037 // At least it's only a try to close these documents before anybody else it does.
2038 // So it seams to be possible to ignore any error here .-)
2041 rInfo.Document.clear();
2045 aCacheLock.unlock();
2046 // <- SAFE
2049 //-----------------------------------------------
2050 /* Currently the document is not closed in case of crash,
2051 so the lock file must be removed explicitly
2053 void lc_removeLockFile(AutoRecovery::TDocumentInfo& rInfo)
2055 if ( rInfo.Document.is() )
2059 css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW);
2060 ::rtl::OUString aURL = xStore->getLocation();
2061 if ( aURL.getLength() )
2063 ::svt::DocumentLockFile aLockFile( aURL );
2064 aLockFile.RemoveFile();
2067 catch( const css::uno::Exception& )
2073 //-----------------------------------------------
2074 /* TODO WORKAROUND:
2076 #i64599#
2078 Normaly the MediaDescriptor argument NoAutoSave indicates,
2079 that a document must be ignored for AutoSave and Recovery.
2080 But sometimes XModel->getArgs() does not contained this information
2081 if implts_registerDocument() was called.
2082 So we have to check a second time, if this property is set ....
2083 Best place doing so is to check it immeditaly before saving
2084 and supressingd saving the document then.
2085 Of course removing the corresponding cache entry isnt an option.
2086 Because it would disturb iteration over the cache !
2087 So we ignore such documents only ...
2088 Hopefully next time they are not inserted in our cache.
2090 sal_Bool lc_checkIfSaveForbiddenByArguments(AutoRecovery::TDocumentInfo& rInfo)
2092 if (! rInfo.Document.is())
2093 return sal_True;
2095 ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs());
2096 sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False));
2098 return bNoAutoSave;
2101 //-----------------------------------------------
2102 AutoRecovery::ETimerType AutoRecovery::implts_saveDocs( sal_Bool bAllowUserIdleLoop,
2103 sal_Bool bRemoveLockFiles,
2104 const DispatchParams* pParams )
2106 // SAFE -> ----------------------------------
2107 ReadGuard aReadLock(m_aLock);
2108 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2109 aReadLock.unlock();
2110 // <- SAFE ----------------------------------
2112 css::uno::Reference< css::task::XStatusIndicator > xExternalProgress;
2113 if (pParams)
2114 xExternalProgress = pParams->m_xProgress;
2116 css::uno::Reference< css::frame::XFramesSupplier > xDesktop (xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY);
2117 ::rtl::OUString sBackupPath (SvtPathOptions().GetBackupPath());
2119 css::uno::Reference< css::frame::XController > xActiveController;
2120 css::uno::Reference< css::frame::XModel > xActiveModel ;
2121 css::uno::Reference< css::frame::XFrame > xActiveFrame = xDesktop->getActiveFrame();
2122 if (xActiveFrame.is())
2123 xActiveController = xActiveFrame->getController();
2124 if (xActiveController.is())
2125 xActiveModel = xActiveController->getModel();
2127 // Set the default timer action for our calli.
2128 // Default = NORMAL_AUTOSAVE
2129 // We return a suggestion for an active timer only.
2130 // It will be ignored if the timer was disabled by the user ...
2131 // Further this state can be set to USER_IDLE only later in this method.
2132 // Its not allowed to reset such state then. Because we must know, if
2133 // there exists POSTPONED documents. see below ...
2134 AutoRecovery::ETimerType eTimer = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
2136 sal_Int32 eJob = m_eJob;
2138 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2140 // SAFE -> ----------------------------------
2141 WriteGuard aWriteLock(m_aLock);
2143 // This list will be filled with every document
2144 // which should be saved as last one. E.g. if it was used
2145 // already for an UI save operation => crashed ... and
2146 // now we try to save it again ... which can fail again ( of course .-) ).
2147 ::std::vector< AutoRecovery::TDocumentList::iterator > lDangerousDocs;
2149 AutoRecovery::TDocumentList::iterator pIt;
2150 for ( pIt = m_lDocCache.begin();
2151 pIt != m_lDocCache.end() ;
2152 ++pIt )
2154 AutoRecovery::TDocumentInfo aInfo = *pIt;
2156 // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
2157 if ( bRemoveLockFiles )
2158 lc_removeLockFile( aInfo );
2160 // WORKAROUND ... see comment of this method
2161 if (lc_checkIfSaveForbiddenByArguments(aInfo))
2162 continue;
2164 // already auto saved during this session :-)
2165 // This state must be reseted for all documents
2166 // if timer is started with normnal AutoSaveTimerIntervall!
2167 if ((aInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED)
2168 continue;
2170 // Not modified documents are not saved.
2171 // We safe an information about the URL only!
2172 sal_Bool bModified = ((aInfo.DocumentState & AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE ) == AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE);
2173 if (! bModified)
2175 aInfo.DocumentState |= AutoRecovery::E_HANDLED;
2176 continue;
2179 // check if this document is still used by a concurrent save operation
2180 // e.g. if the user tried to save via UI.
2181 // Handle it in the following way:
2182 // i) For an AutoSave ... ignore this document! It will be saved and next time we will (hopefully)
2183 // get a notification about the state of this operation.
2184 // And if a document was saved by the user we can remove our temp. file. But that will be done inside
2185 // our callback for SaveDone notification.
2186 // ii) For a CrashSave ... add it to the list of dangerous documents and
2187 // save it after all other documents was saved successfully. That decrease
2188 // the chance for a crash inside a crash.
2189 // On the other side it's not neccessary for documents, which are not modified.
2190 // They can be handled normaly - means we patch the corresponding configuration entry only.
2191 // iii) For a SessionSave ... ignore it! There is no time to wait for this save operation.
2192 // Because the WindowManager will kill the process if it doesnt react immediatly.
2193 // On the other side we cant risk a concurrent save request ... because we know
2194 // that it will produce a crash.
2196 // Attention: Because eJob is used as a flag field, you have to check for the worst case first.
2197 // E.g. a CrashSave can overwrite an AutoSave. So you have to check for a CrashSave before an AutoSave!
2198 if (aInfo.UsedForSaving)
2200 if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE)
2202 lDangerousDocs.push_back(pIt);
2203 continue;
2205 else
2206 if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE)
2208 continue;
2210 else
2211 if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
2213 eTimer = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
2214 aInfo.DocumentState |= AutoRecovery::E_POSTPONED;
2215 continue;
2219 // a) Document was not postponed - and is active now. => postpone it (restart timer, restart loop)
2220 // b) Document was not postponed - and is not active now. => save it
2221 // c) Document was postponed - and is not active now. => save it
2222 // d) Document was postponed - and is active now. => save it (because user idle was checked already)
2223 sal_Bool bActive = (xActiveModel == aInfo.Document);
2224 sal_Bool bWasPostponed = ((aInfo.DocumentState & AutoRecovery::E_POSTPONED) == AutoRecovery::E_POSTPONED);
2226 if (
2227 ! bWasPostponed &&
2228 bActive
2231 aInfo.DocumentState |= AutoRecovery::E_POSTPONED;
2232 *pIt = aInfo;
2233 // postponed documents will be saved if this method is called again!
2234 // That can be done by an outside started timer => E_POLL_FOR_USER_IDLE (if normal AutoSave is active)
2235 // 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!)
2236 eTimer = AutoRecovery::E_POLL_FOR_USER_IDLE;
2237 if (!bAllowUserIdleLoop)
2238 eTimer = AutoRecovery::E_CALL_ME_BACK;
2239 continue;
2242 // b, c, d)
2243 // <- SAFE --------------------------
2244 aWriteLock.unlock();
2245 // changing of aInfo and flushing it is done inside implts_saveOneDoc!
2246 implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
2247 implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
2248 aWriteLock.lock();
2249 // SAFE -> --------------------------
2251 *pIt = aInfo;
2254 // Did we have some "dangerous candidates" ?
2255 // Try to save it ... but may be it will fail !
2256 ::std::vector< AutoRecovery::TDocumentList::iterator >::iterator pIt2;
2257 for ( pIt2 = lDangerousDocs.begin();
2258 pIt2 != lDangerousDocs.end() ;
2259 ++pIt2 )
2261 pIt = *pIt2;
2262 AutoRecovery::TDocumentInfo aInfo = *pIt;
2264 // <- SAFE --------------------------
2265 aWriteLock.unlock();
2266 // changing of aInfo and flushing it is done inside implts_saveOneDoc!
2267 implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
2268 implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
2269 aWriteLock.lock();
2270 // SAFE -> --------------------------
2272 *pIt = aInfo;
2275 return eTimer;
2278 //-----------------------------------------------
2279 void AutoRecovery::implts_saveOneDoc(const ::rtl::OUString& sBackupPath ,
2280 AutoRecovery::TDocumentInfo& rInfo ,
2281 const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress)
2283 // no document? => can occure if we loaded our configuration with files,
2284 // which couldnt be recovered successfully. In such case we have all needed informations
2285 // excepting the real document instance!
2287 // TODO: search right place, where such "dead files" can be removed from the configuration!
2288 if (!rInfo.Document.is())
2289 return;
2291 ::comphelper::MediaDescriptor lOldArgs(rInfo.Document->getArgs());
2292 implts_generateNewTempURL(sBackupPath, lOldArgs, rInfo);
2294 // if the document was loaded with a password, it should be
2295 // stored with password
2296 ::comphelper::MediaDescriptor lNewArgs;
2297 ::rtl::OUString sPassword = lOldArgs.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_PASSWORD(), ::rtl::OUString());
2298 if (sPassword.getLength())
2299 lNewArgs[::comphelper::MediaDescriptor::PROP_PASSWORD()] <<= sPassword;
2301 // Further it must be saved using the default file format of that application.
2302 // Otherwhise we will some data lost.
2303 if (rInfo.DefaultFilter.getLength())
2304 lNewArgs[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.DefaultFilter;
2306 // prepare frame/document/mediadescriptor in a way, that it uses OUR progress .-)
2307 if (xExternalProgress.is())
2308 lNewArgs[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= xExternalProgress;
2309 impl_establishProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
2311 // #i66598# use special handling of property "DocumentBaseURL" (it must be an empty string!)
2312 // for make hyperlinks working
2313 lNewArgs[::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL()] <<= ::rtl::OUString();
2315 // try to save this document as a new temp file everytimes.
2316 // Mark AutoSave state as "INCOMPLETE" if it failed.
2317 // Because the last temp file is to old and does not include all changes.
2318 css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW);
2320 // safe the state about "trying to save"
2321 // ... we need it for recovery if e.g. a crash occures inside next line!
2322 rInfo.DocumentState |= AutoRecovery::E_TRY_SAVE;
2323 implts_flushConfigItem(rInfo);
2325 sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
2326 sal_Bool bError = sal_False;
2331 xStore->storeToURL(rInfo.NewTempURL, lNewArgs.getAsConstPropertyValueList());
2333 #ifdef TRIGGER_FULL_DISC_CHECK
2334 throw css::uno::Exception();
2335 #endif
2337 bError = sal_False;
2338 nRetry = 0;
2340 catch(const css::uno::Exception& ex)
2342 bError = sal_True;
2344 // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300)
2345 // b) unknown problem (may be locking problem) => reset RETRY value to more usefull value(!) (e.g. retry=3)
2346 // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
2348 // SAFE ->
2349 ReadGuard aReadLock2(m_aLock);
2350 sal_Int32 nMinSpaceDocSave = m_nMinSpaceDocSave;
2351 aReadLock2.unlock();
2352 // <- SAFE
2354 if (! impl_enoughDiscSpace(nMinSpaceDocSave))
2355 AutoRecovery::impl_showFullDiscError();
2356 else
2357 if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
2358 nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
2359 else
2360 if (nRetry <= GIVE_UP_RETRY)
2361 throw ex; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
2363 --nRetry;
2366 while(nRetry>0);
2368 if (! bError)
2370 // safe the state about success
2371 // ... you know the reason: to know it on recovery time if next line crash .-)
2372 rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE;
2373 rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2374 rInfo.DocumentState |= AutoRecovery::E_SUCCEDED;
2375 rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED_SINCE_LAST_AUTOSAVE;
2377 else
2379 // safe the state about error ...
2380 rInfo.NewTempURL = ::rtl::OUString();
2381 rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE;
2382 rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2383 rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2386 // make sure the progress isnt referred any longer
2387 impl_forgetProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
2389 // try to remove the old temp file.
2390 // Ignore any error here. We have a new temp file, which is up to date.
2391 // The only thing is: we fill the disk with temp files, if we cant remove old ones :-)
2392 ::rtl::OUString sRemoveFile = rInfo.OldTempURL;
2393 rInfo.OldTempURL = rInfo.NewTempURL;
2394 rInfo.NewTempURL = ::rtl::OUString();
2396 implts_flushConfigItem(rInfo);
2398 // We must know if the user modifies the document again ...
2399 implts_startModifyListeningOnDoc(rInfo);
2401 AutoRecovery::st_impl_removeFile(sRemoveFile);
2404 //-----------------------------------------------
2405 AutoRecovery::ETimerType AutoRecovery::implts_openDocs(const DispatchParams& aParams)
2407 AutoRecovery::ETimerType eTimer = AutoRecovery::E_DONT_START_TIMER;
2409 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2411 // SAFE -> ----------------------------------
2412 WriteGuard aWriteLock(m_aLock);
2414 sal_Int32 eJob = m_eJob;
2415 AutoRecovery::TDocumentList::iterator pIt;
2416 for ( pIt = m_lDocCache.begin();
2417 pIt != m_lDocCache.end() ;
2418 ++pIt )
2420 AutoRecovery::TDocumentInfo& rInfo = *pIt;
2422 // Such documents are already loaded by the last loop.
2423 // Dont check E_SUCCEDED here! Its may be the final state of an AutoSave
2424 // operation before!!!
2425 if ((rInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED)
2426 continue;
2428 // a1,b1,c1,d2,e2,f2)
2429 if ((rInfo.DocumentState & AutoRecovery::E_DAMAGED) == AutoRecovery::E_DAMAGED)
2431 // dont forget to inform listener! May be this document was
2432 // damaged on last saving time ...
2433 // Then our listener need this notification.
2434 // If it was damaged during last "try to open" ...
2435 // it will be notified more then once. SH.. HAPPENS ...
2436 // <- SAFE --------------------------
2437 aWriteLock.unlock();
2438 implts_informListener(eJob,
2439 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2440 aWriteLock.lock();
2441 // SAFE -> --------------------------
2442 continue;
2445 ::comphelper::MediaDescriptor lDescriptor;
2447 // its an UI feature - so the "USER" itself must be set as referer
2448 lDescriptor[::comphelper::MediaDescriptor::PROP_REFERRER()] <<= REFERRER_USER;
2449 lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= ::rtl::OUString();
2451 if (aParams.m_xProgress.is())
2452 lDescriptor[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= aParams.m_xProgress;
2454 sal_Bool bBackupWasTried = (
2455 ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_BACKUP ) == AutoRecovery::E_TRY_LOAD_BACKUP) || // temp. state!
2456 ((rInfo.DocumentState & AutoRecovery::E_INCOMPLETE ) == AutoRecovery::E_INCOMPLETE ) // transport TRY_LOAD_BACKUP from last loop to this new one!
2458 sal_Bool bOriginalWasTried = ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL);
2460 if (bBackupWasTried)
2462 if (!bOriginalWasTried)
2464 rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2465 // try original URL ... ! dont continue with next item here ...
2467 else
2469 rInfo.DocumentState |= AutoRecovery::E_DAMAGED;
2470 continue;
2474 ::rtl::OUString sLoadOriginalURL;
2475 ::rtl::OUString sLoadBackupURL ;
2477 if (!bBackupWasTried)
2478 sLoadBackupURL = rInfo.OldTempURL;
2480 if (rInfo.OrgURL.getLength())
2482 sLoadOriginalURL = rInfo.OrgURL;
2484 else
2485 if (rInfo.TemplateURL.getLength())
2487 sLoadOriginalURL = rInfo.TemplateURL;
2488 lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True;
2489 lDescriptor[::comphelper::MediaDescriptor::PROP_TEMPLATENAME()] <<= rInfo.TemplateURL;
2491 else
2492 if (rInfo.FactoryURL.getLength())
2494 sLoadOriginalURL = rInfo.FactoryURL;
2495 lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True;
2498 // A "Salvaged" item must exists every time. The core can make something special then for recovery.
2499 // Of course it should be the real file name of the original file, in case we load the temp. backup here.
2500 ::rtl::OUString sURL;
2501 if (sLoadBackupURL.getLength())
2503 sURL = sLoadBackupURL;
2504 rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_BACKUP;
2505 lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= sLoadOriginalURL;
2507 else
2508 if (sLoadOriginalURL.getLength())
2510 sURL = sLoadOriginalURL;
2511 rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_ORIGINAL;
2513 else
2514 continue; // TODO ERROR!
2516 // <- SAFE ------------------------------
2517 aWriteLock.unlock();
2519 implts_flushConfigItem(rInfo);
2520 implts_informListener(eJob,
2521 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2525 implts_openOneDoc(sURL, lDescriptor, rInfo);
2527 catch(const css::uno::Exception&)
2529 rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP;
2530 rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL;
2531 if (sLoadBackupURL.getLength())
2533 rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2534 eTimer = AutoRecovery::E_CALL_ME_BACK;
2536 else
2538 rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2539 rInfo.DocumentState |= AutoRecovery::E_DAMAGED;
2542 implts_flushConfigItem(rInfo, sal_True);
2543 implts_informListener(eJob,
2544 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2546 // SAFE -> ------------------------------
2547 // Needed for next loop!
2548 aWriteLock.lock();
2549 continue;
2552 if (rInfo.RealFilter.getLength())
2554 ::comphelper::MediaDescriptor lPatchDescriptor(rInfo.Document->getArgs());
2555 lPatchDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.RealFilter;
2556 rInfo.Document->attachResource(sURL, lPatchDescriptor.getAsConstPropertyValueList());
2559 css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
2560 sal_Bool bModified = ((rInfo.DocumentState & AutoRecovery::E_MODIFIED) == AutoRecovery::E_MODIFIED);
2561 xModify->setModified(bModified);
2563 rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP;
2564 rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL;
2565 rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2566 rInfo.DocumentState |= AutoRecovery::E_SUCCEDED;
2568 implts_flushConfigItem(rInfo);
2569 implts_informListener(eJob,
2570 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2572 /* Normaly we listen as XModifyListener on a document to know if a document was changed
2573 since our last AutoSave. And we deregister us in case we know this state.
2574 But directly after one documentw as recovered ... we must start listening.
2575 Otherwhise the first "modify" dont reach us. Because weself called setModified()
2576 on the document via API. And currently we dont listen for any events (not at the GlobalEventBroadcaster
2577 nor at any document!).
2579 implts_startModifyListeningOnDoc(rInfo);
2581 // SAFE -> ------------------------------
2582 // Needed for next loop. Dont unlock it again!
2583 aWriteLock.lock();
2586 aWriteLock.unlock();
2587 // <- SAFE ----------------------------------
2589 return eTimer;
2592 //-----------------------------------------------
2593 void AutoRecovery::implts_openOneDoc(const ::rtl::OUString& sURL ,
2594 ::comphelper::MediaDescriptor& lDescriptor,
2595 AutoRecovery::TDocumentInfo& rInfo )
2597 // SAFE -> ----------------------------------
2598 ReadGuard aReadLock(m_aLock);
2599 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2600 aReadLock.unlock();
2601 // <- SAFE ----------------------------------
2603 css::uno::Reference< css::util::XURLTransformer > xParser(xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), css::uno::UNO_QUERY_THROW);
2604 css::util::URL aURL;
2605 aURL.Complete = sURL;
2606 xParser->parseStrict(aURL);
2608 LoadDispatchListener* pLoadListener = new LoadDispatchListener();
2609 css::uno::Reference< css::frame::XDispatchResultListener > xLoadListener (static_cast< css::frame::XDispatchResultListener* >(pLoadListener), css::uno::UNO_QUERY_THROW);
2611 css::uno::Reference< css::frame::XFrame > xDesktop (xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY_THROW);
2612 css::uno::Reference< css::frame::XFrame > xNewTarget = xDesktop->findFrame(SPECIALTARGET_BLANK, 0);
2613 css::uno::Reference< css::frame::XDispatchProvider > xProvider (xNewTarget, css::uno::UNO_QUERY_THROW);
2614 css::uno::Reference< css::frame::XNotifyingDispatch > xDispatcher(
2615 xProvider->queryDispatch(aURL, SPECIALTARGET_SELF, 0),
2616 css::uno::UNO_QUERY_THROW);
2618 // load the document and listen for the state of this operation.
2619 pLoadListener->setURL(aURL.Complete);
2621 // make sure the right progress is used always.
2622 impl_establishProgress(rInfo, lDescriptor, xNewTarget);
2626 xDispatcher->dispatchWithNotification(
2627 aURL,
2628 lDescriptor.getAsConstPropertyValueList(),
2629 xLoadListener);
2631 pLoadListener->wait(0); // wait for ever!
2633 css::frame::DispatchResultEvent aResult = pLoadListener->getResult();
2634 if (aResult.State != css::frame::DispatchResultState::SUCCESS)
2636 ::rtl::OUStringBuffer sMsg(256);
2637 sMsg.appendAscii("Recovery of \"");
2638 sMsg.append (aURL.Complete );
2639 sMsg.appendAscii("\" failed." );
2640 throw css::uno::Exception(sMsg.makeStringAndClear(), static_cast< css::frame::XDispatch* >(this));
2643 rInfo.Document = fpf::extractFrameModel(xNewTarget);
2645 catch(const css::uno::RuntimeException&)
2646 { throw; }
2647 catch(const css::uno::Exception&)
2649 css::uno::Reference< css::util::XCloseable > xClose(xNewTarget, css::uno::UNO_QUERY);
2650 xClose->close(sal_True);
2651 xNewTarget.clear();
2652 throw;
2655 // of course we must forget all references to this temp(!) progress
2656 impl_forgetProgress(rInfo, lDescriptor, xNewTarget);
2659 //-----------------------------------------------
2660 void AutoRecovery::implts_generateNewTempURL(const ::rtl::OUString& sBackupPath ,
2661 ::comphelper::MediaDescriptor& /*rMediaDescriptor*/,
2662 AutoRecovery::TDocumentInfo& rInfo )
2664 // SAFE -> ----------------------------------
2665 ReadGuard aReadLock(m_aLock);
2666 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2667 aReadLock.unlock();
2668 // <- SAFE ----------------------------------
2670 // specify URL for saving (which points to a temp file inside backup directory)
2671 // and define an unique name, so we can locate it later.
2672 // This unique name must solve an optimization problem too!
2673 // In case we are asked to save unmodified documents too - and one of them
2674 // is an empty one (because it was new created using e.g. an URL private:factory/...)
2675 // we should not save it realy. Then we put the information about such "empty document"
2676 // into the configuration and dont create any recovery file on disk.
2677 // We use the title of the document to make it unique.
2678 ::rtl::OUStringBuffer sUniqueName;
2679 if (rInfo.OrgURL.getLength())
2681 css::uno::Reference< css::util::XURLTransformer > xParser(xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), css::uno::UNO_QUERY);
2682 css::util::URL aURL;
2683 aURL.Complete = rInfo.OrgURL;
2684 xParser->parseStrict(aURL);
2685 sUniqueName.append(aURL.Name);
2687 else
2688 if (rInfo.FactoryURL.getLength())
2689 sUniqueName.appendAscii("untitled");
2690 sUniqueName.appendAscii("_");
2692 // TODO: Must we strip some illegal signes - if we use the title?
2694 String sName (sUniqueName.makeStringAndClear());
2695 String sExtension(rInfo.Extension );
2696 String sPath (sBackupPath );
2697 ::utl::TempFile aTempFile(sName, &sExtension, &sPath);
2699 rInfo.NewTempURL = aTempFile.GetURL();
2702 //-----------------------------------------------
2703 void AutoRecovery::implts_informListener( sal_Int32 eJob ,
2704 const css::frame::FeatureStateEvent& aEvent)
2706 // Helper shares mutex with us -> threadsafe!
2707 ::cppu::OInterfaceContainerHelper* pListenerForURL = 0;
2708 ::rtl::OUString sJob = AutoRecovery::implst_getJobDescription(eJob);
2710 // inform listener, which are registered for any URLs(!)
2711 pListenerForURL = m_lListener.getContainer(sJob);
2712 if(pListenerForURL != 0)
2714 ::cppu::OInterfaceIteratorHelper pIt(*pListenerForURL);
2715 while(pIt.hasMoreElements())
2719 css::uno::Reference< css::frame::XStatusListener > xListener(((css::frame::XStatusListener*)pIt.next()), css::uno::UNO_QUERY);
2720 xListener->statusChanged(aEvent);
2722 catch(const css::uno::RuntimeException&)
2723 { pIt.remove(); }
2728 //-----------------------------------------------
2729 ::rtl::OUString AutoRecovery::implst_getJobDescription(sal_Int32 eJob)
2731 // describe the current running operation
2732 ::rtl::OUStringBuffer sFeature(256);
2733 sFeature.append(CMD_PROTOCOL);
2735 // Attention: Because "eJob" is used as a flag field the order of checking these
2736 // flags is importent. We must preferr job with higher priorities!
2737 // E.g. EmergencySave has an higher prio then AutoSave ...
2738 // On the other side there exist a well defined order between two different jobs.
2739 // e.g. PrepareEmergencySave must be done before EmergencySave is started of course.
2741 if ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE)
2742 sFeature.append(CMD_DO_PREPARE_EMERGENCY_SAVE);
2743 else
2744 if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE)
2745 sFeature.append(CMD_DO_EMERGENCY_SAVE);
2746 else
2747 if ((eJob & AutoRecovery::E_RECOVERY) == AutoRecovery::E_RECOVERY)
2748 sFeature.append(CMD_DO_RECOVERY);
2749 else
2750 if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE)
2751 sFeature.append(CMD_DO_SESSION_SAVE);
2752 else
2753 if ((eJob & AutoRecovery::E_SESSION_RESTORE) == AutoRecovery::E_SESSION_RESTORE)
2754 sFeature.append(CMD_DO_SESSION_RESTORE);
2755 else
2756 if ((eJob & AutoRecovery::E_ENTRY_BACKUP) == AutoRecovery::E_ENTRY_BACKUP)
2757 sFeature.append(CMD_DO_ENTRY_BACKUP);
2758 else
2759 if ((eJob & AutoRecovery::E_ENTRY_CLEANUP) == AutoRecovery::E_ENTRY_CLEANUP)
2760 sFeature.append(CMD_DO_ENTRY_CLEANUP);
2761 else
2762 if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
2763 sFeature.append(CMD_DO_AUTO_SAVE);
2764 #ifdef ENABLE_WARNINGS
2765 else
2766 LOG_WARNING("AutoRecovery::implst_getJobDescription()", "Invalid job identifier detected.")
2767 #endif
2769 return sFeature.makeStringAndClear();
2772 //-----------------------------------------------
2773 sal_Int32 AutoRecovery::implst_classifyJob(const css::util::URL& aURL)
2775 if (aURL.Protocol.equals(CMD_PROTOCOL))
2777 if (aURL.Path.equals(CMD_DO_PREPARE_EMERGENCY_SAVE))
2778 return AutoRecovery::E_PREPARE_EMERGENCY_SAVE;
2779 else
2780 if (aURL.Path.equals(CMD_DO_EMERGENCY_SAVE))
2781 return AutoRecovery::E_EMERGENCY_SAVE;
2782 else
2783 if (aURL.Path.equals(CMD_DO_RECOVERY))
2784 return AutoRecovery::E_RECOVERY;
2785 else
2786 if (aURL.Path.equals(CMD_DO_ENTRY_BACKUP))
2787 return AutoRecovery::E_ENTRY_BACKUP;
2788 else
2789 if (aURL.Path.equals(CMD_DO_ENTRY_CLEANUP))
2790 return AutoRecovery::E_ENTRY_CLEANUP;
2791 else
2792 if (aURL.Path.equals(CMD_DO_SESSION_SAVE))
2793 return AutoRecovery::E_SESSION_SAVE;
2794 else
2795 if (aURL.Path.equals(CMD_DO_SESSION_RESTORE))
2796 return AutoRecovery::E_SESSION_RESTORE;
2797 else
2798 if (aURL.Path.equals(CMD_DO_DISABLE_RECOVERY))
2799 return AutoRecovery::E_DISABLE_AUTORECOVERY;
2800 else
2801 if (aURL.Path.equals(CMD_DO_SET_AUTOSAVE_STATE))
2802 return AutoRecovery::E_SET_AUTOSAVE_STATE;
2805 LOG_WARNING("AutoRecovery::implts_classifyJob()", "Invalid URL (protocol).")
2806 return AutoRecovery::E_NO_JOB;
2809 //-----------------------------------------------
2810 css::frame::FeatureStateEvent AutoRecovery::implst_createFeatureStateEvent( sal_Int32 eJob ,
2811 const ::rtl::OUString& sEventType,
2812 AutoRecovery::TDocumentInfo* pInfo )
2814 css::frame::FeatureStateEvent aEvent;
2815 aEvent.FeatureURL.Complete = AutoRecovery::implst_getJobDescription(eJob);
2816 aEvent.FeatureDescriptor = sEventType;
2818 if (sEventType.equals(OPERATION_UPDATE) && pInfo)
2820 // pack rInfo for transport via UNO
2821 css::uno::Sequence< css::beans::NamedValue > lInfo(8);
2822 lInfo[0].Name = CFG_ENTRY_PROP_ID;
2823 lInfo[0].Value <<= pInfo->ID;
2825 lInfo[1].Name = CFG_ENTRY_PROP_ORIGINALURL;
2826 lInfo[1].Value <<= pInfo->OrgURL;
2828 lInfo[2].Name = CFG_ENTRY_PROP_FACTORYURL;
2829 lInfo[2].Value <<= pInfo->FactoryURL;
2831 lInfo[3].Name = CFG_ENTRY_PROP_TEMPLATEURL;
2832 lInfo[3].Value <<= pInfo->TemplateURL;
2834 lInfo[4].Name = CFG_ENTRY_PROP_TEMPURL;
2835 if (pInfo->OldTempURL.getLength())
2836 lInfo[4].Value <<= pInfo->OldTempURL;
2837 else
2838 lInfo[4].Value <<= pInfo->NewTempURL;
2840 lInfo[5].Name = CFG_ENTRY_PROP_MODULE;
2841 lInfo[5].Value <<= pInfo->AppModule;
2843 lInfo[6].Name = CFG_ENTRY_PROP_TITLE;
2844 lInfo[6].Value <<= pInfo->Title;
2846 lInfo[7].Name = CFG_ENTRY_PROP_DOCUMENTSTATE;
2847 lInfo[7].Value <<= pInfo->DocumentState;
2849 aEvent.State <<= lInfo;
2852 return aEvent;
2855 //-----------------------------------------------
2856 void AutoRecovery::implts_resetHandleStates(sal_Bool /*bLoadCache*/)
2858 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2860 // SAFE -> ------------------------------
2861 WriteGuard aWriteLock(m_aLock);
2863 AutoRecovery::TDocumentList::iterator pIt;
2864 for ( pIt = m_lDocCache.begin();
2865 pIt != m_lDocCache.end() ;
2866 ++pIt )
2868 AutoRecovery::TDocumentInfo& rInfo = *pIt;
2869 rInfo.DocumentState &= ~AutoRecovery::E_HANDLED ;
2870 rInfo.DocumentState &= ~AutoRecovery::E_POSTPONED;
2872 // SAFE -> ------------------------------
2873 aWriteLock.unlock();
2874 implts_flushConfigItem(rInfo);
2875 aWriteLock.lock();
2876 // <- SAFE ------------------------------
2879 aWriteLock.unlock();
2880 // <- SAFE ----------------------------------
2883 //-----------------------------------------------
2884 void AutoRecovery::implts_prepareEmergencySave()
2886 // Be sure to know all open documents realy .-)
2887 implts_verifyCacheAgainstDesktopDocumentList();
2889 // hide all docs, so the user cant disturb our emergency save .-)
2890 implts_changeAllDocVisibility(sal_False);
2893 //-----------------------------------------------
2894 void AutoRecovery::implts_doEmergencySave(const DispatchParams& aParams)
2896 // Write a hint "we chrashed" into the configuration, so
2897 // the error report tool is started too in case no recovery
2898 // documents exists and was saved.
2899 ::comphelper::ConfigurationHelper::writeDirectKey(
2900 m_xSMGR,
2901 CFG_PACKAGE_RECOVERY,
2902 CFG_PATH_RECOVERYINFO,
2903 CFG_ENTRY_CRASHED,
2904 css::uno::makeAny(sal_True),
2905 ::comphelper::ConfigurationHelper::E_STANDARD);
2907 // The called method for saving documents runs
2908 // during normal AutoSave more then once. Because
2909 // it postpone active documents and save it later.
2910 // That is normaly done by recalling it from a timer.
2911 // Here we must do it immediatly!
2912 // Of course this method returns the right state -
2913 // because it knows, that we are running in ERMERGENCY SAVE mode .-)
2915 sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-)
2916 AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
2919 eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_True, &aParams);
2921 while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
2923 // reset the handle state of all
2924 // cache items. Such handle state indicates, that a document
2925 // was already saved during the THIS(!) EmergencySave session.
2926 // Of course following recovery session must be started without
2927 // any "handle" state ...
2928 implts_resetHandleStates(sal_False);
2930 // flush config cached back to disc.
2931 impl_flushALLConfigChanges();
2933 // try to make sure next time office will be started user wont be
2934 // notified about any other might be running office instance
2935 // remove ".lock" file from disc !
2936 AutoRecovery::st_impl_removeLockFile();
2939 //-----------------------------------------------
2940 void AutoRecovery::implts_doRecovery(const DispatchParams& aParams)
2942 AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
2945 eSuggestedTimer = implts_openDocs(aParams);
2947 while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
2949 // reset the handle state of all
2950 // cache items. Such handle state indicates, that a document
2951 // was already saved during the THIS(!) Recovery session.
2952 // Of course a may be following EmergencySave session must be started without
2953 // any "handle" state ...
2954 implts_resetHandleStates(sal_True);
2956 // Reset the configuration hint "we was crashed"!
2957 ::comphelper::ConfigurationHelper::writeDirectKey(
2958 m_xSMGR,
2959 CFG_PACKAGE_RECOVERY,
2960 CFG_PATH_RECOVERYINFO,
2961 CFG_ENTRY_CRASHED,
2962 css::uno::makeAny(sal_False),
2963 ::comphelper::ConfigurationHelper::E_STANDARD);
2966 //-----------------------------------------------
2967 void AutoRecovery::implts_doSessionSave(const DispatchParams& aParams)
2969 LOG_RECOVERY("AutoRecovery::implts_doSessionSave()")
2971 // try to make sure next time office will be started user wont be
2972 // notified about any other might be running office instance
2973 // remove ".lock" file from disc !
2974 // it is done as a first action for session save since Gnome sessions
2975 // do not provide enough time for shutdown, and the dialog looks to be
2976 // confusing for the user
2977 AutoRecovery::st_impl_removeLockFile();
2979 // Be sure to know all open documents realy .-)
2980 implts_verifyCacheAgainstDesktopDocumentList();
2982 // The called method for saving documents runs
2983 // during normal AutoSave more then once. Because
2984 // it postpone active documents and save it later.
2985 // That is normaly done by recalling it from a timer.
2986 // Here we must do it immediatly!
2987 // Of course this method returns the right state -
2988 // because it knows, that we are running in SESSION SAVE mode .-)
2990 sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-)
2991 AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
2994 eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_True, &aParams);
2996 while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
2998 // reset the handle state of all
2999 // cache items. Such handle state indicates, that a document
3000 // was already saved during the THIS(!) save session.
3001 // Of course following restore session must be started without
3002 // any "handle" state ...
3003 implts_resetHandleStates(sal_False);
3005 // reset all modified documents, so the dont show any UI on closing ...
3006 // and close all documents, so we can shutdown the OS!
3007 implts_prepareSessionShutdown();
3009 // Write a hint for "stored session data" into the configuration, so
3010 // the on next startup we know what's happen last time
3011 ::comphelper::ConfigurationHelper::writeDirectKey(
3012 m_xSMGR,
3013 CFG_PACKAGE_RECOVERY,
3014 CFG_PATH_RECOVERYINFO,
3015 CFG_ENTRY_SESSIONDATA,
3016 css::uno::makeAny(sal_True),
3017 ::comphelper::ConfigurationHelper::E_STANDARD);
3019 // flush config cached back to disc.
3020 impl_flushALLConfigChanges();
3023 //-----------------------------------------------
3024 void AutoRecovery::implts_doSessionRestore(const DispatchParams& aParams)
3026 LOG_RECOVERY("AutoRecovery::implts_doSessionRestore() ...")
3028 AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3031 eSuggestedTimer = implts_openDocs(aParams);
3033 while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3035 // reset the handle state of all
3036 // cache items. Such handle state indicates, that a document
3037 // was already saved during the THIS(!) Restore session.
3038 // Of course a may be following save session must be started without
3039 // any "handle" state ...
3040 implts_resetHandleStates(sal_True);
3042 // make all opened documents visible
3043 implts_changeAllDocVisibility(sal_True);
3045 // Reset the configuration hint for "session save"!
3046 LOG_RECOVERY("... reset config key 'SessionData'")
3047 ::comphelper::ConfigurationHelper::writeDirectKey(
3048 m_xSMGR,
3049 CFG_PACKAGE_RECOVERY,
3050 CFG_PATH_RECOVERYINFO,
3051 CFG_ENTRY_SESSIONDATA,
3052 css::uno::makeAny(sal_False),
3053 ::comphelper::ConfigurationHelper::E_STANDARD);
3055 LOG_RECOVERY("... AutoRecovery::implts_doSessionRestore()")
3058 //-----------------------------------------------
3059 void AutoRecovery::implts_backupWorkingEntry(const DispatchParams& aParams)
3061 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
3063 AutoRecovery::TDocumentList::iterator pIt;
3064 for ( pIt = m_lDocCache.begin();
3065 pIt != m_lDocCache.end() ;
3066 ++pIt )
3068 const AutoRecovery::TDocumentInfo& rInfo = *pIt;
3069 if (rInfo.ID != aParams.m_nWorkingEntryID)
3070 continue;
3072 ::rtl::OUString sSourceURL;
3073 // Prefer temp file. It contains the changes against the original document!
3074 if (rInfo.OldTempURL.getLength())
3075 sSourceURL = rInfo.OldTempURL;
3076 else
3077 if (rInfo.NewTempURL.getLength())
3078 sSourceURL = rInfo.NewTempURL;
3079 else
3080 if (rInfo.OrgURL.getLength())
3081 sSourceURL = rInfo.OrgURL;
3082 else
3083 continue; // nothing real to save! An unmodified but new created document.
3085 INetURLObject aParser(sSourceURL);
3086 // AutoRecovery::EFailureSafeResult eResult =
3087 implts_copyFile(sSourceURL, aParams.m_sSavePath, aParser.getName());
3089 // TODO: Check eResult and react for errors (InteractionHandler!?)
3090 // Currently we ignore it ...
3091 // DONT UPDATE THE CACHE OR REMOVE ANY TEMP. FILES FROM DISK.
3092 // That has to be forced from outside explicitly.
3093 // See implts_cleanUpWorkingEntry() for further details.
3097 //-----------------------------------------------
3098 void AutoRecovery::implts_cleanUpWorkingEntry(const DispatchParams& aParams)
3100 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
3102 AutoRecovery::TDocumentList::iterator pIt;
3103 for ( pIt = m_lDocCache.begin();
3104 pIt != m_lDocCache.end() ;
3105 ++pIt )
3107 AutoRecovery::TDocumentInfo& rInfo = *pIt;
3108 if (rInfo.ID != aParams.m_nWorkingEntryID)
3109 continue;
3111 AutoRecovery::st_impl_removeFile(rInfo.OldTempURL);
3112 AutoRecovery::st_impl_removeFile(rInfo.NewTempURL);
3113 implts_flushConfigItem(rInfo, sal_True); // TRUE => remove it from xml config!
3115 m_lDocCache.erase(pIt);
3116 break; /// !!! pIt is not defined any longer ... further this function has finished it's work
3120 //-----------------------------------------------
3121 AutoRecovery::EFailureSafeResult AutoRecovery::implts_copyFile(const ::rtl::OUString& sSource ,
3122 const ::rtl::OUString& sTargetPath,
3123 const ::rtl::OUString& sTargetName)
3125 // create content for the parent folder and call transfer on that content with the source content
3126 // and the destination file name as parameters
3128 css::uno::Reference< css::ucb::XCommandEnvironment > xEnvironment;
3130 ::ucbhelper::Content aSourceContent;
3131 ::ucbhelper::Content aTargetContent;
3135 aTargetContent = ::ucbhelper::Content(sTargetPath, xEnvironment);
3137 catch(const css::uno::Exception&)
3138 { return AutoRecovery::E_WRONG_TARGET_PATH; }
3140 sal_Int32 nNameClash;
3141 // nNameClash = css::ucb::NameClash::ERROR;
3142 nNameClash = css::ucb::NameClash::RENAME;
3143 // nNameClash = css::ucb::NameClash::OVERWRITE;
3147 ::ucbhelper::Content::create(sSource, xEnvironment, aSourceContent);
3148 aTargetContent.transferContent(aSourceContent, ::ucbhelper::InsertOperation_COPY, sTargetName, nNameClash);
3150 catch(const css::uno::Exception&)
3151 { return AutoRecovery::E_ORIGINAL_FILE_MISSING; }
3153 return AutoRecovery::E_COPIED;
3156 //-----------------------------------------------
3157 sal_Bool SAL_CALL AutoRecovery::convertFastPropertyValue( css::uno::Any& /*aConvertedValue*/,
3158 css::uno::Any& /*aOldValue*/ ,
3159 sal_Int32 /*nHandle*/ ,
3160 const css::uno::Any& /*aValue*/ )
3161 throw(css::lang::IllegalArgumentException)
3163 // not needed currently
3164 return sal_False;
3167 //-----------------------------------------------
3168 void SAL_CALL AutoRecovery::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/,
3169 const css::uno::Any& /*aValue*/ )
3170 throw(css::uno::Exception)
3172 // not needed currently
3175 //-----------------------------------------------
3176 void SAL_CALL AutoRecovery::getFastPropertyValue(css::uno::Any& aValue ,
3177 sal_Int32 nHandle) const
3179 switch(nHandle)
3181 case AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA :
3183 sal_Bool bSessionData = sal_False;
3184 ::comphelper::ConfigurationHelper::readDirectKey(
3185 m_xSMGR,
3186 CFG_PACKAGE_RECOVERY,
3187 CFG_PATH_RECOVERYINFO,
3188 CFG_ENTRY_SESSIONDATA,
3189 ::comphelper::ConfigurationHelper::E_READONLY) >>= bSessionData;
3191 sal_Bool bRecoveryData = ((sal_Bool)(m_lDocCache.size()>0));
3193 // exists session data ... => then we cant say, that these
3194 // data are valid for recovery. So we have to return FALSE then!
3195 if (bSessionData)
3196 bRecoveryData = sal_False;
3198 aValue <<= bRecoveryData;
3200 break;
3202 case AUTORECOVERY_PROPHANDLE_CRASHED :
3203 aValue = ::comphelper::ConfigurationHelper::readDirectKey(
3204 m_xSMGR,
3205 CFG_PACKAGE_RECOVERY,
3206 CFG_PATH_RECOVERYINFO,
3207 CFG_ENTRY_CRASHED,
3208 ::comphelper::ConfigurationHelper::E_READONLY);
3209 break;
3211 case AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA :
3212 aValue = ::comphelper::ConfigurationHelper::readDirectKey(
3213 m_xSMGR,
3214 CFG_PACKAGE_RECOVERY,
3215 CFG_PATH_RECOVERYINFO,
3216 CFG_ENTRY_SESSIONDATA,
3217 ::comphelper::ConfigurationHelper::E_READONLY);
3218 break;
3222 //-----------------------------------------------
3223 const css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor()
3225 static const css::beans::Property pPropertys[] =
3227 css::beans::Property( AUTORECOVERY_PROPNAME_CRASHED , AUTORECOVERY_PROPHANDLE_CRASHED , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3228 css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA, AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA, ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3229 css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA , AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3231 static const css::uno::Sequence< css::beans::Property > lPropertyDescriptor(pPropertys, AUTORECOVERY_PROPCOUNT);
3232 return lPropertyDescriptor;
3235 //-----------------------------------------------
3236 ::cppu::IPropertyArrayHelper& SAL_CALL AutoRecovery::getInfoHelper()
3238 static ::cppu::OPropertyArrayHelper* pInfoHelper = 0;
3239 if(!pInfoHelper)
3241 ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() );
3242 if(!pInfoHelper)
3244 static ::cppu::OPropertyArrayHelper aInfoHelper(impl_getStaticPropertyDescriptor(), sal_True);
3245 pInfoHelper = &aInfoHelper;
3249 return (*pInfoHelper);
3252 //-----------------------------------------------
3253 css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL AutoRecovery::getPropertySetInfo()
3254 throw(css::uno::RuntimeException)
3256 static css::uno::Reference< css::beans::XPropertySetInfo >* pInfo = 0;
3257 if(!pInfo)
3259 ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() );
3260 if(!pInfo)
3262 static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(createPropertySetInfo(getInfoHelper()));
3263 pInfo = &xInfo;
3267 return (*pInfo);
3270 //-----------------------------------------------
3271 void AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()
3273 LOG_RECOVERY("AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() ...")
3275 // SAFE -> ----------------------------------
3276 WriteGuard aWriteLock(m_aLock);
3277 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
3278 aWriteLock.unlock();
3279 // <- SAFE ----------------------------------
3283 css::uno::Reference< css::frame::XFramesSupplier > xDesktop(
3284 xSMGR->createInstance(SERVICENAME_DESKTOP),
3285 css::uno::UNO_QUERY_THROW);
3287 css::uno::Reference< css::container::XIndexAccess > xContainer(
3288 xDesktop->getFrames(),
3289 css::uno::UNO_QUERY_THROW);
3291 sal_Int32 i = 0;
3292 sal_Int32 c = xContainer->getCount();
3294 for (i=0; i<c; ++i)
3296 css::uno::Reference< css::frame::XFrame > xFrame;
3299 xContainer->getByIndex(i) >>= xFrame;
3300 if (!xFrame.is())
3301 continue;
3303 // can happen in multithreaded environments, that frames was removed from the container during this loop runs!
3304 // Ignore it.
3305 catch(const css::lang::IndexOutOfBoundsException&)
3306 { continue; }
3308 // We are interested on visible documents only.
3309 // Note: It's n optional interface .-(
3310 css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(
3311 xFrame->getContainerWindow(),
3312 css::uno::UNO_QUERY);
3313 if (
3314 (!xVisibleCheck.is() ) ||
3315 (!xVisibleCheck->isVisible())
3318 continue;
3321 // extract the model from the frame.
3322 // Ignore "view only" frames, which does not have a model.
3323 css::uno::Reference< css::frame::XController > xController;
3324 css::uno::Reference< css::frame::XModel > xModel;
3326 xController = xFrame->getController();
3327 if (xController.is())
3328 xModel = xController->getModel();
3329 if (!xModel.is())
3330 continue;
3332 // insert model into cache ...
3333 // If the model is already well known inside cache
3334 // it's information set will be updated by asking the
3335 // model again for it's new states.
3336 implts_registerDocument(xModel);
3339 catch(const css::uno::RuntimeException& exRun)
3340 { throw exRun; }
3341 catch(const css::uno::Exception&)
3344 LOG_RECOVERY("... AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()")
3347 //-----------------------------------------------
3348 sal_Bool AutoRecovery::impl_enoughDiscSpace(sal_Int32 nRequiredSpace)
3350 #ifdef SIMULATE_FULL_DISC
3351 return sal_False;
3352 #endif
3354 // In case an error occures and we are not able to retrieve the needed information
3355 // it's better to "disable" the feature ShowErrorOnFullDisc !
3356 // Otherwhise we start a confusing process of error handling ...
3358 sal_uInt64 nFreeSpace = SAL_MAX_UINT64;
3360 ::rtl::OUString sBackupPath(SvtPathOptions().GetBackupPath());
3361 ::osl::VolumeInfo aInfo (VolumeInfoMask_FreeSpace);
3362 ::osl::FileBase::RC aRC = ::osl::Directory::getVolumeInfo(sBackupPath, aInfo);
3364 if (
3365 (aInfo.isValid(VolumeInfoMask_FreeSpace)) &&
3366 (aRC == ::osl::FileBase::E_None )
3369 nFreeSpace = aInfo.getFreeSpace();
3372 sal_uInt64 nFreeMB = (nFreeSpace/1048576);
3373 return (nFreeMB >= (sal_uInt64)nRequiredSpace);
3376 //-----------------------------------------------
3377 void AutoRecovery::impl_showFullDiscError()
3379 static String PLACEHOLDER_PATH = String::CreateFromAscii("%PATH");
3381 String sBtn(FwkResId(STR_FULL_DISC_RETRY_BUTTON));
3382 String sMsg(FwkResId(STR_FULL_DISC_MSG ));
3384 String sBackupURL(SvtPathOptions().GetBackupPath());
3385 INetURLObject aConverter(sBackupURL);
3386 sal_Unicode aDelimiter;
3387 String sBackupPath = aConverter.getFSysPath(INetURLObject::FSYS_DETECT, &aDelimiter);
3388 if (sBackupPath.Len()<1)
3389 sBackupPath = sBackupURL;
3390 sMsg.SearchAndReplace(PLACEHOLDER_PATH, sBackupPath);
3392 ErrorBox dlgError(0, WB_OK, sMsg);
3393 dlgError.SetButtonText(dlgError.GetButtonId(0), sBtn);
3394 dlgError.Execute();
3397 //-----------------------------------------------
3398 void AutoRecovery::impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo ,
3399 ::comphelper::MediaDescriptor& rArgs ,
3400 const css::uno::Reference< css::frame::XFrame >& xNewFrame)
3402 // external well known frame must be preferred (because it was created by ourself
3403 // for loading documents into this frame)!
3404 // But if no frame exists ... we can try to locate it using any frame bound to the provided
3405 // document. Of course we must live without any frame in case the document does not exists at this
3406 // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-)
3407 css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
3408 if (
3409 (!xFrame.is() ) &&
3410 (rInfo.Document.is())
3413 css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
3414 if (xController.is())
3415 xFrame = xController->getFrame();
3418 // Any outside progress must be used ...
3419 // Only if there is no progress, we can create our own one.
3420 css::uno::Reference< css::task::XStatusIndicator > xInternalProgress;
3421 css::uno::Reference< css::task::XStatusIndicator > xExternalProgress = rArgs.getUnpackedValueOrDefault(
3422 ::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(),
3423 css::uno::Reference< css::task::XStatusIndicator >() );
3425 // Normaly a progress is set from outside (e.g. by the CrashSave/Recovery dialog, which uses our dispatch API).
3426 // But for a normal auto save we dont have such "external progress"... because this function is triggered by our own timer then.
3427 // In such case we must create our own progress !
3428 if (
3429 (! xExternalProgress.is()) &&
3430 (xFrame.is() )
3433 css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xFrame, css::uno::UNO_QUERY);
3434 if (xProgressFactory.is())
3435 xInternalProgress = xProgressFactory->createStatusIndicator();
3438 // HACK
3439 // An external provided progress (most given by the CrashSave/Recovery dialog)
3440 // must be preferred. But we know that some application filters query it's own progress instance
3441 // at the frame method Frame::createStatusIndicator().
3442 // So we use a two step mechanism:
3443 // 1) we set the progress inside the MediaDescriptor, which will be provided to the filter
3444 // 2) and we set a special Frame property, which overwrites the normal behaviour of Frame::createStatusIndicator .-)
3445 // But we supress 2) in case we uses an internal progress. Because then it doesnt matter
3446 // if our applications make it wrong. In such case the internal progress resists at the same frame
3447 // and there is no need to forward progress activities to e.g. an outside dialog .-)
3448 if (
3449 (xExternalProgress.is()) &&
3450 (xFrame.is() )
3453 css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
3454 if (xFrameProps.is())
3455 xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(xExternalProgress));
3458 // But inside the MediaDescriptor we must set our own create progress ...
3459 // in case there is not already anothe rprogress set.
3460 rArgs.createItemIfMissing(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(), xInternalProgress);
3463 //-----------------------------------------------
3464 void AutoRecovery::impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo ,
3465 ::comphelper::MediaDescriptor& rArgs ,
3466 const css::uno::Reference< css::frame::XFrame >& xNewFrame)
3468 // external well known frame must be preferred (because it was created by ourself
3469 // for loading documents into this frame)!
3470 // But if no frame exists ... we can try to locate it using any frame bound to the provided
3471 // document. Of course we must live without any frame in case the document does not exists at this
3472 // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-)
3473 css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
3474 if (
3475 (!xFrame.is() ) &&
3476 (rInfo.Document.is())
3479 css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
3480 if (xController.is())
3481 xFrame = xController->getFrame();
3484 // stop progress interception on corresponding frame.
3485 css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
3486 if (xFrameProps.is())
3487 xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(css::uno::Reference< css::task::XStatusIndicator >()));
3489 // forget progress inside list of arguments.
3490 ::comphelper::MediaDescriptor::iterator pArg = rArgs.find(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR());
3491 if (pArg != rArgs.end())
3493 rArgs.erase(pArg);
3494 pArg = rArgs.end();
3498 //-----------------------------------------------
3499 void AutoRecovery::impl_flushALLConfigChanges()
3503 // SAFE ->
3504 ReadGuard aReadLock(m_aLock);
3505 css::uno::Reference< css::uno::XInterface > xRecoveryCfg(m_xRecoveryCFG, css::uno::UNO_QUERY);
3506 aReadLock.unlock();
3507 // <- SAFE
3509 if (xRecoveryCfg.is())
3510 ::comphelper::ConfigurationHelper::flush(xRecoveryCfg);
3512 // SOLAR SAFE ->
3513 ::vos::OGuard aGuard( Application::GetSolarMutex() );
3514 ::utl::ConfigManager* pCfgMgr = ::utl::ConfigManager::GetConfigManager();
3515 if (pCfgMgr)
3516 pCfgMgr->StoreConfigItems();
3518 catch(const css::uno::Exception&)
3522 //-----------------------------------------------
3523 void AutoRecovery::st_impl_removeFile(const ::rtl::OUString& sURL)
3525 if ( ! sURL.getLength())
3526 return;
3530 ::ucbhelper::Content aContent = ::ucbhelper::Content(sURL, css::uno::Reference< css::ucb::XCommandEnvironment >());
3531 aContent.executeCommand(::rtl::OUString::createFromAscii("delete"), css::uno::makeAny(sal_True));
3533 catch(const css::uno::Exception&)
3537 //-----------------------------------------------
3538 void AutoRecovery::st_impl_removeLockFile()
3542 ::rtl::OUString sUserURL;
3543 ::utl::Bootstrap::locateUserInstallation( sUserURL );
3545 ::rtl::OUStringBuffer sLockURLBuf;
3546 sLockURLBuf.append (sUserURL);
3547 sLockURLBuf.appendAscii("/.lock");
3548 ::rtl::OUString sLockURL = sLockURLBuf.makeStringAndClear();
3550 AutoRecovery::st_impl_removeFile(sLockURL);
3552 catch(const css::uno::Exception&)
3556 } // namespace framework