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