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