Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <config_features.h>
21 :
22 : #include <loadenv/loadenv.hxx>
23 :
24 : #include <loadenv/targethelper.hxx>
25 : #include <pattern/frame.hxx>
26 :
27 : #include <classes/resource.hrc>
28 : #include <classes/fwkresid.hxx>
29 : #include <protocols.h>
30 : #include <properties.h>
31 :
32 : #include "helper/mischelper.hxx"
33 :
34 : #include <com/sun/star/ucb/NameClash.hpp>
35 : #include <com/sun/star/container/XNameAccess.hpp>
36 : #include <com/sun/star/frame/Desktop.hpp>
37 : #include <com/sun/star/frame/theGlobalEventBroadcaster.hpp>
38 : #include <com/sun/star/frame/XLoadable.hpp>
39 : #include <com/sun/star/frame/XModel2.hpp>
40 : #include <com/sun/star/frame/ModuleManager.hpp>
41 : #include <com/sun/star/frame/XTitle.hpp>
42 : #include <com/sun/star/frame/XFrame.hpp>
43 : #include <com/sun/star/frame/XController.hpp>
44 : #include <com/sun/star/frame/XModel.hpp>
45 : #include <com/sun/star/frame/XStorable.hpp>
46 : #include <com/sun/star/util/XModifiable.hpp>
47 : #include <com/sun/star/util/URLTransformer.hpp>
48 : #include <com/sun/star/util/XURLTransformer.hpp>
49 : #include <com/sun/star/frame/XDesktop.hpp>
50 : #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
51 : #include <com/sun/star/container/XNameContainer.hpp>
52 : #include <com/sun/star/util/XChangesNotifier.hpp>
53 : #include <com/sun/star/util/XChangesBatch.hpp>
54 : #include <com/sun/star/beans/XPropertySet.hpp>
55 : #include <com/sun/star/beans/PropertyAttribute.hpp>
56 : #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
57 : #include <com/sun/star/document/XDocumentRecovery.hpp>
58 : #include <com/sun/star/util/XCloseable.hpp>
59 : #include <com/sun/star/awt/XWindow2.hpp>
60 : #include <com/sun/star/task/XStatusIndicatorFactory.hpp>
61 : #include <com/sun/star/lang/XTypeProvider.hpp>
62 : #include <com/sun/star/lang/XServiceInfo.hpp>
63 : #include <com/sun/star/lang/XSingleServiceFactory.hpp>
64 : #include <com/sun/star/frame/XDispatch.hpp>
65 : #include <com/sun/star/document/XDocumentEventListener.hpp>
66 : #include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
67 : #include <com/sun/star/util/XChangesListener.hpp>
68 : #include <com/sun/star/task/XStatusIndicator.hpp>
69 : #include <com/sun/star/util/XModifyListener.hpp>
70 :
71 : #include <comphelper/configurationhelper.hxx>
72 : #include <cppuhelper/basemutex.hxx>
73 : #include <cppuhelper/exc_hlp.hxx>
74 : #include <cppuhelper/compbase5.hxx>
75 : #include <cppuhelper/propshlp.hxx>
76 : #include <cppuhelper/supportsservice.hxx>
77 : #include <unotools/mediadescriptor.hxx>
78 : #include <comphelper/namedvaluecollection.hxx>
79 : #include <comphelper/sequence.hxx>
80 : #include <vcl/evntpost.hxx>
81 : #include <vcl/svapp.hxx>
82 : #include <vcl/timer.hxx>
83 : #include <unotools/pathoptions.hxx>
84 : #include <tools/diagnose_ex.h>
85 : #include <unotools/tempfile.hxx>
86 : #include <ucbhelper/content.hxx>
87 :
88 : #include <osl/time.h>
89 : #include <vcl/msgbox.hxx>
90 : #include <osl/file.hxx>
91 : #include <unotools/bootstrap.hxx>
92 : #include <unotools/configmgr.hxx>
93 : #include <svl/documentlockfile.hxx>
94 : #include <tools/urlobj.hxx>
95 :
96 : #include <general.h>
97 : #include <stdtypes.h>
98 :
99 : using namespace css::uno;
100 : using namespace css::document;
101 : using namespace css::frame;
102 : using namespace css::lang;
103 : using namespace framework;
104 :
105 : namespace {
106 :
107 : /** @short hold all needed information for an asynchronous dispatch alive.
108 :
109 : @descr Because some operations are forced to be executed asynchronously
110 : (e.g. requested by our CreashSave/Recovery dialog) ... we must make sure
111 : that these information wont be set as "normal" members of our AtoRecovery
112 : instance. Otherwise they can disturb our normal AutoSave-timer handling.
113 : e.g. it can be unclear then, which progress has to be used for storing documents ...
114 : */
115 : struct DispatchParams
116 : {
117 : public:
118 : DispatchParams();
119 : DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs ,
120 : const css::uno::Reference< css::uno::XInterface >& xOwner);
121 : ~DispatchParams();
122 :
123 : DispatchParams& operator=(const DispatchParams& rCopy);
124 : void forget();
125 :
126 : public:
127 :
128 : /** @short can be set from outside and is provided to
129 : our internal started operations.
130 :
131 : @descr Normally we use the normal status indicator
132 : of the document windows to show a progress.
133 : But in case we are used by any special UI,
134 : it can provide its own status indicator object
135 : to us - so we use it instead of the normal one.
136 : */
137 : css::uno::Reference< css::task::XStatusIndicator > m_xProgress;
138 :
139 : /** TODO document me */
140 : OUString m_sSavePath;
141 :
142 : /** @short define the current cache entry, which should be used for current
143 : backup or cleanUp operation ... which is may be done asynchronous */
144 : sal_Int32 m_nWorkingEntryID;
145 :
146 : /** @short used for asyncoperations, to prevent us from dying.
147 :
148 : @descr If our dispatch() method was forced to start the
149 : internal operation asynchronous ... we send an event
150 : to start and return immediately. But we must be sure that
151 : our instance live if the event callback reach us.
152 : So we hold an uno reference to ourself.
153 : */
154 : css::uno::Reference< css::uno::XInterface > m_xHoldRefForAsyncOpAlive;
155 : };
156 :
157 : /**
158 : implements the functionality of AutoSave and AutoRecovery
159 : of documents - including features of an EmergencySave in
160 : case a GPF occurs.
161 : */
162 : typedef ::cppu::WeakComponentImplHelper5<
163 : css::lang::XServiceInfo,
164 : css::frame::XDispatch,
165 : css::document::XDocumentEventListener, // => css.lang.XEventListener
166 : css::util::XChangesListener, // => css.lang.XEventListener
167 : css::util::XModifyListener > // => css.lang.XEventListener
168 : AutoRecovery_BASE;
169 :
170 : class AutoRecovery : private cppu::BaseMutex
171 : , public AutoRecovery_BASE
172 : , public ::cppu::OPropertySetHelper // => XPropertySet, XFastPropertySet, XMultiPropertySet
173 : {
174 : public:
175 :
176 : /** These values are used as flags and represent the current state of a document.
177 : Every state of the life time of a document has to be recognized here.
178 :
179 : @attention Do not change (means reorganize) already used numbers.
180 : There exists some code inside SVX, which uses the same numbers,
181 : to analyze such document states.
182 : Not the best design ... but may be it will be changed later .-)
183 : */
184 : enum EDocStates
185 : {
186 : /* TEMP STATES */
187 :
188 : /// default state, if a document was new created or loaded
189 : E_UNKNOWN = 0,
190 : /// modified against the original file
191 : E_MODIFIED = 1,
192 : /// an active document can be postponed to be saved later.
193 : E_POSTPONED = 2,
194 : /// was already handled during one AutoSave/Recovery session.
195 : E_HANDLED = 4,
196 : /** an action was started (saving/loading) ... Can be interesting later if the process may be was interrupted by an exception. */
197 : E_TRY_SAVE = 8,
198 : E_TRY_LOAD_BACKUP = 16,
199 : E_TRY_LOAD_ORIGINAL = 32,
200 :
201 : /* FINAL STATES */
202 :
203 : /// the Auto/Emergency saved document is not useable any longer
204 : E_DAMAGED = 64,
205 : /// the Auto/Emergency saved document is not really up-to-date (some changes can be missing)
206 : E_INCOMPLETE = 128,
207 : /// the Auto/Emergency saved document was processed successfully
208 : E_SUCCEDED = 512
209 : };
210 :
211 : /** @short indicates the results of a FAILURE_SAFE operation
212 :
213 : @descr We must know, which reason was the real one in case
214 : we couldnt copy a "failure document" to a user specified path.
215 : We must know, if we can forget our cache entry or not.
216 : */
217 : enum EFailureSafeResult
218 : {
219 : E_COPIED,
220 : E_ORIGINAL_FILE_MISSING,
221 : E_WRONG_TARGET_PATH
222 : };
223 :
224 : // TODO document me
225 : enum ETimerType
226 : {
227 : /** the timer should not be used next time */
228 : E_DONT_START_TIMER,
229 : /** timer (was/must be) started with normal AutoSaveTimeIntervall */
230 : E_NORMAL_AUTOSAVE_INTERVALL,
231 : /** timer must be started with special short time intervall,
232 : to poll for an user idle period */
233 : E_POLL_FOR_USER_IDLE,
234 : /** timer must be started with a very(!) short time interval,
235 : to poll for the end of an user action, which does not allow saving documents in general */
236 : E_POLL_TILL_AUTOSAVE_IS_ALLOWED,
237 : /** dont start the timer - but calls the same action then before immediately again! */
238 : E_CALL_ME_BACK
239 : };
240 :
241 : // TODO document me ... flag field
242 : // Emergency_Save and Recovery overwrites Auto_Save!
243 : enum EJob
244 : {
245 : E_NO_JOB = 0,
246 : E_AUTO_SAVE = 1,
247 : E_EMERGENCY_SAVE = 2,
248 : E_RECOVERY = 4,
249 : E_ENTRY_BACKUP = 8,
250 : E_ENTRY_CLEANUP = 16,
251 : E_PREPARE_EMERGENCY_SAVE = 32,
252 : E_SESSION_SAVE = 64,
253 : E_SESSION_RESTORE = 128,
254 : E_DISABLE_AUTORECOVERY = 256,
255 : E_SET_AUTOSAVE_STATE = 512,
256 : E_SESSION_QUIET_QUIT = 1024,
257 : E_USER_AUTO_SAVE = 2048
258 : };
259 :
260 : /** @short combine different information about one office document. */
261 0 : struct TDocumentInfo
262 : {
263 : public:
264 :
265 0 : TDocumentInfo()
266 : : DocumentState (E_UNKNOWN)
267 : , UsedForSaving (false)
268 : , ListenForModify (false)
269 : , IgnoreClosing (false)
270 0 : , ID (-1 )
271 0 : {}
272 :
273 : /** @short points to the document. */
274 : css::uno::Reference< css::frame::XModel > Document;
275 :
276 : /** @short knows, if the document is really modified since the last autosave,
277 : or was postponed, because it was an active one etcpp...
278 :
279 : @descr Because we have no CHANGE TRACKING mechanism, based on office document,
280 : we implements it by ourself. We listen for MODIFIED events
281 : of each document and update this state flag here.
282 :
283 : Further we postpone saving of active documents, e.g. if the user
284 : works currently on it. We wait for an idle period then ...
285 : */
286 : sal_Int32 DocumentState;
287 :
288 : /** Because our applications not ready for concurrent save requests at the same time,
289 : we have suppress our own AutoSave for the moment, a document will be already saved
290 : by others.
291 : */
292 : bool UsedForSaving;
293 :
294 : /** For every user action, which modifies a document (e.g. key input) we get
295 : a notification as XModifyListener. That seems to be a "performance issue" .-)
296 : So we decided to listen for such modify events only for the time in which the document
297 : was stored as temp. file and was not modified again by the user.
298 : */
299 : bool ListenForModify;
300 :
301 : /** For SessionSave we must close all open documents by ourself.
302 : But because we are listen for documents events, we get some ...
303 : and deregister these documents from our configuration.
304 : That's why we mark these documents as "Closed by ourself" so we can
305 : ignore these "OnUnload" or disposing() events .-)
306 : */
307 : bool IgnoreClosing;
308 :
309 : /** TODO: document me */
310 : OUString OrgURL;
311 : OUString FactoryURL;
312 : OUString TemplateURL;
313 :
314 : OUString OldTempURL;
315 : OUString NewTempURL;
316 :
317 : OUString AppModule; // e.g. com.sun.star.text.TextDocument - used to identify app module
318 : OUString FactoryService; // the service to create a document of the module
319 : OUString RealFilter; // real filter, which was used at loading time
320 : OUString DefaultFilter; // supports saving of the default format without losing data
321 : OUString Extension; // file extension of the default filter
322 : OUString Title; // can be used as "DisplayName" on every recovery UI!
323 : css::uno::Sequence< OUString >
324 : ViewNames; // names of the view which were active at emergency-save time
325 :
326 : sal_Int32 ID;
327 : };
328 :
329 : /** @short used to know every currently open document. */
330 : typedef ::std::vector< TDocumentInfo > TDocumentList;
331 :
332 : // member
333 :
334 : private:
335 :
336 : /** @short the global uno service manager.
337 : @descr Must be used to create own needed services.
338 : */
339 : css::uno::Reference< css::uno::XComponentContext > m_xContext;
340 :
341 : /** @short points to the underlying recovery configuration.
342 : @descr This instance does not cache - it calls directly the
343 : configuration API!
344 : */
345 : css::uno::Reference< css::container::XNameAccess > m_xRecoveryCFG;
346 :
347 : /** @short proxy weak binding to forward Events to ourself without
348 : an ownership cycle
349 : */
350 : css::uno::Reference< css::util::XChangesListener > m_xRecoveryCFGListener;
351 :
352 : /** @short points to the used configuration package or.openoffice.Setup
353 : @descr This instance does not cache - it calls directly the
354 : configuration API!
355 : */
356 : css::uno::Reference< css::container::XNameAccess > m_xModuleCFG;
357 :
358 : /** @short holds the global event broadcaster alive,
359 : where we listen for new created documents.
360 : */
361 : css::uno::Reference< css::frame::XGlobalEventBroadcaster > m_xNewDocBroadcaster;
362 :
363 : /** @short proxy weak binding to forward Events to ourself without
364 : an ownership cycle
365 : */
366 : css::uno::Reference< css::document::XDocumentEventListener > m_xNewDocBroadcasterListener;
367 :
368 : /** @short because we stop/restart listening sometimes, it's a good idea to know
369 : if we already registered as listener .-)
370 : */
371 : bool m_bListenForDocEvents;
372 : bool m_bListenForConfigChanges;
373 :
374 : /** @short specify the time intervall between two save actions.
375 : @descr tools::Time is measured in [min].
376 : */
377 : sal_Int32 m_nAutoSaveTimeIntervall;
378 :
379 : /** @short for an asynchronous operation we must know, if there is
380 : at least one running job (may be asynchronous!).
381 : */
382 : sal_Int32 m_eJob;
383 :
384 : /** @short the timer, which is used to be informed about the next
385 : saving time ...
386 : */
387 : Timer m_aTimer;
388 :
389 : /** @short make our dispatch asynchronous ... if required to do so! */
390 : vcl::EventPoster m_aAsyncDispatcher;
391 :
392 : /** @see DispatchParams
393 : */
394 : DispatchParams m_aDispatchParams;
395 :
396 : /** @short indicates, which time period is currently used by the
397 : internal timer.
398 : */
399 : ETimerType m_eTimerType;
400 :
401 : /** @short this cache is used to hold all information about
402 : recovery/emergency save documents alive.
403 : */
404 : TDocumentList m_lDocCache;
405 :
406 : // TODO document me
407 : sal_Int32 m_nIdPool;
408 :
409 : /** @short contains all status listener registered at this instance.
410 : */
411 : ListenerHash m_lListener;
412 :
413 : /** @descr This member is used to prevent us against re-entrance problems.
414 : A mutex can't help to prevent us from concurrent using of members
415 : inside the same thread. But e.g. our internally used stl structures
416 : are not threadsafe ... and furthermore they can't be used at the same time
417 : for iteration and add/remove requests!
418 : So we have to detect such states and ... show a warning.
419 : May be there will be a better solution next time ... (copying the cache temp.
420 : bevor using).
421 :
422 : And further it's not possible to use a simple boolean value here.
423 : Because if more than one operation iterates over the same stl container ...
424 : (only to modify it's elements but dont add new or removing existing ones!)
425 : it should be possible doing so. But we must guarantee that the last operation reset
426 : this lock ... not the first one ! So we use a "ref count" mechanism for that."
427 : */
428 : sal_Int32 m_nDocCacheLock;
429 :
430 : /** @descr These members are used to check the minimum disc space, which must exists
431 : to start the corresponding operation.
432 : */
433 : sal_Int32 m_nMinSpaceDocSave;
434 : sal_Int32 m_nMinSpaceConfigSave;
435 :
436 : /** @short special debug option to make testing faster.
437 :
438 : @descr We dont interpret the timer unit as [min] ...
439 : we use [ms] instead of that. Further we dont
440 : wait 10 s for user idle ...
441 : */
442 : #if OSL_DEBUG_LEVEL > 1
443 : sal_Bool m_dbg_bMakeItFaster;
444 : #endif
445 :
446 : // HACK ... TODO
447 : css::uno::Reference< css::task::XStatusIndicator > m_xExternalProgress;
448 :
449 : // interface
450 :
451 : public:
452 :
453 : AutoRecovery(const css::uno::Reference< css::uno::XComponentContext >& xContext);
454 : virtual ~AutoRecovery( );
455 :
456 1 : virtual OUString SAL_CALL getImplementationName()
457 : throw (css::uno::RuntimeException, std::exception) SAL_OVERRIDE
458 : {
459 1 : return OUString("com.sun.star.comp.framework.AutoRecovery");
460 : }
461 :
462 0 : virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName)
463 : throw (css::uno::RuntimeException, std::exception) SAL_OVERRIDE
464 : {
465 0 : return cppu::supportsService(this, ServiceName);
466 : }
467 :
468 1 : virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames()
469 : throw (css::uno::RuntimeException, std::exception) SAL_OVERRIDE
470 : {
471 1 : css::uno::Sequence< OUString > aSeq(1);
472 1 : aSeq[0] = "com.sun.star.frame.AutoRecovery";
473 1 : return aSeq;
474 : }
475 :
476 : // XInterface
477 982 : virtual void SAL_CALL acquire() throw () SAL_OVERRIDE
478 982 : { OWeakObject::acquire(); }
479 982 : virtual void SAL_CALL release() throw () SAL_OVERRIDE
480 982 : { OWeakObject::release(); }
481 : virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& type) throw ( css::uno::RuntimeException, std::exception ) SAL_OVERRIDE;
482 :
483 : /// Initialization function after having acquire()'d.
484 : void initListeners();
485 :
486 : // XTypeProvider
487 : virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
488 :
489 : // css.frame.XDispatch
490 : virtual void SAL_CALL dispatch(const css::util::URL& aURL ,
491 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
492 : throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
493 :
494 : virtual void SAL_CALL addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
495 : const css::util::URL& aURL )
496 : throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
497 :
498 : virtual void SAL_CALL removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
499 : const css::util::URL& aURL )
500 : throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
501 :
502 : // css.document.XDocumentEventListener
503 : /** @short informs about created/opened documents.
504 :
505 : @descr Every new opened/created document will be saved internally
506 : so it can be checked if its modified. This modified state
507 : is used later to decide, if it must be saved or not.
508 :
509 : @param aEvent
510 : points to the new created/opened document.
511 : */
512 : virtual void SAL_CALL documentEventOccured(const css::document::DocumentEvent& aEvent)
513 : throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
514 :
515 : // css.util.XChangesListener
516 : virtual void SAL_CALL changesOccurred(const css::util::ChangesEvent& aEvent)
517 : throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
518 :
519 : // css.util.XModifyListener
520 : virtual void SAL_CALL modified(const css::lang::EventObject& aEvent)
521 : throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
522 :
523 : // css.lang.XEventListener
524 : virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent)
525 : throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
526 :
527 : protected:
528 :
529 : // OPropertySetHelper
530 :
531 : virtual sal_Bool SAL_CALL convertFastPropertyValue( css::uno::Any& aConvertedValue,
532 : css::uno::Any& aOldValue ,
533 : sal_Int32 nHandle ,
534 : const css::uno::Any& aValue )
535 : throw(css::lang::IllegalArgumentException) SAL_OVERRIDE;
536 :
537 : virtual void SAL_CALL setFastPropertyValue_NoBroadcast( sal_Int32 nHandle,
538 : const css::uno::Any& aValue )
539 : throw(css::uno::Exception, std::exception) SAL_OVERRIDE;
540 : using cppu::OPropertySetHelper::getFastPropertyValue;
541 : virtual void SAL_CALL getFastPropertyValue(css::uno::Any& aValue ,
542 : sal_Int32 nHandle) const SAL_OVERRIDE;
543 :
544 : virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() SAL_OVERRIDE;
545 :
546 : virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo()
547 : throw(css::uno::RuntimeException, std::exception) SAL_OVERRIDE;
548 :
549 : private:
550 : virtual void SAL_CALL disposing() SAL_OVERRIDE;
551 :
552 : /** @short open the underlying configuration.
553 :
554 : @descr This method must be called every time
555 : a configuartion call is needed. Because
556 : method works together with the member
557 : m_xCFG, open it on demand and cache it
558 : afterwards.
559 :
560 : @return [com.sun.star.container.XNameAccess]
561 : the configuration object
562 :
563 : @throw [com.sun.star.uno.RuntimeException]
564 : if config could not be opened successfully!
565 :
566 : @threadsafe
567 : */
568 : css::uno::Reference< css::container::XNameAccess > implts_openConfig();
569 :
570 : /** @short read the underlying configuration.
571 :
572 : @descr After that we know the initial state - means:
573 : - if AutoSave was enabled by the user
574 : - which time intervall has to be used
575 : - which recovery entries may already exists
576 :
577 : @throw [com.sun.star.uno.RuntimeException]
578 : if config could not be opened or readed successfully!
579 :
580 : @threadsafe
581 : */
582 : void implts_readConfig();
583 :
584 : /** @short read the underlying configuration...
585 :
586 : @descr ... but only keys related to the AutoSave mechanism.
587 : Means: State and Timer intervall.
588 : E.g. the recovery list is not addressed here.
589 :
590 : @throw [com.sun.star.uno.RuntimeException]
591 : if config could not be opened or readed successfully!
592 :
593 : @threadsafe
594 : */
595 : void implts_readAutoSaveConfig();
596 :
597 : // TODO document me
598 : void implts_flushConfigItem(const AutoRecovery::TDocumentInfo& rInfo ,
599 : bool bRemoveIt = false);
600 :
601 : // TODO document me
602 : void implts_startListening();
603 : void implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo);
604 :
605 : // TODO document me
606 : void implts_stopListening();
607 : void implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo);
608 :
609 : /** @short stops and may be(!) restarts the timer.
610 :
611 : @descr A running timer is stopped every time here.
612 : But starting depends from the different internal
613 : timer variables (e.g. AutoSaveEnabled, AutoSaveTimeIntervall,
614 : TimerType etcpp.)
615 :
616 : @throw [com.sun.star.uno.RuntimeException]
617 : if timer could not be stopped or started!
618 :
619 : @threadsafe
620 : */
621 : void implts_updateTimer();
622 :
623 : /** @short stop the timer.
624 :
625 : @descr Double calls will be ignored - means we do
626 : nothing here, if the timer is already disabled.
627 :
628 : @throw [com.sun.star.uno.RuntimeException]
629 : if timer could not be stopped!
630 :
631 : @threadsafe
632 : */
633 : void implts_stopTimer();
634 :
635 : /** @short callback of our internal timer.
636 : */
637 : DECL_LINK_TYPED(implts_timerExpired, Timer*, void);
638 :
639 : /** @short makes our dispatch() method asynchronous!
640 : */
641 : DECL_LINK(implts_asyncDispatch, void*);
642 :
643 : /** @short implements the dispatch real. */
644 : void implts_dispatch(const DispatchParams& aParams);
645 :
646 : /** @short validate new detected document and add it into the internal
647 : document list.
648 :
649 : @descr This method should be called only, if its clear that a new
650 : document was opened/created during office runtime.
651 : This method checks, if it's a top level document (means not an embedded one).
652 : Only such top level documents can be recognized by this auto save mechanism.
653 :
654 : @param xDocument
655 : the new document, which should be checked and registered.
656 :
657 : @threadsafe
658 : */
659 : void implts_registerDocument(const css::uno::Reference< css::frame::XModel >& xDocument);
660 :
661 : /** @short remove the specified document from our internal document list.
662 :
663 : @param xDocument
664 : the new document, which should be deregistered.
665 :
666 : @param bStopListening
667 : sal_False: must be used in case this method is called withion disposing() of the document,
668 : where it make no sense to deregister our listener. The container dies ...
669 : sal_True : must be used in case this method is used on "dergistration" of this document, where
670 : we must deregister our listener .-)
671 :
672 : @threadsafe
673 : */
674 : void implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument ,
675 : bool bStopListening = true);
676 :
677 : // TODO document me
678 : void implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument);
679 :
680 : // TODO document me
681 : void implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument);
682 :
683 : // TODO document me
684 : void implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument ,
685 : bool bSaveInProgress);
686 :
687 : // TODO document me
688 : void implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument);
689 :
690 : /** @short search a document inside given list.
691 :
692 : @param rList
693 : reference to a vector, which can contain such
694 : document.
695 :
696 : @param xDocument
697 : the document, which should be located inside the
698 : given list.
699 :
700 : @return [TDocumentList::iterator]
701 : which points to the located document.
702 : If document does not exists - its set to
703 : rList.end()!
704 : */
705 : static TDocumentList::iterator impl_searchDocument( AutoRecovery::TDocumentList& rList ,
706 : const css::uno::Reference< css::frame::XModel >& xDocument);
707 :
708 : /** TODO document me */
709 : void implts_changeAllDocVisibility(bool bVisible);
710 : void implts_prepareSessionShutdown();
711 :
712 : /** @short save all current opened documents to a specific
713 : backup directory.
714 :
715 : @descr Only really changed documents will be saved here.
716 :
717 : Further this method returns a suggestion, if and how it should
718 : be called again. May be some documents was not saved yet
719 : and must wait for an user idle period ...
720 :
721 : @param bAllowUserIdleLoop
722 : Because this method is used for different uses cases, it must
723 : know, which actions are allowed or not.
724 : AUTO_SAVE =>
725 : If a document is the most active one, saving it
726 : will be postponed if there exists other unsaved
727 : documents. This feature was implemented, because
728 : we dont wish to disturb the user on it's work.
729 : ... bAllowUserIdleLoop should be set to sal_True
730 : EMERGENCY_SAVE / SESSION_SAVE =>
731 : Here we must finish our work ASAP! It's not allowed
732 : to postpone any document.
733 : ... bAllowUserIdleLoop must(!) be set to sal_False
734 :
735 : @param pParams
736 : sometimes this method is required inside an external dispatch request.
737 : The it contains some special environment variables, which overwrites
738 : our normal environment.
739 : AutoSave => pParams == 0
740 : SessionSave/CrashSave => pParams != 0
741 :
742 : @return A suggestion, how the timer (if its not already disabled!)
743 : should be restarted to full fill the requirements.
744 :
745 : @threadsafe
746 : */
747 : AutoRecovery::ETimerType implts_saveDocs( bool bAllowUserIdleLoop,
748 : bool bRemoveLockFiles,
749 : const DispatchParams* pParams = 0);
750 :
751 : /** @short save one of the current documents to a specific
752 : backup directory.
753 :
754 : @descr It:
755 : - defines a new(!) unique temp file name
756 : - save the new temp file
757 : - remove the old temp file
758 : - patch the given info struct
759 : - and return errors.
760 :
761 : It does not:
762 : - patch the configuration.
763 :
764 : Note further: It paches the info struct
765 : more than ones. E.g. the new temp URL is set
766 : before the file is saved. And the old URL is removed
767 : only if removing oft he old file was successfully.
768 : If this method returns without an exception - everything
769 : was OK. Otherwise the info struct can be analyzed to
770 : get more information, e.g. when the problem occurs.
771 :
772 : @param sBackupPath
773 : the base path for saving such temp files.
774 :
775 : @param rInfo
776 : points to an information structure, where
777 : e.g. the document, its modified state, the count
778 : of autosave-retries etcpp. exists.
779 : Its used also to return the new temp file name
780 : and some other state values!
781 :
782 : @threadsafe
783 : */
784 : void implts_saveOneDoc(const OUString& sBackupPath ,
785 : AutoRecovery::TDocumentInfo& rInfo ,
786 : const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress);
787 :
788 : /** @short recovery all documents, which was saved during
789 : a crash before.
790 :
791 : @return A suggestion, how this method must be called back!
792 :
793 : @threadsafe
794 : */
795 : AutoRecovery::ETimerType implts_openDocs(const DispatchParams& aParams);
796 :
797 : // TODO document me
798 : void implts_openOneDoc(const OUString& sURL ,
799 : utl::MediaDescriptor& lDescriptor,
800 : AutoRecovery::TDocumentInfo& rInfo );
801 :
802 : // TODO document me
803 : void implts_generateNewTempURL(const OUString& sBackupPath ,
804 : utl::MediaDescriptor& rMediaDescriptor,
805 : AutoRecovery::TDocumentInfo& rInfo );
806 :
807 : /** @short notifies all interested listener about the current state
808 : of the currently running operation.
809 :
810 : @descr We support different set's of functions. AUTO_SAVE, EMERGENCY_SAVE,
811 : AUTO_RECOVERY, FAILURE_SAVE ... etcpp.
812 : Listener can register itself for any type of supported
813 : functionality ... but not for document URL's in special.
814 :
815 : @param eJob
816 : is used to know, which set of listener we must notify.
817 :
818 : @param aEvent
819 : describe the event more in detail.
820 :
821 : @threadsafe
822 : */
823 : void implts_informListener( sal_Int32 eJob ,
824 : const css::frame::FeatureStateEvent& aEvent);
825 :
826 : /** short create a feature event struct, which can be send
827 : to any interested listener.
828 :
829 : @param eJob
830 : describe the current running operation
831 : AUTOSAVE, EMERGENCYSAVE, RECOVERY
832 :
833 : @param sEventType
834 : describe the type of this event
835 : START, STOP, UPDATE
836 :
837 : @param pInfo
838 : if sOperation is an update, this parameter must be different from NULL
839 : and is used to send information regarding the current handled document.
840 :
841 : @return [css::frame::FeatureStateEvent]
842 : the event structure for sending.
843 : */
844 : static css::frame::FeatureStateEvent implst_createFeatureStateEvent( sal_Int32 eJob ,
845 : const OUString& sEventType,
846 : AutoRecovery::TDocumentInfo* pInfo );
847 :
848 : class ListenerInformer
849 : {
850 : private:
851 : AutoRecovery &m_rRecovery;
852 : sal_Int32 m_eJob;
853 : bool m_bStopped;
854 : public:
855 0 : ListenerInformer(AutoRecovery &rRecovery, sal_Int32 eJob)
856 0 : : m_rRecovery(rRecovery), m_eJob(eJob), m_bStopped(false)
857 : {
858 0 : }
859 : void start();
860 : void stop();
861 0 : ~ListenerInformer()
862 : {
863 0 : stop();
864 0 : }
865 : };
866 :
867 : // TODO document me
868 : void implts_resetHandleStates(bool bLoadCache);
869 :
870 : // TODO document me
871 : void implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo);
872 :
873 : // TODO document me
874 : void implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo);
875 :
876 : /** retrieves the names of all active views of the given document
877 : @param rInfo
878 : the document info, whose <code>Document</code> member must not be <NULL/>.
879 : */
880 : void implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& rInfo );
881 :
882 : /** updates the configuration so that for all documents, their current view/names are stored
883 : */
884 : void implts_persistAllActiveViewNames();
885 :
886 : // TODO document me
887 : void implts_prepareEmergencySave();
888 :
889 : // TODO document me
890 : void implts_doEmergencySave(const DispatchParams& aParams);
891 :
892 : // TODO document me
893 : void implts_doRecovery(const DispatchParams& aParams);
894 :
895 : // TODO document me
896 : void implts_doSessionSave(const DispatchParams& aParams);
897 :
898 : // TODO document me
899 : void implts_doSessionQuietQuit(const DispatchParams& aParams);
900 :
901 : // TODO document me
902 : void implts_doSessionRestore(const DispatchParams& aParams);
903 :
904 : // TODO document me
905 : void implts_backupWorkingEntry(const DispatchParams& aParams);
906 :
907 : // TODO document me
908 : void implts_cleanUpWorkingEntry(const DispatchParams& aParams);
909 :
910 : /** try to make sure that all changed config items (not our used
911 : config access only) will be flushed back to disc.
912 :
913 : E.g. our svtools::ConfigItems() has to be flushed explicitly .-(
914 :
915 : Note: This method can't fail. Flushing of config entries is an
916 : optional feature. Errors can be ignored.
917 : */
918 : void impl_flushALLConfigChanges();
919 :
920 : // TODO document me
921 : AutoRecovery::EFailureSafeResult implts_copyFile(const OUString& sSource ,
922 : const OUString& sTargetPath,
923 : const OUString& sTargetName);
924 :
925 : /** @short converts m_eJob into a job description, which
926 : can be used to inform an outside listener
927 : about the current running operation
928 :
929 : @param eJob
930 : describe the current running operation
931 : AUTOSAVE, EMERGENCYSAVE, RECOVERY
932 :
933 : @return [string]
934 : a suitable job description of form:
935 : vnd.sun.star.autorecovery:/do...
936 : */
937 : static OUString implst_getJobDescription(sal_Int32 eJob);
938 :
939 : /** @short mape the given URL to an internal int representation.
940 :
941 : @param aURL
942 : the url, which describe the next starting or may be already running
943 : operation.
944 :
945 : @return [long]
946 : the internal int representation
947 : see enum EJob
948 : */
949 : static sal_Int32 implst_classifyJob(const css::util::URL& aURL);
950 :
951 : /// TODO
952 : void implts_verifyCacheAgainstDesktopDocumentList();
953 :
954 : /// TODO document me
955 : bool impl_enoughDiscSpace(sal_Int32 nRequiredSpace);
956 :
957 : /// TODO document me
958 : static void impl_showFullDiscError();
959 :
960 : /** @short try to create/use a progress and set it inside the
961 : environment.
962 :
963 : @descr The problem behind: There exists different use case of this method.
964 : a) An external progress is provided by our CrashSave or Recovery dialog.
965 : b) We must create our own progress e.g. for an AutoSave
966 : c) Sometimes our application filters dont use the progress
967 : provided by the MediaDescriptor. They use the Frame every time to create
968 : it's own progress. So we implemented a HACK for these and now we set
969 : an InterceptedProgress there for the time WE use this frame for loading/storing documents .-)
970 :
971 : @param xNewFrame
972 : must be set only in case WE create a new frame (e.g. for loading documents
973 : on session restore or recovery). Then search for a frame using rInfo.Document must
974 : be suppressed and xFrame must be preferred instead .-)
975 :
976 : @param rInfo
977 : used e.g. to find the frame corresponding to a document.
978 : This frame must be used to create a new progress e.g. for an AutoSave.
979 :
980 : @param rArgs
981 : is used to set the new created progress as parameter on these set.
982 : */
983 : void impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo ,
984 : utl::MediaDescriptor& rArgs ,
985 : const css::uno::Reference< css::frame::XFrame >& xNewFrame);
986 :
987 : void impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo ,
988 : utl::MediaDescriptor& rArgs ,
989 : const css::uno::Reference< css::frame::XFrame >& xNewFrame);
990 :
991 : /** try to remove the specified file from disc.
992 :
993 : Every URL supported by our UCB component can be used here.
994 : Further it doesn't matter if the file really exists or not.
995 : Because removing a non exsistent file will have the same
996 : result at the end ... a non existing file .-)
997 :
998 : On the other side removing of files from disc is an optional
999 : feature. If we are not able doing so ... its not a real problem.
1000 : Ok - users disc place will be samller then ... but we should produce
1001 : a crash during crash save because we can't delete a temporary file only !
1002 :
1003 : @param sURL
1004 : the url of the file, which should be removed.
1005 : */
1006 : void st_impl_removeFile(const OUString& sURL);
1007 :
1008 : /** try to remove ".lock" file from disc if office will be terminated
1009 : not using the official way .-)
1010 :
1011 : This method has to be handled "optional". So every error inside
1012 : has to be ignored ! This method CAN NOT FAIL ... it can forget something only .-)
1013 : */
1014 : void st_impl_removeLockFile();
1015 : };
1016 :
1017 : // recovery.xcu
1018 : static const char CFG_PACKAGE_RECOVERY[] = "org.openoffice.Office.Recovery/";
1019 : static const char CFG_ENTRY_RECOVERYLIST[] = "RecoveryList";
1020 : static const char CFG_PATH_RECOVERYINFO[] = "RecoveryInfo";
1021 : static const char CFG_ENTRY_CRASHED[] = "Crashed";
1022 : static const char CFG_ENTRY_SESSIONDATA[] = "SessionData";
1023 :
1024 : static const char CFG_ENTRY_AUTOSAVE_ENABLED[] = "AutoSave/Enabled";
1025 : static const char CFG_ENTRY_AUTOSAVE_TIMEINTERVALL[] = "AutoSave/TimeIntervall"; //sic!
1026 :
1027 : static const char CFG_ENTRY_USERAUTOSAVE_ENABLED[] = "AutoSave/UserAutoSaveEnabled";
1028 :
1029 : static const char CFG_PATH_AUTOSAVE[] = "AutoSave";
1030 : static const char CFG_ENTRY_MINSPACE_DOCSAVE[] = "MinSpaceDocSave";
1031 : static const char CFG_ENTRY_MINSPACE_CONFIGSAVE[] = "MinSpaceConfigSave";
1032 :
1033 : static const char CFG_PACKAGE_MODULES[] = "org.openoffice.Setup/Office/Factories";
1034 : static const char CFG_ENTRY_REALDEFAULTFILTER[] = "ooSetupFactoryActualFilter";
1035 :
1036 : static const char CFG_ENTRY_PROP_TEMPURL[] = "TempURL";
1037 : static const char CFG_ENTRY_PROP_ORIGINALURL[] = "OriginalURL";
1038 : static const char CFG_ENTRY_PROP_TEMPLATEURL[] = "TemplateURL";
1039 : static const char CFG_ENTRY_PROP_FACTORYURL[] = "FactoryURL";
1040 : static const char CFG_ENTRY_PROP_MODULE[] = "Module";
1041 : static const char CFG_ENTRY_PROP_DOCUMENTSTATE[] = "DocumentState";
1042 : static const char CFG_ENTRY_PROP_FILTER[] = "Filter";
1043 : static const char CFG_ENTRY_PROP_TITLE[] = "Title";
1044 : static const char CFG_ENTRY_PROP_ID[] = "ID";
1045 : static const char CFG_ENTRY_PROP_VIEWNAMES[] = "ViewNames";
1046 :
1047 : static const char FILTER_PROP_TYPE[] = "Type";
1048 : static const char TYPE_PROP_EXTENSIONS[] = "Extensions";
1049 :
1050 : // setup.xcu
1051 : static const char CFG_ENTRY_PROP_EMPTYDOCUMENTURL[] = "ooSetupFactoryEmptyDocumentURL";
1052 : static const char CFG_ENTRY_PROP_FACTORYSERVICE[] = "ooSetupFactoryDocumentService";
1053 :
1054 : static const char EVENT_ON_NEW[] = "OnNew";
1055 : static const char EVENT_ON_LOAD[] = "OnLoad";
1056 : static const char EVENT_ON_UNLOAD[] = "OnUnload";
1057 : static const char EVENT_ON_MODIFYCHANGED[] = "OnModifyChanged";
1058 : static const char EVENT_ON_SAVE[] = "OnSave";
1059 : static const char EVENT_ON_SAVEAS[] = "OnSaveAs";
1060 : static const char EVENT_ON_SAVETO[] = "OnCopyTo";
1061 : static const char EVENT_ON_SAVEDONE[] = "OnSaveDone";
1062 : static const char EVENT_ON_SAVEASDONE[] = "OnSaveAsDone";
1063 : static const char EVENT_ON_SAVETODONE[] = "OnCopyToDone";
1064 : static const char EVENT_ON_SAVEFAILED[] = "OnSaveFailed";
1065 : static const char EVENT_ON_SAVEASFAILED[] = "OnSaveAsFailed";
1066 : static const char EVENT_ON_SAVETOFAILED[] = "OnCopyToFailed";
1067 :
1068 : static const char RECOVERY_ITEM_BASE_IDENTIFIER[] = "recovery_item_";
1069 :
1070 : static const char CMD_PROTOCOL[] = "vnd.sun.star.autorecovery:";
1071 :
1072 : static const char CMD_DO_AUTO_SAVE[] = "/doAutoSave"; // force AutoSave ignoring the AutoSave timer
1073 : static const char CMD_DO_PREPARE_EMERGENCY_SAVE[] = "/doPrepareEmergencySave"; // prepare the office for the following EmergencySave step (hide windows etcpp.)
1074 : static const char CMD_DO_EMERGENCY_SAVE[] = "/doEmergencySave"; // do EmergencySave on crash
1075 : static const char CMD_DO_RECOVERY[] = "/doAutoRecovery"; // recover all crashed documents
1076 : static const char CMD_DO_ENTRY_BACKUP[] = "/doEntryBackup"; // try to store a temp or original file to a user defined location
1077 : static const char CMD_DO_ENTRY_CLEANUP[] = "/doEntryCleanUp"; // remove the specified entry from the recovery cache
1078 : static const char CMD_DO_SESSION_SAVE[] = "/doSessionSave"; // save all open documents if e.g. a window manager closes an user session
1079 : 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
1080 : static const char CMD_DO_SESSION_RESTORE[] = "/doSessionRestore"; // restore a saved user session from disc
1081 : static const char CMD_DO_DISABLE_RECOVERY[] = "/disableRecovery"; // disable recovery and auto save (!) temp. for this office session
1082 : static const char CMD_DO_SET_AUTOSAVE_STATE[] = "/setAutoSaveState"; // disable/enable auto save (not crash save) for this office session
1083 :
1084 : static const char REFERRER_USER[] = "private:user";
1085 :
1086 : static const char PROP_DISPATCH_ASYNCHRON[] = "DispatchAsynchron";
1087 : static const char PROP_PROGRESS[] = "StatusIndicator";
1088 : static const char PROP_SAVEPATH[] = "SavePath";
1089 : static const char PROP_ENTRY_ID[] = "EntryID";
1090 : static const char PROP_AUTOSAVE_STATE[] = "AutoSaveState";
1091 :
1092 : static const char OPERATION_START[] = "start";
1093 : static const char OPERATION_STOP[] = "stop";
1094 : static const char OPERATION_UPDATE[] = "update";
1095 :
1096 : static const sal_Int32 MIN_DISCSPACE_DOCSAVE = 5; // [MB]
1097 : static const sal_Int32 MIN_DISCSPACE_CONFIGSAVE = 1; // [MB]
1098 : static const sal_Int32 RETRY_STORE_ON_FULL_DISC_FOREVER = 300; // not forever ... but often enough .-)
1099 : static const sal_Int32 RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL = 3; // in case FULL DISC does not seem the real problem
1100 : static const sal_Int32 GIVE_UP_RETRY = 1; // in case FULL DISC does not seem the real problem
1101 :
1102 : #define SAVE_IN_PROGRESS true
1103 : #define SAVE_FINISHED false
1104 :
1105 : #define LOCK_FOR_CACHE_ADD_REMOVE true
1106 : #define LOCK_FOR_CACHE_USE false
1107 :
1108 : #define MIN_TIME_FOR_USER_IDLE 10000 // 10s user idle
1109 :
1110 : // enable the following defines in case you wish to simulate a full disc for debug purposes .-)
1111 :
1112 : // this define throws every time a document is stored or a configuration change
1113 : // should be flushed an exception ... so the special error handler for this scenario is triggered
1114 : // #define TRIGGER_FULL_DISC_CHECK
1115 :
1116 : // force "return sal_False" for the method impl_enoughDiscSpace().
1117 : // #define SIMULATE_FULL_DISC
1118 :
1119 : class CacheLockGuard
1120 : {
1121 : private:
1122 :
1123 : // holds the outside calli alive, so it's shared resources
1124 : // are valid every time
1125 : css::uno::Reference< css::uno::XInterface > m_xOwner;
1126 :
1127 : // mutex shared with outside calli !
1128 : osl::Mutex& m_rSharedMutex;
1129 :
1130 : // this variable knows the state of the "cache lock"
1131 : sal_Int32& m_rCacheLock;
1132 :
1133 : // to prevent increasing/decreasing of m_rCacheLock more than ones
1134 : // we must know if THIS guard has an actual lock set there !
1135 : bool m_bLockedByThisGuard;
1136 :
1137 : public:
1138 :
1139 : CacheLockGuard(AutoRecovery* pOwner ,
1140 : osl::Mutex& rMutex ,
1141 : sal_Int32& rCacheLock ,
1142 : bool bLockForAddRemoveVectorItems);
1143 : ~CacheLockGuard();
1144 :
1145 : void lock(bool bLockForAddRemoveVectorItems);
1146 : void unlock();
1147 : };
1148 :
1149 65 : CacheLockGuard::CacheLockGuard(AutoRecovery* pOwner ,
1150 : osl::Mutex& rMutex ,
1151 : sal_Int32& rCacheLock ,
1152 : bool bLockForAddRemoveVectorItems)
1153 65 : : m_xOwner (static_cast< css::frame::XDispatch* >(pOwner))
1154 : , m_rSharedMutex (rMutex )
1155 : , m_rCacheLock (rCacheLock )
1156 65 : , m_bLockedByThisGuard(false )
1157 : {
1158 65 : lock(bLockForAddRemoveVectorItems);
1159 65 : }
1160 :
1161 130 : CacheLockGuard::~CacheLockGuard()
1162 : {
1163 65 : unlock();
1164 65 : m_xOwner.clear();
1165 65 : }
1166 :
1167 130 : void CacheLockGuard::lock(bool bLockForAddRemoveVectorItems)
1168 : {
1169 : /* SAFE */ {
1170 130 : osl::MutexGuard g(m_rSharedMutex);
1171 :
1172 130 : if (m_bLockedByThisGuard)
1173 130 : return;
1174 :
1175 : // This cache lock is needed only to prevent us from removing/adding
1176 : // items from/into the recovery cache ... during it's used at another code place
1177 : // for iterating .-)
1178 :
1179 : // Modifying of item properties is allowed and sometimes needed!
1180 : // So we should detect only the dangerous state of concurrent add/remove
1181 : // requests and throw an exception then ... which can of course break the whole
1182 : // operation. On the other side a crash reasoned by an invalid stl iterator
1183 : // will have the same effect .-)
1184 :
1185 130 : if (
1186 130 : (m_rCacheLock > 0 ) &&
1187 : (bLockForAddRemoveVectorItems)
1188 : )
1189 : {
1190 : OSL_FAIL("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp.");
1191 : throw css::uno::RuntimeException(
1192 : OUString("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp."),
1193 0 : m_xOwner);
1194 : }
1195 :
1196 130 : ++m_rCacheLock;
1197 130 : m_bLockedByThisGuard = true;
1198 : } /* SAFE */
1199 : }
1200 :
1201 195 : void CacheLockGuard::unlock()
1202 : {
1203 : /* SAFE */ {
1204 195 : osl::MutexGuard g(m_rSharedMutex);
1205 :
1206 195 : if ( ! m_bLockedByThisGuard)
1207 260 : return;
1208 :
1209 130 : --m_rCacheLock;
1210 130 : m_bLockedByThisGuard = false;
1211 :
1212 130 : if (m_rCacheLock < 0)
1213 : {
1214 : OSL_FAIL("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)");
1215 : throw css::uno::RuntimeException(
1216 : OUString("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)"),
1217 0 : m_xOwner);
1218 130 : }
1219 : } /* SAFE */
1220 : }
1221 :
1222 129 : DispatchParams::DispatchParams()
1223 129 : : m_nWorkingEntryID(-1)
1224 : {
1225 129 : };
1226 :
1227 0 : DispatchParams::DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs ,
1228 0 : const css::uno::Reference< css::uno::XInterface >& xOwner)
1229 : {
1230 0 : m_nWorkingEntryID = lArgs.getUnpackedValueOrDefault(PROP_ENTRY_ID, (sal_Int32)-1 );
1231 0 : m_xProgress = lArgs.getUnpackedValueOrDefault(PROP_PROGRESS, css::uno::Reference< css::task::XStatusIndicator >());
1232 0 : m_sSavePath = lArgs.getUnpackedValueOrDefault(PROP_SAVEPATH, OUString() );
1233 0 : m_xHoldRefForAsyncOpAlive = xOwner;
1234 0 : };
1235 :
1236 129 : DispatchParams::~DispatchParams()
1237 129 : {};
1238 :
1239 0 : DispatchParams& DispatchParams::operator=(const DispatchParams& rCopy)
1240 : {
1241 0 : m_xProgress = rCopy.m_xProgress;
1242 0 : m_sSavePath = rCopy.m_sSavePath;
1243 0 : m_nWorkingEntryID = rCopy.m_nWorkingEntryID;
1244 0 : m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive;
1245 0 : return *this;
1246 : }
1247 :
1248 0 : void DispatchParams::forget()
1249 : {
1250 0 : m_sSavePath.clear();
1251 0 : m_nWorkingEntryID = -1;
1252 0 : m_xProgress.clear();
1253 0 : m_xHoldRefForAsyncOpAlive.clear();
1254 0 : };
1255 :
1256 65 : AutoRecovery::AutoRecovery(const css::uno::Reference< css::uno::XComponentContext >& xContext)
1257 : : AutoRecovery_BASE (m_aMutex)
1258 : , ::cppu::OPropertySetHelper(cppu::WeakComponentImplHelperBase::rBHelper)
1259 : , m_xContext (xContext )
1260 : , m_bListenForDocEvents (false )
1261 : , m_bListenForConfigChanges (false )
1262 : , m_nAutoSaveTimeIntervall (0 )
1263 : , m_eJob (AutoRecovery::E_NO_JOB )
1264 : , m_aAsyncDispatcher ( LINK( this, AutoRecovery, implts_asyncDispatch ) )
1265 : , m_eTimerType (E_DONT_START_TIMER )
1266 : , m_nIdPool (0 )
1267 : , m_lListener (cppu::WeakComponentImplHelperBase::rBHelper.rMutex)
1268 : , m_nDocCacheLock (0 )
1269 : , m_nMinSpaceDocSave (MIN_DISCSPACE_DOCSAVE )
1270 65 : , m_nMinSpaceConfigSave (MIN_DISCSPACE_CONFIGSAVE )
1271 :
1272 : #if OSL_DEBUG_LEVEL > 1
1273 : , m_dbg_bMakeItFaster (sal_False )
1274 : #endif
1275 : {
1276 65 : }
1277 :
1278 65 : void AutoRecovery::initListeners()
1279 : {
1280 : // read configuration to know if autosave/recovery is on/off etcpp...
1281 65 : implts_readConfig();
1282 :
1283 65 : implts_startListening();
1284 :
1285 : // establish callback for our internal used timer.
1286 : // Note: Its only active, if the timer will be started ...
1287 65 : m_aTimer.SetTimeoutHdl(LINK(this, AutoRecovery, implts_timerExpired));
1288 65 : }
1289 :
1290 195 : AutoRecovery::~AutoRecovery()
1291 : {
1292 65 : disposing();
1293 130 : }
1294 :
1295 130 : void AutoRecovery::disposing()
1296 : {
1297 130 : implts_stopTimer();
1298 130 : }
1299 :
1300 261 : Any SAL_CALL AutoRecovery::queryInterface( const css::uno::Type& _rType ) throw(css::uno::RuntimeException, std::exception)
1301 : {
1302 261 : Any aRet = AutoRecovery_BASE::queryInterface( _rType );
1303 261 : if ( !aRet.hasValue() )
1304 0 : aRet = OPropertySetHelper::queryInterface( _rType );
1305 261 : return aRet;
1306 : }
1307 :
1308 0 : Sequence< css::uno::Type > SAL_CALL AutoRecovery::getTypes( ) throw(css::uno::RuntimeException, std::exception)
1309 : {
1310 : return comphelper::concatSequences(
1311 : AutoRecovery_BASE::getTypes(),
1312 : ::cppu::OPropertySetHelper::getTypes()
1313 0 : );
1314 : }
1315 :
1316 64 : void SAL_CALL AutoRecovery::dispatch(const css::util::URL& aURL ,
1317 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments)
1318 : throw(css::uno::RuntimeException, std::exception)
1319 : {
1320 : SAL_INFO("fwk.autorecovery", "AutoRecovery::dispatch() starts ..." << aURL.Complete);
1321 :
1322 : // valid request ?
1323 64 : sal_Int32 eNewJob = AutoRecovery::implst_classifyJob(aURL);
1324 64 : if (eNewJob == AutoRecovery::E_NO_JOB)
1325 64 : return;
1326 :
1327 : bool bAsync;
1328 64 : DispatchParams aParams;
1329 : /* SAFE */ {
1330 64 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1331 :
1332 : // still running operation ... ignoring AUTO_SAVE.
1333 : // All other requests has higher prio!
1334 64 : if (
1335 128 : ( m_eJob != AutoRecovery::E_NO_JOB ) &&
1336 64 : ((m_eJob & AutoRecovery::E_AUTO_SAVE ) != AutoRecovery::E_AUTO_SAVE)
1337 : )
1338 : {
1339 : SAL_INFO("fwk.autorecovery", "AutoRecovery::dispatch(): There is already an asynchronous dispatch() running. New request will be ignored!");
1340 0 : return;
1341 : }
1342 :
1343 64 : ::comphelper::SequenceAsHashMap lArgs(lArguments);
1344 :
1345 : // check if somewhere wish to disable recovery temp. for this office session
1346 : // This can be done immediately ... must not been done asynchronous.
1347 64 : if ((eNewJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
1348 : {
1349 : // it's important to set a flag internally, so AutoRecovery will be suppressed - even if it's requested.
1350 64 : m_eJob |= eNewJob;
1351 64 : implts_stopTimer();
1352 64 : implts_stopListening();
1353 64 : return;
1354 : }
1355 :
1356 : // disable/enable AutoSave for this office session only
1357 : // independent from the configuration entry.
1358 0 : if ((eNewJob & AutoRecovery::E_SET_AUTOSAVE_STATE) == AutoRecovery::E_SET_AUTOSAVE_STATE)
1359 : {
1360 0 : bool bOn = lArgs.getUnpackedValueOrDefault(PROP_AUTOSAVE_STATE, true);
1361 0 : if (bOn)
1362 : {
1363 : // dont enable AutoSave hardly !
1364 : // reload configuration to know the current state.
1365 0 : implts_readAutoSaveConfig();
1366 0 : implts_updateTimer();
1367 : // can it happen that might be the listener was stopped ? .-)
1368 : // make sure it runs always ... even if AutoSave itself was disabled temporarly.
1369 0 : implts_startListening();
1370 : }
1371 : else
1372 : {
1373 0 : implts_stopTimer();
1374 0 : m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
1375 0 : m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
1376 : }
1377 0 : return;
1378 : }
1379 :
1380 0 : m_eJob |= eNewJob;
1381 :
1382 0 : bAsync = lArgs.getUnpackedValueOrDefault(PROP_DISPATCH_ASYNCHRON, false);
1383 0 : aParams = DispatchParams(lArgs, static_cast< css::frame::XDispatch* >(this));
1384 :
1385 : // Hold this instance alive till the asynchronous operation will be finished.
1386 0 : if (bAsync)
1387 0 : m_aDispatchParams = aParams;
1388 :
1389 : } /* SAFE */
1390 :
1391 0 : if (bAsync)
1392 0 : m_aAsyncDispatcher.Post(0);
1393 : else
1394 0 : implts_dispatch(aParams);
1395 : }
1396 :
1397 0 : void AutoRecovery::ListenerInformer::start()
1398 : {
1399 : m_rRecovery.implts_informListener(m_eJob,
1400 0 : AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_START, NULL));
1401 0 : }
1402 :
1403 0 : void AutoRecovery::ListenerInformer::stop()
1404 : {
1405 0 : if (m_bStopped)
1406 0 : return;
1407 : m_rRecovery.implts_informListener(m_eJob,
1408 0 : AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_STOP, NULL));
1409 0 : m_bStopped = true;
1410 : }
1411 :
1412 0 : void AutoRecovery::implts_dispatch(const DispatchParams& aParams)
1413 : {
1414 : sal_Int32 eJob;
1415 : /* SAFE */ {
1416 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1417 0 : eJob = m_eJob;
1418 : } /* SAFE */
1419 :
1420 : // in case a new dispatch overwrites a may ba active AutoSave session
1421 : // we must restore this session later. see below ...
1422 0 : bool bWasAutoSaveActive = ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE);
1423 : bool bWasUserAutoSaveActive =
1424 0 : ((eJob & AutoRecovery::E_USER_AUTO_SAVE) == AutoRecovery::E_USER_AUTO_SAVE);
1425 :
1426 : // On the other side it make no sense to reactivate the AutoSave operation
1427 : // if the new dispatch indicates a final decision ...
1428 : // E.g. an EmergencySave/SessionSave indicates the end of life of the current office session.
1429 : // It make no sense to reactivate an AutoSave then.
1430 : // But a Recovery or SessionRestore should reactivate a may be already active AutoSave.
1431 0 : bool bAllowAutoSaveReactivation = true;
1432 :
1433 0 : implts_stopTimer();
1434 0 : implts_stopListening();
1435 :
1436 0 : ListenerInformer aListenerInformer(*this, eJob);
1437 0 : aListenerInformer.start();
1438 :
1439 : try
1440 : {
1441 : // Auto save is called from our internal timer ... not via dispatch() API !
1442 : // else
1443 0 : if (
1444 0 : ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE) &&
1445 0 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY ) != AutoRecovery::E_DISABLE_AUTORECOVERY )
1446 : )
1447 : {
1448 : SAL_INFO("fwk.autorecovery", "... prepare emergency save ...");
1449 0 : bAllowAutoSaveReactivation = false;
1450 0 : implts_prepareEmergencySave();
1451 : }
1452 : else
1453 0 : if (
1454 0 : ((eJob & AutoRecovery::E_EMERGENCY_SAVE ) == AutoRecovery::E_EMERGENCY_SAVE ) &&
1455 0 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
1456 : )
1457 : {
1458 : SAL_INFO("fwk.autorecovery", "... do emergency save ...");
1459 0 : bAllowAutoSaveReactivation = false;
1460 0 : implts_doEmergencySave(aParams);
1461 : }
1462 : else
1463 0 : if (
1464 0 : ((eJob & AutoRecovery::E_RECOVERY ) == AutoRecovery::E_RECOVERY ) &&
1465 0 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
1466 : )
1467 : {
1468 : SAL_INFO("fwk.autorecovery", "... do recovery ...");
1469 0 : implts_doRecovery(aParams);
1470 : }
1471 : else
1472 0 : if (
1473 0 : ((eJob & AutoRecovery::E_SESSION_SAVE ) == AutoRecovery::E_SESSION_SAVE ) &&
1474 0 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
1475 : )
1476 : {
1477 : SAL_INFO("fwk.autorecovery", "... do session save ...");
1478 0 : bAllowAutoSaveReactivation = false;
1479 0 : implts_doSessionSave(aParams);
1480 : }
1481 : else
1482 0 : if (
1483 0 : ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT ) == AutoRecovery::E_SESSION_QUIET_QUIT ) &&
1484 0 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
1485 : )
1486 : {
1487 : SAL_INFO("fwk.autorecovery", "... do session quiet quit ...");
1488 0 : bAllowAutoSaveReactivation = false;
1489 0 : implts_doSessionQuietQuit(aParams);
1490 : }
1491 : else
1492 0 : if (
1493 0 : ((eJob & AutoRecovery::E_SESSION_RESTORE ) == AutoRecovery::E_SESSION_RESTORE ) &&
1494 0 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
1495 : )
1496 : {
1497 : SAL_INFO("fwk.autorecovery", "... do session restore ...");
1498 0 : implts_doSessionRestore(aParams);
1499 : }
1500 : else
1501 0 : if (
1502 0 : ((eJob & AutoRecovery::E_ENTRY_BACKUP ) == AutoRecovery::E_ENTRY_BACKUP ) &&
1503 0 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
1504 : )
1505 0 : implts_backupWorkingEntry(aParams);
1506 : else
1507 0 : if (
1508 0 : ((eJob & AutoRecovery::E_ENTRY_CLEANUP ) == AutoRecovery::E_ENTRY_CLEANUP ) &&
1509 0 : ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY)
1510 : )
1511 0 : implts_cleanUpWorkingEntry(aParams);
1512 : }
1513 0 : catch(const css::uno::RuntimeException&)
1514 : {
1515 0 : throw;
1516 : }
1517 0 : catch(const css::uno::Exception&)
1518 : {
1519 : // TODO better error handling
1520 : }
1521 :
1522 0 : aListenerInformer.stop();
1523 :
1524 : /* SAFE */ {
1525 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1526 0 : m_eJob = E_NO_JOB;
1527 0 : if (
1528 0 : (bAllowAutoSaveReactivation) &&
1529 : (bWasAutoSaveActive )
1530 : )
1531 : {
1532 0 : m_eJob |= AutoRecovery::E_AUTO_SAVE;
1533 :
1534 0 : if (bWasUserAutoSaveActive)
1535 : {
1536 0 : m_eJob |= AutoRecovery::E_USER_AUTO_SAVE;
1537 : }
1538 0 : }
1539 :
1540 : } /* SAFE */
1541 :
1542 : // depends on bAllowAutoSaveReactivation implicitly by looking on m_eJob=E_AUTO_SAVE! see before ...
1543 0 : implts_updateTimer();
1544 :
1545 0 : if (bAllowAutoSaveReactivation)
1546 0 : implts_startListening();
1547 0 : }
1548 :
1549 0 : void SAL_CALL AutoRecovery::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
1550 : const css::util::URL& aURL )
1551 : throw(css::uno::RuntimeException, std::exception)
1552 : {
1553 0 : if (!xListener.is())
1554 0 : throw css::uno::RuntimeException("Invalid listener reference.", static_cast< css::frame::XDispatch* >(this));
1555 : // container is threadsafe by using a shared mutex!
1556 0 : m_lListener.addInterface(aURL.Complete, xListener);
1557 :
1558 : // REENTRANT !? -> --------------------------------
1559 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
1560 :
1561 : /* SAFE */ {
1562 0 : osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1563 :
1564 0 : AutoRecovery::TDocumentList::iterator pIt;
1565 0 : for( pIt = m_lDocCache.begin();
1566 0 : pIt != m_lDocCache.end();
1567 : ++pIt )
1568 : {
1569 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
1570 0 : css::frame::FeatureStateEvent aEvent = AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_UPDATE, &rInfo);
1571 :
1572 : // } /* SAFE */
1573 0 : g.clear();
1574 0 : xListener->statusChanged(aEvent);
1575 0 : g.reset();
1576 : // /* SAFE */ {
1577 0 : }
1578 :
1579 0 : } /* SAFE */
1580 0 : }
1581 :
1582 0 : void SAL_CALL AutoRecovery::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener,
1583 : const css::util::URL& aURL )
1584 : throw(css::uno::RuntimeException, std::exception)
1585 : {
1586 0 : if (!xListener.is())
1587 0 : throw css::uno::RuntimeException("Invalid listener reference.", static_cast< css::frame::XDispatch* >(this));
1588 : // container is threadsafe by using a shared mutex!
1589 0 : m_lListener.removeInterface(aURL.Complete, xListener);
1590 0 : }
1591 :
1592 0 : void SAL_CALL AutoRecovery::documentEventOccured(const css::document::DocumentEvent& aEvent)
1593 : throw(css::uno::RuntimeException, std::exception)
1594 : {
1595 0 : css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
1596 :
1597 : // new document => put it into the internal list
1598 0 : if (
1599 0 : (aEvent.EventName == EVENT_ON_NEW) ||
1600 0 : (aEvent.EventName == EVENT_ON_LOAD)
1601 : )
1602 : {
1603 0 : implts_registerDocument(xDocument);
1604 : }
1605 : // document modified => set its modify state new (means modified against the original file!)
1606 0 : else if ( aEvent.EventName == EVENT_ON_MODIFYCHANGED )
1607 : {
1608 0 : implts_updateModifiedState(xDocument);
1609 : }
1610 : /* at least one document starts saving process =>
1611 : Our application code is not ready for multiple save requests
1612 : at the same time. So we have to suppress our AutoSave feature
1613 : for the moment, till this other save requests will be finished.
1614 : */
1615 0 : else if (
1616 0 : (aEvent.EventName == EVENT_ON_SAVE) ||
1617 0 : (aEvent.EventName == EVENT_ON_SAVEAS) ||
1618 0 : (aEvent.EventName == EVENT_ON_SAVETO)
1619 : )
1620 : {
1621 0 : implts_updateDocumentUsedForSavingState(xDocument, SAVE_IN_PROGRESS);
1622 : }
1623 : // document saved => remove tmp. files - but hold config entries alive!
1624 0 : else if (
1625 0 : (aEvent.EventName == EVENT_ON_SAVEDONE) ||
1626 0 : (aEvent.EventName == EVENT_ON_SAVEASDONE)
1627 : )
1628 : {
1629 0 : implts_markDocumentAsSaved(xDocument);
1630 0 : implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
1631 : }
1632 : /* document saved as copy => mark it as "non used by concurrent save operation".
1633 : so we can try to create a backup copy if next time AutoSave is started too.
1634 : Dont remove temp. files or change the modified state of the document!
1635 : It was not really saved to the original file ...
1636 : */
1637 0 : else if ( aEvent.EventName == EVENT_ON_SAVETODONE )
1638 : {
1639 0 : implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
1640 : }
1641 : // If saving of a document failed by an error ... we have to save this document
1642 : // by ourself next time AutoSave or EmergencySave is triggered.
1643 : // But we can reset the state "used for other save requests". Otherwhise
1644 : // these documents will never be saved!
1645 0 : else if (
1646 0 : (aEvent.EventName == EVENT_ON_SAVEFAILED) ||
1647 0 : (aEvent.EventName == EVENT_ON_SAVEASFAILED) ||
1648 0 : (aEvent.EventName == EVENT_ON_SAVETOFAILED)
1649 : )
1650 : {
1651 0 : implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED);
1652 : }
1653 : // document closed => remove temp. files and configuration entries
1654 0 : else if ( aEvent.EventName == EVENT_ON_UNLOAD )
1655 : {
1656 0 : implts_deregisterDocument(xDocument, true); // sal_True => stop listening for disposing() !
1657 0 : }
1658 0 : }
1659 :
1660 0 : void SAL_CALL AutoRecovery::changesOccurred(const css::util::ChangesEvent& aEvent)
1661 : throw(css::uno::RuntimeException, std::exception)
1662 : {
1663 0 : const css::uno::Sequence< css::util::ElementChange > lChanges (aEvent.Changes);
1664 0 : const css::util::ElementChange* pChanges = lChanges.getConstArray();
1665 :
1666 0 : sal_Int32 c = lChanges.getLength();
1667 0 : sal_Int32 i = 0;
1668 :
1669 : /* SAFE */ {
1670 0 : osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1671 :
1672 : // Changes of the configuration must be ignored if AutoSave/Recovery was disabled for this
1673 : // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless"
1674 : // was set.
1675 0 : if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
1676 0 : return;
1677 :
1678 0 : for (i=0; i<c; ++i)
1679 : {
1680 0 : OUString sPath;
1681 0 : pChanges[i].Accessor >>= sPath;
1682 :
1683 0 : if ( sPath == CFG_ENTRY_AUTOSAVE_ENABLED )
1684 : {
1685 0 : bool bEnabled = false;
1686 0 : if (pChanges[i].Element >>= bEnabled)
1687 : {
1688 0 : if (bEnabled)
1689 : {
1690 0 : m_eJob |= AutoRecovery::E_AUTO_SAVE;
1691 0 : m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
1692 : }
1693 : else
1694 : {
1695 0 : m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
1696 0 : m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
1697 : }
1698 : }
1699 : }
1700 : else
1701 0 : if ( sPath == CFG_ENTRY_AUTOSAVE_TIMEINTERVALL )
1702 0 : pChanges[i].Element >>= m_nAutoSaveTimeIntervall;
1703 0 : }
1704 :
1705 : } /* SAFE */
1706 :
1707 : // Note: This call stops the timer and starts it again.
1708 : // But it checks the different timer states internally and
1709 : // may be suppress the restart!
1710 0 : implts_updateTimer();
1711 : }
1712 :
1713 0 : void SAL_CALL AutoRecovery::modified(const css::lang::EventObject& aEvent)
1714 : throw(css::uno::RuntimeException, std::exception)
1715 : {
1716 0 : css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
1717 0 : if (! xDocument.is())
1718 0 : return;
1719 :
1720 0 : implts_markDocumentModifiedAgainstLastBackup(xDocument);
1721 : }
1722 :
1723 0 : void SAL_CALL AutoRecovery::disposing(const css::lang::EventObject& aEvent)
1724 : throw(css::uno::RuntimeException, std::exception)
1725 : {
1726 : /* SAFE */ {
1727 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1728 :
1729 0 : if (aEvent.Source == m_xNewDocBroadcaster)
1730 : {
1731 0 : m_xNewDocBroadcaster.clear();
1732 0 : return;
1733 : }
1734 :
1735 0 : if (aEvent.Source == m_xRecoveryCFG)
1736 : {
1737 0 : m_xRecoveryCFG.clear();
1738 0 : return;
1739 : }
1740 :
1741 : // dispose from one of our cached documents ?
1742 : // Normally they should send a OnUnload message ...
1743 : // But some stacktraces shows another possible use case .-)
1744 0 : css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY);
1745 0 : if (xDocument.is())
1746 : {
1747 0 : implts_deregisterDocument(xDocument, false); // sal_False => dont call removeEventListener() .. because it's not needed here
1748 0 : return;
1749 0 : }
1750 :
1751 : } /* SAFE */
1752 : }
1753 :
1754 130 : css::uno::Reference< css::container::XNameAccess > AutoRecovery::implts_openConfig()
1755 : {
1756 : /* SAFE */ {
1757 130 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1758 :
1759 130 : if (m_xRecoveryCFG.is())
1760 65 : return m_xRecoveryCFG;
1761 :
1762 : } /* SAFE */
1763 :
1764 65 : OUString sCFG_PACKAGE_RECOVERY(CFG_PACKAGE_RECOVERY);
1765 : // throws a RuntimeException if an error occurs!
1766 : css::uno::Reference< css::container::XNameAccess > xCFG(
1767 : ::comphelper::ConfigurationHelper::openConfig(m_xContext, sCFG_PACKAGE_RECOVERY, ::comphelper::ConfigurationHelper::E_STANDARD),
1768 130 : css::uno::UNO_QUERY);
1769 :
1770 65 : sal_Int32 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
1771 65 : sal_Int32 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
1772 :
1773 : try
1774 : {
1775 65 : OUString sCFG_PATH_AUTOSAVE(CFG_PATH_AUTOSAVE);
1776 : ::comphelper::ConfigurationHelper::readDirectKey(m_xContext,
1777 : sCFG_PACKAGE_RECOVERY,
1778 : sCFG_PATH_AUTOSAVE,
1779 : OUString(CFG_ENTRY_MINSPACE_DOCSAVE),
1780 65 : ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceDocSave;
1781 :
1782 : ::comphelper::ConfigurationHelper::readDirectKey(m_xContext,
1783 : sCFG_PACKAGE_RECOVERY,
1784 : sCFG_PATH_AUTOSAVE,
1785 : OUString(CFG_ENTRY_MINSPACE_CONFIGSAVE),
1786 65 : ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceConfigSave;
1787 : }
1788 0 : catch(const css::uno::Exception&)
1789 : {
1790 : // These config keys are not sooooo important, that
1791 : // we are interested on errors here really .-)
1792 0 : nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE;
1793 0 : nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE;
1794 : }
1795 :
1796 : /* SAFE */ {
1797 65 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1798 65 : m_xRecoveryCFG = xCFG;
1799 65 : m_nMinSpaceDocSave = nMinSpaceDocSave;
1800 65 : m_nMinSpaceConfigSave = nMinSpaceConfigSave;
1801 : } /* SAFE */
1802 :
1803 130 : return xCFG;
1804 : }
1805 :
1806 65 : void AutoRecovery::implts_readAutoSaveConfig()
1807 : {
1808 65 : css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY);
1809 :
1810 : // AutoSave [bool]
1811 65 : bool bEnabled = false;
1812 65 : xCommonRegistry->getByHierarchicalName(OUString(CFG_ENTRY_AUTOSAVE_ENABLED)) >>= bEnabled;
1813 :
1814 : // UserAutoSave [bool]
1815 65 : bool bUserEnabled = false;
1816 65 : xCommonRegistry->getByHierarchicalName(OUString(CFG_ENTRY_USERAUTOSAVE_ENABLED)) >>= bUserEnabled;
1817 :
1818 : /* SAFE */ {
1819 65 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1820 65 : if (bEnabled)
1821 : {
1822 65 : m_eJob |= AutoRecovery::E_AUTO_SAVE;
1823 65 : m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
1824 :
1825 65 : if (bUserEnabled)
1826 : {
1827 0 : m_eJob |= AutoRecovery::E_USER_AUTO_SAVE;
1828 : }
1829 : else
1830 : {
1831 65 : m_eJob &= ~AutoRecovery::E_USER_AUTO_SAVE;
1832 : }
1833 : }
1834 : else
1835 : {
1836 0 : m_eJob &= ~AutoRecovery::E_AUTO_SAVE;
1837 0 : m_eTimerType = AutoRecovery::E_DONT_START_TIMER;
1838 65 : }
1839 : } /* SAFE */
1840 :
1841 : // AutoSaveTimeIntervall [int] in min
1842 65 : sal_Int32 nTimeIntervall = 15;
1843 65 : xCommonRegistry->getByHierarchicalName(OUString(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL)) >>= nTimeIntervall;
1844 :
1845 : /* SAFE */ {
1846 65 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1847 65 : m_nAutoSaveTimeIntervall = nTimeIntervall;
1848 65 : } /* SAFE */
1849 65 : }
1850 :
1851 65 : void AutoRecovery::implts_readConfig()
1852 : {
1853 65 : implts_readAutoSaveConfig();
1854 :
1855 65 : css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY);
1856 :
1857 : // REENTRANT -> --------------------------------
1858 130 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
1859 :
1860 : /* SAFE */ {
1861 65 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1862 : // reset current cache load cache
1863 65 : m_lDocCache.clear();
1864 65 : m_nIdPool = 0;
1865 : } /* SAFE */
1866 :
1867 65 : aCacheLock.unlock();
1868 : // <- REENTRANT --------------------------------
1869 :
1870 130 : css::uno::Any aValue;
1871 :
1872 : // RecoveryList [set]
1873 65 : aValue = xCommonRegistry->getByHierarchicalName(OUString(CFG_ENTRY_RECOVERYLIST));
1874 130 : css::uno::Reference< css::container::XNameAccess > xList;
1875 65 : aValue >>= xList;
1876 65 : if (xList.is())
1877 : {
1878 65 : const OUString sRECOVERY_ITEM_BASE_IDENTIFIER(RECOVERY_ITEM_BASE_IDENTIFIER);
1879 130 : const css::uno::Sequence< OUString > lItems = xList->getElementNames();
1880 65 : const OUString* pItems = lItems.getConstArray();
1881 65 : sal_Int32 c = lItems.getLength();
1882 65 : sal_Int32 i = 0;
1883 :
1884 : // REENTRANT -> --------------------------
1885 65 : aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
1886 :
1887 65 : for (i=0; i<c; ++i)
1888 : {
1889 0 : css::uno::Reference< css::beans::XPropertySet > xItem;
1890 0 : xList->getByName(pItems[i]) >>= xItem;
1891 0 : if (!xItem.is())
1892 0 : continue;
1893 :
1894 0 : AutoRecovery::TDocumentInfo aInfo;
1895 0 : aInfo.NewTempURL.clear();
1896 0 : aInfo.Document = css::uno::Reference< css::frame::XModel >();
1897 0 : xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_ORIGINALURL)) >>= aInfo.OrgURL;
1898 0 : xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_TEMPURL)) >>= aInfo.OldTempURL;
1899 0 : xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_TEMPLATEURL)) >>= aInfo.TemplateURL;
1900 0 : xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_FILTER)) >>= aInfo.RealFilter;
1901 0 : xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_DOCUMENTSTATE)) >>= aInfo.DocumentState;
1902 0 : xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_MODULE)) >>= aInfo.AppModule;
1903 0 : xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_TITLE)) >>= aInfo.Title;
1904 0 : xItem->getPropertyValue(OUString(CFG_ENTRY_PROP_VIEWNAMES)) >>= aInfo.ViewNames;
1905 0 : implts_specifyAppModuleAndFactory(aInfo);
1906 0 : implts_specifyDefaultFilterAndExtension(aInfo);
1907 :
1908 0 : if (pItems[i].startsWith(sRECOVERY_ITEM_BASE_IDENTIFIER))
1909 : {
1910 0 : OUString sID = pItems[i].copy(sRECOVERY_ITEM_BASE_IDENTIFIER.getLength());
1911 0 : aInfo.ID = sID.toInt32();
1912 : /* SAFE */ {
1913 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1914 0 : if (aInfo.ID > m_nIdPool)
1915 : {
1916 0 : m_nIdPool = aInfo.ID+1;
1917 : SAL_WARN_IF(m_nIdPool<0, "fwk", "AutoRecovery::implts_readConfig(): Overflow of IDPool detected!");
1918 0 : }
1919 0 : } /* SAFE */
1920 : }
1921 : else
1922 : SAL_INFO("fwk", "AutoRecovery::implts_readConfig(): Who changed numbering of recovery items? Cache will be inconsistent then! I do not know, what will happen next time .-)");
1923 :
1924 : /* SAFE */ {
1925 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1926 0 : m_lDocCache.push_back(aInfo);
1927 : } /* SAFE */
1928 0 : }
1929 :
1930 130 : aCacheLock.unlock();
1931 : // <- REENTRANT --------------------------
1932 : }
1933 :
1934 130 : implts_updateTimer();
1935 65 : }
1936 :
1937 0 : void AutoRecovery::implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo)
1938 : {
1939 0 : if (rInfo.AppModule.isEmpty())
1940 : {
1941 : throw css::uno::RuntimeException(
1942 : "Can not find out the default filter and its extension, if no application module is known!",
1943 0 : static_cast< css::frame::XDispatch* >(this));
1944 : }
1945 :
1946 0 : css::uno::Reference< css::container::XNameAccess> xCFG;
1947 : /* SAFE */ {
1948 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1949 0 : xCFG = m_xModuleCFG;
1950 : } /* SAFE */
1951 :
1952 : try
1953 : {
1954 0 : if (! xCFG.is())
1955 : {
1956 : // open module config on demand and cache the update access
1957 0 : xCFG = css::uno::Reference< css::container::XNameAccess >(
1958 : ::comphelper::ConfigurationHelper::openConfig(m_xContext, OUString(CFG_PACKAGE_MODULES),
1959 : ::comphelper::ConfigurationHelper::E_STANDARD),
1960 0 : css::uno::UNO_QUERY_THROW);
1961 :
1962 : /* SAFE */ {
1963 0 : osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
1964 0 : m_xModuleCFG = xCFG;
1965 : } /* SAFE */
1966 : }
1967 :
1968 : css::uno::Reference< css::container::XNameAccess > xModuleProps(
1969 0 : xCFG->getByName(rInfo.AppModule),
1970 0 : css::uno::UNO_QUERY_THROW);
1971 :
1972 0 : xModuleProps->getByName(OUString(CFG_ENTRY_REALDEFAULTFILTER)) >>= rInfo.DefaultFilter;
1973 :
1974 : css::uno::Reference< css::container::XNameAccess > xFilterCFG(
1975 0 : m_xContext->getServiceManager()->createInstanceWithContext(
1976 0 : "com.sun.star.document.FilterFactory", m_xContext), css::uno::UNO_QUERY_THROW);
1977 : css::uno::Reference< css::container::XNameAccess > xTypeCFG(
1978 0 : m_xContext->getServiceManager()->createInstanceWithContext(
1979 0 : "com.sun.star.document.TypeDetection", m_xContext), css::uno::UNO_QUERY_THROW);
1980 :
1981 0 : ::comphelper::SequenceAsHashMap lFilterProps (xFilterCFG->getByName(rInfo.DefaultFilter));
1982 0 : OUString sTypeRegistration = lFilterProps.getUnpackedValueOrDefault(OUString(FILTER_PROP_TYPE), OUString());
1983 0 : ::comphelper::SequenceAsHashMap lTypeProps (xTypeCFG->getByName(sTypeRegistration));
1984 0 : css::uno::Sequence< OUString > lExtensions = lTypeProps.getUnpackedValueOrDefault(OUString(TYPE_PROP_EXTENSIONS), css::uno::Sequence< OUString >());
1985 0 : if (lExtensions.getLength())
1986 : {
1987 0 : rInfo.Extension = "." + lExtensions[0];
1988 : }
1989 : else
1990 0 : rInfo.Extension = ".unknown";
1991 : }
1992 0 : catch(const css::uno::Exception&)
1993 : {
1994 0 : rInfo.DefaultFilter.clear();
1995 0 : rInfo.Extension.clear();
1996 0 : }
1997 0 : }
1998 :
1999 0 : void AutoRecovery::implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo)
2000 : {
2001 0 : ENSURE_OR_THROW2(
2002 : !rInfo.AppModule.isEmpty() || rInfo.Document.is(),
2003 : "Can not find out the application module nor its factory URL, if no application module (or a suitable) document is known!",
2004 : *this );
2005 :
2006 0 : css::uno::Reference< css::frame::XModuleManager2 > xManager = ModuleManager::create(m_xContext);
2007 :
2008 0 : if (rInfo.AppModule.isEmpty())
2009 0 : rInfo.AppModule = xManager->identify(rInfo.Document);
2010 :
2011 0 : ::comphelper::SequenceAsHashMap lModuleDescription(xManager->getByName(rInfo.AppModule));
2012 0 : lModuleDescription[OUString(CFG_ENTRY_PROP_EMPTYDOCUMENTURL)] >>= rInfo.FactoryURL;
2013 0 : lModuleDescription[OUString(CFG_ENTRY_PROP_FACTORYSERVICE)] >>= rInfo.FactoryService;
2014 0 : }
2015 :
2016 0 : void AutoRecovery::implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& i_rInfo )
2017 : {
2018 0 : ENSURE_OR_THROW2( i_rInfo.Document.is(), "need at document, at the very least", *this );
2019 :
2020 0 : i_rInfo.ViewNames.realloc(0);
2021 :
2022 : // obtain list of controllers of this document
2023 0 : ::std::vector< OUString > aViewNames;
2024 0 : const Reference< XModel2 > xModel( i_rInfo.Document, UNO_QUERY );
2025 0 : if ( xModel.is() )
2026 : {
2027 0 : const Reference< css::container::XEnumeration > xEnumControllers( xModel->getControllers() );
2028 0 : while ( xEnumControllers->hasMoreElements() )
2029 : {
2030 0 : const Reference< XController2 > xController( xEnumControllers->nextElement(), UNO_QUERY );
2031 0 : OUString sViewName;
2032 0 : if ( xController.is() )
2033 0 : sViewName = xController->getViewControllerName();
2034 : OSL_ENSURE( !sViewName.isEmpty(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" );
2035 :
2036 0 : if ( !sViewName.isEmpty() )
2037 0 : aViewNames.push_back( sViewName );
2038 0 : }
2039 : }
2040 : else
2041 : {
2042 0 : const Reference< XController2 > xController( xModel->getCurrentController(), UNO_QUERY );
2043 0 : OUString sViewName;
2044 0 : if ( xController.is() )
2045 0 : sViewName = xController->getViewControllerName();
2046 : OSL_ENSURE( !sViewName.isEmpty(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" );
2047 :
2048 0 : if ( !sViewName.isEmpty() )
2049 0 : aViewNames.push_back( sViewName );
2050 : }
2051 :
2052 0 : i_rInfo.ViewNames.realloc( aViewNames.size() );
2053 0 : ::std::copy( aViewNames.begin(), aViewNames.end(), i_rInfo.ViewNames.getArray() );
2054 0 : }
2055 :
2056 0 : void AutoRecovery::implts_persistAllActiveViewNames()
2057 : {
2058 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2059 :
2060 : // This list will be filled with every document
2061 0 : AutoRecovery::TDocumentList::iterator pIt;
2062 0 : for ( pIt = m_lDocCache.begin();
2063 0 : pIt != m_lDocCache.end();
2064 : ++pIt )
2065 : {
2066 0 : implts_collectActiveViewNames( *pIt );
2067 0 : implts_flushConfigItem( *pIt );
2068 0 : }
2069 0 : }
2070 :
2071 0 : void AutoRecovery::implts_flushConfigItem(const AutoRecovery::TDocumentInfo& rInfo, bool bRemoveIt)
2072 : {
2073 0 : css::uno::Reference< css::container::XHierarchicalNameAccess > xCFG;
2074 :
2075 : try
2076 : {
2077 0 : xCFG = css::uno::Reference< css::container::XHierarchicalNameAccess >(implts_openConfig(), css::uno::UNO_QUERY_THROW);
2078 :
2079 0 : css::uno::Reference< css::container::XNameAccess > xCheck;
2080 0 : xCFG->getByHierarchicalName(OUString(CFG_ENTRY_RECOVERYLIST)) >>= xCheck;
2081 :
2082 0 : css::uno::Reference< css::container::XNameContainer > xModify(xCheck, css::uno::UNO_QUERY_THROW);
2083 0 : css::uno::Reference< css::lang::XSingleServiceFactory > xCreate(xCheck, css::uno::UNO_QUERY_THROW);
2084 :
2085 0 : OUStringBuffer sIDBuf;
2086 0 : sIDBuf.append(RECOVERY_ITEM_BASE_IDENTIFIER);
2087 0 : sIDBuf.append((sal_Int32)rInfo.ID);
2088 0 : OUString sID = sIDBuf.makeStringAndClear();
2089 :
2090 : // remove
2091 0 : if (bRemoveIt)
2092 : {
2093 : // Catch NoSuchElementException.
2094 : // Its not a good idea inside multithreaded environments to call hasElement - removeElement.
2095 : // DO IT!
2096 : try
2097 : {
2098 0 : xModify->removeByName(sID);
2099 : }
2100 0 : catch(const css::container::NoSuchElementException&)
2101 : {
2102 0 : return;
2103 : }
2104 : }
2105 : else
2106 : {
2107 : // new/modify
2108 0 : css::uno::Reference< css::beans::XPropertySet > xSet;
2109 0 : bool bNew = (!xCheck->hasByName(sID));
2110 0 : if (bNew)
2111 0 : xSet = css::uno::Reference< css::beans::XPropertySet >(xCreate->createInstance(), css::uno::UNO_QUERY_THROW);
2112 : else
2113 0 : xCheck->getByName(sID) >>= xSet;
2114 :
2115 0 : xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_ORIGINALURL), css::uno::makeAny(rInfo.OrgURL ));
2116 0 : xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_TEMPURL), css::uno::makeAny(rInfo.OldTempURL ));
2117 0 : xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_TEMPLATEURL), css::uno::makeAny(rInfo.TemplateURL ));
2118 0 : xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_FILTER), css::uno::makeAny(rInfo.RealFilter));
2119 0 : xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_DOCUMENTSTATE), css::uno::makeAny(rInfo.DocumentState));
2120 0 : xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_MODULE), css::uno::makeAny(rInfo.AppModule));
2121 0 : xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_TITLE), css::uno::makeAny(rInfo.Title));
2122 0 : xSet->setPropertyValue(OUString(CFG_ENTRY_PROP_VIEWNAMES), css::uno::makeAny(rInfo.ViewNames));
2123 :
2124 0 : if (bNew)
2125 0 : xModify->insertByName(sID, css::uno::makeAny(xSet));
2126 0 : }
2127 : }
2128 0 : catch(const css::uno::RuntimeException&)
2129 : {
2130 0 : throw;
2131 : }
2132 0 : catch(const css::uno::Exception&)
2133 : {
2134 : // ??? can it happen that a full disc let these set of operations fail too ???
2135 : }
2136 :
2137 0 : sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
2138 0 : do
2139 : {
2140 : try
2141 : {
2142 0 : css::uno::Reference< css::util::XChangesBatch > xFlush(xCFG, css::uno::UNO_QUERY_THROW);
2143 0 : xFlush->commitChanges();
2144 :
2145 : #ifdef TRIGGER_FULL_DISC_CHECK
2146 : throw css::uno::Exception();
2147 : #else // TRIGGER_FULL_DISC_CHECK
2148 0 : nRetry = 0;
2149 : #endif // TRIGGER_FULL_DISC_CHECK
2150 : }
2151 0 : catch(const css::uno::Exception&)
2152 : {
2153 : // a) FULL DISC seems to be the problem behind => show error and retry it forever (e.g. retry=300)
2154 : // b) unknown problem (may be locking problem) => reset RETRY value to more useful value(!) (e.g. retry=3)
2155 : // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
2156 :
2157 : sal_Int32 nMinSpaceConfigSave;
2158 : /* SAFE */ {
2159 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2160 0 : nMinSpaceConfigSave = m_nMinSpaceConfigSave;
2161 : } /* SAFE */
2162 :
2163 0 : if (! impl_enoughDiscSpace(nMinSpaceConfigSave))
2164 0 : AutoRecovery::impl_showFullDiscError();
2165 0 : else if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
2166 0 : nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
2167 0 : else if (nRetry <= GIVE_UP_RETRY)
2168 0 : throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
2169 :
2170 0 : --nRetry;
2171 : }
2172 : }
2173 0 : while(nRetry>0);
2174 : }
2175 :
2176 65 : void AutoRecovery::implts_startListening()
2177 : {
2178 65 : css::uno::Reference< css::util::XChangesNotifier > xCFG;
2179 130 : css::uno::Reference< css::frame::XGlobalEventBroadcaster > xBroadcaster;
2180 : bool bListenForDocEvents;
2181 : /* SAFE */ {
2182 65 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2183 65 : xCFG.set (m_xRecoveryCFG, css::uno::UNO_QUERY);
2184 65 : xBroadcaster = m_xNewDocBroadcaster;
2185 65 : bListenForDocEvents = m_bListenForDocEvents;
2186 : } /* SAFE */
2187 :
2188 65 : if (
2189 130 : ( xCFG.is() ) &&
2190 65 : (! m_bListenForConfigChanges)
2191 : )
2192 : {
2193 65 : m_xRecoveryCFGListener = new WeakChangesListener(this);
2194 65 : xCFG->addChangesListener(m_xRecoveryCFGListener);
2195 65 : m_bListenForConfigChanges = true;
2196 : }
2197 :
2198 65 : if (!xBroadcaster.is())
2199 : {
2200 65 : xBroadcaster = css::frame::theGlobalEventBroadcaster::get(m_xContext);
2201 : /* SAFE */ {
2202 65 : osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2203 65 : m_xNewDocBroadcaster = xBroadcaster;
2204 : } /* SAFE */
2205 : }
2206 :
2207 65 : if (
2208 130 : ( xBroadcaster.is() ) &&
2209 65 : (! bListenForDocEvents)
2210 : )
2211 : {
2212 65 : m_xNewDocBroadcasterListener = new WeakDocumentEventListener(this);
2213 65 : xBroadcaster->addDocumentEventListener(m_xNewDocBroadcasterListener);
2214 : /* SAFE */ {
2215 65 : osl::MutexGuard g2(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2216 65 : m_bListenForDocEvents = true;
2217 : } /* SAFE */
2218 65 : }
2219 65 : }
2220 :
2221 64 : void AutoRecovery::implts_stopListening()
2222 : {
2223 64 : css::uno::Reference< css::util::XChangesNotifier > xCFG;
2224 128 : css::uno::Reference< css::document::XDocumentEventBroadcaster > xGlobalEventBroadcaster;
2225 : /* SAFE */ {
2226 64 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2227 : // Attention: Dont reset our internal members here too.
2228 : // May be we must work with our configuration, but dont wish to be informed
2229 : // about changes any longer. Needed e.g. during EMERGENCY_SAVE!
2230 64 : xCFG.set (m_xRecoveryCFG , css::uno::UNO_QUERY);
2231 64 : xGlobalEventBroadcaster.set(m_xNewDocBroadcaster, css::uno::UNO_QUERY);
2232 : } /* SAFE */
2233 :
2234 64 : if (
2235 64 : (xGlobalEventBroadcaster.is()) &&
2236 : (m_bListenForDocEvents )
2237 : )
2238 : {
2239 64 : xGlobalEventBroadcaster->removeDocumentEventListener(m_xNewDocBroadcasterListener);
2240 64 : m_bListenForDocEvents = false;
2241 : }
2242 :
2243 64 : if (
2244 64 : (xCFG.is() ) &&
2245 : (m_bListenForConfigChanges)
2246 : )
2247 : {
2248 64 : xCFG->removeChangesListener(m_xRecoveryCFGListener);
2249 64 : m_bListenForConfigChanges = false;
2250 64 : }
2251 64 : }
2252 :
2253 0 : void AutoRecovery::implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
2254 : {
2255 0 : if (rInfo.ListenForModify)
2256 0 : return;
2257 :
2258 0 : css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
2259 0 : if (xBroadcaster.is())
2260 : {
2261 0 : css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY);
2262 0 : xBroadcaster->addModifyListener(xThis);
2263 0 : rInfo.ListenForModify = true;
2264 0 : }
2265 : }
2266 :
2267 0 : void AutoRecovery::implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo)
2268 : {
2269 0 : if (! rInfo.ListenForModify)
2270 0 : return;
2271 :
2272 0 : css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY);
2273 0 : if (xBroadcaster.is())
2274 : {
2275 0 : css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY);
2276 0 : xBroadcaster->removeModifyListener(xThis);
2277 0 : rInfo.ListenForModify = false;
2278 0 : }
2279 : }
2280 :
2281 65 : void AutoRecovery::implts_updateTimer()
2282 : {
2283 65 : implts_stopTimer();
2284 :
2285 : /* SAFE */ {
2286 65 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2287 :
2288 65 : if (
2289 130 : (m_eJob == AutoRecovery::E_NO_JOB ) || // TODO may be superflous - E_DONT_START_TIMER should be used only
2290 65 : (m_eTimerType == AutoRecovery::E_DONT_START_TIMER)
2291 : )
2292 65 : return;
2293 :
2294 65 : sal_Int32 nMilliSeconds = 0;
2295 65 : if (m_eTimerType == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
2296 : {
2297 65 : nMilliSeconds = (m_nAutoSaveTimeIntervall*60000); // [min] => 60.000 ms
2298 : #if OSL_DEBUG_LEVEL > 1
2299 : if (m_dbg_bMakeItFaster)
2300 : nMilliSeconds = m_nAutoSaveTimeIntervall; // [ms]
2301 : #endif
2302 : }
2303 0 : else if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
2304 : {
2305 0 : nMilliSeconds = MIN_TIME_FOR_USER_IDLE;
2306 : #if OSL_DEBUG_LEVEL > 1
2307 : if (m_dbg_bMakeItFaster)
2308 : nMilliSeconds = 300; // let us some time, to finish this method .-)
2309 : #endif
2310 : }
2311 0 : else if (m_eTimerType == AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED)
2312 0 : nMilliSeconds = 300; // there is a minimum time frame, where the user can lose some key input data!
2313 :
2314 65 : m_aTimer.SetTimeout(nMilliSeconds);
2315 65 : m_aTimer.Start();
2316 :
2317 : } /* SAFE */
2318 : }
2319 :
2320 259 : void AutoRecovery::implts_stopTimer()
2321 : {
2322 259 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2323 :
2324 259 : if (!m_aTimer.IsActive())
2325 453 : return;
2326 65 : m_aTimer.Stop();
2327 : }
2328 :
2329 0 : IMPL_LINK_NOARG_TYPED(AutoRecovery, implts_timerExpired, Timer *, void)
2330 : {
2331 : try
2332 : {
2333 : // This method is called by using a pointer to us.
2334 : // But we must be aware that we can be destroyed hardly
2335 : // if our uno reference will be gone!
2336 : // => Hold this object alive till this method finish its work.
2337 0 : css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XTypeProvider* >(this));
2338 :
2339 : // Needed! Otherwise every reschedule request allow a new triggered timer event :-(
2340 0 : implts_stopTimer();
2341 :
2342 : // The timer must be ignored if AutoSave/Recovery was disabled for this
2343 : // office session. That can happen if e.g. the command line arguments "--norestore" or "--headless"
2344 : // was set. But normally the timer was disabled if recovery was disabled ...
2345 : // But so we are more "safe" .-)
2346 : /* SAFE */ {
2347 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2348 0 : if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY)
2349 0 : return;
2350 : } /* SAFE */
2351 :
2352 : // check some "states", where its not allowed (better: not a good idea) to
2353 : // start an AutoSave. (e.g. if the user makes drag & drop ...)
2354 : // Then we poll till this "disallowed" state is gone.
2355 0 : bool bAutoSaveNotAllowed = Application::IsUICaptured();
2356 0 : if (bAutoSaveNotAllowed)
2357 : {
2358 : /* SAFE */ {
2359 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2360 0 : m_eTimerType = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
2361 : } /* SAFE */
2362 0 : implts_updateTimer();
2363 0 : return;
2364 : }
2365 :
2366 : // analyze timer type.
2367 : // If we poll for an user idle period, may be we must
2368 : // do nothing here and start the timer again.
2369 : /* SAFE */ {
2370 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2371 :
2372 0 : if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE)
2373 : {
2374 0 : bool bUserIdle = (Application::GetLastInputInterval()>MIN_TIME_FOR_USER_IDLE);
2375 0 : if (!bUserIdle)
2376 : {
2377 0 : implts_updateTimer();
2378 0 : return;
2379 : }
2380 0 : }
2381 :
2382 : } /* SAFE */
2383 :
2384 : implts_informListener(AutoRecovery::E_AUTO_SAVE,
2385 0 : AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_START, NULL));
2386 :
2387 : // force save of all currently open documents
2388 : // The called method returns an info, if and how this
2389 : // timer must be restarted.
2390 0 : bool bAllowUserIdleLoop = true;
2391 0 : AutoRecovery::ETimerType eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, false);
2392 :
2393 : // If timer is not used for "short callbacks" (means polling
2394 : // for special states) ... reset the handle state of all
2395 : // cache items. Such handle state indicates, that a document
2396 : // was already saved during the THIS(!) AutoSave session.
2397 : // Of course NEXT AutoSave session must be started without
2398 : // any "handle" state ...
2399 0 : if (
2400 0 : (eSuggestedTimer == AutoRecovery::E_DONT_START_TIMER ) ||
2401 : (eSuggestedTimer == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL)
2402 : )
2403 : {
2404 0 : implts_resetHandleStates(false);
2405 : }
2406 :
2407 : implts_informListener(AutoRecovery::E_AUTO_SAVE,
2408 0 : AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_STOP, NULL));
2409 :
2410 : // restart timer - because it was disabled before ...
2411 : /* SAFE */ {
2412 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2413 0 : m_eTimerType = eSuggestedTimer;
2414 : } /* SAFE */
2415 :
2416 0 : implts_updateTimer();
2417 : }
2418 0 : catch(const css::uno::Exception&)
2419 : {
2420 : }
2421 : }
2422 :
2423 0 : IMPL_LINK_NOARG(AutoRecovery, implts_asyncDispatch)
2424 : {
2425 0 : DispatchParams aParams;
2426 : /* SAFE */ {
2427 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2428 0 : aParams = m_aDispatchParams;
2429 0 : css::uno::Reference< css::uno::XInterface > xHoldRefForMethodAlive = aParams.m_xHoldRefForAsyncOpAlive;
2430 0 : m_aDispatchParams.forget(); // clears all members ... including the ref-hold object .-)
2431 : } /* SAFE */
2432 :
2433 : try
2434 : {
2435 0 : implts_dispatch(aParams);
2436 : }
2437 0 : catch (...)
2438 : {
2439 : }
2440 0 : return 0;
2441 : }
2442 :
2443 0 : void AutoRecovery::implts_registerDocument(const css::uno::Reference< css::frame::XModel >& xDocument)
2444 : {
2445 : // ignore corrupted events, where no document is given ... Runtime Error ?!
2446 0 : if (!xDocument.is())
2447 0 : return;
2448 :
2449 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2450 :
2451 : // notification for already existing document !
2452 : // Can happen if events came in asynchronous on recovery time.
2453 : // Then our cache was filled from the configuration ... but now we get some
2454 : // asynchronous events from the global event broadcaster. We must be sure that
2455 : // we dont add the same document more than once.
2456 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
2457 0 : if (pIt != m_lDocCache.end())
2458 : {
2459 : // Normally nothing must be done for this "late" notification.
2460 : // But may be the modified state was changed inbetween.
2461 : // Check it ...
2462 0 : implts_updateModifiedState(xDocument);
2463 0 : return;
2464 : }
2465 :
2466 0 : aCacheLock.unlock();
2467 :
2468 0 : utl::MediaDescriptor lDescriptor(xDocument->getArgs());
2469 :
2470 : // check if this document must be ignored for recovery !
2471 : // Some use cases dont wish support for AutoSave/Recovery ... as e.g. OLE-Server / ActiveX Control etcpp.
2472 0 : bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_NOAUTOSAVE(), false);
2473 0 : if (bNoAutoSave)
2474 0 : return;
2475 :
2476 : // Check if doc is well known on the desktop. Otherwhise ignore it!
2477 : // Other frames mostly are used from external programs - e.g. the bean ...
2478 0 : css::uno::Reference< css::frame::XController > xController = xDocument->getCurrentController();
2479 0 : if (!xController.is())
2480 0 : return;
2481 :
2482 0 : css::uno::Reference< css::frame::XFrame > xFrame = xController->getFrame();
2483 0 : css::uno::Reference< css::frame::XDesktop > xDesktop (xFrame->getCreator(), css::uno::UNO_QUERY);
2484 0 : if (!xDesktop.is())
2485 0 : return;
2486 :
2487 : // if the document doesn't support the XDocumentRecovery interface, we're not interested in it.
2488 0 : Reference< XDocumentRecovery > xDocRecovery( xDocument, UNO_QUERY );
2489 0 : if ( !xDocRecovery.is() )
2490 0 : return;
2491 :
2492 : // get all needed information of this document
2493 : // We need it to update our cache or to locate already existing elements there!
2494 0 : AutoRecovery::TDocumentInfo aNew;
2495 0 : aNew.Document = xDocument;
2496 :
2497 : // TODO replace getLocation() with getURL() ... it's a workaround currently only!
2498 0 : css::uno::Reference< css::frame::XStorable > xDoc(aNew.Document, css::uno::UNO_QUERY_THROW);
2499 0 : aNew.OrgURL = xDoc->getLocation();
2500 :
2501 0 : css::uno::Reference< css::frame::XTitle > xTitle(aNew.Document, css::uno::UNO_QUERY_THROW);
2502 0 : aNew.Title = xTitle->getTitle ();
2503 :
2504 : // classify the used application module, which is used by this document.
2505 0 : implts_specifyAppModuleAndFactory(aNew);
2506 :
2507 : // Hack! Check for "illegal office documents" ... as e.g. the Basic IDE
2508 : // Its not really a full featured office document. It doesn't provide an URL, any filter, a factory URL etcpp.
2509 : // TODO file bug to Basci IDE developers. They must remove the office document API from its service.
2510 0 : if (
2511 0 : (aNew.OrgURL.isEmpty()) &&
2512 0 : (aNew.FactoryURL.isEmpty())
2513 : )
2514 : {
2515 : OSL_FAIL( "AutoRecovery::implts_registerDocument: this should not happen anymore!" );
2516 : // nowadays, the Basic IDE should already die on the "supports XDocumentRecovery" check. And no other known
2517 : // document type fits in here ...
2518 0 : return;
2519 : }
2520 :
2521 : // By the way - get some information about the default format for saving!
2522 : // and save an information about the real used filter by this document.
2523 : // We save this document with DefaultFilter ... and load it with the RealFilter.
2524 0 : implts_specifyDefaultFilterAndExtension(aNew);
2525 0 : aNew.RealFilter = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME() , OUString());
2526 :
2527 : // Further we must know, if this document base on a template.
2528 : // Then we must load it in a different way.
2529 0 : css::uno::Reference< css::document::XDocumentPropertiesSupplier > xSupplier(aNew.Document, css::uno::UNO_QUERY);
2530 0 : if (xSupplier.is()) // optional interface!
2531 : {
2532 0 : css::uno::Reference< css::document::XDocumentProperties > xDocProps(xSupplier->getDocumentProperties(), css::uno::UNO_QUERY_THROW);
2533 0 : aNew.TemplateURL = xDocProps->getTemplateURL();
2534 : }
2535 :
2536 0 : css::uno::Reference< css::util::XModifiable > xModifyCheck(xDocument, css::uno::UNO_QUERY_THROW);
2537 0 : if (xModifyCheck->isModified())
2538 : {
2539 0 : aNew.DocumentState |= AutoRecovery::E_MODIFIED;
2540 : }
2541 :
2542 0 : aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE);
2543 :
2544 0 : AutoRecovery::TDocumentInfo aInfo;
2545 : /* SAFE */ {
2546 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2547 :
2548 : // create a new cache entry ... this document is not known.
2549 0 : ++m_nIdPool;
2550 0 : aNew.ID = m_nIdPool;
2551 : SAL_WARN_IF(m_nIdPool<0, "fwk", "AutoRecovery::implts_registerDocument(): Overflow of ID pool detected.");
2552 0 : m_lDocCache.push_back(aNew);
2553 :
2554 0 : AutoRecovery::TDocumentList::iterator pIt1 = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
2555 0 : aInfo = *pIt1;
2556 :
2557 : } /* SAFE */
2558 :
2559 0 : implts_flushConfigItem(aInfo);
2560 0 : implts_startModifyListeningOnDoc(aInfo);
2561 :
2562 0 : aCacheLock.unlock();
2563 : }
2564 :
2565 0 : void AutoRecovery::implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument ,
2566 : bool bStopListening)
2567 : {
2568 0 : AutoRecovery::TDocumentInfo aInfo;
2569 : /* SAFE */ {
2570 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2571 :
2572 : // Attention: Dont leave SAFE section, if you work with pIt!
2573 : // Because it points directly into the m_lDocCache list ...
2574 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2575 :
2576 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
2577 0 : if (pIt == m_lDocCache.end())
2578 0 : return; // unknown document => not a runtime error! Because we register only a few documents. see registration ...
2579 :
2580 0 : aInfo = *pIt;
2581 :
2582 0 : aCacheLock.unlock();
2583 :
2584 : // Sometimes we close documents by ourself.
2585 : // And these documents can't be deregistered.
2586 : // Otherwhise we loos our configuration data ... but need it !
2587 : // see SessionSave !
2588 0 : if (aInfo.IgnoreClosing)
2589 0 : return;
2590 :
2591 0 : CacheLockGuard aCacheLock2(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
2592 0 : pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
2593 0 : if (pIt != m_lDocCache.end())
2594 0 : m_lDocCache.erase(pIt);
2595 0 : pIt = m_lDocCache.end(); // otherwise its not specified what pIt means!
2596 0 : aCacheLock2.unlock();
2597 :
2598 : } /* SAFE */
2599 :
2600 : /* This method is called within disposing() of the document too. But there it's not a good idea to
2601 : deregister us as listener. Furter it make no sense - because the broadcaster dies.
2602 : So we suppress deregistration in such case ...
2603 : */
2604 0 : if (bStopListening)
2605 0 : implts_stopModifyListeningOnDoc(aInfo);
2606 :
2607 0 : AutoRecovery::st_impl_removeFile(aInfo.OldTempURL);
2608 0 : AutoRecovery::st_impl_removeFile(aInfo.NewTempURL);
2609 0 : implts_flushConfigItem(aInfo, true); // sal_True => remove it from config
2610 : }
2611 :
2612 0 : void AutoRecovery::implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument)
2613 : {
2614 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2615 :
2616 : /* SAFE */ {
2617 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2618 :
2619 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
2620 0 : if (pIt != m_lDocCache.end())
2621 : {
2622 : /* Now we know, that this document was modified again and must be saved next time.
2623 : But we dont need this information for every e.g. key input of the user.
2624 : So we stop listening here.
2625 : But if the document was saved as temp. file we start listening for this event again.
2626 : */
2627 0 : implts_stopModifyListeningOnDoc(*pIt);
2628 0 : }
2629 :
2630 0 : } /* SAFE */
2631 0 : }
2632 :
2633 0 : void AutoRecovery::implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument)
2634 : {
2635 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2636 :
2637 : /* SAFE */ {
2638 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2639 :
2640 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
2641 0 : if (pIt != m_lDocCache.end())
2642 : {
2643 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
2644 :
2645 : // use sal_True as fallback ... so we recognize every document on EmergencySave/AutoRecovery!
2646 0 : bool bModified = true;
2647 0 : css::uno::Reference< css::util::XModifiable > xModify(xDocument, css::uno::UNO_QUERY);
2648 0 : if (xModify.is())
2649 0 : bModified = xModify->isModified();
2650 0 : if (bModified)
2651 : {
2652 0 : rInfo.DocumentState |= AutoRecovery::E_MODIFIED;
2653 : }
2654 : else
2655 : {
2656 0 : rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED;
2657 0 : }
2658 0 : }
2659 :
2660 0 : } /* SAFE */
2661 0 : }
2662 :
2663 0 : void AutoRecovery::implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument ,
2664 : bool bSaveInProgress)
2665 : {
2666 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2667 :
2668 : /* SAFE */ {
2669 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2670 :
2671 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
2672 0 : if (pIt == m_lDocCache.end())
2673 0 : return;
2674 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
2675 0 : rInfo.UsedForSaving = bSaveInProgress;
2676 :
2677 0 : } /* SAFE */
2678 : }
2679 :
2680 0 : void AutoRecovery::implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument)
2681 : {
2682 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2683 :
2684 0 : AutoRecovery::TDocumentInfo aInfo;
2685 0 : OUString sRemoveURL1;
2686 0 : OUString sRemoveURL2;
2687 : /* SAFE */ {
2688 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2689 :
2690 0 : AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument);
2691 0 : if (pIt == m_lDocCache.end())
2692 0 : return;
2693 0 : aInfo = *pIt;
2694 :
2695 0 : aInfo.DocumentState = AutoRecovery::E_UNKNOWN;
2696 : // TODO replace getLocation() with getURL() ... it's a workaround currently only!
2697 0 : css::uno::Reference< css::frame::XStorable > xDoc(aInfo.Document, css::uno::UNO_QUERY);
2698 0 : aInfo.OrgURL = xDoc->getLocation();
2699 :
2700 0 : sRemoveURL1 = aInfo.OldTempURL;
2701 0 : sRemoveURL2 = aInfo.NewTempURL;
2702 0 : aInfo.OldTempURL.clear();
2703 0 : aInfo.NewTempURL.clear();
2704 :
2705 0 : utl::MediaDescriptor lDescriptor(aInfo.Document->getArgs());
2706 0 : aInfo.RealFilter = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_FILTERNAME(), OUString());
2707 :
2708 0 : css::uno::Reference< css::frame::XTitle > xDocTitle(xDocument, css::uno::UNO_QUERY);
2709 0 : if (xDocTitle.is ())
2710 0 : aInfo.Title = xDocTitle->getTitle ();
2711 : else
2712 : {
2713 0 : aInfo.Title = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_TITLE() , OUString());
2714 0 : if (aInfo.Title.isEmpty())
2715 0 : aInfo.Title = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_DOCUMENTTITLE(), OUString());
2716 : }
2717 :
2718 0 : aInfo.UsedForSaving = false;
2719 :
2720 : } /* SAFE */
2721 :
2722 0 : implts_flushConfigItem(aInfo);
2723 :
2724 0 : aCacheLock.unlock();
2725 :
2726 0 : AutoRecovery::st_impl_removeFile(sRemoveURL1);
2727 0 : AutoRecovery::st_impl_removeFile(sRemoveURL2);
2728 : }
2729 :
2730 0 : AutoRecovery::TDocumentList::iterator AutoRecovery::impl_searchDocument( AutoRecovery::TDocumentList& rList ,
2731 : const css::uno::Reference< css::frame::XModel >& xDocument)
2732 : {
2733 0 : AutoRecovery::TDocumentList::iterator pIt;
2734 0 : for ( pIt = rList.begin();
2735 0 : pIt != rList.end();
2736 : ++pIt )
2737 : {
2738 0 : const AutoRecovery::TDocumentInfo& rInfo = *pIt;
2739 0 : if (rInfo.Document == xDocument)
2740 0 : break;
2741 : }
2742 0 : return pIt;
2743 : }
2744 :
2745 : namespace
2746 : {
2747 0 : void lcl_changeVisibility( const css::uno::Reference< css::frame::XFramesSupplier >& i_rFrames, bool i_bVisible )
2748 : {
2749 0 : css::uno::Reference< css::container::XIndexAccess > xFramesContainer( i_rFrames->getFrames(), css::uno::UNO_QUERY );
2750 0 : const sal_Int32 count = xFramesContainer->getCount();
2751 :
2752 0 : Any aElement;
2753 0 : for ( sal_Int32 i=0; i < count; ++i )
2754 : {
2755 0 : aElement = xFramesContainer->getByIndex(i);
2756 : // check for sub frames
2757 0 : css::uno::Reference< css::frame::XFramesSupplier > xFramesSupp( aElement, css::uno::UNO_QUERY );
2758 0 : if ( xFramesSupp.is() )
2759 0 : lcl_changeVisibility( xFramesSupp, i_bVisible );
2760 :
2761 0 : css::uno::Reference< css::frame::XFrame > xFrame( aElement, css::uno::UNO_QUERY );
2762 0 : if ( !xFrame.is() )
2763 0 : continue;
2764 :
2765 0 : css::uno::Reference< css::awt::XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW );
2766 0 : xWindow->setVisible( i_bVisible );
2767 0 : }
2768 0 : }
2769 : }
2770 :
2771 0 : void AutoRecovery::implts_changeAllDocVisibility(bool bVisible)
2772 : {
2773 0 : css::uno::Reference< css::frame::XFramesSupplier > xDesktop( css::frame::Desktop::create(m_xContext), css::uno::UNO_QUERY);
2774 0 : lcl_changeVisibility( xDesktop, bVisible );
2775 0 : }
2776 :
2777 : /* Currently the document is not closed in case of crash,
2778 : so the lock file must be removed explicitly
2779 : */
2780 0 : void lc_removeLockFile(AutoRecovery::TDocumentInfo& rInfo)
2781 : {
2782 : #if !HAVE_FEATURE_MULTIUSER_ENVIRONMENT || HAVE_FEATURE_MACOSX_SANDBOX
2783 : (void) rInfo;
2784 : #else
2785 0 : if ( rInfo.Document.is() )
2786 : {
2787 : try
2788 : {
2789 0 : css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW);
2790 0 : OUString aURL = xStore->getLocation();
2791 0 : if ( !aURL.isEmpty() )
2792 : {
2793 0 : ::svt::DocumentLockFile aLockFile( aURL );
2794 0 : aLockFile.RemoveFile();
2795 0 : }
2796 : }
2797 0 : catch( const css::uno::Exception& )
2798 : {
2799 : }
2800 : }
2801 : #endif
2802 0 : }
2803 :
2804 0 : void AutoRecovery::implts_prepareSessionShutdown()
2805 : {
2806 : SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_prepareSessionShutdown() starts ...");
2807 :
2808 : // a) reset modified documents (of course the must be saved before this method is called!)
2809 : // b) close it without showing any UI!
2810 :
2811 : /* SAFE */ {
2812 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2813 :
2814 0 : AutoRecovery::TDocumentList::iterator pIt;
2815 0 : for ( pIt = m_lDocCache.begin();
2816 0 : pIt != m_lDocCache.end();
2817 : ++pIt )
2818 : {
2819 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
2820 :
2821 : // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
2822 : // it is not done on documents saving since shutdown can be cancelled
2823 0 : lc_removeLockFile( rInfo );
2824 :
2825 : // Prevent us from deregistration of these documents.
2826 : // Because we close these documents by ourself (see XClosable below) ...
2827 : // it's fact, that we reach our deregistration method. There we
2828 : // must not(!) update our configuration ... Otherwhise all
2829 : // session data are lost !!!
2830 0 : rInfo.IgnoreClosing = true;
2831 :
2832 : // reset modified flag of these documents (ignoring the notification about it!)
2833 : // Otherwise a message box is shown on closing these models.
2834 0 : implts_stopModifyListeningOnDoc(rInfo);
2835 :
2836 : // if the session save is still running the documents should not be thrown away,
2837 : // actually that would be a bad sign, that means that the SessionManager tryes
2838 : // to kill the session before the saving is ready
2839 0 : if ((m_eJob & AutoRecovery::E_SESSION_SAVE) != AutoRecovery::E_SESSION_SAVE)
2840 : {
2841 0 : css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
2842 0 : if (xModify.is())
2843 0 : xModify->setModified(sal_False);
2844 :
2845 : // close the model.
2846 0 : css::uno::Reference< css::util::XCloseable > xClose(rInfo.Document, css::uno::UNO_QUERY);
2847 0 : if (xClose.is())
2848 : {
2849 : try
2850 : {
2851 0 : xClose->close(sal_False);
2852 : }
2853 0 : catch(const css::uno::Exception&)
2854 : {
2855 : // At least it's only a try to close these documents before anybody else it does.
2856 : // So it seems to be possible to ignore any error here .-)
2857 : }
2858 :
2859 0 : rInfo.Document.clear();
2860 0 : }
2861 : }
2862 : }
2863 :
2864 0 : aCacheLock.unlock();
2865 : } /* SAFE */
2866 0 : }
2867 :
2868 : /* TODO WORKAROUND:
2869 :
2870 : #i64599#
2871 :
2872 : Normally the MediaDescriptor argument NoAutoSave indicates,
2873 : that a document must be ignored for AutoSave and Recovery.
2874 : But sometimes XModel->getArgs() does not contained this information
2875 : if implts_registerDocument() was called.
2876 : So we have to check a second time, if this property is set ....
2877 : Best place doing so is to check it immeditaly before saving
2878 : and suppressingd saving the document then.
2879 : Of course removing the corresponding cache entry is not an option.
2880 : Because it would disturb iteration over the cache !
2881 : So we ignore such documents only ...
2882 : Hopefully next time they are not inserted in our cache.
2883 : */
2884 0 : bool lc_checkIfSaveForbiddenByArguments(AutoRecovery::TDocumentInfo& rInfo)
2885 : {
2886 0 : if (! rInfo.Document.is())
2887 0 : return true;
2888 :
2889 0 : utl::MediaDescriptor lDescriptor(rInfo.Document->getArgs());
2890 0 : bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_NOAUTOSAVE(), false);
2891 :
2892 0 : return bNoAutoSave;
2893 : }
2894 :
2895 0 : AutoRecovery::ETimerType AutoRecovery::implts_saveDocs( bool bAllowUserIdleLoop,
2896 : bool bRemoveLockFiles,
2897 : const DispatchParams* pParams )
2898 : {
2899 0 : css::uno::Reference< css::task::XStatusIndicator > xExternalProgress;
2900 0 : if (pParams)
2901 0 : xExternalProgress = pParams->m_xProgress;
2902 :
2903 0 : css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
2904 0 : OUString sBackupPath(SvtPathOptions().GetBackupPath());
2905 :
2906 0 : css::uno::Reference< css::frame::XController > xActiveController;
2907 0 : css::uno::Reference< css::frame::XModel > xActiveModel;
2908 0 : css::uno::Reference< css::frame::XFrame > xActiveFrame = xDesktop->getActiveFrame();
2909 0 : if (xActiveFrame.is())
2910 0 : xActiveController = xActiveFrame->getController();
2911 0 : if (xActiveController.is())
2912 0 : xActiveModel = xActiveController->getModel();
2913 :
2914 : // Set the default timer action for our calli.
2915 : // Default = NORMAL_AUTOSAVE
2916 : // We return a suggestion for an active timer only.
2917 : // It will be ignored if the timer was disabled by the user ...
2918 : // Further this state can be set to USER_IDLE only later in this method.
2919 : // Its not allowed to reset such state then. Because we must know, if
2920 : // there exists POSTPONED documents. see below ...
2921 0 : AutoRecovery::ETimerType eTimer = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL;
2922 :
2923 0 : sal_Int32 eJob = m_eJob;
2924 :
2925 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
2926 :
2927 : /* SAFE */ {
2928 0 : osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
2929 :
2930 : // This list will be filled with every document
2931 : // which should be saved as last one. E.g. if it was used
2932 : // already for an UI save operation => crashed ... and
2933 : // now we try to save it again ... which can fail again ( of course .-) ).
2934 0 : ::std::vector< AutoRecovery::TDocumentList::iterator > lDangerousDocs;
2935 :
2936 0 : AutoRecovery::TDocumentList::iterator pIt;
2937 0 : for ( pIt = m_lDocCache.begin();
2938 0 : pIt != m_lDocCache.end();
2939 : ++pIt )
2940 : {
2941 0 : AutoRecovery::TDocumentInfo aInfo = *pIt;
2942 :
2943 : // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly
2944 0 : if ( bRemoveLockFiles )
2945 0 : lc_removeLockFile( aInfo );
2946 :
2947 : // WORKAROUND ... see comment of this method
2948 0 : if (lc_checkIfSaveForbiddenByArguments(aInfo))
2949 0 : continue;
2950 :
2951 : // already auto saved during this session :-)
2952 : // This state must be reset for all documents
2953 : // if timer is started with normnal AutoSaveTimerIntervall!
2954 0 : if ((aInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED)
2955 0 : continue;
2956 :
2957 : // Not modified documents are not saved.
2958 : // We safe an information about the URL only!
2959 0 : Reference< XDocumentRecovery > xDocRecover( aInfo.Document, UNO_QUERY_THROW );
2960 0 : if ( !xDocRecover->wasModifiedSinceLastSave() )
2961 : {
2962 0 : aInfo.DocumentState |= AutoRecovery::E_HANDLED;
2963 0 : continue;
2964 : }
2965 :
2966 : // check if this document is still used by a concurrent save operation
2967 : // e.g. if the user tried to save via UI.
2968 : // Handle it in the following way:
2969 : // i) For an AutoSave ... ignore this document! It will be saved and next time we will (hopefully)
2970 : // get a notification about the state of this operation.
2971 : // And if a document was saved by the user we can remove our temp. file. But that will be done inside
2972 : // our callback for SaveDone notification.
2973 : // ii) For a CrashSave ... add it to the list of dangerous documents and
2974 : // save it after all other documents was saved successfully. That decrease
2975 : // the chance for a crash inside a crash.
2976 : // On the other side it's not necessary for documents, which are not modified.
2977 : // They can be handled normally - means we patch the corresponding configuration entry only.
2978 : // iii) For a SessionSave ... ignore it! There is no time to wait for this save operation.
2979 : // Because the WindowManager will kill the process if it doesn't react immediately.
2980 : // On the other side we can't risk a concurrent save request ... because we know
2981 : // that it will produce a crash.
2982 :
2983 : // Attention: Because eJob is used as a flag field, you have to check for the worst case first.
2984 : // E.g. a CrashSave can overwrite an AutoSave. So you have to check for a CrashSave before an AutoSave!
2985 0 : if (aInfo.UsedForSaving)
2986 : {
2987 0 : if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE)
2988 : {
2989 0 : lDangerousDocs.push_back(pIt);
2990 0 : continue;
2991 : }
2992 : else
2993 0 : if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE)
2994 : {
2995 0 : continue;
2996 : }
2997 : else
2998 0 : if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
2999 : {
3000 0 : eTimer = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED;
3001 0 : aInfo.DocumentState |= AutoRecovery::E_POSTPONED;
3002 0 : continue;
3003 : }
3004 : }
3005 :
3006 : // a) Document was not postponed - and is active now. => postpone it (restart timer, restart loop)
3007 : // b) Document was not postponed - and is not active now. => save it
3008 : // c) Document was postponed - and is not active now. => save it
3009 : // d) Document was postponed - and is active now. => save it (because user idle was checked already)
3010 0 : bool bActive = (xActiveModel == aInfo.Document);
3011 0 : bool bWasPostponed = ((aInfo.DocumentState & AutoRecovery::E_POSTPONED) == AutoRecovery::E_POSTPONED);
3012 :
3013 0 : if (
3014 0 : ! bWasPostponed &&
3015 : bActive
3016 : )
3017 : {
3018 0 : aInfo.DocumentState |= AutoRecovery::E_POSTPONED;
3019 0 : *pIt = aInfo;
3020 : // postponed documents will be saved if this method is called again!
3021 : // That can be done by an outside started timer => E_POLL_FOR_USER_IDLE (if normal AutoSave is active)
3022 : // 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!)
3023 0 : eTimer = AutoRecovery::E_POLL_FOR_USER_IDLE;
3024 0 : if (!bAllowUserIdleLoop)
3025 0 : eTimer = AutoRecovery::E_CALL_ME_BACK;
3026 0 : continue;
3027 : }
3028 :
3029 : // b, c, d)
3030 : // } /* SAFE */
3031 0 : g.clear();
3032 : // changing of aInfo and flushing it is done inside implts_saveOneDoc!
3033 0 : implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
3034 0 : implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
3035 0 : g.reset();
3036 : // /* SAFE */ {
3037 :
3038 0 : *pIt = aInfo;
3039 0 : }
3040 :
3041 : // Did we have some "dangerous candidates" ?
3042 : // Try to save it ... but may be it will fail !
3043 0 : ::std::vector< AutoRecovery::TDocumentList::iterator >::iterator pIt2;
3044 0 : for ( pIt2 = lDangerousDocs.begin();
3045 0 : pIt2 != lDangerousDocs.end();
3046 : ++pIt2 )
3047 : {
3048 0 : pIt = *pIt2;
3049 0 : AutoRecovery::TDocumentInfo aInfo = *pIt;
3050 :
3051 : // } /* SAFE */
3052 0 : g.clear();
3053 : // changing of aInfo and flushing it is done inside implts_saveOneDoc!
3054 0 : implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress);
3055 0 : implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo));
3056 0 : g.reset();
3057 : // /* SAFE */ {
3058 :
3059 0 : *pIt = aInfo;
3060 0 : }
3061 :
3062 : } /* SAFE */
3063 :
3064 0 : return eTimer;
3065 : }
3066 :
3067 0 : void AutoRecovery::implts_saveOneDoc(const OUString& sBackupPath ,
3068 : AutoRecovery::TDocumentInfo& rInfo ,
3069 : const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress)
3070 : {
3071 : // no document? => can occur if we loaded our configuration with files,
3072 : // which couldnt be recovered successfully. In such case we have all needed information
3073 : // excepting the real document instance!
3074 :
3075 : // TODO: search right place, where such "dead files" can be removed from the configuration!
3076 0 : if (!rInfo.Document.is())
3077 0 : return;
3078 :
3079 0 : utl::MediaDescriptor lOldArgs(rInfo.Document->getArgs());
3080 0 : implts_generateNewTempURL(sBackupPath, lOldArgs, rInfo);
3081 :
3082 : // if the document was loaded with a password, it should be
3083 : // stored with password
3084 0 : utl::MediaDescriptor lNewArgs;
3085 : css::uno::Sequence< css::beans::NamedValue > aEncryptionData =
3086 0 : lOldArgs.getUnpackedValueOrDefault(utl::MediaDescriptor::PROP_ENCRYPTIONDATA(),
3087 0 : css::uno::Sequence< css::beans::NamedValue >());
3088 0 : if (aEncryptionData.getLength() > 0)
3089 0 : lNewArgs[utl::MediaDescriptor::PROP_ENCRYPTIONDATA()] <<= aEncryptionData;
3090 :
3091 : // Further it must be saved using the default file format of that application.
3092 : // Otherwhise we will some data lost.
3093 0 : if (!rInfo.DefaultFilter.isEmpty())
3094 0 : lNewArgs[utl::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.DefaultFilter;
3095 :
3096 : // prepare frame/document/mediadescriptor in a way, that it uses OUR progress .-)
3097 0 : if (xExternalProgress.is())
3098 0 : lNewArgs[utl::MediaDescriptor::PROP_STATUSINDICATOR()] <<= xExternalProgress;
3099 0 : impl_establishProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
3100 :
3101 : // #i66598# use special handling of property "DocumentBaseURL" (it must be an empty string!)
3102 : // for make hyperlinks working
3103 0 : lNewArgs[utl::MediaDescriptor::PROP_DOCUMENTBASEURL()] <<= OUString();
3104 :
3105 : // try to save this document as a new temp file every time.
3106 : // Mark AutoSave state as "INCOMPLETE" if it failed.
3107 : // Because the last temp file is to old and does not include all changes.
3108 0 : Reference< XDocumentRecovery > xDocRecover(rInfo.Document, css::uno::UNO_QUERY_THROW);
3109 :
3110 : // safe the state about "trying to save"
3111 : // ... we need it for recovery if e.g. a crash occurs inside next line!
3112 0 : rInfo.DocumentState |= AutoRecovery::E_TRY_SAVE;
3113 0 : implts_flushConfigItem(rInfo);
3114 :
3115 : // If userautosave is enabled, first try to save the original file.
3116 : // Note that we must do it *before* calling storeToRecoveryFile, so in case of failure here
3117 : // we won't remain with the modified flag set to true, even though the autorecovery save succeeded.
3118 : try
3119 : {
3120 : // We must check here for an empty URL to avoid a "This operation is not supported on this operating system."
3121 : // message during autosave.
3122 0 : if ((m_eJob & AutoRecovery::E_USER_AUTO_SAVE) == AutoRecovery::E_USER_AUTO_SAVE && !rInfo.OrgURL.isEmpty())
3123 : {
3124 0 : Reference< XStorable > xDocSave(rInfo.Document, css::uno::UNO_QUERY_THROW);
3125 0 : xDocSave->store();
3126 : }
3127 : }
3128 0 : catch(const css::uno::Exception&)
3129 : {
3130 : }
3131 :
3132 0 : sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER;
3133 0 : bool bError = false;
3134 0 : do
3135 : {
3136 : try
3137 : {
3138 0 : xDocRecover->storeToRecoveryFile( rInfo.NewTempURL, lNewArgs.getAsConstPropertyValueList() );
3139 :
3140 : #ifdef TRIGGER_FULL_DISC_CHECK
3141 : throw css::uno::Exception();
3142 : #else // TRIGGER_FULL_DISC_CHECK
3143 :
3144 0 : bError = false;
3145 0 : nRetry = 0;
3146 : #endif // TRIGGER_FULL_DISC_CHECK
3147 : }
3148 0 : catch(const css::uno::Exception&)
3149 : {
3150 0 : bError = true;
3151 :
3152 : // a) FULL DISC seems to be the problem behind => show error and retry it forever (e.g. retry=300)
3153 : // b) unknown problem (may be locking problem) => reset RETRY value to more useful value(!) (e.g. retry=3)
3154 : // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace !
3155 :
3156 : sal_Int32 nMinSpaceDocSave;
3157 : /* SAFE */ {
3158 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
3159 0 : nMinSpaceDocSave = m_nMinSpaceDocSave;
3160 : } /* SAFE */
3161 :
3162 0 : if (! impl_enoughDiscSpace(nMinSpaceDocSave))
3163 0 : AutoRecovery::impl_showFullDiscError();
3164 0 : else if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL)
3165 0 : nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL;
3166 0 : else if (nRetry <= GIVE_UP_RETRY)
3167 0 : throw; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!!
3168 :
3169 0 : --nRetry;
3170 : }
3171 : }
3172 : while(nRetry>0);
3173 :
3174 0 : if (! bError)
3175 : {
3176 : // safe the state about success
3177 : // ... you know the reason: to know it on recovery time if next line crash .-)
3178 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE;
3179 0 : rInfo.DocumentState |= AutoRecovery::E_HANDLED;
3180 0 : rInfo.DocumentState |= AutoRecovery::E_SUCCEDED;
3181 : }
3182 : else
3183 : {
3184 : // safe the state about error ...
3185 0 : rInfo.NewTempURL.clear();
3186 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE;
3187 0 : rInfo.DocumentState |= AutoRecovery::E_HANDLED;
3188 0 : rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
3189 : }
3190 :
3191 : // make sure the progress is not referred any longer
3192 0 : impl_forgetProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >());
3193 :
3194 : // try to remove the old temp file.
3195 : // Ignore any error here. We have a new temp file, which is up to date.
3196 : // The only thing is: we fill the disk with temp files, if we can't remove old ones :-)
3197 0 : OUString sRemoveFile = rInfo.OldTempURL;
3198 0 : rInfo.OldTempURL = rInfo.NewTempURL;
3199 0 : rInfo.NewTempURL.clear();
3200 :
3201 0 : implts_flushConfigItem(rInfo);
3202 :
3203 : // We must know if the user modifies the document again ...
3204 0 : implts_startModifyListeningOnDoc(rInfo);
3205 :
3206 0 : AutoRecovery::st_impl_removeFile(sRemoveFile);
3207 : }
3208 :
3209 0 : AutoRecovery::ETimerType AutoRecovery::implts_openDocs(const DispatchParams& aParams)
3210 : {
3211 0 : AutoRecovery::ETimerType eTimer = AutoRecovery::E_DONT_START_TIMER;
3212 :
3213 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
3214 :
3215 : /* SAFE */ {
3216 0 : osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
3217 :
3218 0 : sal_Int32 eJob = m_eJob;
3219 0 : AutoRecovery::TDocumentList::iterator pIt;
3220 0 : for ( pIt = m_lDocCache.begin();
3221 0 : pIt != m_lDocCache.end();
3222 : ++pIt )
3223 : {
3224 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
3225 :
3226 : // Such documents are already loaded by the last loop.
3227 : // Dont check E_SUCCEDED here! Its may be the final state of an AutoSave
3228 : // operation before!!!
3229 0 : if ((rInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED)
3230 0 : continue;
3231 :
3232 : // a1,b1,c1,d2,e2,f2)
3233 0 : if ((rInfo.DocumentState & AutoRecovery::E_DAMAGED) == AutoRecovery::E_DAMAGED)
3234 : {
3235 : // dont forget to inform listener! May be this document was
3236 : // damaged on last saving time ...
3237 : // Then our listener need this notification.
3238 : // If it was damaged during last "try to open" ...
3239 : // it will be notified more than once. SH.. HAPPENS ...
3240 : // } /* SAFE */
3241 0 : g.clear();
3242 : implts_informListener(eJob,
3243 0 : AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
3244 0 : g.reset();
3245 : // /* SAFE */ {
3246 0 : continue;
3247 : }
3248 :
3249 0 : utl::MediaDescriptor lDescriptor;
3250 :
3251 : // its an UI feature - so the "USER" itself must be set as referer
3252 0 : lDescriptor[utl::MediaDescriptor::PROP_REFERRER()] <<= OUString(REFERRER_USER);
3253 0 : lDescriptor[utl::MediaDescriptor::PROP_SALVAGEDFILE()] <<= OUString();
3254 :
3255 : // recovered documents are loaded hidden, and shown all at once, later
3256 0 : lDescriptor[utl::MediaDescriptor::PROP_HIDDEN()] <<= true;
3257 :
3258 0 : if (aParams.m_xProgress.is())
3259 0 : lDescriptor[utl::MediaDescriptor::PROP_STATUSINDICATOR()] <<= aParams.m_xProgress;
3260 :
3261 : bool bBackupWasTried = (
3262 0 : ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_BACKUP ) == AutoRecovery::E_TRY_LOAD_BACKUP) || // temp. state!
3263 0 : ((rInfo.DocumentState & AutoRecovery::E_INCOMPLETE ) == AutoRecovery::E_INCOMPLETE ) // transport TRY_LOAD_BACKUP from last loop to this new one!
3264 0 : );
3265 0 : bool bOriginalWasTried = ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL);
3266 :
3267 0 : if (bBackupWasTried)
3268 : {
3269 0 : if (!bOriginalWasTried)
3270 : {
3271 0 : rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
3272 : // try original URL ... ! dont continue with next item here ...
3273 : }
3274 : else
3275 : {
3276 0 : rInfo.DocumentState |= AutoRecovery::E_DAMAGED;
3277 0 : continue;
3278 : }
3279 : }
3280 :
3281 0 : OUString sLoadOriginalURL;
3282 0 : OUString sLoadBackupURL;
3283 :
3284 0 : if (!bBackupWasTried)
3285 0 : sLoadBackupURL = rInfo.OldTempURL;
3286 :
3287 0 : if (!rInfo.OrgURL.isEmpty())
3288 : {
3289 0 : sLoadOriginalURL = rInfo.OrgURL;
3290 : }
3291 0 : else if (!rInfo.TemplateURL.isEmpty())
3292 : {
3293 0 : sLoadOriginalURL = rInfo.TemplateURL;
3294 0 : lDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True;
3295 0 : lDescriptor[utl::MediaDescriptor::PROP_TEMPLATENAME()] <<= rInfo.TemplateURL;
3296 : }
3297 0 : else if (!rInfo.FactoryURL.isEmpty())
3298 : {
3299 0 : sLoadOriginalURL = rInfo.FactoryURL;
3300 0 : lDescriptor[utl::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True;
3301 : }
3302 :
3303 : // A "Salvaged" item must exists every time. The core can make something special then for recovery.
3304 : // Of course it should be the real file name of the original file, in case we load the temp. backup here.
3305 0 : OUString sURL;
3306 0 : if (!sLoadBackupURL.isEmpty())
3307 : {
3308 0 : sURL = sLoadBackupURL;
3309 0 : rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_BACKUP;
3310 0 : lDescriptor[utl::MediaDescriptor::PROP_SALVAGEDFILE()] <<= sLoadOriginalURL;
3311 : }
3312 0 : else if (!sLoadOriginalURL.isEmpty())
3313 : {
3314 0 : sURL = sLoadOriginalURL;
3315 0 : rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_ORIGINAL;
3316 : }
3317 : else
3318 0 : continue; // TODO ERROR!
3319 :
3320 0 : LoadEnv::initializeUIDefaults( m_xContext, lDescriptor, true, NULL );
3321 :
3322 : // } /* SAFE */
3323 0 : g.clear();
3324 :
3325 0 : implts_flushConfigItem(rInfo);
3326 : implts_informListener(eJob,
3327 0 : AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
3328 :
3329 : try
3330 : {
3331 0 : implts_openOneDoc(sURL, lDescriptor, rInfo);
3332 : }
3333 0 : catch(const css::uno::Exception&)
3334 : {
3335 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP;
3336 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL;
3337 0 : if (!sLoadBackupURL.isEmpty())
3338 : {
3339 0 : rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE;
3340 0 : eTimer = AutoRecovery::E_CALL_ME_BACK;
3341 : }
3342 : else
3343 : {
3344 0 : rInfo.DocumentState |= AutoRecovery::E_HANDLED;
3345 0 : rInfo.DocumentState |= AutoRecovery::E_DAMAGED;
3346 : }
3347 :
3348 0 : implts_flushConfigItem(rInfo, true);
3349 : implts_informListener(eJob,
3350 0 : AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
3351 :
3352 : // /* SAFE */ {
3353 : // Needed for next loop!
3354 0 : g.reset();
3355 0 : continue;
3356 : }
3357 :
3358 0 : if (!rInfo.RealFilter.isEmpty())
3359 : {
3360 0 : utl::MediaDescriptor lPatchDescriptor(rInfo.Document->getArgs());
3361 0 : lPatchDescriptor[utl::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.RealFilter;
3362 0 : rInfo.Document->attachResource(rInfo.Document->getURL(), lPatchDescriptor.getAsConstPropertyValueList());
3363 : // do *not* use sURL here. In case this points to the recovery file, it has already been passed
3364 : // to recoverFromFile. Also, passing it here is logically wrong, as attachResource is intended
3365 : // to take the logical file URL.
3366 : }
3367 :
3368 0 : css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY);
3369 0 : if ( xModify.is() )
3370 : {
3371 0 : bool bModified = ((rInfo.DocumentState & AutoRecovery::E_MODIFIED) == AutoRecovery::E_MODIFIED);
3372 0 : xModify->setModified(bModified);
3373 : }
3374 :
3375 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP;
3376 0 : rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL;
3377 0 : rInfo.DocumentState |= AutoRecovery::E_HANDLED;
3378 0 : rInfo.DocumentState |= AutoRecovery::E_SUCCEDED;
3379 :
3380 0 : implts_flushConfigItem(rInfo);
3381 : implts_informListener(eJob,
3382 0 : AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo));
3383 :
3384 : /* Normally we listen as XModifyListener on a document to know if a document was changed
3385 : since our last AutoSave. And we deregister us in case we know this state.
3386 : But directly after one document as recovered ... we must start listening.
3387 : Otherwhise the first "modify" doesn't reach us. Because we ourself called setModified()
3388 : on the document via API. And currently we dont listen for any events (not at theGlobalEventBroadcaster
3389 : nor at any document!).
3390 : */
3391 0 : implts_startModifyListeningOnDoc(rInfo);
3392 :
3393 : // /* SAFE */ {
3394 : // Needed for next loop. Dont unlock it again!
3395 0 : g.reset();
3396 0 : }
3397 :
3398 : } /* SAFE */
3399 :
3400 0 : return eTimer;
3401 : }
3402 :
3403 0 : void AutoRecovery::implts_openOneDoc(const OUString& sURL ,
3404 : utl::MediaDescriptor& lDescriptor,
3405 : AutoRecovery::TDocumentInfo& rInfo )
3406 : {
3407 0 : css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
3408 :
3409 0 : ::std::vector< Reference< XComponent > > aCleanup;
3410 : try
3411 : {
3412 : // create a new document of the desired type
3413 0 : Reference< XModel2 > xModel(m_xContext->getServiceManager()->createInstanceWithContext(
3414 0 : rInfo.FactoryService, m_xContext), UNO_QUERY_THROW);
3415 0 : aCleanup.push_back( xModel.get() );
3416 :
3417 : // put the filter name into the descriptor - we're not going to involve any type detection, so
3418 : // the document might be lost without the FilterName property
3419 0 : if ( (rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL)
3420 0 : lDescriptor[ utl::MediaDescriptor::PROP_FILTERNAME() ] <<= rInfo.RealFilter;
3421 : else
3422 0 : lDescriptor[ utl::MediaDescriptor::PROP_FILTERNAME() ] <<= rInfo.DefaultFilter;
3423 :
3424 0 : if ( sURL == rInfo.FactoryURL )
3425 : {
3426 : // if the document was a new, unmodified document, then there's nothing to recover, just to init
3427 0 : ENSURE_OR_THROW( ( rInfo.DocumentState & AutoRecovery::E_MODIFIED ) == 0,
3428 : "unexpected document state" );
3429 0 : Reference< XLoadable > xModelLoad( xModel, UNO_QUERY_THROW );
3430 0 : xModelLoad->initNew();
3431 :
3432 : // TODO: remove load-process specific arguments from the descriptor, e.g. the status indicator
3433 0 : xModel->attachResource( sURL, lDescriptor.getAsConstPropertyValueList() );
3434 : }
3435 : else
3436 : {
3437 : // let it recover itself
3438 0 : Reference< XDocumentRecovery > xDocRecover( xModel, UNO_QUERY_THROW );
3439 0 : xDocRecover->recoverFromFile(
3440 : sURL,
3441 0 : lDescriptor.getUnpackedValueOrDefault( utl::MediaDescriptor::PROP_SALVAGEDFILE(), OUString() ),
3442 : lDescriptor.getAsConstPropertyValueList()
3443 0 : );
3444 :
3445 : // No attachResource needed here. By definition (of XDocumentRecovery), the implementation is responsible
3446 : // for completely initializing the model, which includes attachResource (or equivalent), if required.
3447 : }
3448 :
3449 : // re-create all the views
3450 0 : ::std::vector< OUString > aViewsToRestore( rInfo.ViewNames.getLength() );
3451 0 : if ( rInfo.ViewNames.getLength() )
3452 0 : ::std::copy( rInfo.ViewNames.begin(), rInfo.ViewNames.end(), aViewsToRestore.begin() );
3453 : // if we don't have views for whatever reason, then create a default-view, at least
3454 0 : if ( aViewsToRestore.empty() )
3455 0 : aViewsToRestore.push_back( OUString() );
3456 :
3457 0 : for ( ::std::vector< OUString >::const_iterator viewName = aViewsToRestore.begin();
3458 0 : viewName != aViewsToRestore.end();
3459 : ++viewName
3460 : )
3461 : {
3462 : // create a frame
3463 0 : Reference< XFrame > xTargetFrame = xDesktop->findFrame( SPECIALTARGET_BLANK, 0 );
3464 0 : aCleanup.push_back( xTargetFrame.get() );
3465 :
3466 : // create a view to the document
3467 0 : Reference< XController2 > xController;
3468 0 : if ( viewName->getLength() )
3469 : {
3470 0 : xController.set( xModel->createViewController( *viewName, Sequence< css::beans::PropertyValue >(), xTargetFrame ), UNO_SET_THROW );
3471 : }
3472 : else
3473 : {
3474 0 : xController.set( xModel->createDefaultViewController( xTargetFrame ), UNO_SET_THROW );
3475 : }
3476 :
3477 : // introduce model/view/controller to each other
3478 0 : xController->attachModel( xModel.get() );
3479 0 : xModel->connectController( xController.get() );
3480 0 : xTargetFrame->setComponent( xController->getComponentWindow(), xController.get() );
3481 0 : xController->attachFrame( xTargetFrame );
3482 0 : xModel->setCurrentController( xController.get() );
3483 0 : }
3484 :
3485 0 : rInfo.Document = xModel.get();
3486 : }
3487 0 : catch(const css::uno::RuntimeException&)
3488 : {
3489 0 : throw;
3490 : }
3491 0 : catch(const css::uno::Exception&)
3492 : {
3493 0 : Any aCaughtException( ::cppu::getCaughtException() );
3494 :
3495 : // clean up
3496 0 : for ( ::std::vector< Reference< XComponent > >::const_iterator component = aCleanup.begin();
3497 0 : component != aCleanup.end();
3498 : ++component
3499 : )
3500 : {
3501 0 : css::uno::Reference< css::util::XCloseable > xClose( *component, css::uno::UNO_QUERY );
3502 0 : if ( xClose.is() )
3503 0 : xClose->close( sal_True );
3504 : else
3505 0 : (*component)->dispose();
3506 0 : }
3507 :
3508 : // re-throw
3509 0 : OUStringBuffer sMsg(256);
3510 0 : sMsg.appendAscii("Recovery of \"");
3511 0 : sMsg.append (sURL );
3512 0 : sMsg.appendAscii("\" failed." );
3513 :
3514 : throw css::lang::WrappedTargetException(
3515 : sMsg.makeStringAndClear(),
3516 : static_cast< css::frame::XDispatch* >(this),
3517 : aCaughtException
3518 0 : );
3519 0 : }
3520 0 : }
3521 :
3522 0 : void AutoRecovery::implts_generateNewTempURL(const OUString& sBackupPath ,
3523 : utl::MediaDescriptor& /*rMediaDescriptor*/,
3524 : AutoRecovery::TDocumentInfo& rInfo )
3525 : {
3526 : // specify URL for saving (which points to a temp file inside backup directory)
3527 : // and define an unique name, so we can locate it later.
3528 : // This unique name must solve an optimization problem too!
3529 : // In case we are asked to save unmodified documents too - and one of them
3530 : // is an empty one (because it was new created using e.g. an URL private:factory/...)
3531 : // we should not save it really. Then we put the information about such "empty document"
3532 : // into the configuration and dont create any recovery file on disk.
3533 : // We use the title of the document to make it unique.
3534 0 : OUStringBuffer sUniqueName;
3535 0 : if (!rInfo.OrgURL.isEmpty())
3536 : {
3537 0 : css::uno::Reference< css::util::XURLTransformer > xParser(css::util::URLTransformer::create(m_xContext));
3538 0 : css::util::URL aURL;
3539 0 : aURL.Complete = rInfo.OrgURL;
3540 0 : xParser->parseStrict(aURL);
3541 0 : sUniqueName.append(aURL.Name);
3542 : }
3543 0 : else if (!rInfo.FactoryURL.isEmpty())
3544 0 : sUniqueName.appendAscii("untitled");
3545 0 : sUniqueName.appendAscii("_");
3546 :
3547 : // TODO: Must we strip some illegal signes - if we use the title?
3548 :
3549 0 : OUString sName(sUniqueName.makeStringAndClear());
3550 0 : OUString sExtension(rInfo.Extension);
3551 0 : OUString sPath(sBackupPath);
3552 0 : ::utl::TempFile aTempFile(sName, true, &sExtension, &sPath);
3553 :
3554 0 : rInfo.NewTempURL = aTempFile.GetURL();
3555 0 : }
3556 :
3557 0 : void AutoRecovery::implts_informListener( sal_Int32 eJob ,
3558 : const css::frame::FeatureStateEvent& aEvent)
3559 : {
3560 : // Helper shares mutex with us -> threadsafe!
3561 0 : ::cppu::OInterfaceContainerHelper* pListenerForURL = 0;
3562 0 : OUString sJob = AutoRecovery::implst_getJobDescription(eJob);
3563 :
3564 : // inform listener, which are registered for any URLs(!)
3565 0 : pListenerForURL = m_lListener.getContainer(sJob);
3566 0 : if(pListenerForURL != 0)
3567 : {
3568 0 : ::cppu::OInterfaceIteratorHelper pIt(*pListenerForURL);
3569 0 : while(pIt.hasMoreElements())
3570 : {
3571 : try
3572 : {
3573 0 : css::uno::Reference< css::frame::XStatusListener > xListener(static_cast<css::frame::XStatusListener*>(pIt.next()), css::uno::UNO_QUERY);
3574 0 : xListener->statusChanged(aEvent);
3575 : }
3576 0 : catch(const css::uno::RuntimeException&)
3577 : {
3578 0 : pIt.remove();
3579 : }
3580 0 : }
3581 0 : }
3582 0 : }
3583 :
3584 0 : OUString AutoRecovery::implst_getJobDescription(sal_Int32 eJob)
3585 : {
3586 : // describe the current running operation
3587 0 : OUStringBuffer sFeature(256);
3588 0 : sFeature.append(CMD_PROTOCOL);
3589 :
3590 : // Attention: Because "eJob" is used as a flag field the order of checking these
3591 : // flags is important. We must preferr job with higher priorities!
3592 : // E.g. EmergencySave has an higher prio then AutoSave ...
3593 : // On the other side there exist a well defined order between two different jobs.
3594 : // e.g. PrepareEmergencySave must be done before EmergencySave is started of course.
3595 :
3596 0 : if ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE)
3597 0 : sFeature.append(CMD_DO_PREPARE_EMERGENCY_SAVE);
3598 0 : else if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE)
3599 0 : sFeature.append(CMD_DO_EMERGENCY_SAVE);
3600 0 : else if ((eJob & AutoRecovery::E_RECOVERY) == AutoRecovery::E_RECOVERY)
3601 0 : sFeature.append(CMD_DO_RECOVERY);
3602 0 : else if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE)
3603 0 : sFeature.append(CMD_DO_SESSION_SAVE);
3604 0 : else if ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT) == AutoRecovery::E_SESSION_QUIET_QUIT)
3605 0 : sFeature.append(CMD_DO_SESSION_QUIET_QUIT);
3606 0 : else if ((eJob & AutoRecovery::E_SESSION_RESTORE) == AutoRecovery::E_SESSION_RESTORE)
3607 0 : sFeature.append(CMD_DO_SESSION_RESTORE);
3608 0 : else if ((eJob & AutoRecovery::E_ENTRY_BACKUP) == AutoRecovery::E_ENTRY_BACKUP)
3609 0 : sFeature.append(CMD_DO_ENTRY_BACKUP);
3610 0 : else if ((eJob & AutoRecovery::E_ENTRY_CLEANUP) == AutoRecovery::E_ENTRY_CLEANUP)
3611 0 : sFeature.append(CMD_DO_ENTRY_CLEANUP);
3612 0 : else if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE)
3613 0 : sFeature.append(CMD_DO_AUTO_SAVE);
3614 0 : else if ( eJob != AutoRecovery::E_NO_JOB )
3615 : SAL_INFO("fwk", "AutoRecovery::implst_getJobDescription(): Invalid job identifier detected.");
3616 :
3617 0 : return sFeature.makeStringAndClear();
3618 : }
3619 :
3620 64 : sal_Int32 AutoRecovery::implst_classifyJob(const css::util::URL& aURL)
3621 : {
3622 64 : if ( aURL.Protocol == CMD_PROTOCOL )
3623 : {
3624 64 : if ( aURL.Path == CMD_DO_PREPARE_EMERGENCY_SAVE )
3625 0 : return AutoRecovery::E_PREPARE_EMERGENCY_SAVE;
3626 64 : else if ( aURL.Path == CMD_DO_EMERGENCY_SAVE )
3627 0 : return AutoRecovery::E_EMERGENCY_SAVE;
3628 64 : else if ( aURL.Path == CMD_DO_RECOVERY )
3629 0 : return AutoRecovery::E_RECOVERY;
3630 64 : else if ( aURL.Path == CMD_DO_ENTRY_BACKUP )
3631 0 : return AutoRecovery::E_ENTRY_BACKUP;
3632 64 : else if ( aURL.Path == CMD_DO_ENTRY_CLEANUP )
3633 0 : return AutoRecovery::E_ENTRY_CLEANUP;
3634 64 : else if ( aURL.Path == CMD_DO_SESSION_SAVE )
3635 0 : return AutoRecovery::E_SESSION_SAVE;
3636 64 : else if ( aURL.Path == CMD_DO_SESSION_QUIET_QUIT )
3637 0 : return AutoRecovery::E_SESSION_QUIET_QUIT;
3638 64 : else if ( aURL.Path == CMD_DO_SESSION_RESTORE )
3639 0 : return AutoRecovery::E_SESSION_RESTORE;
3640 64 : else if ( aURL.Path == CMD_DO_DISABLE_RECOVERY )
3641 64 : return AutoRecovery::E_DISABLE_AUTORECOVERY;
3642 0 : else if ( aURL.Path == CMD_DO_SET_AUTOSAVE_STATE )
3643 0 : return AutoRecovery::E_SET_AUTOSAVE_STATE;
3644 : }
3645 :
3646 : SAL_INFO("fwk", "AutoRecovery::implts_classifyJob(): Invalid URL (protocol).");
3647 0 : return AutoRecovery::E_NO_JOB;
3648 : }
3649 :
3650 0 : css::frame::FeatureStateEvent AutoRecovery::implst_createFeatureStateEvent( sal_Int32 eJob ,
3651 : const OUString& sEventType,
3652 : AutoRecovery::TDocumentInfo* pInfo )
3653 : {
3654 0 : css::frame::FeatureStateEvent aEvent;
3655 0 : aEvent.FeatureURL.Complete = AutoRecovery::implst_getJobDescription(eJob);
3656 0 : aEvent.FeatureDescriptor = sEventType;
3657 :
3658 0 : if (pInfo && sEventType == OPERATION_UPDATE)
3659 : {
3660 : // pack rInfo for transport via UNO
3661 0 : ::comphelper::NamedValueCollection aInfo;
3662 0 : aInfo.put( OUString(CFG_ENTRY_PROP_ID), pInfo->ID );
3663 0 : aInfo.put( OUString(CFG_ENTRY_PROP_ORIGINALURL), pInfo->OrgURL );
3664 0 : aInfo.put( OUString(CFG_ENTRY_PROP_FACTORYURL), pInfo->FactoryURL );
3665 0 : aInfo.put( OUString(CFG_ENTRY_PROP_TEMPLATEURL), pInfo->TemplateURL );
3666 0 : aInfo.put( OUString(CFG_ENTRY_PROP_TEMPURL), pInfo->OldTempURL.isEmpty() ? pInfo->NewTempURL : pInfo->OldTempURL );
3667 0 : aInfo.put( OUString(CFG_ENTRY_PROP_MODULE), pInfo->AppModule);
3668 0 : aInfo.put( OUString(CFG_ENTRY_PROP_TITLE), pInfo->Title);
3669 0 : aInfo.put( OUString(CFG_ENTRY_PROP_VIEWNAMES), pInfo->ViewNames);
3670 0 : aInfo.put( OUString(CFG_ENTRY_PROP_DOCUMENTSTATE), pInfo->DocumentState);
3671 :
3672 0 : aEvent.State <<= aInfo.getPropertyValues();
3673 : }
3674 :
3675 0 : return aEvent;
3676 : }
3677 :
3678 0 : void AutoRecovery::implts_resetHandleStates(bool /*bLoadCache*/)
3679 : {
3680 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
3681 :
3682 : /* SAFE */ {
3683 0 : osl::ResettableMutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
3684 :
3685 0 : AutoRecovery::TDocumentList::iterator pIt;
3686 0 : for ( pIt = m_lDocCache.begin();
3687 0 : pIt != m_lDocCache.end();
3688 : ++pIt )
3689 : {
3690 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
3691 0 : rInfo.DocumentState &= ~AutoRecovery::E_HANDLED;
3692 0 : rInfo.DocumentState &= ~AutoRecovery::E_POSTPONED;
3693 :
3694 : // } /* SAFE */
3695 0 : g.clear();
3696 0 : implts_flushConfigItem(rInfo);
3697 0 : g.reset();
3698 : // /* SAFE */ {
3699 0 : }
3700 0 : } /* SAFE */
3701 0 : }
3702 :
3703 0 : void AutoRecovery::implts_prepareEmergencySave()
3704 : {
3705 : // Be sure to know all open documents really .-)
3706 0 : implts_verifyCacheAgainstDesktopDocumentList();
3707 :
3708 : // hide all docs, so the user can't disturb our emergency save .-)
3709 0 : implts_changeAllDocVisibility(false);
3710 0 : }
3711 :
3712 0 : void AutoRecovery::implts_doEmergencySave(const DispatchParams& aParams)
3713 : {
3714 : // Write a hint "we chrashed" into the configuration, so
3715 : // the error report tool is started too in case no recovery
3716 : // documents exists and was saved.
3717 : ::comphelper::ConfigurationHelper::writeDirectKey(
3718 : m_xContext,
3719 : OUString(CFG_PACKAGE_RECOVERY),
3720 : OUString(CFG_PATH_RECOVERYINFO),
3721 : OUString(CFG_ENTRY_CRASHED),
3722 : css::uno::makeAny(sal_True),
3723 0 : ::comphelper::ConfigurationHelper::E_STANDARD);
3724 :
3725 : // for all docs, store their current view/names in the configurtion
3726 0 : implts_persistAllActiveViewNames();
3727 :
3728 : // The called method for saving documents runs
3729 : // during normal AutoSave more than once. Because
3730 : // it postpone active documents and save it later.
3731 : // That is normally done by recalling it from a timer.
3732 : // Here we must do it immediately!
3733 : // Of course this method returns the right state -
3734 : // because it knows, that we are running in ERMERGENCY SAVE mode .-)
3735 :
3736 0 : bool bAllowUserIdleLoop = false; // not allowed to change that .-)
3737 0 : AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3738 0 : do
3739 : {
3740 0 : eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, true, &aParams);
3741 : }
3742 : while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3743 :
3744 : // reset the handle state of all
3745 : // cache items. Such handle state indicates, that a document
3746 : // was already saved during the THIS(!) EmergencySave session.
3747 : // Of course following recovery session must be started without
3748 : // any "handle" state ...
3749 0 : implts_resetHandleStates(false);
3750 :
3751 : // flush config cached back to disc.
3752 0 : impl_flushALLConfigChanges();
3753 :
3754 : // try to make sure next time office will be started user wont be
3755 : // notified about any other might be running office instance
3756 : // remove ".lock" file from disc !
3757 0 : AutoRecovery::st_impl_removeLockFile();
3758 0 : }
3759 :
3760 0 : void AutoRecovery::implts_doRecovery(const DispatchParams& aParams)
3761 : {
3762 0 : AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3763 0 : do
3764 : {
3765 0 : eSuggestedTimer = implts_openDocs(aParams);
3766 : }
3767 : while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3768 :
3769 : // reset the handle state of all
3770 : // cache items. Such handle state indicates, that a document
3771 : // was already saved during the THIS(!) Recovery session.
3772 : // Of course a may be following EmergencySave session must be started without
3773 : // any "handle" state ...
3774 0 : implts_resetHandleStates(true);
3775 :
3776 : // Reset the configuration hint "we was crashed"!
3777 : ::comphelper::ConfigurationHelper::writeDirectKey(
3778 : m_xContext,
3779 : OUString(CFG_PACKAGE_RECOVERY),
3780 : OUString(CFG_PATH_RECOVERYINFO),
3781 : OUString(CFG_ENTRY_CRASHED),
3782 : css::uno::makeAny(sal_False),
3783 0 : ::comphelper::ConfigurationHelper::E_STANDARD);
3784 0 : }
3785 :
3786 0 : void AutoRecovery::implts_doSessionSave(const DispatchParams& aParams)
3787 : {
3788 : SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionSave()");
3789 :
3790 : // Be sure to know all open documents really .-)
3791 0 : implts_verifyCacheAgainstDesktopDocumentList();
3792 :
3793 : // for all docs, store their current view/names in the configurtion
3794 0 : implts_persistAllActiveViewNames();
3795 :
3796 : // The called method for saving documents runs
3797 : // during normal AutoSave more than once. Because
3798 : // it postpone active documents and save it later.
3799 : // That is normally done by recalling it from a timer.
3800 : // Here we must do it immediately!
3801 : // Of course this method returns the right state -
3802 : // because it knows, that we are running in SESSION SAVE mode .-)
3803 :
3804 0 : bool bAllowUserIdleLoop = false; // not allowed to change that .-)
3805 0 : AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3806 0 : do
3807 : {
3808 : // do not remove lock files of the documents, it will be done on session quit
3809 0 : eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, false, &aParams);
3810 : }
3811 : while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3812 :
3813 : // reset the handle state of all
3814 : // cache items. Such handle state indicates, that a document
3815 : // was already saved during the THIS(!) save session.
3816 : // Of course following restore session must be started without
3817 : // any "handle" state ...
3818 0 : implts_resetHandleStates(false);
3819 :
3820 : // flush config cached back to disc.
3821 0 : impl_flushALLConfigChanges();
3822 0 : }
3823 :
3824 0 : void AutoRecovery::implts_doSessionQuietQuit(const DispatchParams& /*aParams*/)
3825 : {
3826 : SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionQuietQuit()");
3827 :
3828 : // try to make sure next time office will be started user wont be
3829 : // notified about any other might be running office instance
3830 : // remove ".lock" file from disc !
3831 : // it is done as a first action for session save since Gnome sessions
3832 : // do not provide enough time for shutdown, and the dialog looks to be
3833 : // confusing for the user
3834 0 : AutoRecovery::st_impl_removeLockFile();
3835 :
3836 : // reset all modified documents, so the dont show any UI on closing ...
3837 : // and close all documents, so we can shutdown the OS!
3838 0 : implts_prepareSessionShutdown();
3839 :
3840 : // Write a hint for "stored session data" into the configuration, so
3841 : // the on next startup we know what's happen last time
3842 : ::comphelper::ConfigurationHelper::writeDirectKey(
3843 : m_xContext,
3844 : OUString(CFG_PACKAGE_RECOVERY),
3845 : OUString(CFG_PATH_RECOVERYINFO),
3846 : OUString(CFG_ENTRY_SESSIONDATA),
3847 : css::uno::makeAny(sal_True),
3848 0 : ::comphelper::ConfigurationHelper::E_STANDARD);
3849 :
3850 : // flush config cached back to disc.
3851 0 : impl_flushALLConfigChanges();
3852 0 : }
3853 :
3854 0 : void AutoRecovery::implts_doSessionRestore(const DispatchParams& aParams)
3855 : {
3856 : SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_doSessionRestore() ...");
3857 :
3858 0 : AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER;
3859 0 : do
3860 : {
3861 0 : eSuggestedTimer = implts_openDocs(aParams);
3862 : }
3863 : while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK);
3864 :
3865 : // reset the handle state of all
3866 : // cache items. Such handle state indicates, that a document
3867 : // was already saved during the THIS(!) Restore session.
3868 : // Of course a may be following save session must be started without
3869 : // any "handle" state ...
3870 0 : implts_resetHandleStates(true);
3871 :
3872 : // make all opened documents visible
3873 0 : implts_changeAllDocVisibility(true);
3874 :
3875 : // Reset the configuration hint for "session save"!
3876 : SAL_INFO("fwk.autorecovery", "... reset config key 'SessionData'");
3877 : ::comphelper::ConfigurationHelper::writeDirectKey(
3878 : m_xContext,
3879 : OUString(CFG_PACKAGE_RECOVERY),
3880 : OUString(CFG_PATH_RECOVERYINFO),
3881 : OUString(CFG_ENTRY_SESSIONDATA),
3882 : css::uno::makeAny(sal_False),
3883 0 : ::comphelper::ConfigurationHelper::E_STANDARD);
3884 :
3885 : SAL_INFO("fwk.autorecovery", "... AutoRecovery::implts_doSessionRestore()");
3886 0 : }
3887 :
3888 0 : void AutoRecovery::implts_backupWorkingEntry(const DispatchParams& aParams)
3889 : {
3890 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_USE);
3891 :
3892 0 : AutoRecovery::TDocumentList::iterator pIt;
3893 0 : for ( pIt = m_lDocCache.begin();
3894 0 : pIt != m_lDocCache.end();
3895 : ++pIt )
3896 : {
3897 0 : const AutoRecovery::TDocumentInfo& rInfo = *pIt;
3898 0 : if (rInfo.ID != aParams.m_nWorkingEntryID)
3899 0 : continue;
3900 :
3901 0 : OUString sSourceURL;
3902 : // Prefer temp file. It contains the changes against the original document!
3903 0 : if (!rInfo.OldTempURL.isEmpty())
3904 0 : sSourceURL = rInfo.OldTempURL;
3905 0 : else if (!rInfo.NewTempURL.isEmpty())
3906 0 : sSourceURL = rInfo.NewTempURL;
3907 0 : else if (!rInfo.OrgURL.isEmpty())
3908 0 : sSourceURL = rInfo.OrgURL;
3909 : else
3910 0 : continue; // nothing real to save! An unmodified but new created document.
3911 :
3912 0 : INetURLObject aParser(sSourceURL);
3913 : // AutoRecovery::EFailureSafeResult eResult =
3914 0 : implts_copyFile(sSourceURL, aParams.m_sSavePath, aParser.getName());
3915 :
3916 : // TODO: Check eResult and react for errors (InteractionHandler!?)
3917 : // Currently we ignore it ...
3918 : // DONT UPDATE THE CACHE OR REMOVE ANY TEMP. FILES FROM DISK.
3919 : // That has to be forced from outside explicitly.
3920 : // See implts_cleanUpWorkingEntry() for further details.
3921 0 : }
3922 0 : }
3923 :
3924 0 : void AutoRecovery::implts_cleanUpWorkingEntry(const DispatchParams& aParams)
3925 : {
3926 0 : CacheLockGuard aCacheLock(this, cppu::WeakComponentImplHelperBase::rBHelper.rMutex, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE);
3927 :
3928 0 : AutoRecovery::TDocumentList::iterator pIt;
3929 0 : for ( pIt = m_lDocCache.begin();
3930 0 : pIt != m_lDocCache.end();
3931 : ++pIt )
3932 : {
3933 0 : AutoRecovery::TDocumentInfo& rInfo = *pIt;
3934 0 : if (rInfo.ID != aParams.m_nWorkingEntryID)
3935 0 : continue;
3936 :
3937 0 : AutoRecovery::st_impl_removeFile(rInfo.OldTempURL);
3938 0 : AutoRecovery::st_impl_removeFile(rInfo.NewTempURL);
3939 0 : implts_flushConfigItem(rInfo, true); // sal_True => remove it from xml config!
3940 :
3941 0 : m_lDocCache.erase(pIt);
3942 0 : break; /// !!! pIt is not defined any longer ... further this function has finished it's work
3943 0 : }
3944 0 : }
3945 :
3946 0 : AutoRecovery::EFailureSafeResult AutoRecovery::implts_copyFile(const OUString& sSource ,
3947 : const OUString& sTargetPath,
3948 : const OUString& sTargetName)
3949 : {
3950 : // create content for the parent folder and call transfer on that content with the source content
3951 : // and the destination file name as parameters
3952 :
3953 0 : css::uno::Reference< css::ucb::XCommandEnvironment > xEnvironment;
3954 :
3955 0 : ::ucbhelper::Content aSourceContent;
3956 0 : ::ucbhelper::Content aTargetContent;
3957 :
3958 : try
3959 : {
3960 0 : aTargetContent = ::ucbhelper::Content(sTargetPath, xEnvironment, m_xContext);
3961 : }
3962 0 : catch(const css::uno::Exception&)
3963 : {
3964 0 : return AutoRecovery::E_WRONG_TARGET_PATH;
3965 : }
3966 :
3967 : sal_Int32 nNameClash;
3968 0 : nNameClash = css::ucb::NameClash::RENAME;
3969 :
3970 : try
3971 : {
3972 0 : bool bSuccess = ::ucbhelper::Content::create(sSource, xEnvironment, m_xContext, aSourceContent);
3973 0 : if (!bSuccess)
3974 0 : return AutoRecovery::E_ORIGINAL_FILE_MISSING;
3975 0 : aTargetContent.transferContent(aSourceContent, ::ucbhelper::InsertOperation_COPY, sTargetName, nNameClash);
3976 : }
3977 0 : catch(const css::uno::Exception&)
3978 : {
3979 0 : return AutoRecovery::E_ORIGINAL_FILE_MISSING;
3980 : }
3981 :
3982 0 : return AutoRecovery::E_COPIED;
3983 : }
3984 :
3985 0 : sal_Bool SAL_CALL AutoRecovery::convertFastPropertyValue( css::uno::Any& /*aConvertedValue*/,
3986 : css::uno::Any& /*aOldValue*/ ,
3987 : sal_Int32 /*nHandle*/ ,
3988 : const css::uno::Any& /*aValue*/ )
3989 : throw(css::lang::IllegalArgumentException)
3990 : {
3991 : // not needed currently
3992 0 : return sal_False;
3993 : }
3994 :
3995 0 : void SAL_CALL AutoRecovery::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/,
3996 : const css::uno::Any& /*aValue*/ )
3997 : throw(css::uno::Exception, std::exception)
3998 : {
3999 : // not needed currently
4000 0 : }
4001 :
4002 0 : void SAL_CALL AutoRecovery::getFastPropertyValue(css::uno::Any& aValue ,
4003 : sal_Int32 nHandle) const
4004 : {
4005 0 : switch(nHandle)
4006 : {
4007 : case AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA :
4008 : {
4009 0 : bool bSessionData = false;
4010 : ::comphelper::ConfigurationHelper::readDirectKey(
4011 : m_xContext,
4012 : OUString(CFG_PACKAGE_RECOVERY),
4013 : OUString(CFG_PATH_RECOVERYINFO),
4014 : OUString(CFG_ENTRY_SESSIONDATA),
4015 0 : ::comphelper::ConfigurationHelper::E_READONLY) >>= bSessionData;
4016 :
4017 0 : bool bRecoveryData = m_lDocCache.size() > 0;
4018 :
4019 : // exists session data ... => then we can't say, that these
4020 : // data are valid for recovery. So we have to return sal_False then!
4021 0 : if (bSessionData)
4022 0 : bRecoveryData = false;
4023 :
4024 0 : aValue <<= bRecoveryData;
4025 : }
4026 0 : break;
4027 :
4028 : case AUTORECOVERY_PROPHANDLE_CRASHED :
4029 0 : aValue = ::comphelper::ConfigurationHelper::readDirectKey(
4030 : m_xContext,
4031 : OUString(CFG_PACKAGE_RECOVERY),
4032 : OUString(CFG_PATH_RECOVERYINFO),
4033 : OUString(CFG_ENTRY_CRASHED),
4034 0 : ::comphelper::ConfigurationHelper::E_READONLY);
4035 0 : break;
4036 :
4037 : case AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA :
4038 0 : aValue = ::comphelper::ConfigurationHelper::readDirectKey(
4039 : m_xContext,
4040 : OUString(CFG_PACKAGE_RECOVERY),
4041 : OUString(CFG_PATH_RECOVERYINFO),
4042 : OUString(CFG_ENTRY_SESSIONDATA),
4043 0 : ::comphelper::ConfigurationHelper::E_READONLY);
4044 0 : break;
4045 : }
4046 0 : }
4047 :
4048 0 : const css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor()
4049 : {
4050 : const css::beans::Property pPropertys[] =
4051 : {
4052 0 : css::beans::Property( AUTORECOVERY_PROPNAME_CRASHED , AUTORECOVERY_PROPHANDLE_CRASHED , cppu::UnoType<bool>::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
4053 0 : css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA, AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA, cppu::UnoType<bool>::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
4054 0 : css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA , AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA , cppu::UnoType<bool>::get() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ),
4055 0 : };
4056 0 : const css::uno::Sequence< css::beans::Property > lPropertyDescriptor(pPropertys, AUTORECOVERY_PROPCOUNT);
4057 0 : return lPropertyDescriptor;
4058 : }
4059 :
4060 0 : ::cppu::IPropertyArrayHelper& SAL_CALL AutoRecovery::getInfoHelper()
4061 : {
4062 : static ::cppu::OPropertyArrayHelper* pInfoHelper = 0;
4063 0 : if(!pInfoHelper)
4064 : {
4065 0 : SolarMutexGuard g;
4066 0 : if(!pInfoHelper)
4067 : {
4068 0 : static ::cppu::OPropertyArrayHelper aInfoHelper(impl_getStaticPropertyDescriptor(), sal_True);
4069 0 : pInfoHelper = &aInfoHelper;
4070 0 : }
4071 : }
4072 :
4073 0 : return (*pInfoHelper);
4074 : }
4075 :
4076 0 : css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL AutoRecovery::getPropertySetInfo()
4077 : throw(css::uno::RuntimeException, std::exception)
4078 : {
4079 : static css::uno::Reference< css::beans::XPropertySetInfo >* pInfo = 0;
4080 0 : if(!pInfo)
4081 : {
4082 0 : SolarMutexGuard g;
4083 0 : if(!pInfo)
4084 : {
4085 : static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(
4086 0 : ::cppu::OPropertySetHelper::createPropertySetInfo(getInfoHelper()));
4087 0 : pInfo = &xInfo;
4088 0 : }
4089 : }
4090 :
4091 0 : return (*pInfo);
4092 : }
4093 :
4094 0 : void AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()
4095 : {
4096 : SAL_INFO("fwk.autorecovery", "AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() ...");
4097 : try
4098 : {
4099 0 : css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
4100 :
4101 : css::uno::Reference< css::container::XIndexAccess > xContainer(
4102 0 : xDesktop->getFrames(),
4103 0 : css::uno::UNO_QUERY_THROW);
4104 :
4105 0 : sal_Int32 i = 0;
4106 0 : sal_Int32 c = xContainer->getCount();
4107 :
4108 0 : for (i=0; i<c; ++i)
4109 : {
4110 0 : css::uno::Reference< css::frame::XFrame > xFrame;
4111 : try
4112 : {
4113 0 : xContainer->getByIndex(i) >>= xFrame;
4114 0 : if (!xFrame.is())
4115 0 : continue;
4116 : }
4117 : // can happen in multithreaded environments, that frames was removed from the container during this loop runs!
4118 : // Ignore it.
4119 0 : catch(const css::lang::IndexOutOfBoundsException&)
4120 : {
4121 0 : continue;
4122 : }
4123 :
4124 : // We are interested on visible documents only.
4125 : // Note: It's n optional interface .-(
4126 : css::uno::Reference< css::awt::XWindow2 > xVisibleCheck(
4127 0 : xFrame->getContainerWindow(),
4128 0 : css::uno::UNO_QUERY);
4129 0 : if (
4130 0 : (!xVisibleCheck.is() ) ||
4131 0 : (!xVisibleCheck->isVisible())
4132 : )
4133 : {
4134 0 : continue;
4135 : }
4136 :
4137 : // extract the model from the frame.
4138 : // Ignore "view only" frames, which does not have a model.
4139 0 : css::uno::Reference< css::frame::XController > xController;
4140 0 : css::uno::Reference< css::frame::XModel > xModel;
4141 :
4142 0 : xController = xFrame->getController();
4143 0 : if (xController.is())
4144 0 : xModel = xController->getModel();
4145 0 : if (!xModel.is())
4146 0 : continue;
4147 :
4148 : // insert model into cache ...
4149 : // If the model is already well known inside cache
4150 : // it's information set will be updated by asking the
4151 : // model again for it's new states.
4152 0 : implts_registerDocument(xModel);
4153 0 : }
4154 : }
4155 0 : catch(const css::uno::RuntimeException&)
4156 : {
4157 0 : throw;
4158 : }
4159 0 : catch(const css::uno::Exception&)
4160 : {
4161 : }
4162 :
4163 : SAL_INFO("fwk.autorecovery", "... AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()");
4164 0 : }
4165 :
4166 0 : bool AutoRecovery::impl_enoughDiscSpace(sal_Int32 nRequiredSpace)
4167 : {
4168 : #ifdef SIMULATE_FULL_DISC
4169 : return sal_False;
4170 : #else // SIMULATE_FULL_DISC
4171 : // In case an error occurs and we are not able to retrieve the needed information
4172 : // it's better to "disable" the feature ShowErrorOnFullDisc !
4173 : // Otherwhise we start a confusing process of error handling ...
4174 :
4175 0 : sal_uInt64 nFreeSpace = SAL_MAX_UINT64;
4176 :
4177 0 : OUString sBackupPath(SvtPathOptions().GetBackupPath());
4178 0 : ::osl::VolumeInfo aInfo (osl_VolumeInfo_Mask_FreeSpace);
4179 0 : ::osl::FileBase::RC aRC = ::osl::Directory::getVolumeInfo(sBackupPath, aInfo);
4180 :
4181 0 : if (
4182 0 : (aInfo.isValid(osl_VolumeInfo_Mask_FreeSpace)) &&
4183 : (aRC == ::osl::FileBase::E_None )
4184 : )
4185 : {
4186 0 : nFreeSpace = aInfo.getFreeSpace();
4187 : }
4188 :
4189 0 : sal_uInt64 nFreeMB = (nFreeSpace/1048576);
4190 0 : return (nFreeMB >= (sal_uInt64)nRequiredSpace);
4191 : #endif // SIMULATE_FULL_DISC
4192 : }
4193 :
4194 0 : void AutoRecovery::impl_showFullDiscError()
4195 : {
4196 0 : OUString sBtn(FWK_RESSTR(STR_FULL_DISC_RETRY_BUTTON));
4197 0 : OUString sMsg(FWK_RESSTR(STR_FULL_DISC_MSG));
4198 :
4199 0 : OUString sBackupURL(SvtPathOptions().GetBackupPath());
4200 0 : INetURLObject aConverter(sBackupURL);
4201 : sal_Unicode aDelimiter;
4202 0 : OUString sBackupPath = aConverter.getFSysPath(INetURLObject::FSYS_DETECT, &aDelimiter);
4203 0 : if (sBackupPath.getLength() < 1)
4204 0 : sBackupPath = sBackupURL;
4205 :
4206 : ScopedVclPtrInstance<ErrorBox> dlgError(
4207 : nullptr, WB_OK,
4208 0 : sMsg.replaceAll("%PATH", sBackupPath));
4209 0 : dlgError->SetButtonText(dlgError->GetButtonId(0), sBtn);
4210 0 : dlgError->Execute();
4211 0 : }
4212 :
4213 0 : void AutoRecovery::impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo ,
4214 : utl::MediaDescriptor& rArgs ,
4215 : const css::uno::Reference< css::frame::XFrame >& xNewFrame)
4216 : {
4217 : // external well known frame must be preferred (because it was created by ourself
4218 : // for loading documents into this frame)!
4219 : // But if no frame exists ... we can try to locate it using any frame bound to the provided
4220 : // document. Of course we must live without any frame in case the document does not exists at this
4221 : // point. But this state should not occur. In such case xNewFrame should be valid ... hopefully .-)
4222 0 : css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
4223 0 : if (
4224 0 : (!xFrame.is() ) &&
4225 0 : (rInfo.Document.is())
4226 : )
4227 : {
4228 0 : css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
4229 0 : if (xController.is())
4230 0 : xFrame = xController->getFrame();
4231 : }
4232 :
4233 : // Any outside progress must be used ...
4234 : // Only if there is no progress, we can create our own one.
4235 0 : css::uno::Reference< css::task::XStatusIndicator > xInternalProgress;
4236 : css::uno::Reference< css::task::XStatusIndicator > xExternalProgress = rArgs.getUnpackedValueOrDefault(
4237 0 : utl::MediaDescriptor::PROP_STATUSINDICATOR(),
4238 0 : css::uno::Reference< css::task::XStatusIndicator >() );
4239 :
4240 : // Normally a progress is set from outside (e.g. by the CrashSave/Recovery dialog, which uses our dispatch API).
4241 : // But for a normal auto save we dont have such "external progress"... because this function is triggered by our own timer then.
4242 : // In such case we must create our own progress !
4243 0 : if (
4244 0 : (! xExternalProgress.is()) &&
4245 0 : (xFrame.is() )
4246 : )
4247 : {
4248 0 : css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xFrame, css::uno::UNO_QUERY);
4249 0 : if (xProgressFactory.is())
4250 0 : xInternalProgress = xProgressFactory->createStatusIndicator();
4251 : }
4252 :
4253 : // HACK
4254 : // An external provided progress (most given by the CrashSave/Recovery dialog)
4255 : // must be preferred. But we know that some application filters query it's own progress instance
4256 : // at the frame method Frame::createStatusIndicator().
4257 : // So we use a two step mechanism:
4258 : // 1) we set the progress inside the MediaDescriptor, which will be provided to the filter
4259 : // 2) and we set a special Frame property, which overwrites the normal behaviour of Frame::createStatusIndicator .-)
4260 : // But we suppress 2) in case we uses an internal progress. Because then it doesn't matter
4261 : // if our applications make it wrong. In such case the internal progress resists at the same frame
4262 : // and there is no need to forward progress activities to e.g. an outside dialog .-)
4263 0 : if (
4264 0 : (xExternalProgress.is()) &&
4265 0 : (xFrame.is() )
4266 : )
4267 : {
4268 0 : css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
4269 0 : if (xFrameProps.is())
4270 0 : xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(xExternalProgress));
4271 : }
4272 :
4273 : // But inside the MediaDescriptor we must set our own create progress ...
4274 : // in case there is not already another progress set.
4275 0 : rArgs.createItemIfMissing(utl::MediaDescriptor::PROP_STATUSINDICATOR(), xInternalProgress);
4276 0 : }
4277 :
4278 0 : void AutoRecovery::impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo ,
4279 : utl::MediaDescriptor& rArgs ,
4280 : const css::uno::Reference< css::frame::XFrame >& xNewFrame)
4281 : {
4282 : // external well known frame must be preferred (because it was created by ourself
4283 : // for loading documents into this frame)!
4284 : // But if no frame exists ... we can try to locate it using any frame bound to the provided
4285 : // document. Of course we must live without any frame in case the document does not exists at this
4286 : // point. But this state should not occur. In such case xNewFrame should be valid ... hopefully .-)
4287 0 : css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame;
4288 0 : if (
4289 0 : (!xFrame.is() ) &&
4290 0 : (rInfo.Document.is())
4291 : )
4292 : {
4293 0 : css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController();
4294 0 : if (xController.is())
4295 0 : xFrame = xController->getFrame();
4296 : }
4297 :
4298 : // stop progress interception on corresponding frame.
4299 0 : css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY);
4300 0 : if (xFrameProps.is())
4301 0 : xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(css::uno::Reference< css::task::XStatusIndicator >()));
4302 :
4303 : // forget progress inside list of arguments.
4304 0 : utl::MediaDescriptor::iterator pArg = rArgs.find(utl::MediaDescriptor::PROP_STATUSINDICATOR());
4305 0 : if (pArg != rArgs.end())
4306 : {
4307 0 : rArgs.erase(pArg);
4308 0 : pArg = rArgs.end();
4309 0 : }
4310 0 : }
4311 :
4312 0 : void AutoRecovery::impl_flushALLConfigChanges()
4313 : {
4314 : try
4315 : {
4316 0 : css::uno::Reference< css::uno::XInterface > xRecoveryCfg;
4317 : /* SAFE */ {
4318 0 : osl::MutexGuard g(cppu::WeakComponentImplHelperBase::rBHelper.rMutex);
4319 0 : xRecoveryCfg.set(m_xRecoveryCFG, css::uno::UNO_QUERY);
4320 : } /* SAFE */
4321 :
4322 0 : if (xRecoveryCfg.is())
4323 0 : ::comphelper::ConfigurationHelper::flush(xRecoveryCfg);
4324 :
4325 : // SOLAR SAFE ->
4326 0 : SolarMutexGuard aGuard;
4327 0 : ::utl::ConfigManager::storeConfigItems();
4328 : }
4329 0 : catch(const css::uno::Exception&)
4330 : {
4331 : }
4332 0 : }
4333 :
4334 0 : void AutoRecovery::st_impl_removeFile(const OUString& sURL)
4335 : {
4336 0 : if ( sURL.isEmpty())
4337 0 : return;
4338 :
4339 : try
4340 : {
4341 0 : ::ucbhelper::Content aContent = ::ucbhelper::Content(sURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), m_xContext);
4342 0 : aContent.executeCommand(OUString("delete"), css::uno::makeAny(sal_True));
4343 : }
4344 0 : catch(const css::uno::Exception&)
4345 : {
4346 : }
4347 : }
4348 :
4349 0 : void AutoRecovery::st_impl_removeLockFile()
4350 : {
4351 : try
4352 : {
4353 0 : OUString sUserURL;
4354 0 : ::utl::Bootstrap::locateUserInstallation( sUserURL );
4355 :
4356 0 : OUStringBuffer sLockURLBuf;
4357 0 : sLockURLBuf.append (sUserURL);
4358 0 : sLockURLBuf.appendAscii("/.lock");
4359 0 : OUString sLockURL = sLockURLBuf.makeStringAndClear();
4360 :
4361 0 : AutoRecovery::st_impl_removeFile(sLockURL);
4362 : }
4363 0 : catch(const css::uno::Exception&)
4364 : {
4365 : }
4366 0 : }
4367 :
4368 65 : struct Instance {
4369 65 : explicit Instance(
4370 : css::uno::Reference<css::uno::XComponentContext> const & context):
4371 : instance(
4372 65 : static_cast<cppu::OWeakObject *>(new AutoRecovery(context)))
4373 : {
4374 : // 2nd phase initialization needed
4375 : static_cast<AutoRecovery*>(static_cast<cppu::OWeakObject *>
4376 65 : (instance.get()))->initListeners();
4377 65 : }
4378 :
4379 : css::uno::Reference<css::uno::XInterface> instance;
4380 : };
4381 :
4382 : struct Singleton:
4383 : public rtl::StaticWithArg<
4384 : Instance, css::uno::Reference<css::uno::XComponentContext>, Singleton>
4385 : {};
4386 :
4387 : }
4388 :
4389 : extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * SAL_CALL
4390 65 : com_sun_star_comp_framework_AutoRecovery_get_implementation(
4391 : css::uno::XComponentContext *context,
4392 : css::uno::Sequence<css::uno::Any> const &)
4393 : {
4394 : return cppu::acquire(static_cast<cppu::OWeakObject *>(
4395 65 : Singleton::get(context).instance.get()));
4396 : }
4397 :
4398 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|