bump product version to 4.1.6.2
[LibreOffice.git] / framework / source / services / autorecovery.cxx
blob2136538c67243d18b27e38a61684492ca0707858
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include "services/autorecovery.hxx"
23 #include <loadenv/loadenv.hxx>
25 #include <loadenv/targethelper.hxx>
26 #include <pattern/frame.hxx>
27 #include <threadhelp/readguard.hxx>
28 #include <threadhelp/writeguard.hxx>
30 #include <classes/resource.hrc>
31 #include <classes/fwkresid.hxx>
32 #include <protocols.h>
33 #include <properties.h>
34 #include <services.h>
36 #include "helper/mischelper.hxx"
38 #include <com/sun/star/ucb/NameClash.hpp>
39 #include <com/sun/star/container/XNameAccess.hpp>
40 #include <com/sun/star/frame/Desktop.hpp>
41 #include <com/sun/star/frame/GlobalEventBroadcaster.hpp>
42 #include <com/sun/star/frame/XLoadable.hpp>
43 #include <com/sun/star/frame/XModel2.hpp>
44 #include <com/sun/star/frame/ModuleManager.hpp>
45 #include <com/sun/star/frame/XTitle.hpp>
46 #include <com/sun/star/frame/XFrame.hpp>
47 #include <com/sun/star/frame/XDispatchProvider.hpp>
48 #include <com/sun/star/frame/DispatchResultState.hpp>
49 #include <com/sun/star/frame/XNotifyingDispatch.hpp>
50 #include <com/sun/star/frame/XController.hpp>
51 #include <com/sun/star/frame/XModel.hpp>
52 #include <com/sun/star/frame/XStorable.hpp>
53 #include <com/sun/star/util/XModifiable.hpp>
54 #include <com/sun/star/util/URLTransformer.hpp>
55 #include <com/sun/star/util/XURLTransformer.hpp>
56 #include <com/sun/star/frame/XDesktop.hpp>
57 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
58 #include <com/sun/star/container/XNameContainer.hpp>
59 #include <com/sun/star/util/XChangesNotifier.hpp>
60 #include <com/sun/star/util/XChangesBatch.hpp>
61 #include <com/sun/star/beans/XPropertySet.hpp>
62 #include <com/sun/star/beans/PropertyAttribute.hpp>
63 #include <com/sun/star/container/XContainerQuery.hpp>
64 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
65 #include <com/sun/star/document/XDocumentRecovery.hpp>
66 #include <com/sun/star/util/XCloseable.hpp>
67 #include <com/sun/star/awt/XWindow2.hpp>
68 #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
70 #include <comphelper/configurationhelper.hxx>
71 #include <comphelper/mediadescriptor.hxx>
72 #include <comphelper/namedvaluecollection.hxx>
73 #include <comphelper/processfactory.hxx>
74 #include <vcl/svapp.hxx>
75 #include <unotools/pathoptions.hxx>
76 #include <tools/diagnose_ex.h>
77 #include <unotools/tempfile.hxx>
78 #include <ucbhelper/content.hxx>
80 #include <osl/time.h>
81 #include <vcl/msgbox.hxx>
82 #include <osl/file.hxx>
83 #include <unotools/bootstrap.hxx>
84 #include <unotools/configmgr.hxx>
85 #include <svl/documentlockfile.hxx>
86 #include <cppuhelper/exc_hlp.hxx>
88 #include <tools/urlobj.hxx>
90 #include <fwkdllapi.h>
92 //_______________________________________________
93 // namespaces
95 using ::com::sun::star::uno::Sequence;
96 using ::com::sun::star::uno::UNO_QUERY;
97 using ::com::sun::star::uno::UNO_QUERY_THROW;
98 using ::com::sun::star::uno::UNO_SET_THROW;
99 using ::com::sun::star::uno::Reference;
100 using ::com::sun::star::uno::Any;
101 using ::com::sun::star::beans::PropertyValue;
102 using ::com::sun::star::container::XEnumeration;
103 using ::com::sun::star::document::XDocumentRecovery;
104 using ::com::sun::star::frame::ModuleManager;
105 using ::com::sun::star::frame::XModel2;
106 using ::com::sun::star::frame::XModel;
107 using ::com::sun::star::frame::XFrame;
108 using ::com::sun::star::frame::XController2;
109 using ::com::sun::star::frame::XLoadable;
110 using ::com::sun::star::frame::XStorable;
111 using ::com::sun::star::lang::XComponent;
114 namespace framework
117 //-----------------------------------------------
118 // recovery.xcu
119 static const char CFG_PACKAGE_RECOVERY[] = "org.openoffice.Office.Recovery/";
120 static const char CFG_ENTRY_RECOVERYLIST[] = "RecoveryList";
121 static const char CFG_PATH_RECOVERYINFO[] = "RecoveryInfo";
122 static const char CFG_ENTRY_CRASHED[] = "Crashed";
123 static const char CFG_ENTRY_SESSIONDATA[] = "SessionData";
125 static const char CFG_ENTRY_AUTOSAVE_ENABLED[] = "AutoSave/Enabled";
126 static const char CFG_ENTRY_AUTOSAVE_TIMEINTERVALL[] = "AutoSave/TimeIntervall"; //sic!
128 static const char CFG_ENTRY_USERAUTOSAVE_ENABLED[] = "AutoSave/UserAutoSaveEnabled";
130 static const char CFG_PATH_AUTOSAVE[] = "AutoSave";
131 static const char CFG_ENTRY_MINSPACE_DOCSAVE[] = "MinSpaceDocSave";
132 static const char CFG_ENTRY_MINSPACE_CONFIGSAVE[] = "MinSpaceConfigSave";
134 static const char CFG_PACKAGE_MODULES[] = "org.openoffice.Setup/Office/Factories";
135 static const char CFG_ENTRY_REALDEFAULTFILTER[] = "ooSetupFactoryActualFilter";
137 static const char CFG_ENTRY_PROP_TEMPURL[] = "TempURL";
138 static const char CFG_ENTRY_PROP_ORIGINALURL[] = "OriginalURL";
139 static const char CFG_ENTRY_PROP_TEMPLATEURL[] = "TemplateURL";
140 static const char CFG_ENTRY_PROP_FACTORYURL[] = "FactoryURL";
141 static const char CFG_ENTRY_PROP_MODULE[] = "Module";
142 static const char CFG_ENTRY_PROP_DOCUMENTSTATE[] = "DocumentState";
143 static const char CFG_ENTRY_PROP_FILTER[] = "Filter";
144 static const char CFG_ENTRY_PROP_TITLE[] = "Title";
145 static const char CFG_ENTRY_PROP_ID[] = "ID";
146 static const char CFG_ENTRY_PROP_VIEWNAMES[] = "ViewNames";
148 static const char FILTER_PROP_TYPE[] = "Type";
149 static const char TYPE_PROP_EXTENSIONS[] = "Extensions";
151 // setup.xcu
152 static const char CFG_ENTRY_PROP_EMPTYDOCUMENTURL[] = "ooSetupFactoryEmptyDocumentURL";
153 static const char CFG_ENTRY_PROP_FACTORYSERVICE[] = "ooSetupFactoryDocumentService";
155 static const char EVENT_ON_NEW[] = "OnNew";
156 static const char EVENT_ON_LOAD[] = "OnLoad";
157 static const char EVENT_ON_UNLOAD[] = "OnUnload";
158 static const char EVENT_ON_MODIFYCHANGED[] = "OnModifyChanged";
159 static const char EVENT_ON_SAVE[] = "OnSave";
160 static const char EVENT_ON_SAVEAS[] = "OnSaveAs";
161 static const char EVENT_ON_SAVETO[] = "OnCopyTo";
162 static const char EVENT_ON_SAVEDONE[] = "OnSaveDone";
163 static const char EVENT_ON_SAVEASDONE[] = "OnSaveAsDone";
164 static const char EVENT_ON_SAVETODONE[] = "OnCopyToDone";
165 static const char EVENT_ON_SAVEFAILED[] = "OnSaveFailed";
166 static const char EVENT_ON_SAVEASFAILED[] = "OnSaveAsFailed";
167 static const char EVENT_ON_SAVETOFAILED[] = "OnCopyToFailed";
169 static const char RECOVERY_ITEM_BASE_IDENTIFIER[] = "recovery_item_";
171 static const char CMD_PROTOCOL[] = "vnd.sun.star.autorecovery:";
173 static const char CMD_DO_AUTO_SAVE[] = "/doAutoSave"; // force AutoSave ignoring the AutoSave timer
174 static const char CMD_DO_PREPARE_EMERGENCY_SAVE[] = "/doPrepareEmergencySave"; // prepare the office for the following EmergencySave step (hide windows etcpp.)
175 static const char CMD_DO_EMERGENCY_SAVE[] = "/doEmergencySave"; // do EmergencySave on crash
176 static const char CMD_DO_RECOVERY[] = "/doAutoRecovery"; // recover all crashed documents
177 static const char CMD_DO_ENTRY_BACKUP[] = "/doEntryBackup"; // try to store a temp or original file to a user defined location
178 static const char CMD_DO_ENTRY_CLEANUP[] = "/doEntryCleanUp"; // remove the specified entry from the recovery cache
179 static const char CMD_DO_SESSION_SAVE[] = "/doSessionSave"; // save all open documents if e.g. a window manager closes an user session
180 static const char CMD_DO_SESSION_QUIET_QUIT[] = "/doSessionQuietQuit"; // let the current session be quietly closed ( the saving should be done using doSessionSave previously ) if e.g. a window manager closes an user session
181 static const char CMD_DO_SESSION_RESTORE[] = "/doSessionRestore"; // restore a saved user session from disc
182 static const char CMD_DO_DISABLE_RECOVERY[] = "/disableRecovery"; // disable recovery and auto save (!) temp. for this office session
183 static const char CMD_DO_SET_AUTOSAVE_STATE[] = "/setAutoSaveState"; // disable/enable auto save (not crash save) for this office session
185 static const char REFERRER_USER[] = "private:user";
187 static const char PROP_DISPATCH_ASYNCHRON[] = "DispatchAsynchron";
188 static const char PROP_PROGRESS[] = "StatusIndicator";
189 static const char PROP_SAVEPATH[] = "SavePath";
190 static const char PROP_ENTRY_ID[] = "EntryID";
191 static const char PROP_AUTOSAVE_STATE[] = "AutoSaveState";
193 static const char OPERATION_START[] = "start";
194 static const char OPERATION_STOP[] = "stop";
195 static const char OPERATION_UPDATE[] = "update";
197 static const sal_Int32 MIN_DISCSPACE_DOCSAVE = 5; // [MB]
198 static const sal_Int32 MIN_DISCSPACE_CONFIGSAVE = 1; // [MB]
199 static const sal_Int32 RETRY_STORE_ON_FULL_DISC_FOREVER = 300; // not forever ... but often enough .-)
200 static const sal_Int32 RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL = 3; // in case FULL DISC does not seam the real problem
201 static const sal_Int32 GIVE_UP_RETRY = 1; // in case FULL DISC does not seam the real problem
203 #define SAVE_IN_PROGRESS sal_True
204 #define SAVE_FINISHED sal_False
206 #define LOCK_FOR_CACHE_ADD_REMOVE sal_True
207 #define LOCK_FOR_CACHE_USE sal_False
209 #define MIN_TIME_FOR_USER_IDLE 10000 // 10s user idle
211 // enable the following defines in case you whish to simulate a full disc for debug purposes .-)
213 // this define throws everytime a document is stored or a configuration change
214 // should be flushed an exception ... so the special error handler for this scenario is triggered
215 // #define TRIGGER_FULL_DISC_CHECK
217 // force "return sal_False" for the method impl_enoughDiscSpace().
218 // #define SIMULATE_FULL_DISC
220 //-----------------------------------------------
221 // #define ENABLE_RECOVERY_LOGGING
222 #undef ENABLE_RECOVERY_LOGGING
223 #ifdef ENABLE_RECOVERY_LOGGING
224 #define LOGFILE_RECOVERY "recovery.log"
226 #define LOG_RECOVERY(MSG) \
228 WRITE_LOGFILE(LOGFILE_RECOVERY, MSG) \
229 WRITE_LOGFILE(LOGFILE_RECOVERY, "\n") \
231 #else
232 #undef LOGFILE_RECOVERY
233 #define LOG_RECOVERY(MSG)
234 #endif
236 //-----------------------------------------------
237 class CacheLockGuard
239 private:
241 // holds the outside calli alive, so it's shared resources
242 // are valid everytimes
243 css::uno::Reference< css::uno::XInterface > m_xOwner;
245 // mutex shared with outside calli !
246 LockHelper& m_rSharedMutex;
248 // this variable knows the state of the "cache lock"
249 sal_Int32& m_rCacheLock;
251 // to prevent increasing/decreasing of m_rCacheLock more then ones
252 // we must know if THIS guard has an actual lock set there !
253 sal_Bool m_bLockedByThisGuard;
255 public:
257 CacheLockGuard(AutoRecovery* pOwner ,
258 LockHelper& rMutex ,
259 sal_Int32& rCacheLock ,
260 sal_Bool bLockForAddRemoveVectorItems);
261 ~CacheLockGuard();
263 void lock(sal_Bool bLockForAddRemoveVectorItems);
264 void unlock();
267 //-----------------------------------------------
268 CacheLockGuard::CacheLockGuard(AutoRecovery* pOwner ,
269 LockHelper& rMutex ,
270 sal_Int32& rCacheLock ,
271 sal_Bool bLockForAddRemoveVectorItems)
272 : m_xOwner (static_cast< css::frame::XDispatch* >(pOwner))
273 , m_rSharedMutex (rMutex )
274 , m_rCacheLock (rCacheLock )
275 , m_bLockedByThisGuard(sal_False )
277 lock(bLockForAddRemoveVectorItems);
280 //-----------------------------------------------
281 CacheLockGuard::~CacheLockGuard()
283 unlock();
284 m_xOwner.clear();
287 //-----------------------------------------------
288 void CacheLockGuard::lock(sal_Bool bLockForAddRemoveVectorItems)
290 // SAFE -> ----------------------------------
291 WriteGuard aWriteLock(m_rSharedMutex);
293 if (m_bLockedByThisGuard)
294 return;
296 // This cache lock is needed only to prevent us from removing/adding
297 // items from/into the recovery cache ... during it's used at another code place
298 // for iterating .-)
300 // Modifying of item properties is allowed and sometimes needed!
301 // So we should detect only the dangerous state of concurrent add/remove
302 // requests and throw an exception then ... which can of course break the whole
303 // operation. On the other side a crash reasoned by an invalid stl iterator
304 // will have the same effect .-)
306 if (
307 (m_rCacheLock > 0 ) &&
308 (bLockForAddRemoveVectorItems)
311 OSL_FAIL("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp.");
312 throw css::uno::RuntimeException(
313 OUString("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp."),
314 m_xOwner);
317 ++m_rCacheLock;
318 m_bLockedByThisGuard = sal_True;
320 aWriteLock.unlock();
321 // <- SAFE ----------------------------------
324 //-----------------------------------------------
325 void CacheLockGuard::unlock()
327 // SAFE -> ----------------------------------
328 WriteGuard aWriteLock(m_rSharedMutex);
330 if ( ! m_bLockedByThisGuard)
331 return;
333 --m_rCacheLock;
334 m_bLockedByThisGuard = sal_False;
336 if (m_rCacheLock < 0)
338 OSL_FAIL("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)");
339 throw css::uno::RuntimeException(
340 OUString("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)"),
341 m_xOwner);
343 aWriteLock.unlock();
344 // <- SAFE ----------------------------------
347 //-----------------------------------------------
348 DispatchParams::DispatchParams()
349 : m_nWorkingEntryID(-1)
353 //-----------------------------------------------
354 DispatchParams::DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs ,
355 const css::uno::Reference< css::uno::XInterface >& xOwner)
357 m_nWorkingEntryID = lArgs.getUnpackedValueOrDefault(PROP_ENTRY_ID, (sal_Int32)-1 );
358 m_xProgress = lArgs.getUnpackedValueOrDefault(PROP_PROGRESS, css::uno::Reference< css::task::XStatusIndicator >());
359 m_sSavePath = lArgs.getUnpackedValueOrDefault(PROP_SAVEPATH, OUString() );
360 m_xHoldRefForAsyncOpAlive = xOwner;
363 //-----------------------------------------------
364 DispatchParams::DispatchParams(const DispatchParams& rCopy)
366 m_xProgress = rCopy.m_xProgress;
367 m_sSavePath = rCopy.m_sSavePath;
368 m_nWorkingEntryID = rCopy.m_nWorkingEntryID;
369 m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive;
372 //-----------------------------------------------
373 DispatchParams::~DispatchParams()
376 //-----------------------------------------------
377 DispatchParams& DispatchParams::operator=(const DispatchParams& rCopy)
379 m_xProgress = rCopy.m_xProgress;
380 m_sSavePath = rCopy.m_sSavePath;
381 m_nWorkingEntryID = rCopy.m_nWorkingEntryID;
382 m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive;
383 return *this;
386 //-----------------------------------------------
387 void DispatchParams::forget()
389 m_sSavePath = OUString();
390 m_nWorkingEntryID = -1;
391 m_xProgress.clear();
392 m_xHoldRefForAsyncOpAlive.clear();
395 //-----------------------------------------------
396 DEFINE_XINTERFACE_10(AutoRecovery ,
397 OWeakObject ,
398 DIRECT_INTERFACE (css::lang::XTypeProvider ),
399 DIRECT_INTERFACE (css::lang::XServiceInfo ),
400 DIRECT_INTERFACE (css::frame::XDispatch ),
401 DIRECT_INTERFACE (css::beans::XMultiPropertySet ),
402 DIRECT_INTERFACE (css::beans::XFastPropertySet ),
403 DIRECT_INTERFACE (css::beans::XPropertySet ),
404 DIRECT_INTERFACE (css::document::XEventListener ),
405 DIRECT_INTERFACE (css::util::XChangesListener ),
406 DIRECT_INTERFACE (css::util::XModifyListener ),
407 DERIVED_INTERFACE(css::lang::XEventListener, css::document::XEventListener))
409 //-----------------------------------------------
410 DEFINE_XTYPEPROVIDER_6(AutoRecovery ,
411 css::lang::XTypeProvider ,
412 css::lang::XServiceInfo ,
413 css::frame::XDispatch ,
414 css::beans::XMultiPropertySet,
415 css::beans::XFastPropertySet ,
416 css::beans::XPropertySet )
418 //-----------------------------------------------
419 DEFINE_XSERVICEINFO_ONEINSTANCESERVICE(AutoRecovery ,
420 ::cppu::OWeakObject ,
421 "com.sun.star.frame.AutoRecovery",
422 IMPLEMENTATIONNAME_AUTORECOVERY)
424 //-----------------------------------------------
425 DEFINE_INIT_SERVICE(
426 AutoRecovery,
428 /*Attention
429 I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance()
430 to create a new instance of this class by our own supported service factory.
431 see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further information!
434 // read configuration to know if autosave/recovery is on/off etcpp...
435 implts_readConfig();
437 implts_startListening();
439 // establish callback for our internal used timer.
440 // Note: Its only active, if the timer will be started ...
441 m_aTimer.SetTimeoutHdl(LINK(this, AutoRecovery, implts_timerExpired));
445 //-----------------------------------------------
446 AutoRecovery::AutoRecovery(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR)
447 : ThreadHelpBase (&Application::GetSolarMutex() )
448 , ::cppu::OBroadcastHelper ( m_aLock.getShareableOslMutex() )
449 , ::cppu::OPropertySetHelper( *(static_cast< ::cppu::OBroadcastHelper* >(this)) )
450 , ::cppu::OWeakObject ( )
451 , m_xSMGR (xSMGR )
452 , m_bListenForDocEvents (sal_False )
453 , m_bListenForConfigChanges (sal_False )
454 , m_nAutoSaveTimeIntervall (0 )
455 , m_eJob (AutoRecovery::E_NO_JOB )
456 , m_aAsyncDispatcher ( LINK( this, AutoRecovery, implts_asyncDispatch ) )
457 , m_eTimerType (E_DONT_START_TIMER )
458 , m_nIdPool (0 )
459 , m_lListener (m_aLock.getShareableOslMutex() )
460 , m_nDocCacheLock (0 )
461 , m_nMinSpaceDocSave (MIN_DISCSPACE_DOCSAVE )
462 , m_nMinSpaceConfigSave (MIN_DISCSPACE_CONFIGSAVE )
464 #if OSL_DEBUG_LEVEL > 1
465 , m_dbg_bMakeItFaster (sal_False )
466 #endif
470 //-----------------------------------------------
471 AutoRecovery::~AutoRecovery()
473 implts_stopTimer();
476 //-----------------------------------------------
477 void SAL_CALL AutoRecovery::dispatch(const css::util::URL& aURL ,
478 const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
479 throw(css::uno::RuntimeException)
481 LOG_RECOVERY("AutoRecovery::dispatch() starts ...")
482 LOG_RECOVERY(U2B(aURL.Complete).getStr())
484 // valid request ?
485 sal_Int32 eNewJob = AutoRecovery::implst_classifyJob(aURL);
486 if (eNewJob == AutoRecovery::E_NO_JOB)
487 return;
489 // SAFE -> ----------------------------------
490 WriteGuard aWriteLock(m_aLock);
492 // still running operation ... ignoring AUTO_SAVE.
493 // All other requests has higher prio!
494 if (
495 ( m_eJob != AutoRecovery::E_NO_JOB ) &&
496 ((m_eJob & AutoRecovery::E_AUTO_SAVE ) != AutoRecovery::E_AUTO_SAVE)
499 LOG_WARNING("AutoRecovery::dispatch()", "There is already an asynchronous dispatch() running. New request will be ignored!")
500 return;
503 ::comphelper::SequenceAsHashMap lArgs(lArguments);
505 // check if somewhere wish to disable recovery temp. for this office session
506 // This can be done immediately ... must not been done asynchronous.
507 if ((eNewJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
509 // it's important to set a flag internaly, so AutoRecovery will be supressed - even if it's requested.
510 m_eJob |= eNewJob;
511 implts_stopTimer();
512 implts_stopListening();
513 return;
516 // disable/enable AutoSave for this office session only
517 // independend from the configuration entry.
518 if ((eNewJob & AutoRecovery::E_SET_AUTOSAVE_STATE) == AutoRecovery::E_SET_AUTOSAVE_STATE)
520 sal_Bool bOn = lArgs.getUnpackedValueOrDefault(PROP_AUTOSAVE_STATE, (sal_Bool)sal_True);
521 if (bOn)
523 // dont enable AutoSave hardly !
524 // reload configuration to know the current state.
525 implts_readAutoSaveConfig();
526 implts_updateTimer();
527 // can it happen that might be the listener was stopped ? .-)
528 // make sure it runs always ... even if AutoSave itself was disabled temporarly.
529 implts_startListening();
531 else
533 implts_stopTimer();
534 m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
535 m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
537 return;
540 m_eJob |= eNewJob;
542 sal_Bool bAsync = lArgs.getUnpackedValueOrDefault(PROP_DISPATCH_ASYNCHRON, (sal_Bool)sal_False);
543 DispatchParams aParams (lArgs, static_cast< css::frame::XDispatch* >(this));
545 // Hold this instance alive till the asynchronous operation will be finished.
546 if (bAsync)
547 m_aDispatchParams = aParams;
549 aWriteLock.unlock();
550 // <- SAFE ----------------------------------
552 if (bAsync)
553 m_aAsyncDispatcher.Post(0);
554 else
555 implts_dispatch(aParams);
558 void AutoRecovery::ListenerInformer::start()
560 m_rRecovery.implts_informListener(m_eJob,
561 AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_START, NULL));
564 void AutoRecovery::ListenerInformer::stop()
566 if (m_bStopped)
567 return;
568 m_rRecovery.implts_informListener(m_eJob,
569 AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_STOP, NULL));
570 m_bStopped = true;
573 //-----------------------------------------------
574 void AutoRecovery::implts_dispatch(const DispatchParams& aParams)
576 // SAFE -> ----------------------------------
577 WriteGuard aWriteLock(m_aLock);
578 sal_Int32 eJob = m_eJob;
579 aWriteLock.unlock();
580 // <- SAFE ----------------------------------
582 // in case a new dispatch overwrites a may ba active AutoSave session
583 // we must restore this session later. see below ...
584 sal_Bool bWasAutoSaveActive = ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE);
585 sal_Bool bWasUserAutoSaveActive =
586 ((eJob & AutoRecovery::E_USER_AUTO_SAVE) == AutoRecovery::E_USER_AUTO_SAVE);
588 // On the other side it make no sense to reactivate the AutoSave operation
589 // if the new dispatch indicates a final decision ...
590 // E.g. an EmergencySave/SessionSave indicates the end of life of the current office session.
591 // It make no sense to reactivate an AutoSave then.
592 // But a Recovery or SessionRestore should reactivate a may be already active AutoSave.
593 sal_Bool bAllowAutoSaveReactivation = sal_True;
595 implts_stopTimer();
596 implts_stopListening();
598 ListenerInformer aListenerInformer(*this, eJob);
599 aListenerInformer.start();
603 // Auto save is called from our internal timer ... not via dispatch() API !
604 // else
605 if (
606 ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE) &&
607 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY ) != AutoRecovery::E_DISABLE_AUTORECOVERY )
610 LOG_RECOVERY("... prepare emergency save ...")
611 bAllowAutoSaveReactivation = sal_False;
612 implts_prepareEmergencySave();
614 else
615 if (
616 ((eJob & AutoRecovery::E_EMERGENCY_SAVE ) == AutoRecovery::E_EMERGENCY_SAVE ) &&
617 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
620 LOG_RECOVERY("... do emergency save ...")
621 bAllowAutoSaveReactivation = sal_False;
622 implts_doEmergencySave(aParams);
624 else
625 if (
626 ((eJob & AutoRecovery::E_RECOVERY ) == AutoRecovery::E_RECOVERY ) &&
627 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
630 LOG_RECOVERY("... do recovery ...")
631 implts_doRecovery(aParams);
633 else
634 if (
635 ((eJob & AutoRecovery::E_SESSION_SAVE ) == AutoRecovery::E_SESSION_SAVE ) &&
636 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
639 LOG_RECOVERY("... do session save ...")
640 bAllowAutoSaveReactivation = sal_False;
641 implts_doSessionSave(aParams);
643 else
644 if (
645 ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT ) == AutoRecovery::E_SESSION_QUIET_QUIT ) &&
646 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
649 LOG_RECOVERY("... do session quiet quit ...")
650 bAllowAutoSaveReactivation = sal_False;
651 implts_doSessionQuietQuit(aParams);
653 else
654 if (
655 ((eJob & AutoRecovery::E_SESSION_RESTORE ) == AutoRecovery::E_SESSION_RESTORE ) &&
656 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
659 LOG_RECOVERY("... do session restore ...")
660 implts_doSessionRestore(aParams);
662 else
663 if (
664 ((eJob & AutoRecovery::E_ENTRY_BACKUP ) == AutoRecovery::E_ENTRY_BACKUP ) &&
665 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
667 implts_backupWorkingEntry(aParams);
668 else
669 if (
670 ((eJob & AutoRecovery::E_ENTRY_CLEANUP ) == AutoRecovery::E_ENTRY_CLEANUP ) &&
671 ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
673 implts_cleanUpWorkingEntry(aParams);
675 catch(const css::uno::RuntimeException&)
677 throw;
679 catch(const css::uno::Exception&)
681 // TODO better error handling
684 aListenerInformer.stop();
686 // SAFE -> ----------------------------------
687 aWriteLock.lock();
688 m_eJob = E_NO_JOB;
689 if (
690 (bAllowAutoSaveReactivation) &&
691 (bWasAutoSaveActive )
694 m_eJob |= AutoRecovery::E_AUTO_SAVE;
696 if (bWasUserAutoSaveActive)
698 m_eJob |= AutoRecovery::E_USER_AUTO_SAVE;
702 aWriteLock.unlock();
703 // <- SAFE ----------------------------------
705 // depends on bAllowAutoSaveReactivation implicitly by looking on m_eJob=E_AUTO_SAVE! see before ...
706 implts_updateTimer();
708 if (bAllowAutoSaveReactivation)
709 implts_startListening();
712 //-----------------------------------------------
713 void SAL_CALL AutoRecovery::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
714 const css::util::URL& aURL )
715 throw(css::uno::RuntimeException)
717 if (!xListener.is())
718 throw css::uno::RuntimeException(OUString("Invalid listener reference."), static_cast< css::frame::XDispatch* >(this));
719 // container is threadsafe by using a shared mutex!
720 m_lListener.addInterface(aURL.Complete, xListener);
722 // REENTRANT !? -> --------------------------------
723 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
725 // THREAD SAFE -> ----------------------------------
726 ReadGuard aReadLock(m_aLock);
728 AutoRecovery::TDocumentList::iterator pIt;
729 for( pIt = m_lDocCache.begin();
730 pIt != m_lDocCache.end() ;
731 ++pIt )
733 AutoRecovery::TDocumentInfo& rInfo = *pIt;
734 css::frame::FeatureStateEvent aEvent = AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_UPDATE, &rInfo);
736 // <- SAFE ------------------------------
737 aReadLock.unlock();
738 xListener->statusChanged(aEvent);
739 aReadLock.lock();
740 // SAFE -> ------------------------------
743 aReadLock.unlock();
744 // <- SAFE ----------------------------------
747 //-----------------------------------------------
748 void SAL_CALL AutoRecovery::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
749 const css::util::URL& aURL )
750 throw(css::uno::RuntimeException)
752 if (!xListener.is())
753 throw css::uno::RuntimeException(OUString("Invalid listener reference."), static_cast< css::frame::XDispatch* >(this));
754 // container is threadsafe by using a shared mutex!
755 m_lListener.removeInterface(aURL.Complete, xListener);
758 //-----------------------------------------------
759 void SAL_CALL AutoRecovery::notifyEvent(const css::document::EventObject& aEvent)
760 throw(css::uno::RuntimeException)
762 css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
764 // new document => put it into the internal list
765 if (
766 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_NEW))) ||
767 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_LOAD)))
770 implts_registerDocument(xDocument);
772 // document modified => set its modify state new (means modified against the original file!)
773 else if ( aEvent.EventName == EVENT_ON_MODIFYCHANGED )
775 implts_updateModifiedState(xDocument);
777 /* at least one document starts saving process =>
778 Our application code isnt ready for multiple save requests
779 at the same time. So we have to supress our AutoSave feature
780 for the moment, till this other save requests will be finished.
782 else if (
783 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVE))) ||
784 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEAS))) ||
785 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVETO)))
788 implts_updateDocumentUsedForSavingState(xDocument, SAVE_IN_PROGRESS);
790 // document saved => remove tmp. files - but hold config entries alive!
791 else if (
792 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEDONE))) ||
793 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEASDONE)))
796 implts_markDocumentAsSaved(xDocument);
797 implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
799 /* document saved as copy => mark it as "non used by concurrent save operation".
800 so we can try to create a backup copy if next time AutoSave is started too.
801 Dont remove temp. files or change the modified state of the document!
802 It was not realy saved to the original file ...
804 else if ( aEvent.EventName == EVENT_ON_SAVETODONE )
806 implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
808 // If saving of a document failed by an error ... we have to save this document
809 // by ourself next time AutoSave or EmergencySave is triggered.
810 // But we can reset the state "used for other save requests". Otherwhise
811 // these documents will never be saved!
812 else if (
813 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEFAILED))) ||
814 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVEASFAILED))) ||
815 (aEvent.EventName.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(EVENT_ON_SAVETOFAILED)))
818 implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
820 // document closed => remove temp. files and configuration entries
821 else if ( aEvent.EventName == EVENT_ON_UNLOAD )
823 implts_deregisterDocument(xDocument, sal_True); // sal_True => stop listening for disposing() !
827 //-----------------------------------------------
828 void SAL_CALL AutoRecovery::changesOccurred(const css::util::ChangesEvent& aEvent)
829 throw(css::uno::RuntimeException)
831 const css::uno::Sequence< css::util::ElementChange > lChanges (aEvent.Changes);
832 const css::util::ElementChange* pChanges = lChanges.getConstArray();
834 sal_Int32 c = lChanges.getLength();
835 sal_Int32 i = 0;
837 // SAFE -> ----------------------------------
838 WriteGuard aWriteLock(m_aLock);
840 // Changes of the configuration must be ignored if AutoSave/Recovery was disabled for this
841 // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless"
842 // was set.
843 if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
844 return;
846 for (i=0; i<c; ++i)
848 OUString sPath;
849 pChanges[i].Accessor >>= sPath;
851 if ( sPath == CFG_ENTRY_AUTOSAVE_ENABLED )
853 sal_Bool bEnabled = sal_False;
854 if (pChanges[i].Element >>= bEnabled)
856 if (bEnabled)
858 m_eJob |= AutoRecovery::E_AUTO_SAVE;
859 m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
861 else
863 m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
864 m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
868 else
869 if ( sPath == CFG_ENTRY_AUTOSAVE_TIMEINTERVALL )
870 pChanges[i].Element >>= m_nAutoSaveTimeIntervall;
873 aWriteLock.unlock();
874 // <- SAFE ----------------------------------
876 // Note: This call stops the timer and starts it again.
877 // But it checks the different timer states internaly and
878 // may be supress the restart!
879 implts_updateTimer();
882 //-----------------------------------------------
883 void SAL_CALL AutoRecovery::modified(const css::lang::EventObject& aEvent)
884 throw(css::uno::RuntimeException)
886 css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
887 if (! xDocument.is())
888 return;
890 implts_markDocumentModifiedAgainstLastBackup(xDocument);
893 //-----------------------------------------------
894 void SAL_CALL AutoRecovery::disposing(const css::lang::EventObject& aEvent)
895 throw(css::uno::RuntimeException)
897 // SAFE -> ----------------------------------
898 WriteGuard aWriteLock(m_aLock);
900 if (aEvent.Source == m_xNewDocBroadcaster)
902 m_xNewDocBroadcaster.clear();
903 return;
906 if (aEvent.Source == m_xRecoveryCFG)
908 m_xRecoveryCFG.clear();
909 return;
912 // dispose from one of our cached documents ?
913 // Normaly they should send a OnUnload message ...
914 // But some stacktraces shows another possible use case .-)
915 css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
916 if (xDocument.is())
918 implts_deregisterDocument(xDocument, sal_False); // sal_False => dont call removeEventListener() .. because it's not needed here
919 return;
922 // <- SAFE ----------------------------------
925 //-----------------------------------------------
926 css::uno::Reference< css::container::XNameAccess > AutoRecovery::implts_openConfig()
928 // SAFE -> ----------------------------------
929 WriteGuard aWriteLock(m_aLock);
931 if (m_xRecoveryCFG.is())
932 return m_xRecoveryCFG;
933 css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getComponentContext(m_xSMGR);
935 aWriteLock.unlock();
936 // <- SAFE ----------------------------------
938 OUString sCFG_PACKAGE_RECOVERY(RTL_CONSTASCII_USTRINGPARAM(CFG_PACKAGE_RECOVERY));
939 // throws a RuntimeException if an error occure!
940 css::uno::Reference< css::container::XNameAccess > xCFG(
941 ::comphelper::ConfigurationHelper::openConfig(xContext, sCFG_PACKAGE_RECOVERY, ::comphelper::ConfigurationHelper::E_STANDARD),
942 css::uno::UNO_QUERY);
944 sal_Int32 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
945 sal_Int32 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
949 OUString sCFG_PATH_AUTOSAVE(CFG_PATH_AUTOSAVE);
950 ::comphelper::ConfigurationHelper::readDirectKey(xContext,
951 sCFG_PACKAGE_RECOVERY,
952 sCFG_PATH_AUTOSAVE,
953 OUString(CFG_ENTRY_MINSPACE_DOCSAVE),
954 ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceDocSave;
956 ::comphelper::ConfigurationHelper::readDirectKey(xContext,
957 sCFG_PACKAGE_RECOVERY,
958 sCFG_PATH_AUTOSAVE,
959 OUString(CFG_ENTRY_MINSPACE_CONFIGSAVE),
960 ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceConfigSave;
962 catch(const css::uno::Exception&)
964 // These config keys are not sooooo important, that
965 // we are interested on errors here realy .-)
966 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
967 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
970 // SAFE -> ----------------------------------
971 aWriteLock.lock();
972 m_xRecoveryCFG = xCFG;
973 m_nMinSpaceDocSave = nMinSpaceDocSave;
974 m_nMinSpaceConfigSave = nMinSpaceConfigSave;
975 aWriteLock.unlock();
976 // <- SAFE ----------------------------------
978 return xCFG;
981 //-----------------------------------------------
982 void AutoRecovery::implts_readAutoSaveConfig()
984 css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY);
986 // AutoSave [bool]
987 sal_Bool bEnabled = sal_False;
988 xCommonRegistry->getByHierarchicalName(OUString(CFG_ENTRY_AUTOSAVE_ENABLED)) >>= bEnabled;
990 // UserAutoSave [bool]
991 sal_Bool bUserEnabled = sal_False;
992 xCommonRegistry->getByHierarchicalName(OUString(CFG_ENTRY_USERAUTOSAVE_ENABLED)) >>= bUserEnabled;
994 // SAFE -> ------------------------------
995 WriteGuard aWriteLock(m_aLock);
996 if (bEnabled)
998 m_eJob |= AutoRecovery::E_AUTO_SAVE;
999 m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
1001 if (bUserEnabled)
1003 m_eJob |= AutoRecovery::E_USER_AUTO_SAVE;
1005 else
1007 m_eJob &= ~AutoRecovery::E_USER_AUTO_SAVE;
1010 else
1012 m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
1013 m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
1015 aWriteLock.unlock();
1016 // <- SAFE ------------------------------
1018 // AutoSaveTimeIntervall [int] in min
1019 sal_Int32 nTimeIntervall = 15;
1020 xCommonRegistry->getByHierarchicalName(OUString(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL)) >>= nTimeIntervall;
1022 // SAFE -> ----------------------------------
1023 aWriteLock.lock();
1024 m_nAutoSaveTimeIntervall = nTimeIntervall;
1025 aWriteLock.unlock();
1026 // <- SAFE ----------------------------------
1029 //-----------------------------------------------
1030 void AutoRecovery::implts_readConfig()
1032 implts_readAutoSaveConfig();
1034 css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY);
1036 // REENTRANT -> --------------------------------
1037 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
1039 // THREADSAFE -> -------------------------------
1040 WriteGuard aWriteLock(m_aLock);
1041 // reset current cache load cache
1042 m_lDocCache.clear();
1043 m_nIdPool = 0;
1044 aWriteLock.unlock();
1045 // <- THREADSAFE -------------------------------
1047 aCacheLock.unlock();
1048 // <- REENTRANT --------------------------------
1050 css::uno::Any aValue;
1052 // RecoveryList [set]
1053 aValue = xCommonRegistry->getByHierarchicalName(OUString(CFG_ENTRY_RECOVERYLIST));
1054 css::uno::Reference< css::container::XNameAccess > xList;
1055 aValue >>= xList;
1056 if (xList.is())
1058 const OUString sRECOVERY_ITEM_BASE_IDENTIFIER(RECOVERY_ITEM_BASE_IDENTIFIER);
1059 const css::uno::Sequence< OUString > lItems = xList->getElementNames();
1060 const OUString* pItems = lItems.getConstArray();
1061 sal_Int32 c = lItems.getLength();
1062 sal_Int32 i = 0;
1064 // REENTRANT -> --------------------------
1065 aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
1067 for (i=0; i<c; ++i)
1069 css::uno::Reference< css::beans::XPropertySet > xItem;
1070 xList->getByName(pItems[i]) >>= xItem;
1071 if (!xItem.is())
1072 continue;
1074 AutoRecovery::TDocumentInfo aInfo;
1075 aInfo.NewTempURL = OUString();
1076 aInfo.Document = css::uno::Reference< css::frame::XModel >();
1077 xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_ORIGINALURL)) >>= aInfo.OrgURL ;
1078 xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_TEMPURL)) >>= aInfo.OldTempURL ;
1079 xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_TEMPLATEURL)) >>= aInfo.TemplateURL ;
1080 xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_FILTER)) >>= aInfo.RealFilter ;
1081 xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_DOCUMENTSTATE)) >>= aInfo.DocumentState;
1082 xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_MODULE)) >>= aInfo.AppModule;
1083 xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_TITLE)) >>= aInfo.Title;
1084 xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_VIEWNAMES)) >>= aInfo.ViewNames;
1085 implts_specifyAppModuleAndFactory(aInfo);
1086 implts_specifyDefaultFilterAndExtension(aInfo);
1088 if (pItems[i].indexOf(sRECOVERY_ITEM_BASE_IDENTIFIER)==0)
1090 OUString sID = pItems[i].copy(sRECOVERY_ITEM_BASE_IDENTIFIER.getLength());
1091 aInfo.ID = sID.toInt32();
1092 // SAFE -> ----------------------
1093 aWriteLock.lock();
1094 if (aInfo.ID > m_nIdPool)
1096 m_nIdPool = aInfo.ID+1;
1097 LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_readConfig()\nOverflow of IDPool detected!")
1099 aWriteLock.unlock();
1100 // <- SAFE ----------------------
1102 #ifdef ENABLE_WARNINGS
1103 else
1104 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 .-)")
1105 #endif
1107 // THREADSAFE -> --------------------------
1108 aWriteLock.lock();
1109 m_lDocCache.push_back(aInfo);
1110 aWriteLock.unlock();
1111 // <- THREADSAFE --------------------------
1114 aCacheLock.unlock();
1115 // <- REENTRANT --------------------------
1118 implts_updateTimer();
1121 //-----------------------------------------------
1122 void AutoRecovery::implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo)
1124 if (rInfo.AppModule.isEmpty())
1126 throw css::uno::RuntimeException(
1127 OUString("Cant find out the default filter and its extension, if no application module is known!"),
1128 static_cast< css::frame::XDispatch* >(this));
1131 // SAFE -> ----------------------------------
1132 ReadGuard aReadLock(m_aLock);
1133 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1134 css::uno::Reference< css::container::XNameAccess> xCFG = m_xModuleCFG;
1135 aReadLock.unlock();
1136 // <- SAFE ----------------------------------
1140 if (! xCFG.is())
1142 // open module config on demand and cache the update access
1143 xCFG = css::uno::Reference< css::container::XNameAccess >(
1144 ::comphelper::ConfigurationHelper::openConfig(comphelper::getComponentContext(xSMGR), OUString(CFG_PACKAGE_MODULES),
1145 ::comphelper::ConfigurationHelper::E_STANDARD),
1146 css::uno::UNO_QUERY_THROW);
1148 // SAFE -> ----------------------------------
1149 WriteGuard aWriteLock(m_aLock);
1150 m_xModuleCFG = xCFG;
1151 aWriteLock.unlock();
1152 // <- SAFE ----------------------------------
1155 css::uno::Reference< css::container::XNameAccess > xModuleProps(
1156 xCFG->getByName(rInfo.AppModule),
1157 css::uno::UNO_QUERY_THROW);
1159 xModuleProps->getByName(OUString(CFG_ENTRY_REALDEFAULTFILTER)) >>= rInfo.DefaultFilter;
1161 css::uno::Reference< css::container::XNameAccess > xFilterCFG(xSMGR->createInstance(SERVICENAME_FILTERFACTORY), css::uno::UNO_QUERY_THROW);
1162 css::uno::Reference< css::container::XNameAccess > xTypeCFG (xSMGR->createInstance(SERVICENAME_TYPEDETECTION), css::uno::UNO_QUERY_THROW);
1164 ::comphelper::SequenceAsHashMap lFilterProps (xFilterCFG->getByName(rInfo.DefaultFilter));
1165 OUString sTypeRegistration = lFilterProps.getUnpackedValueOrDefault(OUString(FILTER_PROP_TYPE), OUString());
1166 ::comphelper::SequenceAsHashMap lTypeProps (xTypeCFG->getByName(sTypeRegistration));
1167 css::uno::Sequence< OUString > lExtensions = lTypeProps.getUnpackedValueOrDefault(OUString(TYPE_PROP_EXTENSIONS), css::uno::Sequence< OUString >());
1168 if (lExtensions.getLength())
1170 rInfo.Extension = OUString(".");
1171 rInfo.Extension += lExtensions[0];
1173 else
1174 rInfo.Extension = OUString(".unknown");
1176 catch(const css::uno::Exception&)
1178 rInfo.DefaultFilter = OUString();
1179 rInfo.Extension = OUString();
1183 //-----------------------------------------------
1184 void AutoRecovery::implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo)
1186 ENSURE_OR_THROW2(
1187 !rInfo.AppModule.isEmpty() || rInfo.Document.is(),
1188 "Cant find out the application module nor its factory URL, if no application module (or a suitable) document is known!",
1189 *this );
1191 // SAFE -> ----------------------------------
1192 ReadGuard aReadLock(m_aLock);
1193 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1194 aReadLock.unlock();
1195 // <- SAFE ----------------------------------
1197 css::uno::Reference< css::frame::XModuleManager2 > xManager = ModuleManager::create( comphelper::getComponentContext(xSMGR) );
1199 if (rInfo.AppModule.isEmpty())
1200 rInfo.AppModule = xManager->identify(rInfo.Document);
1202 ::comphelper::SequenceAsHashMap lModuleDescription(xManager->getByName(rInfo.AppModule));
1203 lModuleDescription[OUString(CFG_ENTRY_PROP_EMPTYDOCUMENTURL)] >>= rInfo.FactoryURL;
1204 lModuleDescription[OUString(CFG_ENTRY_PROP_FACTORYSERVICE)] >>= rInfo.FactoryService;
1207 //-----------------------------------------------
1208 void AutoRecovery::implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& i_rInfo )
1210 ENSURE_OR_THROW2( i_rInfo.Document.is(), "need at document, at the very least", *this );
1212 i_rInfo.ViewNames.realloc(0);
1214 // obtain list of controllers of this document
1215 ::std::vector< OUString > aViewNames;
1216 const Reference< XModel2 > xModel( i_rInfo.Document, UNO_QUERY );
1217 if ( xModel.is() )
1219 const Reference< XEnumeration > xEnumControllers( xModel->getControllers() );
1220 while ( xEnumControllers->hasMoreElements() )
1222 const Reference< XController2 > xController( xEnumControllers->nextElement(), UNO_QUERY );
1223 OUString sViewName;
1224 if ( xController.is() )
1225 sViewName = xController->getViewControllerName();
1226 OSL_ENSURE( !sViewName.isEmpty(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" );
1228 if ( !sViewName.isEmpty() )
1229 aViewNames.push_back( sViewName );
1232 else
1234 const Reference< XController2 > xController( xModel->getCurrentController(), UNO_QUERY );
1235 OUString sViewName;
1236 if ( xController.is() )
1237 sViewName = xController->getViewControllerName();
1238 OSL_ENSURE( !sViewName.isEmpty(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" );
1240 if ( !sViewName.isEmpty() )
1241 aViewNames.push_back( sViewName );
1244 i_rInfo.ViewNames.realloc( aViewNames.size() );
1245 ::std::copy( aViewNames.begin(), aViewNames.end(), i_rInfo.ViewNames.getArray() );
1248 //-----------------------------------------------
1249 void AutoRecovery::implts_persistAllActiveViewNames()
1251 // SAFE -> ----------------------------------
1252 WriteGuard aWriteLock(m_aLock);
1254 // This list will be filled with every document
1255 AutoRecovery::TDocumentList::iterator pIt;
1256 for ( pIt = m_lDocCache.begin();
1257 pIt != m_lDocCache.end() ;
1258 ++pIt )
1260 implts_collectActiveViewNames( *pIt );
1261 implts_flushConfigItem( *pIt );
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(OUString(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 OUStringBuffer sIDBuf;
1281 sIDBuf.appendAscii(RTL_CONSTASCII_STRINGPARAM(RECOVERY_ITEM_BASE_IDENTIFIER));
1282 sIDBuf.append((sal_Int32)rInfo.ID);
1283 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&)
1297 return;
1300 else
1302 // new/modify
1303 css::uno::Reference< css::beans::XPropertySet > xSet;
1304 sal_Bool bNew = (!xCheck->hasByName(sID));
1305 if (bNew)
1306 xSet = css::uno::Reference< css::beans::XPropertySet >(xCreate->createInstance(), css::uno::UNO_QUERY_THROW);
1307 else
1308 xCheck->getByName(sID) >>= xSet;
1310 xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_ORIGINALURL), css::uno::makeAny(rInfo.OrgURL ));
1311 xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_TEMPURL), css::uno::makeAny(rInfo.OldTempURL ));
1312 xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_TEMPLATEURL), css::uno::makeAny(rInfo.TemplateURL ));
1313 xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_FILTER), css::uno::makeAny(rInfo.RealFilter));
1314 xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_DOCUMENTSTATE), css::uno::makeAny(rInfo.DocumentState));
1315 xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_MODULE), css::uno::makeAny(rInfo.AppModule));
1316 xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_TITLE), css::uno::makeAny(rInfo.Title));
1317 xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_VIEWNAMES), css::uno::makeAny(rInfo.ViewNames));
1319 if (bNew)
1320 xModify->insertByName(sID, css::uno::makeAny(xSet));
1323 catch(const css::uno::RuntimeException&)
1325 throw;
1327 catch(const css::uno::Exception&)
1329 // ??? can it happen that a full disc let these set of operations fail too ???
1332 sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
1337 css::uno::Reference< css::util::XChangesBatch > xFlush(xCFG, css::uno::UNO_QUERY_THROW);
1338 xFlush->commitChanges();
1340 #ifdef TRIGGER_FULL_DISC_CHECK
1341 throw css::uno::Exception();
1342 #else // TRIGGER_FULL_DISC_CHECK
1343 nRetry = 0;
1344 #endif // TRIGGER_FULL_DISC_CHECK
1346 catch(const css::uno::Exception&)
1348 // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300)
1349 // b) unknown problem (may be locking problem) => reset RETRY value to more useful value(!) (e.g. retry=3)
1350 // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
1352 // SAFE ->
1353 ReadGuard aReadLock(m_aLock);
1354 sal_Int32 nMinSpaceConfigSave = m_nMinSpaceConfigSave;
1355 aReadLock.unlock();
1356 // <- SAFE
1358 if (! impl_enoughDiscSpace(nMinSpaceConfigSave))
1359 AutoRecovery::impl_showFullDiscError();
1360 else if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
1361 nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
1362 else if (nRetry <= GIVE_UP_RETRY)
1363 throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
1365 --nRetry;
1368 while(nRetry>0);
1371 //-----------------------------------------------
1372 void AutoRecovery::implts_startListening()
1374 // SAFE -> ----------------------------------
1375 ReadGuard aReadLock(m_aLock);
1376 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1377 css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG, css::uno::UNO_QUERY);
1378 css::uno::Reference< css::frame::XGlobalEventBroadcaster > xBroadcaster = m_xNewDocBroadcaster;
1379 sal_Bool bListenForDocEvents = m_bListenForDocEvents;
1380 aReadLock.unlock();
1381 // <- SAFE ----------------------------------
1383 if (
1384 ( xCFG.is() ) &&
1385 (! m_bListenForConfigChanges)
1388 m_xRecoveryCFGListener = new WeakChangesListener(this);
1389 xCFG->addChangesListener(m_xRecoveryCFGListener);
1390 m_bListenForConfigChanges = sal_True;
1393 if (!xBroadcaster.is())
1395 xBroadcaster = css::frame::GlobalEventBroadcaster::create( comphelper::getComponentContext(xSMGR) );
1396 // SAFE -> ----------------------------------
1397 WriteGuard aWriteLock(m_aLock);
1398 m_xNewDocBroadcaster = xBroadcaster;
1399 aWriteLock.unlock();
1400 // <- SAFE ----------------------------------
1403 if (
1404 ( xBroadcaster.is() ) &&
1405 (! bListenForDocEvents)
1408 m_xNewDocBroadcasterListener = new WeakDocumentEventListener(this);
1409 xBroadcaster->addEventListener(m_xNewDocBroadcasterListener);
1410 // SAFE ->
1411 WriteGuard aWriteLock(m_aLock);
1412 m_bListenForDocEvents = sal_True;
1413 aWriteLock.unlock();
1414 // <- SAFE
1418 //-----------------------------------------------
1419 void AutoRecovery::implts_stopListening()
1421 // SAFE -> ----------------------------------
1422 ReadGuard aReadLock(m_aLock);
1423 // Attention: Dont reset our internal members here too.
1424 // May be we must work with our configuration, but dont wish to be informed
1425 // about changes any longer. Needed e.g. during EMERGENCY_SAVE!
1426 css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG , css::uno::UNO_QUERY);
1427 css::uno::Reference< css::document::XEventBroadcaster > xGlobalEventBroadcaster(m_xNewDocBroadcaster, css::uno::UNO_QUERY);
1428 aReadLock.unlock();
1429 // <- SAFE ----------------------------------
1431 if (
1432 (xGlobalEventBroadcaster.is()) &&
1433 (m_bListenForDocEvents )
1436 xGlobalEventBroadcaster->removeEventListener(m_xNewDocBroadcasterListener);
1437 m_bListenForDocEvents = sal_False;
1440 if (
1441 (xCFG.is() ) &&
1442 (m_bListenForConfigChanges)
1445 xCFG->removeChangesListener(m_xRecoveryCFGListener);
1446 m_bListenForConfigChanges = sal_False;
1450 //-----------------------------------------------
1451 void AutoRecovery::implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
1453 if (rInfo.ListenForModify)
1454 return;
1456 css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
1457 if (xBroadcaster.is())
1459 css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY);
1460 xBroadcaster->addModifyListener(xThis);
1461 rInfo.ListenForModify = sal_True;
1465 //-----------------------------------------------
1466 void AutoRecovery::implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
1468 if (! rInfo.ListenForModify)
1469 return;
1471 css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
1472 if (xBroadcaster.is())
1474 css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY);
1475 xBroadcaster->removeModifyListener(xThis);
1476 rInfo.ListenForModify = sal_False;
1480 //-----------------------------------------------
1481 void AutoRecovery::implts_updateTimer()
1483 implts_stopTimer();
1485 // SAFE -> ----------------------------------
1486 WriteGuard aWriteLock(m_aLock);
1488 if (
1489 (m_eJob == AutoRecovery::E_NO_JOB ) || // TODO may be superflous - E_DONT_START_TIMER should be used only
1490 (m_eTimerType == AutoRecovery::E_DONT_START_TIMER)
1492 return;
1494 sal_uLong nMilliSeconds = 0;
1495 if (m_eTimerType == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
1497 nMilliSeconds = (m_nAutoSaveTimeIntervall*60000); // [min] => 60.000 ms
1498 #if OSL_DEBUG_LEVEL > 1
1499 if (m_dbg_bMakeItFaster)
1500 nMilliSeconds = m_nAutoSaveTimeIntervall; // [ms]
1501 #endif
1503 else if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
1505 nMilliSeconds = MIN_TIME_FOR_USER_IDLE;
1506 #if OSL_DEBUG_LEVEL > 1
1507 if (m_dbg_bMakeItFaster)
1508 nMilliSeconds = 300; // let us some time, to finish this method .-)
1509 #endif
1511 else if (m_eTimerType == AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED)
1512 nMilliSeconds = 300; // there is a minimum time frame, where the user can loose some key input data!
1514 m_aTimer.SetTimeout(nMilliSeconds);
1515 m_aTimer.Start();
1517 aWriteLock.unlock();
1518 // <- SAFE ----------------------------------
1521 //-----------------------------------------------
1522 void AutoRecovery::implts_stopTimer()
1524 // SAFE -> ----------------------------------
1525 WriteGuard aWriteLock(m_aLock);
1527 if (!m_aTimer.IsActive())
1528 return;
1529 m_aTimer.Stop();
1531 // <- SAFE ----------------------------------
1534 //-----------------------------------------------
1535 IMPL_LINK_NOARG(AutoRecovery, implts_timerExpired)
1539 // This method is called by using a pointer to us.
1540 // But we must be aware that we can be destroyed hardly
1541 // if our uno reference will be gone!
1542 // => Hold this object alive till this method finish its work.
1543 css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XTypeProvider* >(this));
1545 // Needed! Otherwise every reschedule request allow a new triggered timer event :-(
1546 implts_stopTimer();
1548 // The timer must be ignored if AutoSave/Recovery was disabled for this
1549 // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless"
1550 // was set. But normaly the timer was disabled if recovery was disabled ...
1551 // But so we are more "safe" .-)
1552 // SAFE -> ----------------------------------
1553 ReadGuard aReadLock(m_aLock);
1554 if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
1555 return 0;
1556 aReadLock.unlock();
1557 // <- SAFE ----------------------------------
1559 // check some "states", where its not allowed (better: not a good idea) to
1560 // start an AutoSave. (e.g. if the user makes drag & drop ...)
1561 // Then we poll till this "disallowed" state is gone.
1562 sal_Bool bAutoSaveNotAllowed = Application::IsUICaptured();
1563 if (bAutoSaveNotAllowed)
1565 // SAFE -> ------------------------------
1566 WriteGuard aWriteLock(m_aLock);
1567 m_eTimerType = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
1568 aWriteLock.unlock();
1569 // <- SAFE ------------------------------
1570 implts_updateTimer();
1571 return 0;
1574 // analyze timer type.
1575 // If we poll for an user idle period, may be we must
1576 // do nothing here and start the timer again.
1577 // SAFE -> ----------------------------------
1578 WriteGuard aWriteLock(m_aLock);
1580 if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
1582 sal_Bool bUserIdle = (Application::GetLastInputInterval()>MIN_TIME_FOR_USER_IDLE);
1583 if (!bUserIdle)
1585 implts_updateTimer();
1586 return 0;
1590 aWriteLock.unlock();
1591 // <- SAFE ----------------------------------
1593 implts_informListener(AutoRecovery::E_AUTO_SAVE,
1594 AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_START, NULL));
1596 // force save of all currently open documents
1597 // The called method returns an info, if and how this
1598 // timer must be restarted.
1599 sal_Bool bAllowUserIdleLoop = sal_True;
1600 AutoRecovery::ETimerType eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False);
1602 // If timer isnt used for "short callbacks" (means polling
1603 // for special states) ... reset the handle state of all
1604 // cache items. Such handle state indicates, that a document
1605 // was already saved during the THIS(!) AutoSave session.
1606 // Of course NEXT AutoSave session must be started without
1607 // any "handle" state ...
1608 if (
1609 (eSuggestedTimer == AutoRecovery::E_DONT_START_TIMER ) ||
1610 (eSuggestedTimer == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
1613 implts_resetHandleStates(sal_False);
1616 implts_informListener(AutoRecovery::E_AUTO_SAVE,
1617 AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_STOP, NULL));
1619 // restart timer - because it was disabled before ...
1620 // SAFE -> ----------------------------------
1621 aWriteLock.lock();
1622 m_eTimerType = eSuggestedTimer;
1623 aWriteLock.unlock();
1624 // <- SAFE ----------------------------------
1626 implts_updateTimer();
1628 catch(const css::uno::Exception&)
1632 return 0;
1635 //-----------------------------------------------
1636 IMPL_LINK_NOARG(AutoRecovery, implts_asyncDispatch)
1638 // SAFE ->
1639 WriteGuard aWriteLock(m_aLock);
1640 DispatchParams aParams = m_aDispatchParams;
1641 css::uno::Reference< css::uno::XInterface > xHoldRefForMethodAlive = aParams.m_xHoldRefForAsyncOpAlive;
1642 m_aDispatchParams.forget(); // clears all members ... including the ref-hold object .-)
1643 aWriteLock.unlock();
1644 // <- SAFE
1648 implts_dispatch(aParams);
1650 catch (...)
1653 return 0;
1656 //-----------------------------------------------
1657 void AutoRecovery::implts_registerDocument(const css::uno::Reference< css::frame::XModel >& xDocument)
1659 // ignore corrupted events, where no document is given ... Runtime Error ?!
1660 if (!xDocument.is())
1661 return;
1663 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1665 // notification for already existing document !
1666 // Can happen if events came in asynchronous on recovery time.
1667 // Then our cache was filled from the configuration ... but now we get some
1668 // asynchronous events from the global event broadcaster. We must be sure that
1669 // we dont add the same document more then once.
1670 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1671 if (pIt != m_lDocCache.end())
1673 // Normaly nothing must be done for this "late" notification.
1674 // But may be the modified state was changed inbetween.
1675 // Check it ...
1676 implts_updateModifiedState(xDocument);
1677 return;
1680 aCacheLock.unlock();
1682 ::comphelper::MediaDescriptor lDescriptor(xDocument->getArgs());
1684 // check if this document must be ignored for recovery !
1685 // Some use cases dont wish support for AutoSave/Recovery ... as e.g. OLE-Server / ActiveX Control etcpp.
1686 sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False));
1687 if (bNoAutoSave)
1688 return;
1690 // Check if doc is well known on the desktop. Otherwhise ignore it!
1691 // Other frames mostly are used from external programs - e.g. the bean ...
1692 css::uno::Reference< css::frame::XController > xController = xDocument->getCurrentController();
1693 if (!xController.is())
1694 return;
1696 css::uno::Reference< css::frame::XFrame > xFrame = xController->getFrame();
1697 css::uno::Reference< css::frame::XDesktop > xDesktop (xFrame->getCreator(), css::uno::UNO_QUERY);
1698 if (!xDesktop.is())
1699 return;
1701 // if the document doesn't support the XDocumentRecovery interface, we're not interested in it.
1702 Reference< XDocumentRecovery > xDocRecovery( xDocument, UNO_QUERY );
1703 if ( !xDocRecovery.is() )
1704 return;
1706 // get all needed information of this document
1707 // We need it to update our cache or to locate already existing elements there!
1708 AutoRecovery::TDocumentInfo aNew;
1709 aNew.Document = xDocument;
1711 // TODO replace getLocation() with getURL() ... its a workaround currently only!
1712 css::uno::Reference< css::frame::XStorable > xDoc(aNew.Document, css::uno::UNO_QUERY_THROW);
1713 aNew.OrgURL = xDoc->getLocation();
1715 css::uno::Reference< css::frame::XTitle > xTitle(aNew.Document, css::uno::UNO_QUERY_THROW);
1716 aNew.Title = xTitle->getTitle ();
1718 // SAFE -> ----------------------------------
1719 ReadGuard aReadLock(m_aLock);
1720 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
1721 aReadLock.unlock();
1722 // <- SAFE ----------------------------------
1724 // classify the used application module, which is used by this document.
1725 implts_specifyAppModuleAndFactory(aNew);
1727 // Hack! Check for "illegal office documents" ... as e.g. the Basic IDE
1728 // Its not realy a full featured office document. It doesnt provide an URL, any filter, a factory URL etcpp.
1729 // TODO file bug to Basci IDE developers. They must remove the office document API from its service.
1730 if (
1731 (aNew.OrgURL.isEmpty()) &&
1732 (aNew.FactoryURL.isEmpty())
1735 OSL_FAIL( "AutoRecovery::implts_registerDocument: this should not happen anymore!" );
1736 // nowadays, the Basic IDE should already die on the "supports XDocumentRecovery" check. And no other known
1737 // document type fits in here ...
1738 return;
1741 // By the way - get some information about the default format for saving!
1742 // and save an information about the real used filter by this document.
1743 // We save this document with DefaultFilter ... and load it with the RealFilter.
1744 implts_specifyDefaultFilterAndExtension(aNew);
1745 aNew.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME() , OUString());
1747 // Further we must know, if this document base on a template.
1748 // Then we must load it in a different way.
1749 css::uno::Reference< css::document::XDocumentPropertiesSupplier > xSupplier(aNew.Document, css::uno::UNO_QUERY);
1750 if (xSupplier.is()) // optional interface!
1752 css::uno::Reference< css::document::XDocumentProperties > xDocProps(xSupplier->getDocumentProperties(), css::uno::UNO_QUERY_THROW);
1753 aNew.TemplateURL = xDocProps->getTemplateURL();
1756 css::uno::Reference< css::util::XModifiable > xModifyCheck(xDocument, css::uno::UNO_QUERY_THROW);
1757 if (xModifyCheck->isModified())
1759 aNew.DocumentState |= AutoRecovery::E_MODIFIED;
1762 aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
1764 // SAFE -> ----------------------------------
1765 WriteGuard aWriteLock(m_aLock);
1767 // create a new cache entry ... this document isn't known.
1768 ++m_nIdPool;
1769 aNew.ID = m_nIdPool;
1770 LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_registerDocument()\nOverflow of ID pool detected.")
1771 m_lDocCache.push_back(aNew);
1773 AutoRecovery::TDocumentList::iterator pIt1 = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1774 AutoRecovery::TDocumentInfo& rInfo = *pIt1;
1776 aWriteLock.unlock();
1777 // <- SAFE ----------------------------------
1779 implts_flushConfigItem(rInfo);
1780 implts_startModifyListeningOnDoc(rInfo);
1782 aCacheLock.unlock();
1785 //-----------------------------------------------
1786 void AutoRecovery::implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument ,
1787 sal_Bool bStopListening)
1790 // SAFE -> ----------------------------------
1791 WriteGuard aWriteLock(m_aLock);
1793 // Attention: Dont leave SAFE section, if you work with pIt!
1794 // Because it points directly into the m_lDocCache list ...
1795 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1797 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1798 if (pIt == m_lDocCache.end())
1799 return; // unknown document => not a runtime error! Because we register only a few documents. see registration ...
1801 AutoRecovery::TDocumentInfo aInfo = *pIt;
1803 aCacheLock.unlock();
1805 // Sometimes we close documents by ourself.
1806 // And these documents cant be deregistered.
1807 // Otherwhise we loos our configuration data ... but need it !
1808 // see SessionSave !
1809 if (aInfo.IgnoreClosing)
1810 return;
1812 CacheLockGuard aCacheLock2(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
1813 pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1814 if (pIt != m_lDocCache.end())
1815 m_lDocCache.erase(pIt);
1816 pIt = m_lDocCache.end(); // otherwise its not specified what pIt means!
1817 aCacheLock2.unlock();
1819 aWriteLock.unlock();
1820 // <- SAFE ----------------------------------
1822 /* This method is called within disposing() of the document too. But there it's not a good idea to
1823 deregister us as listener. Furter it make no sense - because the broadcaster dies.
1824 So we supress deregistration in such case ...
1826 if (bStopListening)
1827 implts_stopModifyListeningOnDoc(aInfo);
1829 AutoRecovery::st_impl_removeFile(aInfo.OldTempURL);
1830 AutoRecovery::st_impl_removeFile(aInfo.NewTempURL);
1831 implts_flushConfigItem(aInfo, sal_True); // sal_True => remove it from config
1834 //-----------------------------------------------
1835 void AutoRecovery::implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument)
1837 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1839 // SAFE -> ----------------------------------
1840 WriteGuard aWriteLock(m_aLock);
1842 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1843 if (pIt != m_lDocCache.end())
1845 AutoRecovery::TDocumentInfo& rInfo = *pIt;
1847 /* Now we know, that this document was modified again and must be saved next time.
1848 But we dont need this information for every e.g. key input of the user.
1849 So we stop listening here.
1850 But if the document was saved as temp. file we start listening for this event again.
1852 implts_stopModifyListeningOnDoc(rInfo);
1855 aWriteLock.unlock();
1856 // <- SAFE ----------------------------------
1859 //-----------------------------------------------
1860 void AutoRecovery::implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument)
1862 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1864 // SAFE -> ----------------------------------
1865 WriteGuard aWriteLock(m_aLock);
1867 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1868 if (pIt != m_lDocCache.end())
1870 AutoRecovery::TDocumentInfo& rInfo = *pIt;
1872 // use sal_True as fallback ... so we recognize every document on EmergencySave/AutoRecovery!
1873 sal_Bool bModified = sal_True;
1874 css::uno::Reference< css::util::XModifiable > xModify(xDocument, css::uno::UNO_QUERY);
1875 if (xModify.is())
1876 bModified = xModify->isModified();
1877 if (bModified)
1879 rInfo.DocumentState |= AutoRecovery::E_MODIFIED;
1881 else
1883 rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED;
1887 aWriteLock.unlock();
1888 // <- SAFE ----------------------------------
1891 //-----------------------------------------------
1892 void AutoRecovery::implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument ,
1893 sal_Bool bSaveInProgress)
1895 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1897 // SAFE -> ----------------------------------
1898 WriteGuard aWriteLock(m_aLock);
1900 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1901 if (pIt == m_lDocCache.end())
1902 return;
1903 AutoRecovery::TDocumentInfo& rInfo = *pIt;
1904 rInfo.UsedForSaving = bSaveInProgress;
1906 aWriteLock.unlock();
1907 // <- SAFE ----------------------------------
1910 //-----------------------------------------------
1911 void AutoRecovery::implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument)
1913 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1915 // SAFE -> ----------------------------------
1916 WriteGuard aWriteLock(m_aLock);
1918 AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
1919 if (pIt == m_lDocCache.end())
1920 return;
1921 AutoRecovery::TDocumentInfo& rInfo = *pIt;
1923 rInfo.DocumentState = AutoRecovery::E_UNKNOWN;
1924 // TODO replace getLocation() with getURL() ... its a workaround currently only!
1925 css::uno::Reference< css::frame::XStorable > xDoc(rInfo.Document, css::uno::UNO_QUERY);
1926 rInfo.OrgURL = xDoc->getLocation();
1928 OUString sRemoveURL1 = rInfo.OldTempURL;
1929 OUString sRemoveURL2 = rInfo.NewTempURL;
1930 rInfo.OldTempURL = OUString();
1931 rInfo.NewTempURL = OUString();
1933 ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs());
1934 rInfo.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME(), OUString());
1936 css::uno::Reference< css::frame::XTitle > xDocTitle(xDocument, css::uno::UNO_QUERY);
1937 if (xDocTitle.is ())
1938 rInfo.Title = xDocTitle->getTitle ();
1939 else
1941 rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_TITLE() , OUString());
1942 if (rInfo.Title.isEmpty())
1943 rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_DOCUMENTTITLE(), OUString());
1946 rInfo.UsedForSaving = sal_False;
1948 aWriteLock.unlock();
1949 // <- SAFE ----------------------------------
1951 implts_flushConfigItem(rInfo);
1953 aCacheLock.unlock();
1955 AutoRecovery::st_impl_removeFile(sRemoveURL1);
1956 AutoRecovery::st_impl_removeFile(sRemoveURL2);
1959 //-----------------------------------------------
1960 AutoRecovery::TDocumentList::iterator AutoRecovery::impl_searchDocument( AutoRecovery::TDocumentList& rList ,
1961 const css::uno::Reference< css::frame::XModel >& xDocument)
1963 AutoRecovery::TDocumentList::iterator pIt;
1964 for ( pIt = rList.begin();
1965 pIt != rList.end() ;
1966 ++pIt )
1968 const AutoRecovery::TDocumentInfo& rInfo = *pIt;
1969 if (rInfo.Document == xDocument)
1970 break;
1972 return pIt;
1975 //-----------------------------------------------
1976 namespace
1978 void lcl_changeVisibility( const css::uno::Reference< css::frame::XFramesSupplier >& i_rFrames, sal_Bool i_bVisible )
1980 css::uno::Reference< css::container::XIndexAccess > xFramesContainer( i_rFrames->getFrames(), css::uno::UNO_QUERY );
1981 const sal_Int32 count = xFramesContainer->getCount();
1983 Any aElement;
1984 for ( sal_Int32 i=0; i < count; ++i )
1986 aElement = xFramesContainer->getByIndex(i);
1987 // check for sub frames
1988 css::uno::Reference< css::frame::XFramesSupplier > xFramesSupp( aElement, css::uno::UNO_QUERY );
1989 if ( xFramesSupp.is() )
1990 lcl_changeVisibility( xFramesSupp, i_bVisible );
1992 css::uno::Reference< css::frame::XFrame > xFrame( aElement, css::uno::UNO_QUERY );
1993 if ( !xFrame.is() )
1994 continue;
1996 css::uno::Reference< css::awt::XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW );
1997 xWindow->setVisible( i_bVisible );
2002 //-----------------------------------------------
2003 void AutoRecovery::implts_changeAllDocVisibility(sal_Bool bVisible)
2005 // SAFE -> ----------------------------------
2006 ReadGuard aReadLock(m_aLock);
2007 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2008 aReadLock.unlock();
2009 // <- SAFE ----------------------------------
2011 css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create( comphelper::getComponentContext(xSMGR) ), css::uno::UNO_QUERY);
2012 lcl_changeVisibility( xDesktop, bVisible );
2014 aReadLock.unlock();
2015 // <- SAFE ----------------------------------
2018 //-----------------------------------------------
2019 /* Currently the document is not closed in case of crash,
2020 so the lock file must be removed explicitly
2022 void lc_removeLockFile(AutoRecovery::TDocumentInfo& rInfo)
2024 #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT
2025 (void) rInfo;
2026 #else
2027 if ( rInfo.Document.is() )
2031 css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW);
2032 OUString aURL = xStore->getLocation();
2033 if ( !aURL.isEmpty() )
2035 ::svt::DocumentLockFile aLockFile( aURL );
2036 aLockFile.RemoveFile();
2039 catch( const css::uno::Exception& )
2043 #endif
2047 //-----------------------------------------------
2048 void AutoRecovery::implts_prepareSessionShutdown()
2050 LOG_RECOVERY("AutoRecovery::implts_prepareSessionShutdown() starts ...")
2052 // a) reset modified documents (of course the must be saved before this method is called!)
2053 // b) close it without showing any UI!
2055 // SAFE ->
2056 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2058 AutoRecovery::TDocumentList::iterator pIt;
2059 for ( pIt = m_lDocCache.begin();
2060 pIt != m_lDocCache.end() ;
2061 ++pIt )
2063 AutoRecovery::TDocumentInfo& rInfo = *pIt;
2065 // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
2066 // it is not done on documents saving since shutdown can be cancelled
2067 lc_removeLockFile( rInfo );
2069 // Prevent us from deregistration of these documents.
2070 // Because we close these documents by ourself (see XClosable below) ...
2071 // it's fact, that we reach our deregistration method. There we
2072 // must not(!) update our configuration ... Otherwhise all
2073 // session data are lost !!!
2074 rInfo.IgnoreClosing = sal_True;
2076 // reset modified flag of these documents (ignoring the notification about it!)
2077 // Otherwise a message box is shown on closing these models.
2078 implts_stopModifyListeningOnDoc(rInfo);
2080 // if the session save is still running the documents should not be thrown away,
2081 // actually that would be a bad sign, that means that the SessionManager tryes
2082 // to kill the session before the saving is ready
2083 if ((m_eJob & AutoRecovery::E_SESSION_SAVE) != AutoRecovery::E_SESSION_SAVE)
2085 css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
2086 if (xModify.is())
2087 xModify->setModified(sal_False);
2089 // close the model.
2090 css::uno::Reference< css::util::XCloseable > xClose(rInfo.Document, css::uno::UNO_QUERY);
2091 if (xClose.is())
2095 xClose->close(sal_False);
2097 catch(const css::uno::Exception&)
2099 // At least it's only a try to close these documents before anybody else it does.
2100 // So it seams to be possible to ignore any error here .-)
2103 rInfo.Document.clear();
2108 aCacheLock.unlock();
2109 // <- SAFE
2112 //-----------------------------------------------
2113 /* TODO WORKAROUND:
2115 #i64599#
2117 Normaly the MediaDescriptor argument NoAutoSave indicates,
2118 that a document must be ignored for AutoSave and Recovery.
2119 But sometimes XModel->getArgs() does not contained this information
2120 if implts_registerDocument() was called.
2121 So we have to check a second time, if this property is set ....
2122 Best place doing so is to check it immeditaly before saving
2123 and supressingd saving the document then.
2124 Of course removing the corresponding cache entry isnt an option.
2125 Because it would disturb iteration over the cache !
2126 So we ignore such documents only ...
2127 Hopefully next time they are not inserted in our cache.
2129 sal_Bool lc_checkIfSaveForbiddenByArguments(AutoRecovery::TDocumentInfo& rInfo)
2131 if (! rInfo.Document.is())
2132 return sal_True;
2134 ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs());
2135 sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False));
2137 return bNoAutoSave;
2140 //-----------------------------------------------
2141 AutoRecovery::ETimerType AutoRecovery::implts_saveDocs( sal_Bool bAllowUserIdleLoop,
2142 sal_Bool bRemoveLockFiles,
2143 const DispatchParams* pParams )
2145 // SAFE -> ----------------------------------
2146 ReadGuard aReadLock(m_aLock);
2147 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2148 aReadLock.unlock();
2149 // <- SAFE ----------------------------------
2151 css::uno::Reference< css::task::XStatusIndicator > xExternalProgress;
2152 if (pParams)
2153 xExternalProgress = pParams->m_xProgress;
2155 css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( comphelper::getComponentContext(xSMGR));
2156 OUString sBackupPath(SvtPathOptions().GetBackupPath());
2158 css::uno::Reference< css::frame::XController > xActiveController;
2159 css::uno::Reference< css::frame::XModel > xActiveModel ;
2160 css::uno::Reference< css::frame::XFrame > xActiveFrame = xDesktop->getActiveFrame();
2161 if (xActiveFrame.is())
2162 xActiveController = xActiveFrame->getController();
2163 if (xActiveController.is())
2164 xActiveModel = xActiveController->getModel();
2166 // Set the default timer action for our calli.
2167 // Default = NORMAL_AUTOSAVE
2168 // We return a suggestion for an active timer only.
2169 // It will be ignored if the timer was disabled by the user ...
2170 // Further this state can be set to USER_IDLE only later in this method.
2171 // Its not allowed to reset such state then. Because we must know, if
2172 // there exists POSTPONED documents. see below ...
2173 AutoRecovery::ETimerType eTimer = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
2175 sal_Int32 eJob = m_eJob;
2177 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2179 // SAFE -> ----------------------------------
2180 WriteGuard aWriteLock(m_aLock);
2182 // This list will be filled with every document
2183 // which should be saved as last one. E.g. if it was used
2184 // already for an UI save operation => crashed ... and
2185 // now we try to save it again ... which can fail again ( of course .-) ).
2186 ::std::vector< AutoRecovery::TDocumentList::iterator > lDangerousDocs;
2188 AutoRecovery::TDocumentList::iterator pIt;
2189 for ( pIt = m_lDocCache.begin();
2190 pIt != m_lDocCache.end() ;
2191 ++pIt )
2193 AutoRecovery::TDocumentInfo aInfo = *pIt;
2195 // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
2196 if ( bRemoveLockFiles )
2197 lc_removeLockFile( aInfo );
2199 // WORKAROUND ... see comment of this method
2200 if (lc_checkIfSaveForbiddenByArguments(aInfo))
2201 continue;
2203 // already auto saved during this session :-)
2204 // This state must be reset for all documents
2205 // if timer is started with normnal AutoSaveTimerIntervall!
2206 if ((aInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED)
2207 continue;
2209 // Not modified documents are not saved.
2210 // We safe an information about the URL only!
2211 Reference< XDocumentRecovery > xDocRecover( aInfo.Document, UNO_QUERY_THROW );
2212 if ( !xDocRecover->wasModifiedSinceLastSave() )
2214 aInfo.DocumentState |= AutoRecovery::E_HANDLED;
2215 continue;
2218 // check if this document is still used by a concurrent save operation
2219 // e.g. if the user tried to save via UI.
2220 // Handle it in the following way:
2221 // i) For an AutoSave ... ignore this document! It will be saved and next time we will (hopefully)
2222 // get a notification about the state of this operation.
2223 // And if a document was saved by the user we can remove our temp. file. But that will be done inside
2224 // our callback for SaveDone notification.
2225 // ii) For a CrashSave ... add it to the list of dangerous documents and
2226 // save it after all other documents was saved successfully. That decrease
2227 // the chance for a crash inside a crash.
2228 // On the other side it's not necessary for documents, which are not modified.
2229 // They can be handled normaly - means we patch the corresponding configuration entry only.
2230 // iii) For a SessionSave ... ignore it! There is no time to wait for this save operation.
2231 // Because the WindowManager will kill the process if it doesnt react immediately.
2232 // On the other side we cant risk a concurrent save request ... because we know
2233 // that it will produce a crash.
2235 // Attention: Because eJob is used as a flag field, you have to check for the worst case first.
2236 // E.g. a CrashSave can overwrite an AutoSave. So you have to check for a CrashSave before an AutoSave!
2237 if (aInfo.UsedForSaving)
2239 if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE)
2241 lDangerousDocs.push_back(pIt);
2242 continue;
2244 else
2245 if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE)
2247 continue;
2249 else
2250 if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
2252 eTimer = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
2253 aInfo.DocumentState |= AutoRecovery::E_POSTPONED;
2254 continue;
2258 // a) Document was not postponed - and is active now. => postpone it (restart timer, restart loop)
2259 // b) Document was not postponed - and is not active now. => save it
2260 // c) Document was postponed - and is not active now. => save it
2261 // d) Document was postponed - and is active now. => save it (because user idle was checked already)
2262 sal_Bool bActive = (xActiveModel == aInfo.Document);
2263 sal_Bool bWasPostponed = ((aInfo.DocumentState & AutoRecovery::E_POSTPONED) == AutoRecovery::E_POSTPONED);
2265 if (
2266 ! bWasPostponed &&
2267 bActive
2270 aInfo.DocumentState |= AutoRecovery::E_POSTPONED;
2271 *pIt = aInfo;
2272 // postponed documents will be saved if this method is called again!
2273 // That can be done by an outside started timer => E_POLL_FOR_USER_IDLE (if normal AutoSave is active)
2274 // 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!)
2275 eTimer = AutoRecovery::E_POLL_FOR_USER_IDLE;
2276 if (!bAllowUserIdleLoop)
2277 eTimer = AutoRecovery::E_CALL_ME_BACK;
2278 continue;
2281 // b, c, d)
2282 // <- SAFE --------------------------
2283 aWriteLock.unlock();
2284 // changing of aInfo and flushing it is done inside implts_saveOneDoc!
2285 implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
2286 implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
2287 aWriteLock.lock();
2288 // SAFE -> --------------------------
2290 *pIt = aInfo;
2293 // Did we have some "dangerous candidates" ?
2294 // Try to save it ... but may be it will fail !
2295 ::std::vector< AutoRecovery::TDocumentList::iterator >::iterator pIt2;
2296 for ( pIt2 = lDangerousDocs.begin();
2297 pIt2 != lDangerousDocs.end() ;
2298 ++pIt2 )
2300 pIt = *pIt2;
2301 AutoRecovery::TDocumentInfo aInfo = *pIt;
2303 // <- SAFE --------------------------
2304 aWriteLock.unlock();
2305 // changing of aInfo and flushing it is done inside implts_saveOneDoc!
2306 implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
2307 implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
2308 aWriteLock.lock();
2309 // SAFE -> --------------------------
2311 *pIt = aInfo;
2314 return eTimer;
2317 //-----------------------------------------------
2318 void AutoRecovery::implts_saveOneDoc(const OUString& sBackupPath ,
2319 AutoRecovery::TDocumentInfo& rInfo ,
2320 const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress)
2322 // no document? => can occure if we loaded our configuration with files,
2323 // which couldnt be recovered successfully. In such case we have all needed information
2324 // excepting the real document instance!
2326 // TODO: search right place, where such "dead files" can be removed from the configuration!
2327 if (!rInfo.Document.is())
2328 return;
2330 ::comphelper::MediaDescriptor lOldArgs(rInfo.Document->getArgs());
2331 implts_generateNewTempURL(sBackupPath, lOldArgs, rInfo);
2333 // if the document was loaded with a password, it should be
2334 // stored with password
2335 ::comphelper::MediaDescriptor lNewArgs;
2336 css::uno::Sequence< css::beans::NamedValue > aEncryptionData =
2337 lOldArgs.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_ENCRYPTIONDATA(),
2338 css::uno::Sequence< css::beans::NamedValue >());
2339 if (aEncryptionData.getLength() > 0)
2340 lNewArgs[::comphelper::MediaDescriptor::PROP_ENCRYPTIONDATA()] <<= aEncryptionData;
2342 // Further it must be saved using the default file format of that application.
2343 // Otherwhise we will some data lost.
2344 if (!rInfo.DefaultFilter.isEmpty())
2345 lNewArgs[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.DefaultFilter;
2347 // prepare frame/document/mediadescriptor in a way, that it uses OUR progress .-)
2348 if (xExternalProgress.is())
2349 lNewArgs[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= xExternalProgress;
2350 impl_establishProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
2352 // #i66598# use special handling of property "DocumentBaseURL" (it must be an empty string!)
2353 // for make hyperlinks working
2354 lNewArgs[::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL()] <<= OUString();
2356 // try to save this document as a new temp file everytimes.
2357 // Mark AutoSave state as "INCOMPLETE" if it failed.
2358 // Because the last temp file is to old and does not include all changes.
2359 Reference< XDocumentRecovery > xDocRecover(rInfo.Document, css::uno::UNO_QUERY_THROW);
2361 // safe the state about "trying to save"
2362 // ... we need it for recovery if e.g. a crash occures inside next line!
2363 rInfo.DocumentState |= AutoRecovery::E_TRY_SAVE;
2364 implts_flushConfigItem(rInfo);
2366 sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
2367 sal_Bool bError = sal_False;
2372 xDocRecover->storeToRecoveryFile( rInfo.NewTempURL, lNewArgs.getAsConstPropertyValueList() );
2374 // if userautosave is enabled, also save to the original file
2375 if((m_eJob & AutoRecovery::E_USER_AUTO_SAVE) == AutoRecovery::E_USER_AUTO_SAVE)
2377 Reference< XStorable > xDocSave(rInfo.Document, css::uno::UNO_QUERY_THROW);
2378 xDocSave->store();
2381 #ifdef TRIGGER_FULL_DISC_CHECK
2382 throw css::uno::Exception();
2383 #else // TRIGGER_FULL_DISC_CHECK
2385 bError = sal_False;
2386 nRetry = 0;
2387 #endif // TRIGGER_FULL_DISC_CHECK
2389 catch(const css::uno::Exception&)
2391 bError = sal_True;
2393 // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300)
2394 // b) unknown problem (may be locking problem) => reset RETRY value to more useful value(!) (e.g. retry=3)
2395 // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
2397 // SAFE ->
2398 ReadGuard aReadLock2(m_aLock);
2399 sal_Int32 nMinSpaceDocSave = m_nMinSpaceDocSave;
2400 aReadLock2.unlock();
2401 // <- SAFE
2403 if (! impl_enoughDiscSpace(nMinSpaceDocSave))
2404 AutoRecovery::impl_showFullDiscError();
2405 else if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
2406 nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
2407 else if (nRetry <= GIVE_UP_RETRY)
2408 throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
2410 --nRetry;
2413 while(nRetry>0);
2415 if (! bError)
2417 // safe the state about success
2418 // ... you know the reason: to know it on recovery time if next line crash .-)
2419 rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE;
2420 rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2421 rInfo.DocumentState |= AutoRecovery::E_SUCCEDED;
2423 else
2425 // safe the state about error ...
2426 rInfo.NewTempURL = OUString();
2427 rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE;
2428 rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2429 rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2432 // make sure the progress isnt referred any longer
2433 impl_forgetProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
2435 // try to remove the old temp file.
2436 // Ignore any error here. We have a new temp file, which is up to date.
2437 // The only thing is: we fill the disk with temp files, if we cant remove old ones :-)
2438 OUString sRemoveFile = rInfo.OldTempURL;
2439 rInfo.OldTempURL = rInfo.NewTempURL;
2440 rInfo.NewTempURL = OUString();
2442 implts_flushConfigItem(rInfo);
2444 // We must know if the user modifies the document again ...
2445 implts_startModifyListeningOnDoc(rInfo);
2447 AutoRecovery::st_impl_removeFile(sRemoveFile);
2450 //-----------------------------------------------
2451 AutoRecovery::ETimerType AutoRecovery::implts_openDocs(const DispatchParams& aParams)
2453 AutoRecovery::ETimerType eTimer = AutoRecovery::E_DONT_START_TIMER;
2455 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2457 // SAFE -> ----------------------------------
2458 WriteGuard aWriteLock(m_aLock);
2460 sal_Int32 eJob = m_eJob;
2461 AutoRecovery::TDocumentList::iterator pIt;
2462 for ( pIt = m_lDocCache.begin();
2463 pIt != m_lDocCache.end() ;
2464 ++pIt )
2466 AutoRecovery::TDocumentInfo& rInfo = *pIt;
2468 // Such documents are already loaded by the last loop.
2469 // Dont check E_SUCCEDED here! Its may be the final state of an AutoSave
2470 // operation before!!!
2471 if ((rInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED)
2472 continue;
2474 // a1,b1,c1,d2,e2,f2)
2475 if ((rInfo.DocumentState & AutoRecovery::E_DAMAGED) == AutoRecovery::E_DAMAGED)
2477 // dont forget to inform listener! May be this document was
2478 // damaged on last saving time ...
2479 // Then our listener need this notification.
2480 // If it was damaged during last "try to open" ...
2481 // it will be notified more then once. SH.. HAPPENS ...
2482 // <- SAFE --------------------------
2483 aWriteLock.unlock();
2484 implts_informListener(eJob,
2485 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2486 aWriteLock.lock();
2487 // SAFE -> --------------------------
2488 continue;
2491 ::comphelper::MediaDescriptor lDescriptor;
2493 // its an UI feature - so the "USER" itself must be set as referer
2494 lDescriptor[::comphelper::MediaDescriptor::PROP_REFERRER()] <<= OUString(REFERRER_USER);
2495 lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= OUString();
2497 // recovered documents are loaded hidden, and shown all at once, later
2498 lDescriptor[::comphelper::MediaDescriptor::PROP_HIDDEN()] <<= true;
2500 if (aParams.m_xProgress.is())
2501 lDescriptor[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= aParams.m_xProgress;
2503 sal_Bool bBackupWasTried = (
2504 ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_BACKUP ) == AutoRecovery::E_TRY_LOAD_BACKUP) || // temp. state!
2505 ((rInfo.DocumentState & AutoRecovery::E_INCOMPLETE ) == AutoRecovery::E_INCOMPLETE ) // transport TRY_LOAD_BACKUP from last loop to this new one!
2507 sal_Bool bOriginalWasTried = ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL);
2509 if (bBackupWasTried)
2511 if (!bOriginalWasTried)
2513 rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2514 // try original URL ... ! dont continue with next item here ...
2516 else
2518 rInfo.DocumentState |= AutoRecovery::E_DAMAGED;
2519 continue;
2523 OUString sLoadOriginalURL;
2524 OUString sLoadBackupURL ;
2526 if (!bBackupWasTried)
2527 sLoadBackupURL = rInfo.OldTempURL;
2529 if (!rInfo.OrgURL.isEmpty())
2531 sLoadOriginalURL = rInfo.OrgURL;
2533 else if (!rInfo.TemplateURL.isEmpty())
2535 sLoadOriginalURL = rInfo.TemplateURL;
2536 lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True;
2537 lDescriptor[::comphelper::MediaDescriptor::PROP_TEMPLATENAME()] <<= rInfo.TemplateURL;
2539 else if (!rInfo.FactoryURL.isEmpty())
2541 sLoadOriginalURL = rInfo.FactoryURL;
2542 lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True;
2545 // A "Salvaged" item must exists every time. The core can make something special then for recovery.
2546 // Of course it should be the real file name of the original file, in case we load the temp. backup here.
2547 OUString sURL;
2548 if (!sLoadBackupURL.isEmpty())
2550 sURL = sLoadBackupURL;
2551 rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_BACKUP;
2552 lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= sLoadOriginalURL;
2554 else if (!sLoadOriginalURL.isEmpty())
2556 sURL = sLoadOriginalURL;
2557 rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_ORIGINAL;
2559 else
2560 continue; // TODO ERROR!
2562 LoadEnv::initializeUIDefaults( comphelper::getComponentContext(m_xSMGR), lDescriptor, true, NULL );
2564 // <- SAFE ------------------------------
2565 aWriteLock.unlock();
2567 implts_flushConfigItem(rInfo);
2568 implts_informListener(eJob,
2569 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2573 implts_openOneDoc(sURL, lDescriptor, rInfo);
2575 catch(const css::uno::Exception&)
2577 rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP;
2578 rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL;
2579 if (!sLoadBackupURL.isEmpty())
2581 rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
2582 eTimer = AutoRecovery::E_CALL_ME_BACK;
2584 else
2586 rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2587 rInfo.DocumentState |= AutoRecovery::E_DAMAGED;
2590 implts_flushConfigItem(rInfo, sal_True);
2591 implts_informListener(eJob,
2592 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2594 // SAFE -> ------------------------------
2595 // Needed for next loop!
2596 aWriteLock.lock();
2597 continue;
2600 if (!rInfo.RealFilter.isEmpty())
2602 ::comphelper::MediaDescriptor lPatchDescriptor(rInfo.Document->getArgs());
2603 lPatchDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.RealFilter;
2604 rInfo.Document->attachResource(rInfo.Document->getURL(), lPatchDescriptor.getAsConstPropertyValueList());
2605 // do *not* use sURL here. In case this points to the recovery file, it has already been passed
2606 // to recoverFromFile. Also, passing it here is logically wrong, as attachResource is intended
2607 // to take the logical file URL.
2610 css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
2611 if ( xModify.is() )
2613 sal_Bool bModified = ((rInfo.DocumentState & AutoRecovery::E_MODIFIED) == AutoRecovery::E_MODIFIED);
2614 xModify->setModified(bModified);
2617 rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP;
2618 rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL;
2619 rInfo.DocumentState |= AutoRecovery::E_HANDLED;
2620 rInfo.DocumentState |= AutoRecovery::E_SUCCEDED;
2622 implts_flushConfigItem(rInfo);
2623 implts_informListener(eJob,
2624 AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
2626 /* Normaly we listen as XModifyListener on a document to know if a document was changed
2627 since our last AutoSave. And we deregister us in case we know this state.
2628 But directly after one document as recovered ... we must start listening.
2629 Otherwhise the first "modify" doesnt reach us. Because we ourself called setModified()
2630 on the document via API. And currently we dont listen for any events (not at the GlobalEventBroadcaster
2631 nor at any document!).
2633 implts_startModifyListeningOnDoc(rInfo);
2635 // SAFE -> ------------------------------
2636 // Needed for next loop. Dont unlock it again!
2637 aWriteLock.lock();
2640 aWriteLock.unlock();
2641 // <- SAFE ----------------------------------
2643 return eTimer;
2646 //-----------------------------------------------
2647 void AutoRecovery::implts_openOneDoc(const OUString& sURL ,
2648 ::comphelper::MediaDescriptor& lDescriptor,
2649 AutoRecovery::TDocumentInfo& rInfo )
2651 // SAFE -> ----------------------------------
2652 ReadGuard aReadLock(m_aLock);
2653 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2654 aReadLock.unlock();
2655 // <- SAFE ----------------------------------
2657 css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( comphelper::getComponentContext(xSMGR));
2659 ::std::vector< Reference< XComponent > > aCleanup;
2662 // create a new document of the desired type
2663 Reference< XModel2 > xModel( xSMGR->createInstance( rInfo.FactoryService ), UNO_QUERY_THROW );
2664 aCleanup.push_back( xModel.get() );
2666 // put the filter name into the descriptor - we're not going to involve any type detection, so
2667 // the document might be lost without the FilterName property
2668 if ( (rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL)
2669 lDescriptor[ ::comphelper::MediaDescriptor::PROP_FILTERNAME() ] <<= rInfo.RealFilter;
2670 else
2671 lDescriptor[ ::comphelper::MediaDescriptor::PROP_FILTERNAME() ] <<= rInfo.DefaultFilter;
2673 if ( sURL == rInfo.FactoryURL )
2675 // if the document was a new, unmodified document, then there's nothing to recover, just to init
2676 ENSURE_OR_THROW( ( rInfo.DocumentState & AutoRecovery::E_MODIFIED ) == 0,
2677 "unexpected document state" );
2678 Reference< XLoadable > xModelLoad( xModel, UNO_QUERY_THROW );
2679 xModelLoad->initNew();
2681 // TODO: remove load-process specific arguments from the descriptor, e.g. the status indicator
2682 xModel->attachResource( sURL, lDescriptor.getAsConstPropertyValueList() );
2684 else
2686 // let it recover itself
2687 Reference< XDocumentRecovery > xDocRecover( xModel, UNO_QUERY_THROW );
2688 xDocRecover->recoverFromFile(
2689 sURL,
2690 lDescriptor.getUnpackedValueOrDefault( ::comphelper::MediaDescriptor::PROP_SALVAGEDFILE(), OUString() ),
2691 lDescriptor.getAsConstPropertyValueList()
2694 // No attachResource needed here. By definition (of XDocumentRecovery), the implementation is responsible
2695 // for completely initializing the model, which includes attachResource (or equivalent), if required.
2698 // re-create all the views
2699 ::std::vector< OUString > aViewsToRestore( rInfo.ViewNames.getLength() );
2700 if ( rInfo.ViewNames.getLength() )
2701 ::std::copy( rInfo.ViewNames.getConstArray(), rInfo.ViewNames.getConstArray() + rInfo.ViewNames.getLength(), aViewsToRestore.begin() );
2702 // if we don't have views for whatever reason, then create a default-view, at least
2703 if ( aViewsToRestore.empty() )
2704 aViewsToRestore.push_back( OUString() );
2706 for ( ::std::vector< OUString >::const_iterator viewName = aViewsToRestore.begin();
2707 viewName != aViewsToRestore.end();
2708 ++viewName
2711 // create a frame
2712 Reference< XFrame > xTargetFrame = xDesktop->findFrame( SPECIALTARGET_BLANK, 0 );
2713 aCleanup.push_back( xTargetFrame.get() );
2715 // create a view to the document
2716 Reference< XController2 > xController;
2717 if ( viewName->getLength() )
2719 xController.set( xModel->createViewController( *viewName, Sequence< PropertyValue >(), xTargetFrame ), UNO_SET_THROW );
2721 else
2723 xController.set( xModel->createDefaultViewController( xTargetFrame ), UNO_SET_THROW );
2726 // introduce model/view/controller to each other
2727 xController->attachModel( xModel.get() );
2728 xModel->connectController( xController.get() );
2729 xTargetFrame->setComponent( xController->getComponentWindow(), xController.get() );
2730 xController->attachFrame( xTargetFrame );
2731 xModel->setCurrentController( xController.get() );
2734 rInfo.Document = xModel.get();
2736 catch(const css::uno::RuntimeException&)
2738 throw;
2740 catch(const css::uno::Exception&)
2742 Any aCaughtException( ::cppu::getCaughtException() );
2744 // clean up
2745 for ( ::std::vector< Reference< XComponent > >::const_iterator component = aCleanup.begin();
2746 component != aCleanup.end();
2747 ++component
2750 css::uno::Reference< css::util::XCloseable > xClose( *component, css::uno::UNO_QUERY );
2751 if ( xClose.is() )
2752 xClose->close( sal_True );
2753 else
2754 (*component)->dispose();
2757 // re-throw
2758 OUStringBuffer sMsg(256);
2759 sMsg.appendAscii("Recovery of \"");
2760 sMsg.append (sURL );
2761 sMsg.appendAscii("\" failed." );
2763 throw css::lang::WrappedTargetException(
2764 sMsg.makeStringAndClear(),
2765 static_cast< css::frame::XDispatch* >(this),
2766 aCaughtException
2771 //-----------------------------------------------
2772 void AutoRecovery::implts_generateNewTempURL(const OUString& sBackupPath ,
2773 ::comphelper::MediaDescriptor& /*rMediaDescriptor*/,
2774 AutoRecovery::TDocumentInfo& rInfo )
2776 // SAFE -> ----------------------------------
2777 ReadGuard aReadLock(m_aLock);
2778 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
2779 aReadLock.unlock();
2780 // <- SAFE ----------------------------------
2782 // specify URL for saving (which points to a temp file inside backup directory)
2783 // and define an unique name, so we can locate it later.
2784 // This unique name must solve an optimization problem too!
2785 // In case we are asked to save unmodified documents too - and one of them
2786 // is an empty one (because it was new created using e.g. an URL private:factory/...)
2787 // we should not save it realy. Then we put the information about such "empty document"
2788 // into the configuration and dont create any recovery file on disk.
2789 // We use the title of the document to make it unique.
2790 OUStringBuffer sUniqueName;
2791 if (!rInfo.OrgURL.isEmpty())
2793 css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(::comphelper::getComponentContext(m_xSMGR)));
2794 css::util::URL aURL;
2795 aURL.Complete = rInfo.OrgURL;
2796 xParser->parseStrict(aURL);
2797 sUniqueName.append(aURL.Name);
2799 else if (!rInfo.FactoryURL.isEmpty())
2800 sUniqueName.appendAscii("untitled");
2801 sUniqueName.appendAscii("_");
2803 // TODO: Must we strip some illegal signes - if we use the title?
2805 OUString sName(sUniqueName.makeStringAndClear());
2806 String sExtension(rInfo.Extension);
2807 String sPath(sBackupPath);
2808 ::utl::TempFile aTempFile(sName, &sExtension, &sPath);
2810 rInfo.NewTempURL = aTempFile.GetURL();
2813 //-----------------------------------------------
2814 void AutoRecovery::implts_informListener( sal_Int32 eJob ,
2815 const css::frame::FeatureStateEvent& aEvent)
2817 // Helper shares mutex with us -> threadsafe!
2818 ::cppu::OInterfaceContainerHelper* pListenerForURL = 0;
2819 OUString sJob = AutoRecovery::implst_getJobDescription(eJob);
2821 // inform listener, which are registered for any URLs(!)
2822 pListenerForURL = m_lListener.getContainer(sJob);
2823 if(pListenerForURL != 0)
2825 ::cppu::OInterfaceIteratorHelper pIt(*pListenerForURL);
2826 while(pIt.hasMoreElements())
2830 css::uno::Reference< css::frame::XStatusListener > xListener(((css::frame::XStatusListener*)pIt.next()), css::uno::UNO_QUERY);
2831 xListener->statusChanged(aEvent);
2833 catch(const css::uno::RuntimeException&)
2835 pIt.remove();
2841 //-----------------------------------------------
2842 OUString AutoRecovery::implst_getJobDescription(sal_Int32 eJob)
2844 // describe the current running operation
2845 OUStringBuffer sFeature(256);
2846 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_PROTOCOL));
2848 // Attention: Because "eJob" is used as a flag field the order of checking these
2849 // flags is important. We must preferr job with higher priorities!
2850 // E.g. EmergencySave has an higher prio then AutoSave ...
2851 // On the other side there exist a well defined order between two different jobs.
2852 // e.g. PrepareEmergencySave must be done before EmergencySave is started of course.
2854 if ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE)
2855 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_PREPARE_EMERGENCY_SAVE));
2856 else if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE)
2857 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_EMERGENCY_SAVE));
2858 else if ((eJob & AutoRecovery::E_RECOVERY) == AutoRecovery::E_RECOVERY)
2859 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_RECOVERY));
2860 else if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE)
2861 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_SESSION_SAVE));
2862 else if ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT) == AutoRecovery::E_SESSION_QUIET_QUIT)
2863 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_SESSION_QUIET_QUIT));
2864 else if ((eJob & AutoRecovery::E_SESSION_RESTORE) == AutoRecovery::E_SESSION_RESTORE)
2865 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_SESSION_RESTORE));
2866 else if ((eJob & AutoRecovery::E_ENTRY_BACKUP) == AutoRecovery::E_ENTRY_BACKUP)
2867 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_ENTRY_BACKUP));
2868 else if ((eJob & AutoRecovery::E_ENTRY_CLEANUP) == AutoRecovery::E_ENTRY_CLEANUP)
2869 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_ENTRY_CLEANUP));
2870 else if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
2871 sFeature.appendAscii(RTL_CONSTASCII_STRINGPARAM(CMD_DO_AUTO_SAVE));
2872 #ifdef ENABLE_WARNINGS
2873 else if ( eJob != AutoRecovery::E_NO_JOB )
2874 LOG_WARNING("AutoRecovery::implst_getJobDescription()", "Invalid job identifier detected.")
2875 #endif
2877 return sFeature.makeStringAndClear();
2880 //-----------------------------------------------
2881 sal_Int32 AutoRecovery::implst_classifyJob(const css::util::URL& aURL)
2883 if ( aURL.Protocol == CMD_PROTOCOL )
2885 if ( aURL.Path == CMD_DO_PREPARE_EMERGENCY_SAVE )
2886 return AutoRecovery::E_PREPARE_EMERGENCY_SAVE;
2887 else if ( aURL.Path == CMD_DO_EMERGENCY_SAVE )
2888 return AutoRecovery::E_EMERGENCY_SAVE;
2889 else if ( aURL.Path == CMD_DO_RECOVERY )
2890 return AutoRecovery::E_RECOVERY;
2891 else if ( aURL.Path == CMD_DO_ENTRY_BACKUP )
2892 return AutoRecovery::E_ENTRY_BACKUP;
2893 else if ( aURL.Path == CMD_DO_ENTRY_CLEANUP )
2894 return AutoRecovery::E_ENTRY_CLEANUP;
2895 else if ( aURL.Path == CMD_DO_SESSION_SAVE )
2896 return AutoRecovery::E_SESSION_SAVE;
2897 else if ( aURL.Path == CMD_DO_SESSION_QUIET_QUIT )
2898 return AutoRecovery::E_SESSION_QUIET_QUIT;
2899 else if ( aURL.Path == CMD_DO_SESSION_RESTORE )
2900 return AutoRecovery::E_SESSION_RESTORE;
2901 else if ( aURL.Path == CMD_DO_DISABLE_RECOVERY )
2902 return AutoRecovery::E_DISABLE_AUTORECOVERY;
2903 else if ( aURL.Path == CMD_DO_SET_AUTOSAVE_STATE )
2904 return AutoRecovery::E_SET_AUTOSAVE_STATE;
2907 LOG_WARNING("AutoRecovery::implts_classifyJob()", "Invalid URL (protocol).")
2908 return AutoRecovery::E_NO_JOB;
2911 //-----------------------------------------------
2912 css::frame::FeatureStateEvent AutoRecovery::implst_createFeatureStateEvent( sal_Int32 eJob ,
2913 const OUString& sEventType,
2914 AutoRecovery::TDocumentInfo* pInfo )
2916 css::frame::FeatureStateEvent aEvent;
2917 aEvent.FeatureURL.Complete = AutoRecovery::implst_getJobDescription(eJob);
2918 aEvent.FeatureDescriptor = sEventType;
2920 if (pInfo && sEventType.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM(OPERATION_UPDATE)))
2922 // pack rInfo for transport via UNO
2923 ::comphelper::NamedValueCollection aInfo;
2924 aInfo.put( OUString(CFG_ENTRY_PROP_ID), pInfo->ID );
2925 aInfo.put( OUString(CFG_ENTRY_PROP_ORIGINALURL), pInfo->OrgURL );
2926 aInfo.put( OUString(CFG_ENTRY_PROP_FACTORYURL), pInfo->FactoryURL );
2927 aInfo.put( OUString(CFG_ENTRY_PROP_TEMPLATEURL), pInfo->TemplateURL );
2928 aInfo.put( OUString(CFG_ENTRY_PROP_TEMPURL), pInfo->OldTempURL.isEmpty() ? pInfo->NewTempURL : pInfo->OldTempURL );
2929 aInfo.put( OUString(CFG_ENTRY_PROP_MODULE), pInfo->AppModule) ;
2930 aInfo.put( OUString(CFG_ENTRY_PROP_TITLE), pInfo->Title);
2931 aInfo.put( OUString(CFG_ENTRY_PROP_VIEWNAMES), pInfo->ViewNames);
2932 aInfo.put( OUString(CFG_ENTRY_PROP_DOCUMENTSTATE), pInfo->DocumentState);
2934 aEvent.State <<= aInfo.getPropertyValues();
2937 return aEvent;
2940 //-----------------------------------------------
2941 void AutoRecovery::implts_resetHandleStates(sal_Bool /*bLoadCache*/)
2943 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2945 // SAFE -> ------------------------------
2946 WriteGuard aWriteLock(m_aLock);
2948 AutoRecovery::TDocumentList::iterator pIt;
2949 for ( pIt = m_lDocCache.begin();
2950 pIt != m_lDocCache.end() ;
2951 ++pIt )
2953 AutoRecovery::TDocumentInfo& rInfo = *pIt;
2954 rInfo.DocumentState &= ~AutoRecovery::E_HANDLED ;
2955 rInfo.DocumentState &= ~AutoRecovery::E_POSTPONED;
2957 // SAFE -> ------------------------------
2958 aWriteLock.unlock();
2959 implts_flushConfigItem(rInfo);
2960 aWriteLock.lock();
2961 // <- SAFE ------------------------------
2964 aWriteLock.unlock();
2965 // <- SAFE ----------------------------------
2968 //-----------------------------------------------
2969 void AutoRecovery::implts_prepareEmergencySave()
2971 // Be sure to know all open documents realy .-)
2972 implts_verifyCacheAgainstDesktopDocumentList();
2974 // hide all docs, so the user cant disturb our emergency save .-)
2975 implts_changeAllDocVisibility(sal_False);
2978 //-----------------------------------------------
2979 void AutoRecovery::implts_doEmergencySave(const DispatchParams& aParams)
2981 // Write a hint "we chrashed" into the configuration, so
2982 // the error report tool is started too in case no recovery
2983 // documents exists and was saved.
2984 ::comphelper::ConfigurationHelper::writeDirectKey(
2985 comphelper::getComponentContext(m_xSMGR),
2986 OUString(CFG_PACKAGE_RECOVERY),
2987 OUString(CFG_PATH_RECOVERYINFO),
2988 OUString(CFG_ENTRY_CRASHED),
2989 css::uno::makeAny(sal_True),
2990 ::comphelper::ConfigurationHelper::E_STANDARD);
2992 // for all docs, store their current view/names in the configurtion
2993 implts_persistAllActiveViewNames();
2995 // The called method for saving documents runs
2996 // during normal AutoSave more then once. Because
2997 // it postpone active documents and save it later.
2998 // That is normaly done by recalling it from a timer.
2999 // Here we must do it immediately!
3000 // Of course this method returns the right state -
3001 // because it knows, that we are running in ERMERGENCY SAVE mode .-)
3003 sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-)
3004 AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3007 eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_True, &aParams);
3009 while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3011 // reset the handle state of all
3012 // cache items. Such handle state indicates, that a document
3013 // was already saved during the THIS(!) EmergencySave session.
3014 // Of course following recovery session must be started without
3015 // any "handle" state ...
3016 implts_resetHandleStates(sal_False);
3018 // flush config cached back to disc.
3019 impl_flushALLConfigChanges();
3021 // try to make sure next time office will be started user wont be
3022 // notified about any other might be running office instance
3023 // remove ".lock" file from disc !
3024 AutoRecovery::st_impl_removeLockFile();
3027 //-----------------------------------------------
3028 void AutoRecovery::implts_doRecovery(const DispatchParams& aParams)
3030 AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3033 eSuggestedTimer = implts_openDocs(aParams);
3035 while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3037 // reset the handle state of all
3038 // cache items. Such handle state indicates, that a document
3039 // was already saved during the THIS(!) Recovery session.
3040 // Of course a may be following EmergencySave session must be started without
3041 // any "handle" state ...
3042 implts_resetHandleStates(sal_True);
3044 // Reset the configuration hint "we was crashed"!
3045 ::comphelper::ConfigurationHelper::writeDirectKey(
3046 comphelper::getComponentContext(m_xSMGR),
3047 OUString(CFG_PACKAGE_RECOVERY),
3048 OUString(CFG_PATH_RECOVERYINFO),
3049 OUString(CFG_ENTRY_CRASHED),
3050 css::uno::makeAny(sal_False),
3051 ::comphelper::ConfigurationHelper::E_STANDARD);
3054 //-----------------------------------------------
3055 void AutoRecovery::implts_doSessionSave(const DispatchParams& aParams)
3057 LOG_RECOVERY("AutoRecovery::implts_doSessionSave()")
3059 // Be sure to know all open documents realy .-)
3060 implts_verifyCacheAgainstDesktopDocumentList();
3062 // for all docs, store their current view/names in the configurtion
3063 implts_persistAllActiveViewNames();
3065 // The called method for saving documents runs
3066 // during normal AutoSave more then once. Because
3067 // it postpone active documents and save it later.
3068 // That is normaly done by recalling it from a timer.
3069 // Here we must do it immediately!
3070 // Of course this method returns the right state -
3071 // because it knows, that we are running in SESSION SAVE mode .-)
3073 sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-)
3074 AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3077 // do not remove lock files of the documents, it will be done on session quit
3078 eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False, &aParams);
3080 while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3082 // reset the handle state of all
3083 // cache items. Such handle state indicates, that a document
3084 // was already saved during the THIS(!) save session.
3085 // Of course following restore session must be started without
3086 // any "handle" state ...
3087 implts_resetHandleStates(sal_False);
3089 // flush config cached back to disc.
3090 impl_flushALLConfigChanges();
3093 //-----------------------------------------------
3094 void AutoRecovery::implts_doSessionQuietQuit(const DispatchParams& /*aParams*/)
3096 LOG_RECOVERY("AutoRecovery::implts_doSessionQuietQuit()")
3098 // try to make sure next time office will be started user wont be
3099 // notified about any other might be running office instance
3100 // remove ".lock" file from disc !
3101 // it is done as a first action for session save since Gnome sessions
3102 // do not provide enough time for shutdown, and the dialog looks to be
3103 // confusing for the user
3104 AutoRecovery::st_impl_removeLockFile();
3106 // reset all modified documents, so the dont show any UI on closing ...
3107 // and close all documents, so we can shutdown the OS!
3108 implts_prepareSessionShutdown();
3110 // Write a hint for "stored session data" into the configuration, so
3111 // the on next startup we know what's happen last time
3112 ::comphelper::ConfigurationHelper::writeDirectKey(
3113 comphelper::getComponentContext(m_xSMGR),
3114 OUString(CFG_PACKAGE_RECOVERY),
3115 OUString(CFG_PATH_RECOVERYINFO),
3116 OUString(CFG_ENTRY_SESSIONDATA),
3117 css::uno::makeAny(sal_True),
3118 ::comphelper::ConfigurationHelper::E_STANDARD);
3120 // flush config cached back to disc.
3121 impl_flushALLConfigChanges();
3125 //-----------------------------------------------
3126 void AutoRecovery::implts_doSessionRestore(const DispatchParams& aParams)
3128 LOG_RECOVERY("AutoRecovery::implts_doSessionRestore() ...")
3130 AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3133 eSuggestedTimer = implts_openDocs(aParams);
3135 while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3137 // reset the handle state of all
3138 // cache items. Such handle state indicates, that a document
3139 // was already saved during the THIS(!) Restore session.
3140 // Of course a may be following save session must be started without
3141 // any "handle" state ...
3142 implts_resetHandleStates(sal_True);
3144 // make all opened documents visible
3145 implts_changeAllDocVisibility(sal_True);
3147 // Reset the configuration hint for "session save"!
3148 LOG_RECOVERY("... reset config key 'SessionData'")
3149 ::comphelper::ConfigurationHelper::writeDirectKey(
3150 comphelper::getComponentContext(m_xSMGR),
3151 OUString(CFG_PACKAGE_RECOVERY),
3152 OUString(CFG_PATH_RECOVERYINFO),
3153 OUString(CFG_ENTRY_SESSIONDATA),
3154 css::uno::makeAny(sal_False),
3155 ::comphelper::ConfigurationHelper::E_STANDARD);
3157 LOG_RECOVERY("... AutoRecovery::implts_doSessionRestore()")
3160 //-----------------------------------------------
3161 void AutoRecovery::implts_backupWorkingEntry(const DispatchParams& aParams)
3163 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
3165 AutoRecovery::TDocumentList::iterator pIt;
3166 for ( pIt = m_lDocCache.begin();
3167 pIt != m_lDocCache.end() ;
3168 ++pIt )
3170 const AutoRecovery::TDocumentInfo& rInfo = *pIt;
3171 if (rInfo.ID != aParams.m_nWorkingEntryID)
3172 continue;
3174 OUString sSourceURL;
3175 // Prefer temp file. It contains the changes against the original document!
3176 if (!rInfo.OldTempURL.isEmpty())
3177 sSourceURL = rInfo.OldTempURL;
3178 else if (!rInfo.NewTempURL.isEmpty())
3179 sSourceURL = rInfo.NewTempURL;
3180 else if (!rInfo.OrgURL.isEmpty())
3181 sSourceURL = rInfo.OrgURL;
3182 else
3183 continue; // nothing real to save! An unmodified but new created document.
3185 INetURLObject aParser(sSourceURL);
3186 // AutoRecovery::EFailureSafeResult eResult =
3187 implts_copyFile(sSourceURL, aParams.m_sSavePath, aParser.getName());
3189 // TODO: Check eResult and react for errors (InteractionHandler!?)
3190 // Currently we ignore it ...
3191 // DONT UPDATE THE CACHE OR REMOVE ANY TEMP. FILES FROM DISK.
3192 // That has to be forced from outside explicitly.
3193 // See implts_cleanUpWorkingEntry() for further details.
3197 //-----------------------------------------------
3198 void AutoRecovery::implts_cleanUpWorkingEntry(const DispatchParams& aParams)
3200 CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
3202 AutoRecovery::TDocumentList::iterator pIt;
3203 for ( pIt = m_lDocCache.begin();
3204 pIt != m_lDocCache.end() ;
3205 ++pIt )
3207 AutoRecovery::TDocumentInfo& rInfo = *pIt;
3208 if (rInfo.ID != aParams.m_nWorkingEntryID)
3209 continue;
3211 AutoRecovery::st_impl_removeFile(rInfo.OldTempURL);
3212 AutoRecovery::st_impl_removeFile(rInfo.NewTempURL);
3213 implts_flushConfigItem(rInfo, sal_True); // sal_True => remove it from xml config!
3215 m_lDocCache.erase(pIt);
3216 break; /// !!! pIt is not defined any longer ... further this function has finished it's work
3220 //-----------------------------------------------
3221 AutoRecovery::EFailureSafeResult AutoRecovery::implts_copyFile(const OUString& sSource ,
3222 const OUString& sTargetPath,
3223 const OUString& sTargetName)
3225 // create content for the parent folder and call transfer on that content with the source content
3226 // and the destination file name as parameters
3228 css::uno::Reference< css::ucb::XCommandEnvironment > xEnvironment;
3230 ::ucbhelper::Content aSourceContent;
3231 ::ucbhelper::Content aTargetContent;
3235 aTargetContent = ::ucbhelper::Content(sTargetPath, xEnvironment, comphelper::getComponentContext(m_xSMGR));
3237 catch(const css::uno::Exception&)
3239 return AutoRecovery::E_WRONG_TARGET_PATH;
3242 sal_Int32 nNameClash;
3243 nNameClash = css::ucb::NameClash::RENAME;
3247 ::ucbhelper::Content::create(sSource, xEnvironment, comphelper::getComponentContext(m_xSMGR), aSourceContent);
3248 aTargetContent.transferContent(aSourceContent, ::ucbhelper::InsertOperation_COPY, sTargetName, nNameClash);
3250 catch(const css::uno::Exception&)
3252 return AutoRecovery::E_ORIGINAL_FILE_MISSING;
3255 return AutoRecovery::E_COPIED;
3258 //-----------------------------------------------
3259 sal_Bool SAL_CALL AutoRecovery::convertFastPropertyValue( css::uno::Any& /*aConvertedValue*/,
3260 css::uno::Any& /*aOldValue*/ ,
3261 sal_Int32 /*nHandle*/ ,
3262 const css::uno::Any& /*aValue*/ )
3263 throw(css::lang::IllegalArgumentException)
3265 // not needed currently
3266 return sal_False;
3269 //-----------------------------------------------
3270 void SAL_CALL AutoRecovery::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/,
3271 const css::uno::Any& /*aValue*/ )
3272 throw(css::uno::Exception)
3274 // not needed currently
3277 //-----------------------------------------------
3278 void SAL_CALL AutoRecovery::getFastPropertyValue(css::uno::Any& aValue ,
3279 sal_Int32 nHandle) const
3281 switch(nHandle)
3283 case AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA :
3285 sal_Bool bSessionData = sal_False;
3286 ::comphelper::ConfigurationHelper::readDirectKey(
3287 comphelper::getComponentContext(m_xSMGR),
3288 OUString(CFG_PACKAGE_RECOVERY),
3289 OUString(CFG_PATH_RECOVERYINFO),
3290 OUString(CFG_ENTRY_SESSIONDATA),
3291 ::comphelper::ConfigurationHelper::E_READONLY) >>= bSessionData;
3293 sal_Bool bRecoveryData = ((sal_Bool)(m_lDocCache.size()>0));
3295 // exists session data ... => then we cant say, that these
3296 // data are valid for recovery. So we have to return sal_False then!
3297 if (bSessionData)
3298 bRecoveryData = sal_False;
3300 aValue <<= bRecoveryData;
3302 break;
3304 case AUTORECOVERY_PROPHANDLE_CRASHED :
3305 aValue = ::comphelper::ConfigurationHelper::readDirectKey(
3306 comphelper::getComponentContext(m_xSMGR),
3307 OUString(CFG_PACKAGE_RECOVERY),
3308 OUString(CFG_PATH_RECOVERYINFO),
3309 OUString(CFG_ENTRY_CRASHED),
3310 ::comphelper::ConfigurationHelper::E_READONLY);
3311 break;
3313 case AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA :
3314 aValue = ::comphelper::ConfigurationHelper::readDirectKey(
3315 comphelper::getComponentContext(m_xSMGR),
3316 OUString(CFG_PACKAGE_RECOVERY),
3317 OUString(CFG_PATH_RECOVERYINFO),
3318 OUString(CFG_ENTRY_SESSIONDATA),
3319 ::comphelper::ConfigurationHelper::E_READONLY);
3320 break;
3324 //-----------------------------------------------
3325 const css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor()
3327 const css::beans::Property pPropertys[] =
3329 css::beans::Property( AUTORECOVERY_PROPNAME_CRASHED , AUTORECOVERY_PROPHANDLE_CRASHED , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3330 css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA, AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA, ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3331 css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA , AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
3333 const css::uno::Sequence< css::beans::Property > lPropertyDescriptor(pPropertys, AUTORECOVERY_PROPCOUNT);
3334 return lPropertyDescriptor;
3337 //-----------------------------------------------
3338 ::cppu::IPropertyArrayHelper& SAL_CALL AutoRecovery::getInfoHelper()
3340 static ::cppu::OPropertyArrayHelper* pInfoHelper = 0;
3341 if(!pInfoHelper)
3343 ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() );
3344 if(!pInfoHelper)
3346 static ::cppu::OPropertyArrayHelper aInfoHelper(impl_getStaticPropertyDescriptor(), sal_True);
3347 pInfoHelper = &aInfoHelper;
3351 return (*pInfoHelper);
3354 //-----------------------------------------------
3355 css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL AutoRecovery::getPropertySetInfo()
3356 throw(css::uno::RuntimeException)
3358 static css::uno::Reference< css::beans::XPropertySetInfo >* pInfo = 0;
3359 if(!pInfo)
3361 ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() );
3362 if(!pInfo)
3364 static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(createPropertySetInfo(getInfoHelper()));
3365 pInfo = &xInfo;
3369 return (*pInfo);
3372 //-----------------------------------------------
3373 void AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()
3375 LOG_RECOVERY("AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() ...")
3377 // SAFE -> ----------------------------------
3378 WriteGuard aWriteLock(m_aLock);
3379 css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR;
3380 aWriteLock.unlock();
3381 // <- SAFE ----------------------------------
3385 css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create( comphelper::getComponentContext(xSMGR));
3387 css::uno::Reference< css::container::XIndexAccess > xContainer(
3388 xDesktop->getFrames(),
3389 css::uno::UNO_QUERY_THROW);
3391 sal_Int32 i = 0;
3392 sal_Int32 c = xContainer->getCount();
3394 for (i=0; i<c; ++i)
3396 css::uno::Reference< css::frame::XFrame > xFrame;
3399 xContainer->getByIndex(i) >>= xFrame;
3400 if (!xFrame.is())
3401 continue;
3403 // can happen in multithreaded environments, that frames was removed from the container during this loop runs!
3404 // Ignore it.
3405 catch(const css::lang::IndexOutOfBoundsException&)
3407 continue;
3410 // We are interested on visible documents only.
3411 // Note: It's n optional interface .-(
3412 css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(
3413 xFrame->getContainerWindow(),
3414 css::uno::UNO_QUERY);
3415 if (
3416 (!xVisibleCheck.is() ) ||
3417 (!xVisibleCheck->isVisible())
3420 continue;
3423 // extract the model from the frame.
3424 // Ignore "view only" frames, which does not have a model.
3425 css::uno::Reference< css::frame::XController > xController;
3426 css::uno::Reference< css::frame::XModel > xModel;
3428 xController = xFrame->getController();
3429 if (xController.is())
3430 xModel = xController->getModel();
3431 if (!xModel.is())
3432 continue;
3434 // insert model into cache ...
3435 // If the model is already well known inside cache
3436 // it's information set will be updated by asking the
3437 // model again for it's new states.
3438 implts_registerDocument(xModel);
3441 catch(const css::uno::RuntimeException&)
3443 throw;
3445 catch(const css::uno::Exception&)
3449 LOG_RECOVERY("... AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()")
3452 //-----------------------------------------------
3453 sal_Bool AutoRecovery::impl_enoughDiscSpace(sal_Int32 nRequiredSpace)
3455 #ifdef SIMULATE_FULL_DISC
3456 return sal_False;
3457 #else // SIMULATE_FULL_DISC
3458 // In case an error occures and we are not able to retrieve the needed information
3459 // it's better to "disable" the feature ShowErrorOnFullDisc !
3460 // Otherwhise we start a confusing process of error handling ...
3462 sal_uInt64 nFreeSpace = SAL_MAX_UINT64;
3464 OUString sBackupPath(SvtPathOptions().GetBackupPath());
3465 ::osl::VolumeInfo aInfo (osl_VolumeInfo_Mask_FreeSpace);
3466 ::osl::FileBase::RC aRC = ::osl::Directory::getVolumeInfo(sBackupPath, aInfo);
3468 if (
3469 (aInfo.isValid(osl_VolumeInfo_Mask_FreeSpace)) &&
3470 (aRC == ::osl::FileBase::E_None )
3473 nFreeSpace = aInfo.getFreeSpace();
3476 sal_uInt64 nFreeMB = (nFreeSpace/1048576);
3477 return (nFreeMB >= (sal_uInt64)nRequiredSpace);
3478 #endif // SIMULATE_FULL_DISC
3481 //-----------------------------------------------
3482 void AutoRecovery::impl_showFullDiscError()
3484 OUString sBtn(FWK_RESSTR(STR_FULL_DISC_RETRY_BUTTON));
3485 OUString sMsg(FWK_RESSTR(STR_FULL_DISC_MSG));
3487 OUString sBackupURL(SvtPathOptions().GetBackupPath());
3488 INetURLObject aConverter(sBackupURL);
3489 sal_Unicode aDelimiter;
3490 OUString sBackupPath = aConverter.getFSysPath(INetURLObject::FSYS_DETECT, &aDelimiter);
3491 if (sBackupPath.getLength() < 1)
3492 sBackupPath = sBackupURL;
3494 ErrorBox dlgError(
3495 0, WB_OK,
3496 sMsg.replaceAll("%PATH", sBackupPath));
3497 dlgError.SetButtonText(dlgError.GetButtonId(0), sBtn);
3498 dlgError.Execute();
3501 //-----------------------------------------------
3502 void AutoRecovery::impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo ,
3503 ::comphelper::MediaDescriptor& rArgs ,
3504 const css::uno::Reference< css::frame::XFrame >& xNewFrame)
3506 // external well known frame must be preferred (because it was created by ourself
3507 // for loading documents into this frame)!
3508 // But if no frame exists ... we can try to locate it using any frame bound to the provided
3509 // document. Of course we must live without any frame in case the document does not exists at this
3510 // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-)
3511 css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
3512 if (
3513 (!xFrame.is() ) &&
3514 (rInfo.Document.is())
3517 css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
3518 if (xController.is())
3519 xFrame = xController->getFrame();
3522 // Any outside progress must be used ...
3523 // Only if there is no progress, we can create our own one.
3524 css::uno::Reference< css::task::XStatusIndicator > xInternalProgress;
3525 css::uno::Reference< css::task::XStatusIndicator > xExternalProgress = rArgs.getUnpackedValueOrDefault(
3526 ::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(),
3527 css::uno::Reference< css::task::XStatusIndicator >() );
3529 // Normaly a progress is set from outside (e.g. by the CrashSave/Recovery dialog, which uses our dispatch API).
3530 // But for a normal auto save we dont have such "external progress"... because this function is triggered by our own timer then.
3531 // In such case we must create our own progress !
3532 if (
3533 (! xExternalProgress.is()) &&
3534 (xFrame.is() )
3537 css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xFrame, css::uno::UNO_QUERY);
3538 if (xProgressFactory.is())
3539 xInternalProgress = xProgressFactory->createStatusIndicator();
3542 // HACK
3543 // An external provided progress (most given by the CrashSave/Recovery dialog)
3544 // must be preferred. But we know that some application filters query it's own progress instance
3545 // at the frame method Frame::createStatusIndicator().
3546 // So we use a two step mechanism:
3547 // 1) we set the progress inside the MediaDescriptor, which will be provided to the filter
3548 // 2) and we set a special Frame property, which overwrites the normal behaviour of Frame::createStatusIndicator .-)
3549 // But we supress 2) in case we uses an internal progress. Because then it doesnt matter
3550 // if our applications make it wrong. In such case the internal progress resists at the same frame
3551 // and there is no need to forward progress activities to e.g. an outside dialog .-)
3552 if (
3553 (xExternalProgress.is()) &&
3554 (xFrame.is() )
3557 css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
3558 if (xFrameProps.is())
3559 xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(xExternalProgress));
3562 // But inside the MediaDescriptor we must set our own create progress ...
3563 // in case there is not already another progress set.
3564 rArgs.createItemIfMissing(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(), xInternalProgress);
3567 //-----------------------------------------------
3568 void AutoRecovery::impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo ,
3569 ::comphelper::MediaDescriptor& rArgs ,
3570 const css::uno::Reference< css::frame::XFrame >& xNewFrame)
3572 // external well known frame must be preferred (because it was created by ourself
3573 // for loading documents into this frame)!
3574 // But if no frame exists ... we can try to locate it using any frame bound to the provided
3575 // document. Of course we must live without any frame in case the document does not exists at this
3576 // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-)
3577 css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
3578 if (
3579 (!xFrame.is() ) &&
3580 (rInfo.Document.is())
3583 css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
3584 if (xController.is())
3585 xFrame = xController->getFrame();
3588 // stop progress interception on corresponding frame.
3589 css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
3590 if (xFrameProps.is())
3591 xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(css::uno::Reference< css::task::XStatusIndicator >()));
3593 // forget progress inside list of arguments.
3594 ::comphelper::MediaDescriptor::iterator pArg = rArgs.find(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR());
3595 if (pArg != rArgs.end())
3597 rArgs.erase(pArg);
3598 pArg = rArgs.end();
3602 //-----------------------------------------------
3603 void AutoRecovery::impl_flushALLConfigChanges()
3607 // SAFE ->
3608 ReadGuard aReadLock(m_aLock);
3609 css::uno::Reference< css::uno::XInterface > xRecoveryCfg(m_xRecoveryCFG, css::uno::UNO_QUERY);
3610 aReadLock.unlock();
3611 // <- SAFE
3613 if (xRecoveryCfg.is())
3614 ::comphelper::ConfigurationHelper::flush(xRecoveryCfg);
3616 // SOLAR SAFE ->
3617 SolarMutexGuard aGuard;
3618 ::utl::ConfigManager::storeConfigItems();
3620 catch(const css::uno::Exception&)
3625 //-----------------------------------------------
3626 void AutoRecovery::st_impl_removeFile(const OUString& sURL)
3628 if ( sURL.isEmpty())
3629 return;
3633 ::ucbhelper::Content aContent = ::ucbhelper::Content(sURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getComponentContext(m_xSMGR));
3634 aContent.executeCommand(OUString("delete"), css::uno::makeAny(sal_True));
3636 catch(const css::uno::Exception&)
3641 //-----------------------------------------------
3642 void AutoRecovery::st_impl_removeLockFile()
3646 OUString sUserURL;
3647 ::utl::Bootstrap::locateUserInstallation( sUserURL );
3649 OUStringBuffer sLockURLBuf;
3650 sLockURLBuf.append (sUserURL);
3651 sLockURLBuf.appendAscii("/.lock");
3652 OUString sLockURL = sLockURLBuf.makeStringAndClear();
3654 AutoRecovery::st_impl_removeFile(sLockURL);
3656 catch(const css::uno::Exception&)
3661 } // namespace framework
3663 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */