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