1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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 //_______________________________________________
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
;
117 //-----------------------------------------------
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";
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") \
232 #undef LOGFILE_RECOVERY
233 #define LOG_RECOVERY(MSG)
236 //-----------------------------------------------
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
;
257 CacheLockGuard(AutoRecovery
* pOwner
,
259 sal_Int32
& rCacheLock
,
260 sal_Bool bLockForAddRemoveVectorItems
);
263 void lock(sal_Bool bLockForAddRemoveVectorItems
);
267 //-----------------------------------------------
268 CacheLockGuard::CacheLockGuard(AutoRecovery
* pOwner
,
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()
287 //-----------------------------------------------
288 void CacheLockGuard::lock(sal_Bool bLockForAddRemoveVectorItems
)
290 // SAFE -> ----------------------------------
291 WriteGuard
aWriteLock(m_rSharedMutex
);
293 if (m_bLockedByThisGuard
)
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
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 .-)
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."),
318 m_bLockedByThisGuard
= sal_True
;
321 // <- SAFE ----------------------------------
324 //-----------------------------------------------
325 void CacheLockGuard::unlock()
327 // SAFE -> ----------------------------------
328 WriteGuard
aWriteLock(m_rSharedMutex
);
330 if ( ! m_bLockedByThisGuard
)
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 .-)"),
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
;
386 //-----------------------------------------------
387 void DispatchParams::forget()
389 m_sSavePath
= OUString();
390 m_nWorkingEntryID
= -1;
392 m_xHoldRefForAsyncOpAlive
.clear();
395 //-----------------------------------------------
396 DEFINE_XINTERFACE_10(AutoRecovery
,
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 //-----------------------------------------------
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...
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 ( )
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
)
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
)
470 //-----------------------------------------------
471 AutoRecovery::~AutoRecovery()
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())
485 sal_Int32 eNewJob
= AutoRecovery::implst_classifyJob(aURL
);
486 if (eNewJob
== AutoRecovery::E_NO_JOB
)
489 // SAFE -> ----------------------------------
490 WriteGuard
aWriteLock(m_aLock
);
492 // still running operation ... ignoring AUTO_SAVE.
493 // All other requests has higher prio!
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!")
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.
512 implts_stopListening();
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
);
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();
534 m_eJob
&= ~AutoRecovery::E_AUTO_SAVE
;
535 m_eTimerType
= AutoRecovery::E_DONT_START_TIMER
;
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.
547 m_aDispatchParams
= aParams
;
550 // <- SAFE ----------------------------------
553 m_aAsyncDispatcher
.Post(0);
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()
568 m_rRecovery
.implts_informListener(m_eJob
,
569 AutoRecovery::implst_createFeatureStateEvent(m_eJob
, OPERATION_STOP
, NULL
));
573 //-----------------------------------------------
574 void AutoRecovery::implts_dispatch(const DispatchParams
& aParams
)
576 // SAFE -> ----------------------------------
577 WriteGuard
aWriteLock(m_aLock
);
578 sal_Int32 eJob
= m_eJob
;
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
;
596 implts_stopListening();
598 ListenerInformer
aListenerInformer(*this, eJob
);
599 aListenerInformer
.start();
603 // Auto save is called from our internal timer ... not via dispatch() API !
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();
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
);
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
);
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
);
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
);
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
);
664 ((eJob
& AutoRecovery::E_ENTRY_BACKUP
) == AutoRecovery::E_ENTRY_BACKUP
) &&
665 ((eJob
& AutoRecovery::E_DISABLE_AUTORECOVERY
) != AutoRecovery::E_DISABLE_AUTORECOVERY
)
667 implts_backupWorkingEntry(aParams
);
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
&)
679 catch(const css::uno::Exception
&)
681 // TODO better error handling
684 aListenerInformer
.stop();
686 // SAFE -> ----------------------------------
690 (bAllowAutoSaveReactivation
) &&
691 (bWasAutoSaveActive
)
694 m_eJob
|= AutoRecovery::E_AUTO_SAVE
;
696 if (bWasUserAutoSaveActive
)
698 m_eJob
|= AutoRecovery::E_USER_AUTO_SAVE
;
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
)
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() ;
733 AutoRecovery::TDocumentInfo
& rInfo
= *pIt
;
734 css::frame::FeatureStateEvent aEvent
= AutoRecovery::implst_createFeatureStateEvent(m_eJob
, OPERATION_UPDATE
, &rInfo
);
736 // <- SAFE ------------------------------
738 xListener
->statusChanged(aEvent
);
740 // SAFE -> ------------------------------
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
)
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
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.
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!
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!
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();
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"
843 if ((m_eJob
& AutoRecovery::E_DISABLE_AUTORECOVERY
) == AutoRecovery::E_DISABLE_AUTORECOVERY
)
849 pChanges
[i
].Accessor
>>= sPath
;
851 if ( sPath
== CFG_ENTRY_AUTOSAVE_ENABLED
)
853 sal_Bool bEnabled
= sal_False
;
854 if (pChanges
[i
].Element
>>= bEnabled
)
858 m_eJob
|= AutoRecovery::E_AUTO_SAVE
;
859 m_eTimerType
= AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL
;
863 m_eJob
&= ~AutoRecovery::E_AUTO_SAVE
;
864 m_eTimerType
= AutoRecovery::E_DONT_START_TIMER
;
869 if ( sPath
== CFG_ENTRY_AUTOSAVE_TIMEINTERVALL
)
870 pChanges
[i
].Element
>>= m_nAutoSaveTimeIntervall
;
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())
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();
906 if (aEvent
.Source
== m_xRecoveryCFG
)
908 m_xRecoveryCFG
.clear();
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
);
918 implts_deregisterDocument(xDocument
, sal_False
); // sal_False => dont call removeEventListener() .. because it's not needed here
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
);
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
,
953 OUString(CFG_ENTRY_MINSPACE_DOCSAVE
),
954 ::comphelper::ConfigurationHelper::E_STANDARD
) >>= nMinSpaceDocSave
;
956 ::comphelper::ConfigurationHelper::readDirectKey(xContext
,
957 sCFG_PACKAGE_RECOVERY
,
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 -> ----------------------------------
972 m_xRecoveryCFG
= xCFG
;
973 m_nMinSpaceDocSave
= nMinSpaceDocSave
;
974 m_nMinSpaceConfigSave
= nMinSpaceConfigSave
;
976 // <- SAFE ----------------------------------
981 //-----------------------------------------------
982 void AutoRecovery::implts_readAutoSaveConfig()
984 css::uno::Reference
< css::container::XHierarchicalNameAccess
> xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY
);
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
);
998 m_eJob
|= AutoRecovery::E_AUTO_SAVE
;
999 m_eTimerType
= AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL
;
1003 m_eJob
|= AutoRecovery::E_USER_AUTO_SAVE
;
1007 m_eJob
&= ~AutoRecovery::E_USER_AUTO_SAVE
;
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 -> ----------------------------------
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();
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
;
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();
1064 // REENTRANT -> --------------------------
1065 aCacheLock
.lock(LOCK_FOR_CACHE_ADD_REMOVE
);
1069 css::uno::Reference
< css::beans::XPropertySet
> xItem
;
1070 xList
->getByName(pItems
[i
]) >>= xItem
;
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 -> ----------------------
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
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 .-)")
1107 // THREADSAFE -> --------------------------
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
;
1136 // <- SAFE ----------------------------------
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];
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
)
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!",
1191 // SAFE -> ----------------------------------
1192 ReadGuard
aReadLock(m_aLock
);
1193 css::uno::Reference
< css::lang::XMultiServiceFactory
> xSMGR
= m_xSMGR
;
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
);
1219 const Reference
< XEnumeration
> xEnumControllers( xModel
->getControllers() );
1220 while ( xEnumControllers
->hasMoreElements() )
1222 const Reference
< XController2
> xController( xEnumControllers
->nextElement(), UNO_QUERY
);
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
);
1234 const Reference
< XController2
> xController( xModel
->getCurrentController(), UNO_QUERY
);
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() ;
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();
1288 // Catch NoSuchElementException.
1289 // Its not a good idea inside multithreaded environments to call hasElement - removeElement.
1293 xModify
->removeByName(sID
);
1295 catch(const css::container::NoSuchElementException
&)
1303 css::uno::Reference
< css::beans::XPropertySet
> xSet
;
1304 sal_Bool bNew
= (!xCheck
->hasByName(sID
));
1306 xSet
= css::uno::Reference
< css::beans::XPropertySet
>(xCreate
->createInstance(), css::uno::UNO_QUERY_THROW
);
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
));
1320 xModify
->insertByName(sID
, css::uno::makeAny(xSet
));
1323 catch(const css::uno::RuntimeException
&)
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
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 !
1353 ReadGuard
aReadLock(m_aLock
);
1354 sal_Int32 nMinSpaceConfigSave
= m_nMinSpaceConfigSave
;
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 !!!
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
;
1381 // <- SAFE ----------------------------------
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 ----------------------------------
1404 ( xBroadcaster
.is() ) &&
1405 (! bListenForDocEvents
)
1408 m_xNewDocBroadcasterListener
= new WeakDocumentEventListener(this);
1409 xBroadcaster
->addEventListener(m_xNewDocBroadcasterListener
);
1411 WriteGuard
aWriteLock(m_aLock
);
1412 m_bListenForDocEvents
= sal_True
;
1413 aWriteLock
.unlock();
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
);
1429 // <- SAFE ----------------------------------
1432 (xGlobalEventBroadcaster
.is()) &&
1433 (m_bListenForDocEvents
)
1436 xGlobalEventBroadcaster
->removeEventListener(m_xNewDocBroadcasterListener
);
1437 m_bListenForDocEvents
= sal_False
;
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
)
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
)
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()
1485 // SAFE -> ----------------------------------
1486 WriteGuard
aWriteLock(m_aLock
);
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
)
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]
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 .-)
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
);
1517 aWriteLock
.unlock();
1518 // <- SAFE ----------------------------------
1521 //-----------------------------------------------
1522 void AutoRecovery::implts_stopTimer()
1524 // SAFE -> ----------------------------------
1525 WriteGuard
aWriteLock(m_aLock
);
1527 if (!m_aTimer
.IsActive())
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 :-(
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
)
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();
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
);
1585 implts_updateTimer();
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 ...
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 -> ----------------------------------
1622 m_eTimerType
= eSuggestedTimer
;
1623 aWriteLock
.unlock();
1624 // <- SAFE ----------------------------------
1626 implts_updateTimer();
1628 catch(const css::uno::Exception
&)
1635 //-----------------------------------------------
1636 IMPL_LINK_NOARG(AutoRecovery
, implts_asyncDispatch
)
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();
1648 implts_dispatch(aParams
);
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())
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.
1676 implts_updateModifiedState(xDocument
);
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
));
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())
1696 css::uno::Reference
< css::frame::XFrame
> xFrame
= xController
->getFrame();
1697 css::uno::Reference
< css::frame::XDesktop
> xDesktop (xFrame
->getCreator(), css::uno::UNO_QUERY
);
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() )
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
;
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.
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 ...
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.
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
)
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 ...
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
);
1876 bModified
= xModify
->isModified();
1879 rInfo
.DocumentState
|= AutoRecovery::E_MODIFIED
;
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())
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())
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 ();
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() ;
1968 const AutoRecovery::TDocumentInfo
& rInfo
= *pIt
;
1969 if (rInfo
.Document
== xDocument
)
1975 //-----------------------------------------------
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();
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
);
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
;
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
);
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
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
& )
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!
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() ;
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
);
2087 xModify
->setModified(sal_False
);
2090 css::uno::Reference
< css::util::XCloseable
> xClose(rInfo
.Document
, css::uno::UNO_QUERY
);
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();
2112 //-----------------------------------------------
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())
2134 ::comphelper::MediaDescriptor
lDescriptor(rInfo
.Document
->getArgs());
2135 sal_Bool bNoAutoSave
= lDescriptor
.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool
)(sal_False
));
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
;
2149 // <- SAFE ----------------------------------
2151 css::uno::Reference
< css::task::XStatusIndicator
> xExternalProgress
;
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() ;
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
))
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
)
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
;
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
);
2245 if ((eJob
& AutoRecovery::E_SESSION_SAVE
) == AutoRecovery::E_SESSION_SAVE
)
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
;
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
);
2270 aInfo
.DocumentState
|= AutoRecovery::E_POSTPONED
;
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
;
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
));
2288 // SAFE -> --------------------------
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() ;
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
));
2309 // SAFE -> --------------------------
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())
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
);
2381 #ifdef TRIGGER_FULL_DISC_CHECK
2382 throw css::uno::Exception();
2383 #else // TRIGGER_FULL_DISC_CHECK
2387 #endif // TRIGGER_FULL_DISC_CHECK
2389 catch(const css::uno::Exception
&)
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 !
2398 ReadGuard
aReadLock2(m_aLock
);
2399 sal_Int32 nMinSpaceDocSave
= m_nMinSpaceDocSave
;
2400 aReadLock2
.unlock();
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 !!!
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
;
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() ;
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
)
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
));
2487 // SAFE -> --------------------------
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 ...
2518 rInfo
.DocumentState
|= AutoRecovery::E_DAMAGED
;
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.
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
;
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
;
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!
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
);
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!
2640 aWriteLock
.unlock();
2641 // <- SAFE ----------------------------------
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
;
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
;
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() );
2686 // let it recover itself
2687 Reference
< XDocumentRecovery
> xDocRecover( xModel
, UNO_QUERY_THROW
);
2688 xDocRecover
->recoverFromFile(
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();
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
);
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
&)
2740 catch(const css::uno::Exception
&)
2742 Any
aCaughtException( ::cppu::getCaughtException() );
2745 for ( ::std::vector
< Reference
< XComponent
> >::const_iterator component
= aCleanup
.begin();
2746 component
!= aCleanup
.end();
2750 css::uno::Reference
< css::util::XCloseable
> xClose( *component
, css::uno::UNO_QUERY
);
2752 xClose
->close( sal_True
);
2754 (*component
)->dispose();
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),
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
;
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
&)
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.")
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();
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() ;
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
);
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() ;
3170 const AutoRecovery::TDocumentInfo
& rInfo
= *pIt
;
3171 if (rInfo
.ID
!= aParams
.m_nWorkingEntryID
)
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
;
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() ;
3207 AutoRecovery::TDocumentInfo
& rInfo
= *pIt
;
3208 if (rInfo
.ID
!= aParams
.m_nWorkingEntryID
)
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
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
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!
3298 bRecoveryData
= sal_False
;
3300 aValue
<<= bRecoveryData
;
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
);
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
);
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;
3343 ::osl::MutexGuard
aGuard( LockHelper::getGlobalLock().getShareableOslMutex() );
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;
3361 ::osl::MutexGuard
aGuard( LockHelper::getGlobalLock().getShareableOslMutex() );
3364 static css::uno::Reference
< css::beans::XPropertySetInfo
> xInfo(createPropertySetInfo(getInfoHelper()));
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
);
3392 sal_Int32 c
= xContainer
->getCount();
3396 css::uno::Reference
< css::frame::XFrame
> xFrame
;
3399 xContainer
->getByIndex(i
) >>= xFrame
;
3403 // can happen in multithreaded environments, that frames was removed from the container during this loop runs!
3405 catch(const css::lang::IndexOutOfBoundsException
&)
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
);
3416 (!xVisibleCheck
.is() ) ||
3417 (!xVisibleCheck
->isVisible())
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();
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
&)
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
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
);
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
;
3496 sMsg
.replaceAll("%PATH", sBackupPath
));
3497 dlgError
.SetButtonText(dlgError
.GetButtonId(0), sBtn
);
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
;
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 !
3533 (! xExternalProgress
.is()) &&
3537 css::uno::Reference
< css::task::XStatusIndicatorFactory
> xProgressFactory(xFrame
, css::uno::UNO_QUERY
);
3538 if (xProgressFactory
.is())
3539 xInternalProgress
= xProgressFactory
->createStatusIndicator();
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 .-)
3553 (xExternalProgress
.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
;
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())
3602 //-----------------------------------------------
3603 void AutoRecovery::impl_flushALLConfigChanges()
3608 ReadGuard
aReadLock(m_aLock
);
3609 css::uno::Reference
< css::uno::XInterface
> xRecoveryCfg(m_xRecoveryCFG
, css::uno::UNO_QUERY
);
3613 if (xRecoveryCfg
.is())
3614 ::comphelper::ConfigurationHelper::flush(xRecoveryCfg
);
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())
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()
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: */