diff options
Diffstat (limited to 'framework/source/services')
20 files changed, 18018 insertions, 0 deletions
diff --git a/framework/source/services/autorecovery.cxx b/framework/source/services/autorecovery.cxx new file mode 100644 index 000000000000..c4cc7149ab6d --- /dev/null +++ b/framework/source/services/autorecovery.cxx @@ -0,0 +1,3743 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" +#include "services/autorecovery.hxx" +#include <loadenv/loadenv.hxx> + +//_______________________________________________ +// own includes +#include <loadenv/targethelper.hxx> +#include <pattern/frame.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> + +#include <classes/resource.hrc> +#include <classes/fwkresid.hxx> +#include <protocols.h> +#include <properties.h> +#include <services.h> + +//_______________________________________________ +// interface includes +#include <com/sun/star/ucb/NameClash.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/frame/XLoadable.hpp> +#include <com/sun/star/frame/XModel2.hpp> +#include <com/sun/star/frame/XModuleManager.hpp> +#include <com/sun/star/frame/XTitle.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/util/XChangesNotifier.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/container/XContainerQuery.hpp> +#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp> +#include <com/sun/star/document/XDocumentRecovery.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/awt/XWindow2.hpp> +#include <com/sun/star/task/XStatusIndicatorFactory.hpp> + +//_______________________________________________ +// other includes +#include <comphelper/configurationhelper.hxx> +#include <comphelper/mediadescriptor.hxx> +#include <comphelper/namedvaluecollection.hxx> +#include <vcl/svapp.hxx> +#include <unotools/pathoptions.hxx> +#include <tools/link.hxx> +#include <tools/string.hxx> +#include <tools/diagnose_ex.h> +#include <unotools/tempfile.hxx> +#include <ucbhelper/content.hxx> + +#include <osl/time.h> +#include <vcl/msgbox.hxx> +#include <osl/file.hxx> +#include <unotools/bootstrap.hxx> +#include <unotools/configmgr.hxx> +#include <svl/documentlockfile.hxx> +#include <cppuhelper/exc_hlp.hxx> + +#include <tools/urlobj.hxx> + +//_______________________________________________ +// namespaces + +#ifndef css +namespace css = ::com::sun::star; +#endif + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::UNO_SET_THROW; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Any; +using ::com::sun::star::beans::PropertyValue; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::document::XDocumentRecovery; +using ::com::sun::star::frame::XModel2; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::frame::XFrame; +using ::com::sun::star::frame::XController2; +using ::com::sun::star::frame::XLoadable; +using ::com::sun::star::frame::XStorable; +using ::com::sun::star::lang::XComponent; + +namespace fpf = ::framework::pattern::frame; + +namespace framework +{ + +//----------------------------------------------- +// recovery.xcu +static const ::rtl::OUString CFG_PACKAGE_RECOVERY = ::rtl::OUString::createFromAscii("org.openoffice.Office.Recovery/"); +static const ::rtl::OUString CFG_ENTRY_RECOVERYLIST = ::rtl::OUString::createFromAscii("RecoveryList" ); +static const ::rtl::OUString CFG_PATH_RECOVERYINFO = ::rtl::OUString::createFromAscii("RecoveryInfo" ); +static const ::rtl::OUString CFG_ENTRY_ENABLED = ::rtl::OUString::createFromAscii("Enabled" ); +static const ::rtl::OUString CFG_ENTRY_CRASHED = ::rtl::OUString::createFromAscii("Crashed" ); +static const ::rtl::OUString CFG_ENTRY_SESSIONDATA = ::rtl::OUString::createFromAscii("SessionData" ); + +static const ::rtl::OUString CFG_ENTRY_AUTOSAVE_ENABLED = ::rtl::OUString::createFromAscii("AutoSave/Enabled" ); +static const ::rtl::OUString CFG_ENTRY_AUTOSAVE_TIMEINTERVALL = ::rtl::OUString::createFromAscii("AutoSave/TimeIntervall" ); + +static const ::rtl::OUString CFG_PATH_AUTOSAVE = ::rtl::OUString::createFromAscii("AutoSave" ); +static const ::rtl::OUString CFG_ENTRY_MINSPACE_DOCSAVE = ::rtl::OUString::createFromAscii("MinSpaceDocSave" ); +static const ::rtl::OUString CFG_ENTRY_MINSPACE_CONFIGSAVE = ::rtl::OUString::createFromAscii("MinSpaceConfigSave" ); + +static const ::rtl::OUString CFG_PACKAGE_MODULES = ::rtl::OUString::createFromAscii("org.openoffice.Setup/Office/Factories"); +static const ::rtl::OUString CFG_ENTRY_REALDEFAULTFILTER = ::rtl::OUString::createFromAscii("ooSetupFactoryActualFilter" ); + +static const ::rtl::OUString CFG_ENTRY_PROP_TEMPURL = ::rtl::OUString::createFromAscii("TempURL" ); +static const ::rtl::OUString CFG_ENTRY_PROP_ORIGINALURL = ::rtl::OUString::createFromAscii("OriginalURL" ); +static const ::rtl::OUString CFG_ENTRY_PROP_TEMPLATEURL = ::rtl::OUString::createFromAscii("TemplateURL" ); +static const ::rtl::OUString CFG_ENTRY_PROP_FACTORYURL = ::rtl::OUString::createFromAscii("FactoryURL" ); +static const ::rtl::OUString CFG_ENTRY_PROP_MODULE = ::rtl::OUString::createFromAscii("Module" ); +static const ::rtl::OUString CFG_ENTRY_PROP_DOCUMENTSTATE = ::rtl::OUString::createFromAscii("DocumentState"); +static const ::rtl::OUString CFG_ENTRY_PROP_FILTER = ::rtl::OUString::createFromAscii("Filter" ); +static const ::rtl::OUString CFG_ENTRY_PROP_TITLE = ::rtl::OUString::createFromAscii("Title" ); +static const ::rtl::OUString CFG_ENTRY_PROP_ID = ::rtl::OUString::createFromAscii("ID" ); +static const ::rtl::OUString CFG_ENTRY_PROP_VIEWNAMES = ::rtl::OUString::createFromAscii("ViewNames" ); + +static const ::rtl::OUString FILTER_PROP_TYPE = ::rtl::OUString::createFromAscii("Type" ); +static const ::rtl::OUString FILTER_PROP_NAME = ::rtl::OUString::createFromAscii("Name" ); +static const ::rtl::OUString TYPE_PROP_EXTENSIONS = ::rtl::OUString::createFromAscii("Extensions" ); +static const ::rtl::OUString DOCINFO_PROP_TEMPLATE = ::rtl::OUString::createFromAscii("TemplateFileName"); + +// setup.xcu +static const ::rtl::OUString CFG_ENTRY_PROP_EMPTYDOCUMENTURL = ::rtl::OUString::createFromAscii("ooSetupFactoryEmptyDocumentURL"); +static const ::rtl::OUString CFG_ENTRY_PROP_DEFAULTFILTER = ::rtl::OUString::createFromAscii("ooSetupFactoryDefaultFilter" ); +static const ::rtl::OUString CFG_ENTRY_PROP_FACTORYSERVICE = ::rtl::OUString::createFromAscii("ooSetupFactoryDocumentService" ); + +static const ::rtl::OUString EVENT_ON_NEW = ::rtl::OUString::createFromAscii("OnNew" ); +static const ::rtl::OUString EVENT_ON_LOAD = ::rtl::OUString::createFromAscii("OnLoad" ); +static const ::rtl::OUString EVENT_ON_UNLOAD = ::rtl::OUString::createFromAscii("OnUnload" ); +static const ::rtl::OUString EVENT_ON_MODIFYCHANGED = ::rtl::OUString::createFromAscii("OnModifyChanged"); +static const ::rtl::OUString EVENT_ON_SAVE = ::rtl::OUString::createFromAscii("OnSave" ); +static const ::rtl::OUString EVENT_ON_SAVEAS = ::rtl::OUString::createFromAscii("OnSaveAs" ); +static const ::rtl::OUString EVENT_ON_SAVETO = ::rtl::OUString::createFromAscii("OnCopyTo" ); +static const ::rtl::OUString EVENT_ON_SAVEDONE = ::rtl::OUString::createFromAscii("OnSaveDone" ); +static const ::rtl::OUString EVENT_ON_SAVEASDONE = ::rtl::OUString::createFromAscii("OnSaveAsDone" ); +static const ::rtl::OUString EVENT_ON_SAVETODONE = ::rtl::OUString::createFromAscii("OnCopyToDone" ); +static const ::rtl::OUString EVENT_ON_SAVEFAILED = ::rtl::OUString::createFromAscii("OnSaveFailed" ); +static const ::rtl::OUString EVENT_ON_SAVEASFAILED = ::rtl::OUString::createFromAscii("OnSaveAsFailed" ); +static const ::rtl::OUString EVENT_ON_SAVETOFAILED = ::rtl::OUString::createFromAscii("OnCopyToFailed" ); + +static const ::rtl::OUString RECOVERY_ITEM_BASE_IDENTIFIER = ::rtl::OUString::createFromAscii("recovery_item_" ); + +static const ::rtl::OUString CMD_PROTOCOL = ::rtl::OUString::createFromAscii("vnd.sun.star.autorecovery:"); + +static const ::rtl::OUString CMD_DO_AUTO_SAVE = ::rtl::OUString::createFromAscii("/doAutoSave" ); // force AutoSave ignoring the AutoSave timer +static const ::rtl::OUString CMD_DO_PREPARE_EMERGENCY_SAVE = ::rtl::OUString::createFromAscii("/doPrepareEmergencySave" ); // prepare the office for the following EmergencySave step (hide windows etcpp.) +static const ::rtl::OUString CMD_DO_EMERGENCY_SAVE = ::rtl::OUString::createFromAscii("/doEmergencySave" ); // do EmergencySave on crash +static const ::rtl::OUString CMD_DO_RECOVERY = ::rtl::OUString::createFromAscii("/doAutoRecovery" ); // recover all crashed documents +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 +static const ::rtl::OUString CMD_DO_ENTRY_CLEANUP = ::rtl::OUString::createFromAscii("/doEntryCleanUp" ); // remove the specified entry from the recovery cache +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 +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 +static const ::rtl::OUString CMD_DO_SESSION_RESTORE = ::rtl::OUString::createFromAscii("/doSessionRestore" ); // restore a saved user session from disc +static const ::rtl::OUString CMD_DO_DISABLE_RECOVERY = ::rtl::OUString::createFromAscii("/disableRecovery" ); // disable recovery and auto save (!) temp. for this office session +static const ::rtl::OUString CMD_DO_SET_AUTOSAVE_STATE = ::rtl::OUString::createFromAscii("/setAutoSaveState" ); // disable/enable auto save (not crash save) for this office session + +static const ::rtl::OUString REFERRER_USER = ::rtl::OUString::createFromAscii("private:user"); + +static const ::rtl::OUString PROP_DISPATCH_ASYNCHRON = ::rtl::OUString::createFromAscii("DispatchAsynchron"); +static const ::rtl::OUString PROP_PROGRESS = ::rtl::OUString::createFromAscii("StatusIndicator" ); +static const ::rtl::OUString PROP_SAVEPATH = ::rtl::OUString::createFromAscii("SavePath" ); +static const ::rtl::OUString PROP_ENTRY_ID = ::rtl::OUString::createFromAscii("EntryID" ); +static const ::rtl::OUString PROP_DBG_MAKE_IT_FASTER = ::rtl::OUString::createFromAscii("DBGMakeItFaster" ); +static const ::rtl::OUString PROP_AUTOSAVE_STATE = ::rtl::OUString::createFromAscii("AutoSaveState" ); + +static const ::rtl::OUString OPERATION_START = ::rtl::OUString::createFromAscii("start" ); +static const ::rtl::OUString OPERATION_STOP = ::rtl::OUString::createFromAscii("stop" ); +static const ::rtl::OUString OPERATION_UPDATE = ::rtl::OUString::createFromAscii("update"); + +static const sal_Int32 MIN_DISCSPACE_DOCSAVE = 5; // [MB] +static const sal_Int32 MIN_DISCSPACE_CONFIGSAVE = 1; // [MB] +static const sal_Int32 RETRY_STORE_ON_FULL_DISC_FOREVER = 300; // not forever ... but often enough .-) +static const sal_Int32 RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL = 3; // in case FULL DISC does not seam the real problem +static const sal_Int32 GIVE_UP_RETRY = 1; // in case FULL DISC does not seam the real problem + +#define SAVE_IN_PROGRESS sal_True +#define SAVE_FINISHED sal_False + +#define LOCK_FOR_CACHE_ADD_REMOVE sal_True +#define LOCK_FOR_CACHE_USE sal_False + +#define MIN_TIME_FOR_USER_IDLE 10000 // 10s user idle + +// enable the following defines in case you whish to simulate a full disc for debug purposes .-) + +// this define throws everytime a document is stored or a configuration change +// should be flushed an exception ... so the special error handler for this scenario is triggered +// #define TRIGGER_FULL_DISC_CHECK + +// force "return FALSE" for the method impl_enoughDiscSpace(). +// #define SIMULATE_FULL_DISC + +//----------------------------------------------- +// #define ENABLE_RECOVERY_LOGGING +#undef ENABLE_RECOVERY_LOGGING +#ifdef ENABLE_RECOVERY_LOGGING + #define LOGFILE_RECOVERY "recovery.log" + + #define LOG_RECOVERY(MSG) \ + { \ + WRITE_LOGFILE(LOGFILE_RECOVERY, MSG) \ + WRITE_LOGFILE(LOGFILE_RECOVERY, "\n") \ + } +#else + #undef LOGFILE_RECOVERY + #define LOG_RECOVERY(MSG) +#endif + +//----------------------------------------------- +// TODO debug - remove it! +class DbgListener : private ThreadHelpBase + , public ::cppu::OWeakObject + , public css::frame::XStatusListener +{ + public: + + FWK_DECLARE_XINTERFACE + + DbgListener() + { + WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::ctor()\n\n") + } + + virtual ~DbgListener() + { + WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::dtor()\n\n") + } + + void startListening(const css::uno::Reference< css::frame::XDispatch >& xBroadcaster) + { + ::rtl::OUStringBuffer sMsg1(256); + sMsg1.appendAscii("//**********************************************************************************\n"); + sMsg1.appendAscii("start listening\n{\n"); + WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg1.makeStringAndClear())) + + ++m_refCount; + + css::util::URL aURL; + aURL.Complete = ::rtl::OUString(); + xBroadcaster->addStatusListener(static_cast< css::frame::XStatusListener* >(this), aURL); + + --m_refCount; + + ::rtl::OUStringBuffer sMsg2(256); + sMsg2.appendAscii("}\nstart listening\n"); + sMsg2.appendAscii("//**********************************************************************************\n"); + WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg2.makeStringAndClear())) + } + + virtual void SAL_CALL disposing(const css::lang::EventObject&) + throw(css::uno::RuntimeException) + { + WRITE_LOGFILE("autorecovery_states.txt", "\n\nDbgListener::dtor()\n\n") + } + + virtual void SAL_CALL statusChanged(const css::frame::FeatureStateEvent& aEvent) + throw(css::uno::RuntimeException) + { + ::rtl::OUStringBuffer sMsg(256); + + sMsg.appendAscii("//**********************************************************************************\n"); + + sMsg.appendAscii("FeatureURL = \""); + sMsg.append (aEvent.FeatureURL.Complete); + sMsg.appendAscii("\"\n"); + + sMsg.appendAscii("State = ["); + sal_Int32 nState = -1; + aEvent.State >>= nState; + if (nState==-1) + { + sMsg.appendAscii("?-"); + sMsg.append (::rtl::OUString::valueOf(nState)); + sMsg.appendAscii("-? "); + } + if (nState==0) + sMsg.appendAscii("UNKNOWN "); + if ((nState & 1)==1) + sMsg.appendAscii("MODIFIED "); + if ((nState & 2)==2) + sMsg.appendAscii("TRYIT "); + if ((nState & 4)==4) + sMsg.appendAscii("HANDLED "); + if ((nState & 8)==8) + sMsg.appendAscii("POSTPONED "); + if ((nState & 16)==16) + sMsg.appendAscii("INCOMPLETE "); + if ((nState & 32)==32) + sMsg.appendAscii("DAMAGED "); + sMsg.appendAscii("]\n"); +/* + sMsg.appendAscii("IsEnabled = \""); + sMsg.append (::rtl::OUString::valueOf(aEvent.IsEnabled)); + sMsg.appendAscii("\"\n"); + + sMsg.appendAscii("Requery = \""); + sMsg.append (::rtl::OUString::valueOf(aEvent.Requery)); + sMsg.appendAscii("\"\n"); +*/ + sMsg.appendAscii("\n"); + + WRITE_LOGFILE("autorecovery_states.txt", U2B(sMsg.makeStringAndClear())) + } +}; + +//----------------------------------------------- +class CacheLockGuard +{ + private: + + // holds the outside calli alive, so it's shared resources + // are valid everytimes + css::uno::Reference< css::uno::XInterface > m_xOwner; + + // mutex shared with outside calli ! + LockHelper& m_rSharedMutex; + + // this variable knows the state of the "cache lock" + sal_Int32& m_rCacheLock; + + // to prevent increasing/decreasing of m_rCacheLock more then ones + // we must know if THIS guard has an actual lock set there ! + sal_Bool m_bLockedByThisGuard; + + public: + + CacheLockGuard(AutoRecovery* pOwner , + LockHelper& rMutex , + sal_Int32& rCacheLock , + sal_Bool bLockForAddRemoveVectorItems); + ~CacheLockGuard(); + + void lock(sal_Bool bLockForAddRemoveVectorItems); + void unlock(); +}; + +//----------------------------------------------- +CacheLockGuard::CacheLockGuard(AutoRecovery* pOwner , + LockHelper& rMutex , + sal_Int32& rCacheLock , + sal_Bool bLockForAddRemoveVectorItems) + : m_xOwner (static_cast< css::frame::XDispatch* >(pOwner)) + , m_rSharedMutex (rMutex ) + , m_rCacheLock (rCacheLock ) + , m_bLockedByThisGuard(sal_False ) +{ + lock(bLockForAddRemoveVectorItems); +} + +//----------------------------------------------- +CacheLockGuard::~CacheLockGuard() +{ + unlock(); + m_xOwner.clear(); +} + +//----------------------------------------------- +void CacheLockGuard::lock(sal_Bool bLockForAddRemoveVectorItems) +{ + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_rSharedMutex); + + if (m_bLockedByThisGuard) + return; + + // This cache lock is needed only to prevent us from removing/adding + // items from/into the recovery cache ... during it's used at another code place + // for iterating .-) + + // Modifying of item properties is allowed and sometimes needed! + // So we should detect only the dangerous state of concurrent add/remove + // requests and throw an exception then ... which can of course break the whole + // operation. On the other side a crash reasoned by an invalid stl iterator + // will have the same effect .-) + + if ( + (m_rCacheLock > 0 ) && + (bLockForAddRemoveVectorItems) + ) + { + OSL_ENSURE(sal_False, "Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp."); + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("Re-entrance problem detected. Using of an stl structure in combination with iteration, adding, removing of elements etcpp."), + m_xOwner); + } + + ++m_rCacheLock; + m_bLockedByThisGuard = sal_True; + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +void CacheLockGuard::unlock() +{ + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_rSharedMutex); + + if ( ! m_bLockedByThisGuard) + return; + + --m_rCacheLock; + m_bLockedByThisGuard = sal_False; + + if (m_rCacheLock < 0) + { + OSL_ENSURE(sal_False, "Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)"); + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("Wrong using of member m_nDocCacheLock detected. A ref counted value shouldn't reach values <0 .-)"), + m_xOwner); + } + aWriteLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +DispatchParams::DispatchParams() + : m_nWorkingEntryID(-1) +{ +}; + +//----------------------------------------------- +DispatchParams::DispatchParams(const ::comphelper::SequenceAsHashMap& lArgs , + const css::uno::Reference< css::uno::XInterface >& xOwner) +{ + m_nWorkingEntryID = lArgs.getUnpackedValueOrDefault(PROP_ENTRY_ID, (sal_Int32)-1 ); + m_xProgress = lArgs.getUnpackedValueOrDefault(PROP_PROGRESS, css::uno::Reference< css::task::XStatusIndicator >()); + m_sSavePath = lArgs.getUnpackedValueOrDefault(PROP_SAVEPATH, ::rtl::OUString() ); + m_xHoldRefForAsyncOpAlive = xOwner; +}; + +//----------------------------------------------- +DispatchParams::DispatchParams(const DispatchParams& rCopy) +{ + m_xProgress = rCopy.m_xProgress; + m_sSavePath = rCopy.m_sSavePath; + m_nWorkingEntryID = rCopy.m_nWorkingEntryID; + m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive; +}; + +//----------------------------------------------- +DispatchParams::~DispatchParams() +{}; + +//----------------------------------------------- +DispatchParams& DispatchParams::operator=(const DispatchParams& rCopy) +{ + m_xProgress = rCopy.m_xProgress; + m_sSavePath = rCopy.m_sSavePath; + m_nWorkingEntryID = rCopy.m_nWorkingEntryID; + m_xHoldRefForAsyncOpAlive = rCopy.m_xHoldRefForAsyncOpAlive; + return *this; +} + +//----------------------------------------------- +void DispatchParams::forget() +{ + m_sSavePath = ::rtl::OUString(); + m_nWorkingEntryID = -1; + m_xProgress.clear(); + m_xHoldRefForAsyncOpAlive.clear(); +}; + +//----------------------------------------------- +DEFINE_XINTERFACE_1(DbgListener , + OWeakObject , + DIRECT_INTERFACE(css::frame::XStatusListener)) + +//----------------------------------------------- +DEFINE_XINTERFACE_10(AutoRecovery , + OWeakObject , + DIRECT_INTERFACE (css::lang::XTypeProvider ), + DIRECT_INTERFACE (css::lang::XServiceInfo ), + DIRECT_INTERFACE (css::frame::XDispatch ), + DIRECT_INTERFACE (css::beans::XMultiPropertySet ), + DIRECT_INTERFACE (css::beans::XFastPropertySet ), + DIRECT_INTERFACE (css::beans::XPropertySet ), + DIRECT_INTERFACE (css::document::XEventListener ), + DIRECT_INTERFACE (css::util::XChangesListener ), + DIRECT_INTERFACE (css::util::XModifyListener ), + DERIVED_INTERFACE(css::lang::XEventListener, css::document::XEventListener)) + +//----------------------------------------------- +DEFINE_XTYPEPROVIDER_6(AutoRecovery , + css::lang::XTypeProvider , + css::lang::XServiceInfo , + css::frame::XDispatch , + css::beans::XMultiPropertySet, + css::beans::XFastPropertySet , + css::beans::XPropertySet ) + +//----------------------------------------------- +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE(AutoRecovery , + ::cppu::OWeakObject , + SERVICENAME_AUTORECOVERY , + IMPLEMENTATIONNAME_AUTORECOVERY) + +//----------------------------------------------- +DEFINE_INIT_SERVICE( + AutoRecovery, + { + /*Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + + // read configuration to know if autosave/recovery is on/off etcpp... + implts_readConfig(); + + implts_startListening(); + + // establish callback for our internal used timer. + // Note: Its only active, if the timer will be started ... + m_aTimer.SetTimeoutHdl(LINK(this, AutoRecovery, implts_timerExpired)); +/* + DbgListener* pListener = new DbgListener(); + pListener->startListening(this); +*/ + } + ) + +//----------------------------------------------- +AutoRecovery::AutoRecovery(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR) + : ThreadHelpBase (&Application::GetSolarMutex() ) + , ::cppu::OBroadcastHelper ( m_aLock.getShareableOslMutex() ) + , ::cppu::OPropertySetHelper( *(static_cast< ::cppu::OBroadcastHelper* >(this)) ) + , ::cppu::OWeakObject ( ) + , m_xSMGR (xSMGR ) + , m_bListenForDocEvents (sal_False ) + , m_bListenForConfigChanges (sal_False ) + , m_nAutoSaveTimeIntervall (0 ) + , m_eJob (AutoRecovery::E_NO_JOB ) + , m_aAsyncDispatcher ( LINK( this, AutoRecovery, implts_asyncDispatch ) ) + , m_eTimerType (E_DONT_START_TIMER ) + , m_nIdPool (0 ) + , m_lListener (m_aLock.getShareableOslMutex() ) + , m_nDocCacheLock (0 ) + , m_nMinSpaceDocSave (MIN_DISCSPACE_DOCSAVE ) + , m_nMinSpaceConfigSave (MIN_DISCSPACE_CONFIGSAVE ) + + #if OSL_DEBUG_LEVEL > 1 + , m_dbg_bMakeItFaster (sal_False ) + #endif +{ +} + +//----------------------------------------------- +AutoRecovery::~AutoRecovery() +{ + implts_stopTimer(); +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::dispatch(const css::util::URL& aURL , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments) + throw(css::uno::RuntimeException) +{ + LOG_RECOVERY("AutoRecovery::dispatch() starts ...") + LOG_RECOVERY(U2B(aURL.Complete).getStr()) + + // valid request ? + sal_Int32 eNewJob = AutoRecovery::implst_classifyJob(aURL); + if (eNewJob == AutoRecovery::E_NO_JOB) + return; + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + // still running operation ... ignoring AUTO_SAVE. + // All other requests has higher prio! + if ( + ( m_eJob != AutoRecovery::E_NO_JOB ) && + ((m_eJob & AutoRecovery::E_AUTO_SAVE ) != AutoRecovery::E_AUTO_SAVE) + ) + { + LOG_WARNING("AutoRecovery::dispatch()", "There is already an asynchronous dispatch() running. New request will be ignored!") + return; + } + + ::comphelper::SequenceAsHashMap lArgs(lArguments); + + // check if somewhere wish to disable recovery temp. for this office session + // This can be done immediatly ... must not been done asynchronous. + if ((eNewJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY) + { + // it's important to set a flag internaly, so AutoRecovery will be supressed - even if it's requested. + m_eJob |= eNewJob; + implts_stopTimer(); + implts_stopListening(); + return; + } + + // disable/enable AutoSave for this office session only + // independend from the configuration entry. + if ((eNewJob & AutoRecovery::E_SET_AUTOSAVE_STATE) == AutoRecovery::E_SET_AUTOSAVE_STATE) + { + sal_Bool bOn = lArgs.getUnpackedValueOrDefault(PROP_AUTOSAVE_STATE, (sal_Bool)sal_True); + if (bOn) + { + // dont enable AutoSave hardly ! + // reload configuration to know the current state. + implts_readAutoSaveConfig(); + implts_updateTimer(); + // can it happen that might be the listener was stopped ? .-) + // make sure it runs always ... even if AutoSave itself was disabled temporarly. + implts_startListening(); + } + else + { + implts_stopTimer(); + m_eJob &= ~AutoRecovery::E_AUTO_SAVE; + m_eTimerType = AutoRecovery::E_DONT_START_TIMER; + } + return; + } + + m_eJob |= eNewJob; + + sal_Bool bAsync = lArgs.getUnpackedValueOrDefault(PROP_DISPATCH_ASYNCHRON, (sal_Bool)sal_False); + DispatchParams aParams (lArgs, static_cast< css::frame::XDispatch* >(this)); + + // Hold this instance alive till the asynchronous operation will be finished. + if (bAsync) + m_aDispatchParams = aParams; + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + if (bAsync) + m_aAsyncDispatcher.Post(0); + else + implts_dispatch(aParams); +} + +//----------------------------------------------- +void AutoRecovery::implts_dispatch(const DispatchParams& aParams) +{ + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + sal_Int32 eJob = m_eJob; + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + // in case a new dispatch overwrites a may ba active AutoSave session + // we must restore this session later. see below ... + sal_Bool bWasAutoSaveActive = ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE); + + // On the other side it make no sense to reactivate the AutoSave operation + // if the new dispatch indicates a final decision ... + // E.g. an EmergencySave/SessionSave indicates the end of life of the current office session. + // It make no sense to reactivate an AutoSave then. + // But a Recovery or SessionRestore should reactivate a may be already active AutoSave. + sal_Bool bAllowAutoSaveReactivation = sal_True; + + implts_stopTimer(); + implts_stopListening(); + + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_START, NULL)); + + try + { + // if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE) + // Auto save is called from our internal timer ... not via dispatch() API ! + // else + if ( + ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE) && + ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY ) != AutoRecovery::E_DISABLE_AUTORECOVERY ) + ) + { + LOG_RECOVERY("... prepare emergency save ...") + bAllowAutoSaveReactivation = sal_False; + implts_prepareEmergencySave(); + } + else + if ( + ((eJob & AutoRecovery::E_EMERGENCY_SAVE ) == AutoRecovery::E_EMERGENCY_SAVE ) && + ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) + ) + { + LOG_RECOVERY("... do emergency save ...") + bAllowAutoSaveReactivation = sal_False; + implts_doEmergencySave(aParams); + } + else + if ( + ((eJob & AutoRecovery::E_RECOVERY ) == AutoRecovery::E_RECOVERY ) && + ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) + ) + { + LOG_RECOVERY("... do recovery ...") + implts_doRecovery(aParams); + } + else + if ( + ((eJob & AutoRecovery::E_SESSION_SAVE ) == AutoRecovery::E_SESSION_SAVE ) && + ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) + ) + { + LOG_RECOVERY("... do session save ...") + bAllowAutoSaveReactivation = sal_False; + implts_doSessionSave(aParams); + } + else + if ( + ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT ) == AutoRecovery::E_SESSION_QUIET_QUIT ) && + ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) + ) + { + LOG_RECOVERY("... do session quiet quit ...") + bAllowAutoSaveReactivation = sal_False; + implts_doSessionQuietQuit(aParams); + } + else + if ( + ((eJob & AutoRecovery::E_SESSION_RESTORE ) == AutoRecovery::E_SESSION_RESTORE ) && + ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) + ) + { + LOG_RECOVERY("... do session restore ...") + implts_doSessionRestore(aParams); + } + else + if ( + ((eJob & AutoRecovery::E_ENTRY_BACKUP ) == AutoRecovery::E_ENTRY_BACKUP ) && + ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) + ) + implts_backupWorkingEntry(aParams); + else + if ( + ((eJob & AutoRecovery::E_ENTRY_CLEANUP ) == AutoRecovery::E_ENTRY_CLEANUP ) && + ((eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) != AutoRecovery::E_DISABLE_AUTORECOVERY) + ) + implts_cleanUpWorkingEntry(aParams); + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::uno::Exception&) + {} // TODO better error handling + + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_STOP, NULL)); + + // SAFE -> ---------------------------------- + aWriteLock.lock(); + m_eJob = E_NO_JOB; + if ( + (bAllowAutoSaveReactivation) && + (bWasAutoSaveActive ) + ) + { + m_eJob |= AutoRecovery::E_AUTO_SAVE; + } + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + // depends on bAllowAutoSaveReactivation implicitly by looking on m_eJob=E_AUTO_SAVE! see before ... + implts_updateTimer(); + + if (bAllowAutoSaveReactivation) + implts_startListening(); +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::addStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener, + const css::util::URL& aURL ) + throw(css::uno::RuntimeException) +{ + if (!xListener.is()) + throw css::uno::RuntimeException(::rtl::OUString::createFromAscii("Invalid listener reference."), static_cast< css::frame::XDispatch* >(this)); + // container is threadsafe by using a shared mutex! + m_lListener.addInterface(aURL.Complete, xListener); + + // REENTRANT !? -> -------------------------------- + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // THREAD SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + + AutoRecovery::TDocumentList::iterator pIt; + for( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end() ; + ++pIt ) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + css::frame::FeatureStateEvent aEvent = AutoRecovery::implst_createFeatureStateEvent(m_eJob, OPERATION_UPDATE, &rInfo); + + // <- SAFE ------------------------------ + aReadLock.unlock(); + xListener->statusChanged(aEvent); + aReadLock.lock(); + // SAFE -> ------------------------------ + } + + aReadLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::removeStatusListener(const css::uno::Reference< css::frame::XStatusListener >& xListener, + const css::util::URL& aURL ) + throw(css::uno::RuntimeException) +{ + if (!xListener.is()) + throw css::uno::RuntimeException(::rtl::OUString::createFromAscii("Invalid listener reference."), static_cast< css::frame::XDispatch* >(this)); + // container is threadsafe by using a shared mutex! + m_lListener.removeInterface(aURL.Complete, xListener); +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::notifyEvent(const css::document::EventObject& aEvent) + throw(css::uno::RuntimeException) +{ + css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY); + + // new document => put it into the internal list + if ( + (aEvent.EventName.equals(EVENT_ON_NEW )) || + (aEvent.EventName.equals(EVENT_ON_LOAD)) + ) + { + implts_registerDocument(xDocument); + } + // document modified => set its modify state new (means modified against the original file!) + else + if (aEvent.EventName.equals(EVENT_ON_MODIFYCHANGED)) + { + implts_updateModifiedState(xDocument); + } + /* at least one document starts saving process => + Our application code isnt ready for multiple save requests + at the same time. So we have to supress our AutoSave feature + for the moment, till this other save requests will be finished. + */ + else + if ( + (aEvent.EventName.equals(EVENT_ON_SAVE )) || + (aEvent.EventName.equals(EVENT_ON_SAVEAS)) || + (aEvent.EventName.equals(EVENT_ON_SAVETO)) + ) + { + implts_updateDocumentUsedForSavingState(xDocument, SAVE_IN_PROGRESS); + } + // document saved => remove tmp. files - but hold config entries alive! + else + if ( + (aEvent.EventName.equals(EVENT_ON_SAVEDONE )) || + (aEvent.EventName.equals(EVENT_ON_SAVEASDONE)) + ) + { + implts_markDocumentAsSaved(xDocument); + implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); + } + /* document saved as copy => mark it as "non used by concurrent save operation". + so we can try to create a backup copy if next time AutoSave is started too. + Dont remove temp. files or change the modified state of the document! + It was not realy saved to the original file ... + */ + else + if (aEvent.EventName.equals(EVENT_ON_SAVETODONE)) + { + implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); + } + // If saving of a document failed by an error ... we have to save this document + // by ourself next time AutoSave or EmergencySave is triggered. + // But we can reset the state "used for other save requests". Otherwhise + // these documents will never be saved! + else + if ( + (aEvent.EventName.equals(EVENT_ON_SAVEFAILED )) || + (aEvent.EventName.equals(EVENT_ON_SAVEASFAILED)) || + (aEvent.EventName.equals(EVENT_ON_SAVETOFAILED)) + ) + { + implts_updateDocumentUsedForSavingState(xDocument, SAVE_FINISHED); + } + // document closed => remove temp. files and configuration entries + else + if (aEvent.EventName.equals(EVENT_ON_UNLOAD)) + { + implts_deregisterDocument(xDocument, sal_True); // TRUE => stop listening for disposing() ! + } +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::changesOccurred(const css::util::ChangesEvent& aEvent) + throw(css::uno::RuntimeException) +{ + const css::uno::Sequence< css::util::ElementChange > lChanges (aEvent.Changes); + const css::util::ElementChange* pChanges = lChanges.getConstArray(); + + sal_Int32 c = lChanges.getLength(); + sal_Int32 i = 0; + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + // Changes of the configuration must be ignored if AutoSave/Recovery was disabled for this + // office session. That can happen if e.g. the command line arguments "-norestore" or "-headless" + // was set. + if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY) + return; + + for (i=0; i<c; ++i) + { + ::rtl::OUString sPath; + pChanges[i].Accessor >>= sPath; + + if (sPath.equals(CFG_ENTRY_AUTOSAVE_ENABLED)) + { + sal_Bool bEnabled = sal_False; + if (pChanges[i].Element >>= bEnabled) + { + if (bEnabled) + { + m_eJob |= AutoRecovery::E_AUTO_SAVE; + m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; + } + else + { + m_eJob &= ~AutoRecovery::E_AUTO_SAVE; + m_eTimerType = AutoRecovery::E_DONT_START_TIMER; + } + } + } + else + if (sPath.equals(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL)) + pChanges[i].Element >>= m_nAutoSaveTimeIntervall; + } + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + // Note: This call stops the timer and starts it again. + // But it checks the different timer states internaly and + // may be supress the restart! + implts_updateTimer(); +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::modified(const css::lang::EventObject& aEvent) + throw(css::uno::RuntimeException) +{ + css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY); + if (! xDocument.is()) + return; + + implts_markDocumentModifiedAgainstLastBackup(xDocument); +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::disposing(const css::lang::EventObject& aEvent) + throw(css::uno::RuntimeException) +{ + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + if (aEvent.Source == m_xNewDocBroadcaster) + { + m_xNewDocBroadcaster.clear(); + return; + } + + if (aEvent.Source == m_xRecoveryCFG) + { + m_xRecoveryCFG.clear(); + return; + } + + // dispose from one of our cached documents ? + // Normaly they should send a OnUnload message ... + // But some stacktraces shows another possible use case .-) + css::uno::Reference< css::frame::XModel > xDocument(aEvent.Source, css::uno::UNO_QUERY); + if (xDocument.is()) + { + implts_deregisterDocument(xDocument, sal_False); // FALSE => dont call removeEventListener() .. because it's not needed here + return; + } + + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +css::uno::Reference< css::container::XNameAccess > AutoRecovery::implts_openConfig() +{ + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + if (m_xRecoveryCFG.is()) + return m_xRecoveryCFG; + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + // throws a RuntimeException if an error occure! + css::uno::Reference< css::container::XNameAccess > xCFG( + ::comphelper::ConfigurationHelper::openConfig(xSMGR, CFG_PACKAGE_RECOVERY, ::comphelper::ConfigurationHelper::E_STANDARD), + css::uno::UNO_QUERY); + + sal_Int32 nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE; + sal_Int32 nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE; + + try + { + ::comphelper::ConfigurationHelper::readDirectKey(xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_AUTOSAVE, + CFG_ENTRY_MINSPACE_DOCSAVE, + ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceDocSave; + + ::comphelper::ConfigurationHelper::readDirectKey(xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_AUTOSAVE, + CFG_ENTRY_MINSPACE_CONFIGSAVE, + ::comphelper::ConfigurationHelper::E_STANDARD) >>= nMinSpaceConfigSave; + } + catch(const css::uno::Exception&) + { + // These config keys are not sooooo important, that + // we are interested on errors here realy .-) + nMinSpaceDocSave = MIN_DISCSPACE_DOCSAVE; + nMinSpaceConfigSave = MIN_DISCSPACE_CONFIGSAVE; + } + + // SAFE -> ---------------------------------- + aWriteLock.lock(); + m_xRecoveryCFG = xCFG; + m_nMinSpaceDocSave = nMinSpaceDocSave; + m_nMinSpaceConfigSave = nMinSpaceConfigSave; + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + return xCFG; +} + +//----------------------------------------------- +void AutoRecovery::implts_readAutoSaveConfig() +{ + css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY); + + // AutoSave [bool] + sal_Bool bEnabled = sal_False; + xCommonRegistry->getByHierarchicalName(CFG_ENTRY_AUTOSAVE_ENABLED) >>= bEnabled; + + // SAFE -> ------------------------------ + WriteGuard aWriteLock(m_aLock); + if (bEnabled) + { + m_eJob |= AutoRecovery::E_AUTO_SAVE; + m_eTimerType = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; + } + else + { + m_eJob &= ~AutoRecovery::E_AUTO_SAVE; + m_eTimerType = AutoRecovery::E_DONT_START_TIMER; + } + aWriteLock.unlock(); + // <- SAFE ------------------------------ + + // AutoSaveTimeIntervall [int] in min + sal_Int32 nTimeIntervall = 15; + xCommonRegistry->getByHierarchicalName(CFG_ENTRY_AUTOSAVE_TIMEINTERVALL) >>= nTimeIntervall; + + // SAFE -> ---------------------------------- + aWriteLock.lock(); + m_nAutoSaveTimeIntervall = nTimeIntervall; + aWriteLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +void AutoRecovery::implts_readConfig() +{ + implts_readAutoSaveConfig(); + + css::uno::Reference< css::container::XHierarchicalNameAccess > xCommonRegistry(implts_openConfig(), css::uno::UNO_QUERY); + + // REENTRANT -> -------------------------------- + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); + + // THREADSAFE -> ------------------------------- + WriteGuard aWriteLock(m_aLock); + // reset current cache load cache + m_lDocCache.clear(); + m_nIdPool = 0; + aWriteLock.unlock(); + // <- THREADSAFE ------------------------------- + + aCacheLock.unlock(); + // <- REENTRANT -------------------------------- + + css::uno::Any aValue; + + // RecoveryList [set] + aValue = xCommonRegistry->getByHierarchicalName(CFG_ENTRY_RECOVERYLIST); + css::uno::Reference< css::container::XNameAccess > xList; + aValue >>= xList; + if (xList.is()) + { + const css::uno::Sequence< ::rtl::OUString > lItems = xList->getElementNames(); + const ::rtl::OUString* pItems = lItems.getConstArray(); + sal_Int32 c = lItems.getLength(); + sal_Int32 i = 0; + + // REENTRANT -> -------------------------- + aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE); + + for (i=0; i<c; ++i) + { + css::uno::Reference< css::beans::XPropertySet > xItem; + xList->getByName(pItems[i]) >>= xItem; + if (!xItem.is()) + continue; + + AutoRecovery::TDocumentInfo aInfo; + aInfo.NewTempURL = ::rtl::OUString(); + aInfo.Document = css::uno::Reference< css::frame::XModel >(); + xItem->getPropertyValue(CFG_ENTRY_PROP_ORIGINALURL ) >>= aInfo.OrgURL ; + xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPURL ) >>= aInfo.OldTempURL ; + xItem->getPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL ) >>= aInfo.TemplateURL ; + xItem->getPropertyValue(CFG_ENTRY_PROP_FILTER ) >>= aInfo.RealFilter ; + xItem->getPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE) >>= aInfo.DocumentState; + xItem->getPropertyValue(CFG_ENTRY_PROP_MODULE ) >>= aInfo.AppModule ; + xItem->getPropertyValue(CFG_ENTRY_PROP_TITLE ) >>= aInfo.Title ; + xItem->getPropertyValue(CFG_ENTRY_PROP_VIEWNAMES ) >>= aInfo.ViewNames ; + implts_specifyAppModuleAndFactory(aInfo); + implts_specifyDefaultFilterAndExtension(aInfo); + + if (pItems[i].indexOf(RECOVERY_ITEM_BASE_IDENTIFIER)==0) + { + ::rtl::OUString sID = pItems[i].copy(RECOVERY_ITEM_BASE_IDENTIFIER.getLength()); + aInfo.ID = sID.toInt32(); + // SAFE -> ---------------------- + aWriteLock.lock(); + if (aInfo.ID > m_nIdPool) + { + m_nIdPool = aInfo.ID+1; + LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_readConfig()\nOverflow of IDPool detected!") + } + aWriteLock.unlock(); + // <- SAFE ---------------------- + } + #ifdef ENABLE_WARNINGS + else + 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 .-)") + #endif + + // THREADSAFE -> -------------------------- + aWriteLock.lock(); + m_lDocCache.push_back(aInfo); + aWriteLock.unlock(); + // <- THREADSAFE -------------------------- + } + + aCacheLock.unlock(); + // <- REENTRANT -------------------------- + } + + implts_updateTimer(); +} + +//----------------------------------------------- +void AutoRecovery::implts_specifyDefaultFilterAndExtension(AutoRecovery::TDocumentInfo& rInfo) +{ + if (!rInfo.AppModule.getLength()) + { + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("Cant find out the default filter and its extension, if no application module is known!"), + static_cast< css::frame::XDispatch* >(this)); + } + + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + css::uno::Reference< css::container::XNameAccess> xCFG = m_xModuleCFG; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + try + { + if (! xCFG.is()) + { + // open module config on demand and cache the update access + xCFG = css::uno::Reference< css::container::XNameAccess >( + ::comphelper::ConfigurationHelper::openConfig(xSMGR, CFG_PACKAGE_MODULES, ::comphelper::ConfigurationHelper::E_STANDARD), + css::uno::UNO_QUERY_THROW); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + m_xModuleCFG = xCFG; + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + } + + css::uno::Reference< css::container::XNameAccess > xModuleProps( + xCFG->getByName(rInfo.AppModule), + css::uno::UNO_QUERY_THROW); + + xModuleProps->getByName(CFG_ENTRY_REALDEFAULTFILTER) >>= rInfo.DefaultFilter; + + css::uno::Reference< css::container::XNameAccess > xFilterCFG(xSMGR->createInstance(SERVICENAME_FILTERFACTORY), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameAccess > xTypeCFG (xSMGR->createInstance(SERVICENAME_TYPEDETECTION), css::uno::UNO_QUERY_THROW); + + ::comphelper::SequenceAsHashMap lFilterProps (xFilterCFG->getByName(rInfo.DefaultFilter)); + ::rtl::OUString sTypeRegistration = lFilterProps.getUnpackedValueOrDefault(FILTER_PROP_TYPE, ::rtl::OUString()); + ::comphelper::SequenceAsHashMap lTypeProps (xTypeCFG->getByName(sTypeRegistration)); + css::uno::Sequence< ::rtl::OUString > lExtensions = lTypeProps.getUnpackedValueOrDefault(TYPE_PROP_EXTENSIONS, css::uno::Sequence< ::rtl::OUString >()); + if (lExtensions.getLength()) + { + rInfo.Extension = ::rtl::OUString::createFromAscii("."); + rInfo.Extension += lExtensions[0]; + } + else + rInfo.Extension = ::rtl::OUString::createFromAscii(".unknown"); + } + catch(const css::uno::Exception&) + { + rInfo.DefaultFilter = ::rtl::OUString(); + rInfo.Extension = ::rtl::OUString(); + } +} + +//----------------------------------------------- +void AutoRecovery::implts_specifyAppModuleAndFactory(AutoRecovery::TDocumentInfo& rInfo) +{ + ENSURE_OR_THROW2( + rInfo.AppModule.getLength() || rInfo.Document.is(), + "Cant find out the application module nor its factory URL, if no application module (or a suitable) document is known!", + *this ); + + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + css::uno::Reference< css::frame::XModuleManager > xManager (xSMGR->createInstance(SERVICENAME_MODULEMANAGER), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameAccess > xModuleConfig(xManager , css::uno::UNO_QUERY_THROW); + + if (!rInfo.AppModule.getLength()) + rInfo.AppModule = xManager->identify(rInfo.Document); + + ::comphelper::SequenceAsHashMap lModuleDescription(xModuleConfig->getByName(rInfo.AppModule)); + lModuleDescription[CFG_ENTRY_PROP_EMPTYDOCUMENTURL] >>= rInfo.FactoryURL; + lModuleDescription[CFG_ENTRY_PROP_FACTORYSERVICE] >>= rInfo.FactoryService; +} + +//----------------------------------------------- +void AutoRecovery::implts_collectActiveViewNames( AutoRecovery::TDocumentInfo& i_rInfo ) +{ + ENSURE_OR_THROW2( i_rInfo.Document.is(), "need at document, at the very least", *this ); + + i_rInfo.ViewNames.realloc(0); + + // obtain list of controllers of this document + ::std::vector< ::rtl::OUString > aViewNames; + const Reference< XModel2 > xModel( i_rInfo.Document, UNO_QUERY ); + if ( xModel.is() ) + { + const Reference< XEnumeration > xEnumControllers( xModel->getControllers() ); + while ( xEnumControllers->hasMoreElements() ) + { + const Reference< XController2 > xController( xEnumControllers->nextElement(), UNO_QUERY ); + ::rtl::OUString sViewName; + if ( xController.is() ) + sViewName = xController->getViewControllerName(); + OSL_ENSURE( sViewName.getLength(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" ); + + if ( sViewName.getLength() ) + aViewNames.push_back( sViewName ); + } + } + else + { + const Reference< XController2 > xController( xModel->getCurrentController(), UNO_QUERY ); + ::rtl::OUString sViewName; + if ( xController.is() ) + sViewName = xController->getViewControllerName(); + OSL_ENSURE( sViewName.getLength(), "AutoRecovery::implts_collectActiveViewNames: (no XController2 ->) no view name -> no recovery of this view!" ); + + if ( sViewName.getLength() ) + aViewNames.push_back( sViewName ); + } + + i_rInfo.ViewNames.realloc( aViewNames.size() ); + ::std::copy( aViewNames.begin(), aViewNames.end(), i_rInfo.ViewNames.getArray() ); +} + +//----------------------------------------------- +void AutoRecovery::implts_persistAllActiveViewNames() +{ + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + // This list will be filled with every document + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end() ; + ++pIt ) + { + implts_collectActiveViewNames( *pIt ); + implts_flushConfigItem( *pIt ); + } +} + +//----------------------------------------------- +void AutoRecovery::implts_flushConfigItem(const AutoRecovery::TDocumentInfo& rInfo, sal_Bool bRemoveIt) +{ + css::uno::Reference< css::container::XHierarchicalNameAccess > xCFG; + + try + { + xCFG = css::uno::Reference< css::container::XHierarchicalNameAccess >(implts_openConfig(), css::uno::UNO_QUERY_THROW); + + css::uno::Reference< css::container::XNameAccess > xCheck; + xCFG->getByHierarchicalName(CFG_ENTRY_RECOVERYLIST) >>= xCheck; + + css::uno::Reference< css::container::XNameContainer > xModify(xCheck, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::lang::XSingleServiceFactory > xCreate(xCheck, css::uno::UNO_QUERY_THROW); + + ::rtl::OUStringBuffer sIDBuf; + sIDBuf.append(RECOVERY_ITEM_BASE_IDENTIFIER); + sIDBuf.append((sal_Int32)rInfo.ID); + ::rtl::OUString sID = sIDBuf.makeStringAndClear(); + + // remove + if (bRemoveIt) + { + // Catch NoSuchElementException. + // Its not a good idea inside multithreaded environments to call hasElement - removeElement. + // DO IT! + try + { + xModify->removeByName(sID); + } + catch(const css::container::NoSuchElementException&) + { return; } + } + else + { + // new/modify + css::uno::Reference< css::beans::XPropertySet > xSet; + sal_Bool bNew = (!xCheck->hasByName(sID)); + if (bNew) + xSet = css::uno::Reference< css::beans::XPropertySet >(xCreate->createInstance(), css::uno::UNO_QUERY_THROW); + else + xCheck->getByName(sID) >>= xSet; + + xSet->setPropertyValue(CFG_ENTRY_PROP_ORIGINALURL , css::uno::makeAny(rInfo.OrgURL )); + xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPURL , css::uno::makeAny(rInfo.OldTempURL )); + xSet->setPropertyValue(CFG_ENTRY_PROP_TEMPLATEURL , css::uno::makeAny(rInfo.TemplateURL )); + xSet->setPropertyValue(CFG_ENTRY_PROP_FILTER , css::uno::makeAny(rInfo.RealFilter )); + xSet->setPropertyValue(CFG_ENTRY_PROP_DOCUMENTSTATE, css::uno::makeAny(rInfo.DocumentState)); + xSet->setPropertyValue(CFG_ENTRY_PROP_MODULE , css::uno::makeAny(rInfo.AppModule )); + xSet->setPropertyValue(CFG_ENTRY_PROP_TITLE , css::uno::makeAny(rInfo.Title )); + xSet->setPropertyValue(CFG_ENTRY_PROP_VIEWNAMES , css::uno::makeAny(rInfo.ViewNames )); + + if (bNew) + xModify->insertByName(sID, css::uno::makeAny(xSet)); + } + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::uno::Exception&) + {} // ??? can it happen that a full disc let these set of operations fail too ??? + + sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER; + do + { + try + { + css::uno::Reference< css::util::XChangesBatch > xFlush(xCFG, css::uno::UNO_QUERY_THROW); + xFlush->commitChanges(); + + #ifdef TRIGGER_FULL_DISC_CHECK + throw css::uno::Exception(); + #endif + + nRetry = 0; + } + catch(const css::uno::Exception& ex) + { + // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300) + // b) unknown problem (may be locking problem) => reset RETRY value to more usefull value(!) (e.g. retry=3) + // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace ! + + // SAFE -> + ReadGuard aReadLock(m_aLock); + sal_Int32 nMinSpaceConfigSave = m_nMinSpaceConfigSave; + aReadLock.unlock(); + // <- SAFE + + if (! impl_enoughDiscSpace(nMinSpaceConfigSave)) + AutoRecovery::impl_showFullDiscError(); + else + if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL) + nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL; + else + if (nRetry <= GIVE_UP_RETRY) + throw ex; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!! + + --nRetry; + } + } + while(nRetry>0); +} + +//----------------------------------------------- +void AutoRecovery::implts_startListening() +{ + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG, css::uno::UNO_QUERY); + css::uno::Reference< css::document::XEventBroadcaster > xBroadcaster = m_xNewDocBroadcaster; + sal_Bool bListenForDocEvents = m_bListenForDocEvents; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + if ( + ( xCFG.is() ) && + (! m_bListenForConfigChanges) + ) + { + xCFG->addChangesListener(static_cast< css::util::XChangesListener* >(this)); + m_bListenForConfigChanges = sal_True; + } + + if (!xBroadcaster.is()) + { + xBroadcaster = css::uno::Reference< css::document::XEventBroadcaster >(xSMGR->createInstance(SERVICENAME_GLOBALEVENTBROADCASTER), css::uno::UNO_QUERY_THROW); + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + m_xNewDocBroadcaster = xBroadcaster; + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + } + + if ( + ( xBroadcaster.is() ) && + (! bListenForDocEvents) + ) + { + xBroadcaster->addEventListener(static_cast< css::document::XEventListener* >(this)); + // SAFE -> + WriteGuard aWriteLock(m_aLock); + m_bListenForDocEvents = sal_True; + aWriteLock.unlock(); + // <- SAFE + } +} + +//----------------------------------------------- +void AutoRecovery::implts_stopListening() +{ + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + // Attention: Dont reset our internal members here too. + // May be we must work with our configuration, but dont wish to be informed + // about changes any longer. Needed e.g. during EMERGENCY_SAVE! + css::uno::Reference< css::util::XChangesNotifier > xCFG (m_xRecoveryCFG , css::uno::UNO_QUERY); + css::uno::Reference< css::document::XEventBroadcaster > xGlobalEventBroadcaster(m_xNewDocBroadcaster, css::uno::UNO_QUERY); + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + if ( + (xGlobalEventBroadcaster.is()) && + (m_bListenForDocEvents ) + ) + { + xGlobalEventBroadcaster->removeEventListener(static_cast< css::document::XEventListener* >(this)); + m_bListenForDocEvents = sal_False; + } + + if ( + (xCFG.is() ) && + (m_bListenForConfigChanges) + ) + { + xCFG->removeChangesListener(static_cast< css::util::XChangesListener* >(this)); + m_bListenForConfigChanges = sal_False; + } +} + +//----------------------------------------------- +void AutoRecovery::implts_startModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo) +{ + if (rInfo.ListenForModify) + return; + + css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY); + if (xBroadcaster.is()) + { + css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY); + xBroadcaster->addModifyListener(xThis); + rInfo.ListenForModify = sal_True; + } +} + +//----------------------------------------------- +void AutoRecovery::implts_stopModifyListeningOnDoc(AutoRecovery::TDocumentInfo& rInfo) +{ + if (! rInfo.ListenForModify) + return; + + css::uno::Reference< css::util::XModifyBroadcaster > xBroadcaster(rInfo.Document, css::uno::UNO_QUERY); + if (xBroadcaster.is()) + { + css::uno::Reference< css::util::XModifyListener > xThis(static_cast< css::frame::XDispatch* >(this), css::uno::UNO_QUERY); + xBroadcaster->removeModifyListener(xThis); + rInfo.ListenForModify = sal_False; + } +} + +//----------------------------------------------- +void AutoRecovery::implts_updateTimer() +{ + implts_stopTimer(); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + if ( + (m_eJob == AutoRecovery::E_NO_JOB ) || // TODO may be superflous - E_DONT_START_TIMER should be used only + (m_eTimerType == AutoRecovery::E_DONT_START_TIMER) + ) + return; + + ULONG nMilliSeconds = 0; + if (m_eTimerType == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL) + { + nMilliSeconds = (m_nAutoSaveTimeIntervall*60000); // [min] => 60.000 ms + #if OSL_DEBUG_LEVEL > 1 + if (m_dbg_bMakeItFaster) + nMilliSeconds = m_nAutoSaveTimeIntervall; // [ms] + #endif + } + else + if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE) + { + nMilliSeconds = MIN_TIME_FOR_USER_IDLE; + #if OSL_DEBUG_LEVEL > 1 + if (m_dbg_bMakeItFaster) + nMilliSeconds = 300; // let us some time, to finish this method .-) + #endif + } + else + if (m_eTimerType == AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED) + nMilliSeconds = 300; // there is a minimum time frame, where the user can loose some key input data! + + m_aTimer.SetTimeout(nMilliSeconds); + m_aTimer.Start(); + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +void AutoRecovery::implts_stopTimer() +{ + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + if (!m_aTimer.IsActive()) + return; + m_aTimer.Stop(); + + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +IMPL_LINK(AutoRecovery, implts_timerExpired, void*, EMPTYARG) +{ + try + { + // This method is called by using a pointer to us. + // But we must be aware that we can be destroyed hardly + // if our uno reference will be gone! + // => Hold this object alive till this method finish its work. + css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XTypeProvider* >(this)); + + // Needed! Otherwise every reschedule request allow a new triggered timer event :-( + implts_stopTimer(); + + // The timer must be ignored if AutoSave/Recovery was disabled for this + // office session. That can happen if e.g. the command line arguments "-norestore" or "-headless" + // was set. But normaly the timer was disabled if recovery was disabled ... + // But so we are more "safe" .-) + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + if ((m_eJob & AutoRecovery::E_DISABLE_AUTORECOVERY) == AutoRecovery::E_DISABLE_AUTORECOVERY) + return 0; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + // check some "states", where its not allowed (better: not a good idea) to + // start an AutoSave. (e.g. if the user makes drag & drop ...) + // Then we poll till this "disallowed" state is gone. + sal_Bool bAutoSaveNotAllowed = Application::IsUICaptured(); + if (bAutoSaveNotAllowed) + { + // SAFE -> ------------------------------ + WriteGuard aWriteLock(m_aLock); + m_eTimerType = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED; + aWriteLock.unlock(); + // <- SAFE ------------------------------ + implts_updateTimer(); + return 0; + } + + // analyze timer type. + // If we poll for an user idle period, may be we must + // do nothing here and start the timer again. + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + if (m_eTimerType == AutoRecovery::E_POLL_FOR_USER_IDLE) + { + sal_Bool bUserIdle = (Application::GetLastInputInterval()>MIN_TIME_FOR_USER_IDLE); + if (!bUserIdle) + { + implts_updateTimer(); + return 0; + } + } + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + implts_informListener(AutoRecovery::E_AUTO_SAVE, + AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_START, NULL)); + + // force save of all currently open documents + // The called method returns an info, if and how this + // timer must be restarted. + sal_Bool bAllowUserIdleLoop = sal_True; + AutoRecovery::ETimerType eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False); + + // If timer isnt used for "short callbacks" (means polling + // for special states) ... reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) AutoSave session. + // Of course NEXT AutoSave session must be started without + // any "handle" state ... + if ( + (eSuggestedTimer == AutoRecovery::E_DONT_START_TIMER ) || + (eSuggestedTimer == AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL) + ) + { + implts_resetHandleStates(sal_False); + } + + implts_informListener(AutoRecovery::E_AUTO_SAVE, + AutoRecovery::implst_createFeatureStateEvent(AutoRecovery::E_AUTO_SAVE, OPERATION_STOP, NULL)); + + // restart timer - because it was disabled before ... + // SAFE -> ---------------------------------- + aWriteLock.lock(); + m_eTimerType = eSuggestedTimer; + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + implts_updateTimer(); + } + catch(const css::uno::Exception&) + { + LOG_ASSERT(sal_False, "May be you found the reason for bug #125528#. Please report a test scenario to the right developer. THX."); + } + + return 0; +} + +//----------------------------------------------- +IMPL_LINK(AutoRecovery, implts_asyncDispatch, void*, EMPTYARG) +{ + // SAFE -> + WriteGuard aWriteLock(m_aLock); + DispatchParams aParams = m_aDispatchParams; + css::uno::Reference< css::uno::XInterface > xHoldRefForMethodAlive = aParams.m_xHoldRefForAsyncOpAlive; + m_aDispatchParams.forget(); // clears all members ... including the ref-hold object .-) + aWriteLock.unlock(); + // <- SAFE + + implts_dispatch(aParams); + return 0; +} + +//----------------------------------------------- +void AutoRecovery::implts_registerDocument(const css::uno::Reference< css::frame::XModel >& xDocument) +{ + // ignore corrupted events, where no document is given ... Runtime Error ?! + if (!xDocument.is()) + return; + + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // notification for already existing document ! + // Can happen if events came in asynchronous on recovery time. + // Then our cache was filled from the configuration ... but now we get some + // asynchronous events from the global event broadcaster. We must be sure that + // we dont add the same document more then once. + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt != m_lDocCache.end()) + { + // Normaly nothing must be done for this "late" notification. + // But may be the modified state was changed inbetween. + // Check it ... + implts_updateModifiedState(xDocument); + return; + } + + aCacheLock.unlock(); + + ::comphelper::MediaDescriptor lDescriptor(xDocument->getArgs()); + + // check if this document must be ignored for recovery ! + // Some use cases dont wish support for AutoSave/Recovery ... as e.g. OLE-Server / ActiveX Control etcpp. + sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False)); + if (bNoAutoSave) + return; + + // Check if doc is well known on the desktop. Otherwhise ignore it! + // Other frames mostly are used from external programs - e.g. the bean ... + css::uno::Reference< css::frame::XController > xController = xDocument->getCurrentController(); + if (!xController.is()) + return; + + css::uno::Reference< css::frame::XFrame > xFrame = xController->getFrame(); + css::uno::Reference< css::frame::XDesktop > xDesktop (xFrame->getCreator(), css::uno::UNO_QUERY); + if (!xDesktop.is()) + return; + + // if the document doesn't support the XDocumentRecovery interface, we're not interested in it. + Reference< XDocumentRecovery > xDocRecovery( xDocument, UNO_QUERY ); + if ( !xDocRecovery.is() ) + return; + + // get all needed informations of this document + // We need it to update our cache or to locate already existing elements there! + AutoRecovery::TDocumentInfo aNew; + aNew.Document = xDocument; + + // TODO replace getLocation() with getURL() ... its a workaround currently only! + css::uno::Reference< css::frame::XStorable > xDoc(aNew.Document, css::uno::UNO_QUERY_THROW); + aNew.OrgURL = xDoc->getLocation(); + + css::uno::Reference< css::frame::XTitle > xTitle(aNew.Document, css::uno::UNO_QUERY_THROW); + aNew.Title = xTitle->getTitle (); + + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + // classify the used application module, which is used by this document. + implts_specifyAppModuleAndFactory(aNew); + + // Hack! Check for "illegal office documents" ... as e.g. the Basic IDE + // Its not realy a full featured office document. It doesnt provide an URL, any filter, a factory URL etcpp. + // TODO file bug to Basci IDE developers. They must remove the office document API from its service. + if ( + (!aNew.OrgURL.getLength() ) && + (!aNew.FactoryURL.getLength()) + ) + { + OSL_ENSURE( false, "AutoRecovery::implts_registerDocument: this should not happen anymore!" ); + // nowadays, the Basic IDE should already die on the "supports XDocumentRecovery" check. And no other known + // document type fits in here ... + return; + } + + // By the way - get some information about the default format for saving! + // and save an information about the real used filter by this document. + // We save this document with DefaultFilter ... and load it with the RealFilter. + implts_specifyDefaultFilterAndExtension(aNew); + aNew.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME() , ::rtl::OUString()); + + // Further we must know, if this document base on a template. + // Then we must load it in a different way. + css::uno::Reference< css::document::XDocumentPropertiesSupplier > xSupplier(aNew.Document, css::uno::UNO_QUERY); + if (xSupplier.is()) // optional interface! + { + css::uno::Reference< css::document::XDocumentProperties > xDocProps(xSupplier->getDocumentProperties(), css::uno::UNO_QUERY_THROW); + aNew.TemplateURL = xDocProps->getTemplateURL(); + } + + css::uno::Reference< css::util::XModifiable > xModifyCheck(xDocument, css::uno::UNO_QUERY_THROW); + if (xModifyCheck->isModified()) + { + aNew.DocumentState |= AutoRecovery::E_MODIFIED; + } + + aCacheLock.lock(LOCK_FOR_CACHE_ADD_REMOVE); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + // create a new cache entry ... this document isn't known. + ++m_nIdPool; + aNew.ID = m_nIdPool; + LOG_ASSERT(m_nIdPool>=0, "AutoRecovery::implts_registerDocument()\nOverflow of ID pool detected.") + m_lDocCache.push_back(aNew); + + AutoRecovery::TDocumentList::iterator pIt1 = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + AutoRecovery::TDocumentInfo& rInfo = *pIt1; + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + implts_flushConfigItem(rInfo); + implts_startModifyListeningOnDoc(rInfo); + + aCacheLock.unlock(); +} + +//----------------------------------------------- +void AutoRecovery::implts_deregisterDocument(const css::uno::Reference< css::frame::XModel >& xDocument , + sal_Bool bStopListening) +{ + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + // Attention: Dont leave SAFE section, if you work with pIt! + // Because it points directly into the m_lDocCache list ... + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt == m_lDocCache.end()) + return; // unknown document => not a runtime error! Because we register only a few documents. see registration ... + + AutoRecovery::TDocumentInfo aInfo = *pIt; + + aCacheLock.unlock(); + + // Sometimes we close documents by ourself. + // And these documents cant be deregistered. + // Otherwhise we loos our configuration data ... but need it ! + // see SessionSave ! + if (aInfo.IgnoreClosing) + return; + + CacheLockGuard aCacheLock2(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); + pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt != m_lDocCache.end()) + m_lDocCache.erase(pIt); + pIt = m_lDocCache.end(); // otherwhise its not specified what pIt means! + aCacheLock2.unlock(); + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + /* This method is called within disposing() of the document too. But there it's not a good idea to + deregister us as listener. Furter it make no sense - because the broadcaster dies. + So we supress deregistration in such case ... + */ + if (bStopListening) + implts_stopModifyListeningOnDoc(aInfo); + + AutoRecovery::st_impl_removeFile(aInfo.OldTempURL); + AutoRecovery::st_impl_removeFile(aInfo.NewTempURL); + implts_flushConfigItem(aInfo, sal_True); // TRUE => remove it from config +} + +//----------------------------------------------- +void AutoRecovery::implts_markDocumentModifiedAgainstLastBackup(const css::uno::Reference< css::frame::XModel >& xDocument) +{ + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt != m_lDocCache.end()) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + + /* Now we know, that this document was modified again and must be saved next time. + But we dont need this information for every e.g. key input of the user. + So we stop listening here. + But if the document was saved as temp. file we start listening for this event again. + */ + implts_stopModifyListeningOnDoc(rInfo); + } + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +void AutoRecovery::implts_updateModifiedState(const css::uno::Reference< css::frame::XModel >& xDocument) +{ + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt != m_lDocCache.end()) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + + // use TRUE as fallback ... so we recognize every document on EmergencySave/AutoRecovery! + sal_Bool bModified = sal_True; + css::uno::Reference< css::util::XModifiable > xModify(xDocument, css::uno::UNO_QUERY); + if (xModify.is()) + bModified = xModify->isModified(); + if (bModified) + { + rInfo.DocumentState |= AutoRecovery::E_MODIFIED; + } + else + { + rInfo.DocumentState &= ~AutoRecovery::E_MODIFIED; + } + } + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +void AutoRecovery::implts_updateDocumentUsedForSavingState(const css::uno::Reference< css::frame::XModel >& xDocument , + sal_Bool bSaveInProgress) +{ + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt == m_lDocCache.end()) + return; + AutoRecovery::TDocumentInfo& rInfo = *pIt; + rInfo.UsedForSaving = bSaveInProgress; + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +void AutoRecovery::implts_markDocumentAsSaved(const css::uno::Reference< css::frame::XModel >& xDocument) +{ + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + AutoRecovery::TDocumentList::iterator pIt = AutoRecovery::impl_searchDocument(m_lDocCache, xDocument); + if (pIt == m_lDocCache.end()) + return; + AutoRecovery::TDocumentInfo& rInfo = *pIt; + + rInfo.DocumentState = AutoRecovery::E_UNKNOWN; + // TODO replace getLocation() with getURL() ... its a workaround currently only! + css::uno::Reference< css::frame::XStorable > xDoc(rInfo.Document, css::uno::UNO_QUERY); + rInfo.OrgURL = xDoc->getLocation(); + + ::rtl::OUString sRemoveURL1 = rInfo.OldTempURL; + ::rtl::OUString sRemoveURL2 = rInfo.NewTempURL; + rInfo.OldTempURL = ::rtl::OUString(); + rInfo.NewTempURL = ::rtl::OUString(); + + ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs()); + rInfo.RealFilter = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_FILTERNAME(), ::rtl::OUString()); + + css::uno::Reference< css::frame::XTitle > xDocTitle(xDocument, css::uno::UNO_QUERY); + if (xDocTitle.is ()) + rInfo.Title = xDocTitle->getTitle (); + else + { + rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_TITLE() , ::rtl::OUString()); + if (!rInfo.Title.getLength()) + rInfo.Title = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_DOCUMENTTITLE(), ::rtl::OUString()); + } + + rInfo.UsedForSaving = sal_False; + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + implts_flushConfigItem(rInfo); + + aCacheLock.unlock(); + + AutoRecovery::st_impl_removeFile(sRemoveURL1); + AutoRecovery::st_impl_removeFile(sRemoveURL2); +} + +//----------------------------------------------- +AutoRecovery::TDocumentList::iterator AutoRecovery::impl_searchDocument( AutoRecovery::TDocumentList& rList , + const css::uno::Reference< css::frame::XModel >& xDocument) +{ + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = rList.begin(); + pIt != rList.end() ; + ++pIt ) + { + const AutoRecovery::TDocumentInfo& rInfo = *pIt; + if (rInfo.Document == xDocument) + break; + } + return pIt; +} + +//----------------------------------------------- +namespace +{ + void lcl_changeVisibility( const css::uno::Reference< css::frame::XFramesSupplier >& i_rFrames, sal_Bool i_bVisible ) + { + css::uno::Reference< css::container::XIndexAccess > xFramesContainer( i_rFrames->getFrames(), css::uno::UNO_QUERY ); + const sal_Int32 count = xFramesContainer->getCount(); + + Any aElement; + for ( sal_Int32 i=0; i < count; ++i ) + { + aElement = xFramesContainer->getByIndex(i); + // check for sub frames + css::uno::Reference< css::frame::XFramesSupplier > xFramesSupp( aElement, css::uno::UNO_QUERY ); + if ( xFramesSupp.is() ) + lcl_changeVisibility( xFramesSupp, i_bVisible ); + + css::uno::Reference< css::frame::XFrame > xFrame( aElement, css::uno::UNO_QUERY ); + if ( !xFrame.is() ) + continue; + + css::uno::Reference< css::awt::XWindow > xWindow( xFrame->getContainerWindow(), UNO_SET_THROW ); + xWindow->setVisible( i_bVisible ); + } + } +} + +//----------------------------------------------- +void AutoRecovery::implts_changeAllDocVisibility(sal_Bool bVisible) +{ + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + css::uno::Reference< css::frame::XFramesSupplier > xDesktop(xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY); + lcl_changeVisibility( xDesktop, bVisible ); + + aReadLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +/* Currently the document is not closed in case of crash, + so the lock file must be removed explicitly +*/ +void lc_removeLockFile(AutoRecovery::TDocumentInfo& rInfo) +{ + if ( rInfo.Document.is() ) + { + try + { + css::uno::Reference< css::frame::XStorable > xStore(rInfo.Document, css::uno::UNO_QUERY_THROW); + ::rtl::OUString aURL = xStore->getLocation(); + if ( aURL.getLength() ) + { + ::svt::DocumentLockFile aLockFile( aURL ); + aLockFile.RemoveFile(); + } + } + catch( const css::uno::Exception& ) + {} + } +} + + +//----------------------------------------------- +void AutoRecovery::implts_prepareSessionShutdown() +{ + LOG_RECOVERY("AutoRecovery::implts_prepareSessionShutdown() starts ...") + + // a) reset modified documents (of course the must be saved before this method is called!) + // b) close it without showing any UI! + + // SAFE -> + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end() ; + ++pIt ) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + + // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly + // it is not done on documents saving since shutdown can be cancelled + lc_removeLockFile( rInfo ); + + // Prevent us from deregistration of these documents. + // Because we close these documents by ourself (see XClosable below) ... + // it's fact, that we reach our deregistration method. There we + // must not(!) update our configuration ... Otherwhise all + // session data are lost !!! + rInfo.IgnoreClosing = sal_True; + + // reset modified flag of these documents (ignoring the notification about it!) + // Otherwise a message box is shown on closing these models. + implts_stopModifyListeningOnDoc(rInfo); + + // if the session save is still running the documents should not be thrown away, + // actually that would be a bad sign, that means that the SessionManager tryes + // to kill the session before the saving is ready + if ((m_eJob & AutoRecovery::E_SESSION_SAVE) != AutoRecovery::E_SESSION_SAVE) + { + css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY); + if (xModify.is()) + xModify->setModified(sal_False); + + // close the model. + css::uno::Reference< css::util::XCloseable > xClose(rInfo.Document, css::uno::UNO_QUERY); + if (xClose.is()) + { + try + { + xClose->close(sal_False); + } + /* + catch(const css::lang::DisposedException&) + { + // closed ... disposed ... always the same .-) + } + */ + catch(const css::uno::Exception&) + { + // At least it's only a try to close these documents before anybody else it does. + // So it seams to be possible to ignore any error here .-) + } + + rInfo.Document.clear(); + } + } + } + + aCacheLock.unlock(); + // <- SAFE +} + +//----------------------------------------------- +/* TODO WORKAROUND: + + #i64599# + + Normaly the MediaDescriptor argument NoAutoSave indicates, + that a document must be ignored for AutoSave and Recovery. + But sometimes XModel->getArgs() does not contained this information + if implts_registerDocument() was called. + So we have to check a second time, if this property is set .... + Best place doing so is to check it immeditaly before saving + and supressingd saving the document then. + Of course removing the corresponding cache entry isnt an option. + Because it would disturb iteration over the cache ! + So we ignore such documents only ... + Hopefully next time they are not inserted in our cache. +*/ +sal_Bool lc_checkIfSaveForbiddenByArguments(AutoRecovery::TDocumentInfo& rInfo) +{ + if (! rInfo.Document.is()) + return sal_True; + + ::comphelper::MediaDescriptor lDescriptor(rInfo.Document->getArgs()); + sal_Bool bNoAutoSave = lDescriptor.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_NOAUTOSAVE(), (sal_Bool)(sal_False)); + + return bNoAutoSave; +} + +//----------------------------------------------- +AutoRecovery::ETimerType AutoRecovery::implts_saveDocs( sal_Bool bAllowUserIdleLoop, + sal_Bool bRemoveLockFiles, + const DispatchParams* pParams ) +{ + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + css::uno::Reference< css::task::XStatusIndicator > xExternalProgress; + if (pParams) + xExternalProgress = pParams->m_xProgress; + + css::uno::Reference< css::frame::XFramesSupplier > xDesktop (xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY); + ::rtl::OUString sBackupPath (SvtPathOptions().GetBackupPath()); + + css::uno::Reference< css::frame::XController > xActiveController; + css::uno::Reference< css::frame::XModel > xActiveModel ; + css::uno::Reference< css::frame::XFrame > xActiveFrame = xDesktop->getActiveFrame(); + if (xActiveFrame.is()) + xActiveController = xActiveFrame->getController(); + if (xActiveController.is()) + xActiveModel = xActiveController->getModel(); + + // Set the default timer action for our calli. + // Default = NORMAL_AUTOSAVE + // We return a suggestion for an active timer only. + // It will be ignored if the timer was disabled by the user ... + // Further this state can be set to USER_IDLE only later in this method. + // Its not allowed to reset such state then. Because we must know, if + // there exists POSTPONED documents. see below ... + AutoRecovery::ETimerType eTimer = AutoRecovery::E_NORMAL_AUTOSAVE_INTERVALL; + + sal_Int32 eJob = m_eJob; + + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + // This list will be filled with every document + // which should be saved as last one. E.g. if it was used + // already for an UI save operation => crashed ... and + // now we try to save it again ... which can fail again ( of course .-) ). + ::std::vector< AutoRecovery::TDocumentList::iterator > lDangerousDocs; + + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end() ; + ++pIt ) + { + AutoRecovery::TDocumentInfo aInfo = *pIt; + + // WORKAROUND... Since the documents are not closed the lock file must be removed explicitly + if ( bRemoveLockFiles ) + lc_removeLockFile( aInfo ); + + // WORKAROUND ... see comment of this method + if (lc_checkIfSaveForbiddenByArguments(aInfo)) + continue; + + // already auto saved during this session :-) + // This state must be reset for all documents + // if timer is started with normnal AutoSaveTimerIntervall! + if ((aInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED) + continue; + + // Not modified documents are not saved. + // We safe an information about the URL only! + Reference< XDocumentRecovery > xDocRecover( aInfo.Document, UNO_QUERY_THROW ); + if ( !xDocRecover->wasModifiedSinceLastSave() ) + { + aInfo.DocumentState |= AutoRecovery::E_HANDLED; + continue; + } + + // check if this document is still used by a concurrent save operation + // e.g. if the user tried to save via UI. + // Handle it in the following way: + // i) For an AutoSave ... ignore this document! It will be saved and next time we will (hopefully) + // get a notification about the state of this operation. + // And if a document was saved by the user we can remove our temp. file. But that will be done inside + // our callback for SaveDone notification. + // ii) For a CrashSave ... add it to the list of dangerous documents and + // save it after all other documents was saved successfully. That decrease + // the chance for a crash inside a crash. + // On the other side it's not neccessary for documents, which are not modified. + // They can be handled normaly - means we patch the corresponding configuration entry only. + // iii) For a SessionSave ... ignore it! There is no time to wait for this save operation. + // Because the WindowManager will kill the process if it doesnt react immediatly. + // On the other side we cant risk a concurrent save request ... because we know + // that it will produce a crash. + + // Attention: Because eJob is used as a flag field, you have to check for the worst case first. + // E.g. a CrashSave can overwrite an AutoSave. So you have to check for a CrashSave before an AutoSave! + if (aInfo.UsedForSaving) + { + if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE) + { + lDangerousDocs.push_back(pIt); + continue; + } + else + if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE) + { + continue; + } + else + if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE) + { + eTimer = AutoRecovery::E_POLL_TILL_AUTOSAVE_IS_ALLOWED; + aInfo.DocumentState |= AutoRecovery::E_POSTPONED; + continue; + } + } + + // a) Document was not postponed - and is active now. => postpone it (restart timer, restart loop) + // b) Document was not postponed - and is not active now. => save it + // c) Document was postponed - and is not active now. => save it + // d) Document was postponed - and is active now. => save it (because user idle was checked already) + sal_Bool bActive = (xActiveModel == aInfo.Document); + sal_Bool bWasPostponed = ((aInfo.DocumentState & AutoRecovery::E_POSTPONED) == AutoRecovery::E_POSTPONED); + + if ( + ! bWasPostponed && + bActive + ) + { + aInfo.DocumentState |= AutoRecovery::E_POSTPONED; + *pIt = aInfo; + // postponed documents will be saved if this method is called again! + // That can be done by an outside started timer => E_POLL_FOR_USER_IDLE (if normal AutoSave is active) + // 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!) + eTimer = AutoRecovery::E_POLL_FOR_USER_IDLE; + if (!bAllowUserIdleLoop) + eTimer = AutoRecovery::E_CALL_ME_BACK; + continue; + } + + // b, c, d) + // <- SAFE -------------------------- + aWriteLock.unlock(); + // changing of aInfo and flushing it is done inside implts_saveOneDoc! + implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress); + implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo)); + aWriteLock.lock(); + // SAFE -> -------------------------- + + *pIt = aInfo; + } + + // Did we have some "dangerous candidates" ? + // Try to save it ... but may be it will fail ! + ::std::vector< AutoRecovery::TDocumentList::iterator >::iterator pIt2; + for ( pIt2 = lDangerousDocs.begin(); + pIt2 != lDangerousDocs.end() ; + ++pIt2 ) + { + pIt = *pIt2; + AutoRecovery::TDocumentInfo aInfo = *pIt; + + // <- SAFE -------------------------- + aWriteLock.unlock(); + // changing of aInfo and flushing it is done inside implts_saveOneDoc! + implts_saveOneDoc(sBackupPath, aInfo, xExternalProgress); + implts_informListener(eJob, AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &aInfo)); + aWriteLock.lock(); + // SAFE -> -------------------------- + + *pIt = aInfo; + } + + return eTimer; +} + +//----------------------------------------------- +void AutoRecovery::implts_saveOneDoc(const ::rtl::OUString& sBackupPath , + AutoRecovery::TDocumentInfo& rInfo , + const css::uno::Reference< css::task::XStatusIndicator >& xExternalProgress) +{ + // no document? => can occure if we loaded our configuration with files, + // which couldnt be recovered successfully. In such case we have all needed informations + // excepting the real document instance! + + // TODO: search right place, where such "dead files" can be removed from the configuration! + if (!rInfo.Document.is()) + return; + + ::comphelper::MediaDescriptor lOldArgs(rInfo.Document->getArgs()); + implts_generateNewTempURL(sBackupPath, lOldArgs, rInfo); + + // if the document was loaded with a password, it should be + // stored with password + ::comphelper::MediaDescriptor lNewArgs; + ::rtl::OUString sPassword = lOldArgs.getUnpackedValueOrDefault(::comphelper::MediaDescriptor::PROP_PASSWORD(), ::rtl::OUString()); + if (sPassword.getLength()) + lNewArgs[::comphelper::MediaDescriptor::PROP_PASSWORD()] <<= sPassword; + + // Further it must be saved using the default file format of that application. + // Otherwhise we will some data lost. + if (rInfo.DefaultFilter.getLength()) + lNewArgs[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.DefaultFilter; + + // prepare frame/document/mediadescriptor in a way, that it uses OUR progress .-) + if (xExternalProgress.is()) + lNewArgs[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= xExternalProgress; + impl_establishProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >()); + + // #i66598# use special handling of property "DocumentBaseURL" (it must be an empty string!) + // for make hyperlinks working + lNewArgs[::comphelper::MediaDescriptor::PROP_DOCUMENTBASEURL()] <<= ::rtl::OUString(); + + // try to save this document as a new temp file everytimes. + // Mark AutoSave state as "INCOMPLETE" if it failed. + // Because the last temp file is to old and does not include all changes. + Reference< XDocumentRecovery > xDocRecover(rInfo.Document, css::uno::UNO_QUERY_THROW); + + // safe the state about "trying to save" + // ... we need it for recovery if e.g. a crash occures inside next line! + rInfo.DocumentState |= AutoRecovery::E_TRY_SAVE; + implts_flushConfigItem(rInfo); + + sal_Int32 nRetry = RETRY_STORE_ON_FULL_DISC_FOREVER; + sal_Bool bError = sal_False; + do + { + try + { + xDocRecover->storeToRecoveryFile( rInfo.NewTempURL, lNewArgs.getAsConstPropertyValueList() ); + + #ifdef TRIGGER_FULL_DISC_CHECK + throw css::uno::Exception(); + #endif + + bError = sal_False; + nRetry = 0; + } + catch(const css::uno::Exception& ex) + { + bError = sal_True; + + // a) FULL DISC seams to be the problem behind => show error and retry it forever (e.g. retry=300) + // b) unknown problem (may be locking problem) => reset RETRY value to more usefull value(!) (e.g. retry=3) + // c) unknown problem (may be locking problem) + 1..2 repeating operations => throw the original exception to force generation of a stacktrace ! + + // SAFE -> + ReadGuard aReadLock2(m_aLock); + sal_Int32 nMinSpaceDocSave = m_nMinSpaceDocSave; + aReadLock2.unlock(); + // <- SAFE + + if (! impl_enoughDiscSpace(nMinSpaceDocSave)) + AutoRecovery::impl_showFullDiscError(); + else + if (nRetry > RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL) + nRetry = RETRY_STORE_ON_MIGHT_FULL_DISC_USEFULL; + else + if (nRetry <= GIVE_UP_RETRY) + throw ex; // force stacktrace to know if there exist might other reasons, why an AutoSave can fail !!! + + --nRetry; + } + } + while(nRetry>0); + + if (! bError) + { + // safe the state about success + // ... you know the reason: to know it on recovery time if next line crash .-) + rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE; + rInfo.DocumentState |= AutoRecovery::E_HANDLED; + rInfo.DocumentState |= AutoRecovery::E_SUCCEDED; + } + else + { + // safe the state about error ... + rInfo.NewTempURL = ::rtl::OUString(); + rInfo.DocumentState &= ~AutoRecovery::E_TRY_SAVE; + rInfo.DocumentState |= AutoRecovery::E_HANDLED; + rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE; + } + + // make sure the progress isnt referred any longer + impl_forgetProgress(rInfo, lNewArgs, css::uno::Reference< css::frame::XFrame >()); + + // try to remove the old temp file. + // Ignore any error here. We have a new temp file, which is up to date. + // The only thing is: we fill the disk with temp files, if we cant remove old ones :-) + ::rtl::OUString sRemoveFile = rInfo.OldTempURL; + rInfo.OldTempURL = rInfo.NewTempURL; + rInfo.NewTempURL = ::rtl::OUString(); + + implts_flushConfigItem(rInfo); + + // We must know if the user modifies the document again ... + implts_startModifyListeningOnDoc(rInfo); + + AutoRecovery::st_impl_removeFile(sRemoveFile); +} + +//----------------------------------------------- +AutoRecovery::ETimerType AutoRecovery::implts_openDocs(const DispatchParams& aParams) +{ + AutoRecovery::ETimerType eTimer = AutoRecovery::E_DONT_START_TIMER; + + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + + sal_Int32 eJob = m_eJob; + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end() ; + ++pIt ) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + + // Such documents are already loaded by the last loop. + // Dont check E_SUCCEDED here! Its may be the final state of an AutoSave + // operation before!!! + if ((rInfo.DocumentState & AutoRecovery::E_HANDLED) == AutoRecovery::E_HANDLED) + continue; + + // a1,b1,c1,d2,e2,f2) + if ((rInfo.DocumentState & AutoRecovery::E_DAMAGED) == AutoRecovery::E_DAMAGED) + { + // dont forget to inform listener! May be this document was + // damaged on last saving time ... + // Then our listener need this notification. + // If it was damaged during last "try to open" ... + // it will be notified more then once. SH.. HAPPENS ... + // <- SAFE -------------------------- + aWriteLock.unlock(); + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo)); + aWriteLock.lock(); + // SAFE -> -------------------------- + continue; + } + + ::comphelper::MediaDescriptor lDescriptor; + + // its an UI feature - so the "USER" itself must be set as referer + lDescriptor[::comphelper::MediaDescriptor::PROP_REFERRER()] <<= REFERRER_USER; + lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= ::rtl::OUString(); + + // recovered documents are loaded hidden, and shown all at once, later + lDescriptor[::comphelper::MediaDescriptor::PROP_HIDDEN()] <<= true; + + if (aParams.m_xProgress.is()) + lDescriptor[::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()] <<= aParams.m_xProgress; + + sal_Bool bBackupWasTried = ( + ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_BACKUP ) == AutoRecovery::E_TRY_LOAD_BACKUP) || // temp. state! + ((rInfo.DocumentState & AutoRecovery::E_INCOMPLETE ) == AutoRecovery::E_INCOMPLETE ) // transport TRY_LOAD_BACKUP from last loop to this new one! + ); + sal_Bool bOriginalWasTried = ((rInfo.DocumentState & AutoRecovery::E_TRY_LOAD_ORIGINAL) == AutoRecovery::E_TRY_LOAD_ORIGINAL); + + if (bBackupWasTried) + { + if (!bOriginalWasTried) + { + rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE; + // try original URL ... ! dont continue with next item here ... + } + else + { + rInfo.DocumentState |= AutoRecovery::E_DAMAGED; + continue; + } + } + + ::rtl::OUString sLoadOriginalURL; + ::rtl::OUString sLoadBackupURL ; + + if (!bBackupWasTried) + sLoadBackupURL = rInfo.OldTempURL; + + if (rInfo.OrgURL.getLength()) + { + sLoadOriginalURL = rInfo.OrgURL; + } + else + if (rInfo.TemplateURL.getLength()) + { + sLoadOriginalURL = rInfo.TemplateURL; + lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True; + lDescriptor[::comphelper::MediaDescriptor::PROP_TEMPLATENAME()] <<= rInfo.TemplateURL; + } + else + if (rInfo.FactoryURL.getLength()) + { + sLoadOriginalURL = rInfo.FactoryURL; + lDescriptor[::comphelper::MediaDescriptor::PROP_ASTEMPLATE()] <<= sal_True; + } + + // A "Salvaged" item must exists every time. The core can make something special then for recovery. + // Of course it should be the real file name of the original file, in case we load the temp. backup here. + ::rtl::OUString sURL; + if (sLoadBackupURL.getLength()) + { + sURL = sLoadBackupURL; + rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_BACKUP; + lDescriptor[::comphelper::MediaDescriptor::PROP_SALVAGEDFILE()] <<= sLoadOriginalURL; + } + else + if (sLoadOriginalURL.getLength()) + { + sURL = sLoadOriginalURL; + rInfo.DocumentState |= AutoRecovery::E_TRY_LOAD_ORIGINAL; + } + else + continue; // TODO ERROR! + + LoadEnv::initializeUIDefaults( m_xSMGR, lDescriptor, true, NULL ); + + // <- SAFE ------------------------------ + aWriteLock.unlock(); + + implts_flushConfigItem(rInfo); + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo)); + + try + { + implts_openOneDoc(sURL, lDescriptor, rInfo); + } + catch(const css::uno::Exception&) + { + rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP; + rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL; + if (sLoadBackupURL.getLength()) + { + rInfo.DocumentState |= AutoRecovery::E_INCOMPLETE; + eTimer = AutoRecovery::E_CALL_ME_BACK; + } + else + { + rInfo.DocumentState |= AutoRecovery::E_HANDLED; + rInfo.DocumentState |= AutoRecovery::E_DAMAGED; + } + + implts_flushConfigItem(rInfo, sal_True); + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo)); + + // SAFE -> ------------------------------ + // Needed for next loop! + aWriteLock.lock(); + continue; + } + + if (rInfo.RealFilter.getLength()) + { + ::comphelper::MediaDescriptor lPatchDescriptor(rInfo.Document->getArgs()); + lPatchDescriptor[::comphelper::MediaDescriptor::PROP_FILTERNAME()] <<= rInfo.RealFilter; + rInfo.Document->attachResource(rInfo.Document->getURL(), lPatchDescriptor.getAsConstPropertyValueList()); + // do *not* use sURL here. In case this points to the recovery file, it has already been passed + // to recoverFromFile. Also, passing it here is logically wrong, as attachResource is intended + // to take the logical file URL. + } + + css::uno::Reference< css::util::XModifiable > xModify(rInfo.Document, css::uno::UNO_QUERY); + if ( xModify.is() ) + { + sal_Bool bModified = ((rInfo.DocumentState & AutoRecovery::E_MODIFIED) == AutoRecovery::E_MODIFIED); + xModify->setModified(bModified); + } + + rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_BACKUP; + rInfo.DocumentState &= ~AutoRecovery::E_TRY_LOAD_ORIGINAL; + rInfo.DocumentState |= AutoRecovery::E_HANDLED; + rInfo.DocumentState |= AutoRecovery::E_SUCCEDED; + + implts_flushConfigItem(rInfo); + implts_informListener(eJob, + AutoRecovery::implst_createFeatureStateEvent(eJob, OPERATION_UPDATE, &rInfo)); + + /* Normaly we listen as XModifyListener on a document to know if a document was changed + since our last AutoSave. And we deregister us in case we know this state. + But directly after one document as recovered ... we must start listening. + Otherwhise the first "modify" doesnt reach us. Because we ourself called setModified() + on the document via API. And currently we dont listen for any events (not at the GlobalEventBroadcaster + nor at any document!). + */ + implts_startModifyListeningOnDoc(rInfo); + + // SAFE -> ------------------------------ + // Needed for next loop. Dont unlock it again! + aWriteLock.lock(); + } + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + return eTimer; +} + +//----------------------------------------------- +void AutoRecovery::implts_openOneDoc(const ::rtl::OUString& sURL , + ::comphelper::MediaDescriptor& lDescriptor, + AutoRecovery::TDocumentInfo& rInfo ) +{ + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + css::uno::Reference< css::frame::XFrame > xDesktop( xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY_THROW ); + + ::std::vector< Reference< XComponent > > aCleanup; + try + { + // create a new document of the desired type + Reference< XModel2 > xModel( xSMGR->createInstance( rInfo.FactoryService ), UNO_QUERY_THROW ); + aCleanup.push_back( xModel.get() ); + + // put the filter name into the descriptor - we're not going to involve any type detection, so + // the document might be lost without the FilterName property + lDescriptor[ ::comphelper::MediaDescriptor::PROP_FILTERNAME() ] <<= rInfo.RealFilter; + + if ( sURL == rInfo.FactoryURL ) + { + // if the document was a new, unmodified document, then there's nothing to recover, just to init + ENSURE_OR_THROW( ( rInfo.DocumentState & AutoRecovery::E_MODIFIED ) == 0, + "unexpected document state" ); + Reference< XLoadable > xModelLoad( xModel, UNO_QUERY_THROW ); + xModelLoad->initNew(); + + // TODO: remove load-process specific arguments from the descriptor, e.g. the status indicator + xModel->attachResource( sURL, lDescriptor.getAsConstPropertyValueList() ); + } + else + { + // let it recover itself + Reference< XDocumentRecovery > xDocRecover( xModel, UNO_QUERY_THROW ); + xDocRecover->recoverFromFile( + sURL, + lDescriptor.getUnpackedValueOrDefault( ::comphelper::MediaDescriptor::PROP_SALVAGEDFILE(), ::rtl::OUString() ), + lDescriptor.getAsConstPropertyValueList() + ); + + // No attachResource needed here. By definition (of XDocumentRecovery), the implementation is responsible + // for completely initializing the model, which includes attachResource (or equivalent), if required. + } + + // re-create all the views + ::std::vector< ::rtl::OUString > aViewsToRestore( rInfo.ViewNames.getLength() ); + if ( rInfo.ViewNames.getLength() ) + ::std::copy( rInfo.ViewNames.getConstArray(), rInfo.ViewNames.getConstArray() + rInfo.ViewNames.getLength(), aViewsToRestore.begin() ); + // if we don't have views for whatever reason, then create a default-view, at least + if ( aViewsToRestore.empty() ) + aViewsToRestore.push_back( ::rtl::OUString() ); + + for ( ::std::vector< ::rtl::OUString >::const_iterator viewName = aViewsToRestore.begin(); + viewName != aViewsToRestore.end(); + ++viewName + ) + { + // create a frame + Reference< XFrame > xTargetFrame = xDesktop->findFrame( SPECIALTARGET_BLANK, 0 ); + aCleanup.push_back( xTargetFrame.get() ); + + // create a view to the document + Reference< XController2 > xController; + if ( viewName->getLength() ) + { + xController.set( xModel->createViewController( *viewName, Sequence< PropertyValue >(), xTargetFrame ), UNO_SET_THROW ); + } + else + { + xController.set( xModel->createDefaultViewController( xTargetFrame ), UNO_SET_THROW ); + } + + // introduce model/view/controller to each other + xController->attachModel( xModel.get() ); + xModel->connectController( xController.get() ); + xTargetFrame->setComponent( xController->getComponentWindow(), xController.get() ); + xController->attachFrame( xTargetFrame ); + xModel->setCurrentController( xController.get() ); + } + + rInfo.Document = xModel.get(); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { + Any aCaughtException( ::cppu::getCaughtException() ); + + // clean up + for ( ::std::vector< Reference< XComponent > >::const_iterator component = aCleanup.begin(); + component != aCleanup.end(); + ++component + ) + { + css::uno::Reference< css::util::XCloseable > xClose( *component, css::uno::UNO_QUERY ); + if ( xClose.is() ) + xClose->close( sal_True ); + else + (*component)->dispose(); + } + + // re-throw + ::rtl::OUStringBuffer sMsg(256); + sMsg.appendAscii("Recovery of \""); + sMsg.append (sURL ); + sMsg.appendAscii("\" failed." ); + + throw css::lang::WrappedTargetException( + sMsg.makeStringAndClear(), + static_cast< css::frame::XDispatch* >(this), + aCaughtException + ); + } +} + +//----------------------------------------------- +void AutoRecovery::implts_generateNewTempURL(const ::rtl::OUString& sBackupPath , + ::comphelper::MediaDescriptor& /*rMediaDescriptor*/, + AutoRecovery::TDocumentInfo& rInfo ) +{ + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + // specify URL for saving (which points to a temp file inside backup directory) + // and define an unique name, so we can locate it later. + // This unique name must solve an optimization problem too! + // In case we are asked to save unmodified documents too - and one of them + // is an empty one (because it was new created using e.g. an URL private:factory/...) + // we should not save it realy. Then we put the information about such "empty document" + // into the configuration and dont create any recovery file on disk. + // We use the title of the document to make it unique. + ::rtl::OUStringBuffer sUniqueName; + if (rInfo.OrgURL.getLength()) + { + css::uno::Reference< css::util::XURLTransformer > xParser(xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), css::uno::UNO_QUERY); + css::util::URL aURL; + aURL.Complete = rInfo.OrgURL; + xParser->parseStrict(aURL); + sUniqueName.append(aURL.Name); + } + else + if (rInfo.FactoryURL.getLength()) + sUniqueName.appendAscii("untitled"); + sUniqueName.appendAscii("_"); + + // TODO: Must we strip some illegal signes - if we use the title? + + String sName (sUniqueName.makeStringAndClear()); + String sExtension(rInfo.Extension ); + String sPath (sBackupPath ); + ::utl::TempFile aTempFile(sName, &sExtension, &sPath); + + rInfo.NewTempURL = aTempFile.GetURL(); +} + +//----------------------------------------------- +void AutoRecovery::implts_informListener( sal_Int32 eJob , + const css::frame::FeatureStateEvent& aEvent) +{ + // Helper shares mutex with us -> threadsafe! + ::cppu::OInterfaceContainerHelper* pListenerForURL = 0; + ::rtl::OUString sJob = AutoRecovery::implst_getJobDescription(eJob); + + // inform listener, which are registered for any URLs(!) + pListenerForURL = m_lListener.getContainer(sJob); + if(pListenerForURL != 0) + { + ::cppu::OInterfaceIteratorHelper pIt(*pListenerForURL); + while(pIt.hasMoreElements()) + { + try + { + css::uno::Reference< css::frame::XStatusListener > xListener(((css::frame::XStatusListener*)pIt.next()), css::uno::UNO_QUERY); + xListener->statusChanged(aEvent); + } + catch(const css::uno::RuntimeException&) + { pIt.remove(); } + } + } +} + +//----------------------------------------------- +::rtl::OUString AutoRecovery::implst_getJobDescription(sal_Int32 eJob) +{ + // describe the current running operation + ::rtl::OUStringBuffer sFeature(256); + sFeature.append(CMD_PROTOCOL); + + // Attention: Because "eJob" is used as a flag field the order of checking these + // flags is importent. We must preferr job with higher priorities! + // E.g. EmergencySave has an higher prio then AutoSave ... + // On the other side there exist a well defined order between two different jobs. + // e.g. PrepareEmergencySave must be done before EmergencySave is started of course. + + if ((eJob & AutoRecovery::E_PREPARE_EMERGENCY_SAVE) == AutoRecovery::E_PREPARE_EMERGENCY_SAVE) + sFeature.append(CMD_DO_PREPARE_EMERGENCY_SAVE); + else + if ((eJob & AutoRecovery::E_EMERGENCY_SAVE) == AutoRecovery::E_EMERGENCY_SAVE) + sFeature.append(CMD_DO_EMERGENCY_SAVE); + else + if ((eJob & AutoRecovery::E_RECOVERY) == AutoRecovery::E_RECOVERY) + sFeature.append(CMD_DO_RECOVERY); + else + if ((eJob & AutoRecovery::E_SESSION_SAVE) == AutoRecovery::E_SESSION_SAVE) + sFeature.append(CMD_DO_SESSION_SAVE); + else + if ((eJob & AutoRecovery::E_SESSION_QUIET_QUIT) == AutoRecovery::E_SESSION_QUIET_QUIT) + sFeature.append(CMD_DO_SESSION_QUIET_QUIT); + else + if ((eJob & AutoRecovery::E_SESSION_RESTORE) == AutoRecovery::E_SESSION_RESTORE) + sFeature.append(CMD_DO_SESSION_RESTORE); + else + if ((eJob & AutoRecovery::E_ENTRY_BACKUP) == AutoRecovery::E_ENTRY_BACKUP) + sFeature.append(CMD_DO_ENTRY_BACKUP); + else + if ((eJob & AutoRecovery::E_ENTRY_CLEANUP) == AutoRecovery::E_ENTRY_CLEANUP) + sFeature.append(CMD_DO_ENTRY_CLEANUP); + else + if ((eJob & AutoRecovery::E_AUTO_SAVE) == AutoRecovery::E_AUTO_SAVE) + sFeature.append(CMD_DO_AUTO_SAVE); + #ifdef ENABLE_WARNINGS + else if ( eJob != AutoRecovery::E_NO_JOB ) + LOG_WARNING("AutoRecovery::implst_getJobDescription()", "Invalid job identifier detected.") + #endif + + return sFeature.makeStringAndClear(); +} + +//----------------------------------------------- +sal_Int32 AutoRecovery::implst_classifyJob(const css::util::URL& aURL) +{ + if (aURL.Protocol.equals(CMD_PROTOCOL)) + { + if (aURL.Path.equals(CMD_DO_PREPARE_EMERGENCY_SAVE)) + return AutoRecovery::E_PREPARE_EMERGENCY_SAVE; + else + if (aURL.Path.equals(CMD_DO_EMERGENCY_SAVE)) + return AutoRecovery::E_EMERGENCY_SAVE; + else + if (aURL.Path.equals(CMD_DO_RECOVERY)) + return AutoRecovery::E_RECOVERY; + else + if (aURL.Path.equals(CMD_DO_ENTRY_BACKUP)) + return AutoRecovery::E_ENTRY_BACKUP; + else + if (aURL.Path.equals(CMD_DO_ENTRY_CLEANUP)) + return AutoRecovery::E_ENTRY_CLEANUP; + else + if (aURL.Path.equals(CMD_DO_SESSION_SAVE)) + return AutoRecovery::E_SESSION_SAVE; + else + if (aURL.Path.equals(CMD_DO_SESSION_QUIET_QUIT)) + return AutoRecovery::E_SESSION_QUIET_QUIT; + else + if (aURL.Path.equals(CMD_DO_SESSION_RESTORE)) + return AutoRecovery::E_SESSION_RESTORE; + else + if (aURL.Path.equals(CMD_DO_DISABLE_RECOVERY)) + return AutoRecovery::E_DISABLE_AUTORECOVERY; + else + if (aURL.Path.equals(CMD_DO_SET_AUTOSAVE_STATE)) + return AutoRecovery::E_SET_AUTOSAVE_STATE; + } + + LOG_WARNING("AutoRecovery::implts_classifyJob()", "Invalid URL (protocol).") + return AutoRecovery::E_NO_JOB; +} + +//----------------------------------------------- +css::frame::FeatureStateEvent AutoRecovery::implst_createFeatureStateEvent( sal_Int32 eJob , + const ::rtl::OUString& sEventType, + AutoRecovery::TDocumentInfo* pInfo ) +{ + css::frame::FeatureStateEvent aEvent; + aEvent.FeatureURL.Complete = AutoRecovery::implst_getJobDescription(eJob); + aEvent.FeatureDescriptor = sEventType; + + if (sEventType.equals(OPERATION_UPDATE) && pInfo) + { + // pack rInfo for transport via UNO + ::comphelper::NamedValueCollection aInfo; + aInfo.put( CFG_ENTRY_PROP_ID, pInfo->ID ); + aInfo.put( CFG_ENTRY_PROP_ORIGINALURL, pInfo->OrgURL ); + aInfo.put( CFG_ENTRY_PROP_FACTORYURL, pInfo->FactoryURL ); + aInfo.put( CFG_ENTRY_PROP_TEMPLATEURL, pInfo->TemplateURL ); + aInfo.put( CFG_ENTRY_PROP_TEMPURL, pInfo->OldTempURL.getLength() ? pInfo->OldTempURL : pInfo->NewTempURL ); + aInfo.put( CFG_ENTRY_PROP_MODULE, pInfo->AppModule ); + aInfo.put( CFG_ENTRY_PROP_TITLE, pInfo->Title ); + aInfo.put( CFG_ENTRY_PROP_VIEWNAMES, pInfo->ViewNames ); + aInfo.put( CFG_ENTRY_PROP_DOCUMENTSTATE, pInfo->DocumentState ); + + aEvent.State <<= aInfo.getPropertyValues(); + } + + return aEvent; +} + +//----------------------------------------------- +void AutoRecovery::implts_resetHandleStates(sal_Bool /*bLoadCache*/) +{ + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + // SAFE -> ------------------------------ + WriteGuard aWriteLock(m_aLock); + + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end() ; + ++pIt ) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + rInfo.DocumentState &= ~AutoRecovery::E_HANDLED ; + rInfo.DocumentState &= ~AutoRecovery::E_POSTPONED; + + // SAFE -> ------------------------------ + aWriteLock.unlock(); + implts_flushConfigItem(rInfo); + aWriteLock.lock(); + // <- SAFE ------------------------------ + } + + aWriteLock.unlock(); + // <- SAFE ---------------------------------- +} + +//----------------------------------------------- +void AutoRecovery::implts_prepareEmergencySave() +{ + // Be sure to know all open documents realy .-) + implts_verifyCacheAgainstDesktopDocumentList(); + + // hide all docs, so the user cant disturb our emergency save .-) + implts_changeAllDocVisibility(sal_False); +} + +//----------------------------------------------- +void AutoRecovery::implts_doEmergencySave(const DispatchParams& aParams) +{ + // Write a hint "we chrashed" into the configuration, so + // the error report tool is started too in case no recovery + // documents exists and was saved. + ::comphelper::ConfigurationHelper::writeDirectKey( + m_xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_RECOVERYINFO, + CFG_ENTRY_CRASHED, + css::uno::makeAny(sal_True), + ::comphelper::ConfigurationHelper::E_STANDARD); + + // for all docs, store their current view/names in the configurtion + implts_persistAllActiveViewNames(); + + // The called method for saving documents runs + // during normal AutoSave more then once. Because + // it postpone active documents and save it later. + // That is normaly done by recalling it from a timer. + // Here we must do it immediatly! + // Of course this method returns the right state - + // because it knows, that we are running in ERMERGENCY SAVE mode .-) + + sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-) + AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; + do + { + eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_True, &aParams); + } + while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); + + // reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) EmergencySave session. + // Of course following recovery session must be started without + // any "handle" state ... + implts_resetHandleStates(sal_False); + + // flush config cached back to disc. + impl_flushALLConfigChanges(); + + // try to make sure next time office will be started user wont be + // notified about any other might be running office instance + // remove ".lock" file from disc ! + AutoRecovery::st_impl_removeLockFile(); +} + +//----------------------------------------------- +void AutoRecovery::implts_doRecovery(const DispatchParams& aParams) +{ + AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; + do + { + eSuggestedTimer = implts_openDocs(aParams); + } + while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); + + // reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) Recovery session. + // Of course a may be following EmergencySave session must be started without + // any "handle" state ... + implts_resetHandleStates(sal_True); + + // Reset the configuration hint "we was crashed"! + ::comphelper::ConfigurationHelper::writeDirectKey( + m_xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_RECOVERYINFO, + CFG_ENTRY_CRASHED, + css::uno::makeAny(sal_False), + ::comphelper::ConfigurationHelper::E_STANDARD); +} + +//----------------------------------------------- +void AutoRecovery::implts_doSessionSave(const DispatchParams& aParams) +{ + LOG_RECOVERY("AutoRecovery::implts_doSessionSave()") + + // Be sure to know all open documents realy .-) + implts_verifyCacheAgainstDesktopDocumentList(); + + // for all docs, store their current view/names in the configurtion + implts_persistAllActiveViewNames(); + + // The called method for saving documents runs + // during normal AutoSave more then once. Because + // it postpone active documents and save it later. + // That is normaly done by recalling it from a timer. + // Here we must do it immediatly! + // Of course this method returns the right state - + // because it knows, that we are running in SESSION SAVE mode .-) + + sal_Bool bAllowUserIdleLoop = sal_False; // not allowed to change that .-) + AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; + do + { + // do not remove lock files of the documents, it will be done on session quit + eSuggestedTimer = implts_saveDocs(bAllowUserIdleLoop, sal_False, &aParams); + } + while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); + + // reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) save session. + // Of course following restore session must be started without + // any "handle" state ... + implts_resetHandleStates(sal_False); + + // flush config cached back to disc. + impl_flushALLConfigChanges(); +} + +//----------------------------------------------- +void AutoRecovery::implts_doSessionQuietQuit(const DispatchParams& /*aParams*/) +{ + LOG_RECOVERY("AutoRecovery::implts_doSessionQuietQuit()") + + // try to make sure next time office will be started user wont be + // notified about any other might be running office instance + // remove ".lock" file from disc ! + // it is done as a first action for session save since Gnome sessions + // do not provide enough time for shutdown, and the dialog looks to be + // confusing for the user + AutoRecovery::st_impl_removeLockFile(); + + // reset all modified documents, so the dont show any UI on closing ... + // and close all documents, so we can shutdown the OS! + implts_prepareSessionShutdown(); + + // Write a hint for "stored session data" into the configuration, so + // the on next startup we know what's happen last time + ::comphelper::ConfigurationHelper::writeDirectKey( + m_xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_RECOVERYINFO, + CFG_ENTRY_SESSIONDATA, + css::uno::makeAny(sal_True), + ::comphelper::ConfigurationHelper::E_STANDARD); + + // flush config cached back to disc. + impl_flushALLConfigChanges(); +} + + +//----------------------------------------------- +void AutoRecovery::implts_doSessionRestore(const DispatchParams& aParams) +{ + LOG_RECOVERY("AutoRecovery::implts_doSessionRestore() ...") + + AutoRecovery::ETimerType eSuggestedTimer = AutoRecovery::E_DONT_START_TIMER; + do + { + eSuggestedTimer = implts_openDocs(aParams); + } + while(eSuggestedTimer == AutoRecovery::E_CALL_ME_BACK); + + // reset the handle state of all + // cache items. Such handle state indicates, that a document + // was already saved during the THIS(!) Restore session. + // Of course a may be following save session must be started without + // any "handle" state ... + implts_resetHandleStates(sal_True); + + // make all opened documents visible + implts_changeAllDocVisibility(sal_True); + + // Reset the configuration hint for "session save"! + LOG_RECOVERY("... reset config key 'SessionData'") + ::comphelper::ConfigurationHelper::writeDirectKey( + m_xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_RECOVERYINFO, + CFG_ENTRY_SESSIONDATA, + css::uno::makeAny(sal_False), + ::comphelper::ConfigurationHelper::E_STANDARD); + + LOG_RECOVERY("... AutoRecovery::implts_doSessionRestore()") +} + +//----------------------------------------------- +void AutoRecovery::implts_backupWorkingEntry(const DispatchParams& aParams) +{ + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_USE); + + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end() ; + ++pIt ) + { + const AutoRecovery::TDocumentInfo& rInfo = *pIt; + if (rInfo.ID != aParams.m_nWorkingEntryID) + continue; + + ::rtl::OUString sSourceURL; + // Prefer temp file. It contains the changes against the original document! + if (rInfo.OldTempURL.getLength()) + sSourceURL = rInfo.OldTempURL; + else + if (rInfo.NewTempURL.getLength()) + sSourceURL = rInfo.NewTempURL; + else + if (rInfo.OrgURL.getLength()) + sSourceURL = rInfo.OrgURL; + else + continue; // nothing real to save! An unmodified but new created document. + + INetURLObject aParser(sSourceURL); + // AutoRecovery::EFailureSafeResult eResult = + implts_copyFile(sSourceURL, aParams.m_sSavePath, aParser.getName()); + + // TODO: Check eResult and react for errors (InteractionHandler!?) + // Currently we ignore it ... + // DONT UPDATE THE CACHE OR REMOVE ANY TEMP. FILES FROM DISK. + // That has to be forced from outside explicitly. + // See implts_cleanUpWorkingEntry() for further details. + } +} + +//----------------------------------------------- +void AutoRecovery::implts_cleanUpWorkingEntry(const DispatchParams& aParams) +{ + CacheLockGuard aCacheLock(this, m_aLock, m_nDocCacheLock, LOCK_FOR_CACHE_ADD_REMOVE); + + AutoRecovery::TDocumentList::iterator pIt; + for ( pIt = m_lDocCache.begin(); + pIt != m_lDocCache.end() ; + ++pIt ) + { + AutoRecovery::TDocumentInfo& rInfo = *pIt; + if (rInfo.ID != aParams.m_nWorkingEntryID) + continue; + + AutoRecovery::st_impl_removeFile(rInfo.OldTempURL); + AutoRecovery::st_impl_removeFile(rInfo.NewTempURL); + implts_flushConfigItem(rInfo, sal_True); // TRUE => remove it from xml config! + + m_lDocCache.erase(pIt); + break; /// !!! pIt is not defined any longer ... further this function has finished it's work + } +} + +//----------------------------------------------- +AutoRecovery::EFailureSafeResult AutoRecovery::implts_copyFile(const ::rtl::OUString& sSource , + const ::rtl::OUString& sTargetPath, + const ::rtl::OUString& sTargetName) +{ + // create content for the parent folder and call transfer on that content with the source content + // and the destination file name as parameters + + css::uno::Reference< css::ucb::XCommandEnvironment > xEnvironment; + + ::ucbhelper::Content aSourceContent; + ::ucbhelper::Content aTargetContent; + + try + { + aTargetContent = ::ucbhelper::Content(sTargetPath, xEnvironment); + } + catch(const css::uno::Exception&) + { return AutoRecovery::E_WRONG_TARGET_PATH; } + + sal_Int32 nNameClash; +// nNameClash = css::ucb::NameClash::ERROR; + nNameClash = css::ucb::NameClash::RENAME; +// nNameClash = css::ucb::NameClash::OVERWRITE; + + try + { + ::ucbhelper::Content::create(sSource, xEnvironment, aSourceContent); + aTargetContent.transferContent(aSourceContent, ::ucbhelper::InsertOperation_COPY, sTargetName, nNameClash); + } + catch(const css::uno::Exception&) + { return AutoRecovery::E_ORIGINAL_FILE_MISSING; } + + return AutoRecovery::E_COPIED; +} + +//----------------------------------------------- +sal_Bool SAL_CALL AutoRecovery::convertFastPropertyValue( css::uno::Any& /*aConvertedValue*/, + css::uno::Any& /*aOldValue*/ , + sal_Int32 /*nHandle*/ , + const css::uno::Any& /*aValue*/ ) + throw(css::lang::IllegalArgumentException) +{ + // not needed currently + return sal_False; +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::setFastPropertyValue_NoBroadcast( sal_Int32 /*nHandle*/, + const css::uno::Any& /*aValue*/ ) + throw(css::uno::Exception) +{ + // not needed currently +} + +//----------------------------------------------- +void SAL_CALL AutoRecovery::getFastPropertyValue(css::uno::Any& aValue , + sal_Int32 nHandle) const +{ + switch(nHandle) + { + case AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA : + { + sal_Bool bSessionData = sal_False; + ::comphelper::ConfigurationHelper::readDirectKey( + m_xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_RECOVERYINFO, + CFG_ENTRY_SESSIONDATA, + ::comphelper::ConfigurationHelper::E_READONLY) >>= bSessionData; + + sal_Bool bRecoveryData = ((sal_Bool)(m_lDocCache.size()>0)); + + // exists session data ... => then we cant say, that these + // data are valid for recovery. So we have to return FALSE then! + if (bSessionData) + bRecoveryData = sal_False; + + aValue <<= bRecoveryData; + } + break; + + case AUTORECOVERY_PROPHANDLE_CRASHED : + aValue = ::comphelper::ConfigurationHelper::readDirectKey( + m_xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_RECOVERYINFO, + CFG_ENTRY_CRASHED, + ::comphelper::ConfigurationHelper::E_READONLY); + break; + + case AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA : + aValue = ::comphelper::ConfigurationHelper::readDirectKey( + m_xSMGR, + CFG_PACKAGE_RECOVERY, + CFG_PATH_RECOVERYINFO, + CFG_ENTRY_SESSIONDATA, + ::comphelper::ConfigurationHelper::E_READONLY); + break; + } +} + +//----------------------------------------------- +const css::uno::Sequence< css::beans::Property > impl_getStaticPropertyDescriptor() +{ + static const css::beans::Property pPropertys[] = + { + css::beans::Property( AUTORECOVERY_PROPNAME_CRASHED , AUTORECOVERY_PROPHANDLE_CRASHED , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_RECOVERYDATA, AUTORECOVERY_PROPHANDLE_EXISTS_RECOVERYDATA, ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( AUTORECOVERY_PROPNAME_EXISTS_SESSIONDATA , AUTORECOVERY_PROPHANDLE_EXISTS_SESSIONDATA , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + }; + static const css::uno::Sequence< css::beans::Property > lPropertyDescriptor(pPropertys, AUTORECOVERY_PROPCOUNT); + return lPropertyDescriptor; +} + +//----------------------------------------------- +::cppu::IPropertyArrayHelper& SAL_CALL AutoRecovery::getInfoHelper() +{ + static ::cppu::OPropertyArrayHelper* pInfoHelper = 0; + if(!pInfoHelper) + { + ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() ); + if(!pInfoHelper) + { + static ::cppu::OPropertyArrayHelper aInfoHelper(impl_getStaticPropertyDescriptor(), sal_True); + pInfoHelper = &aInfoHelper; + } + } + + return (*pInfoHelper); +} + +//----------------------------------------------- +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL AutoRecovery::getPropertySetInfo() + throw(css::uno::RuntimeException) +{ + static css::uno::Reference< css::beans::XPropertySetInfo >* pInfo = 0; + if(!pInfo) + { + ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() ); + if(!pInfo) + { + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(createPropertySetInfo(getInfoHelper())); + pInfo = &xInfo; + } + } + + return (*pInfo); +} + +//----------------------------------------------- +void AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() +{ + LOG_RECOVERY("AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList() ...") + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + try + { + css::uno::Reference< css::frame::XFramesSupplier > xDesktop( + xSMGR->createInstance(SERVICENAME_DESKTOP), + css::uno::UNO_QUERY_THROW); + + css::uno::Reference< css::container::XIndexAccess > xContainer( + xDesktop->getFrames(), + css::uno::UNO_QUERY_THROW); + + sal_Int32 i = 0; + sal_Int32 c = xContainer->getCount(); + + for (i=0; i<c; ++i) + { + css::uno::Reference< css::frame::XFrame > xFrame; + try + { + xContainer->getByIndex(i) >>= xFrame; + if (!xFrame.is()) + continue; + } + // can happen in multithreaded environments, that frames was removed from the container during this loop runs! + // Ignore it. + catch(const css::lang::IndexOutOfBoundsException&) + { continue; } + + // We are interested on visible documents only. + // Note: It's n optional interface .-( + css::uno::Reference< css::awt::XWindow2 > xVisibleCheck( + xFrame->getContainerWindow(), + css::uno::UNO_QUERY); + if ( + (!xVisibleCheck.is() ) || + (!xVisibleCheck->isVisible()) + ) + { + continue; + } + + // extract the model from the frame. + // Ignore "view only" frames, which does not have a model. + css::uno::Reference< css::frame::XController > xController; + css::uno::Reference< css::frame::XModel > xModel; + + xController = xFrame->getController(); + if (xController.is()) + xModel = xController->getModel(); + if (!xModel.is()) + continue; + + // insert model into cache ... + // If the model is already well known inside cache + // it's information set will be updated by asking the + // model again for it's new states. + implts_registerDocument(xModel); + } + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::uno::Exception&) + {} + + LOG_RECOVERY("... AutoRecovery::implts_verifyCacheAgainstDesktopDocumentList()") +} + +//----------------------------------------------- +sal_Bool AutoRecovery::impl_enoughDiscSpace(sal_Int32 nRequiredSpace) +{ + #ifdef SIMULATE_FULL_DISC + return sal_False; + #endif + + // In case an error occures and we are not able to retrieve the needed information + // it's better to "disable" the feature ShowErrorOnFullDisc ! + // Otherwhise we start a confusing process of error handling ... + + sal_uInt64 nFreeSpace = SAL_MAX_UINT64; + + ::rtl::OUString sBackupPath(SvtPathOptions().GetBackupPath()); + ::osl::VolumeInfo aInfo (VolumeInfoMask_FreeSpace); + ::osl::FileBase::RC aRC = ::osl::Directory::getVolumeInfo(sBackupPath, aInfo); + + if ( + (aInfo.isValid(VolumeInfoMask_FreeSpace)) && + (aRC == ::osl::FileBase::E_None ) + ) + { + nFreeSpace = aInfo.getFreeSpace(); + } + + sal_uInt64 nFreeMB = (nFreeSpace/1048576); + return (nFreeMB >= (sal_uInt64)nRequiredSpace); +} + +//----------------------------------------------- +void AutoRecovery::impl_showFullDiscError() +{ + static String PLACEHOLDER_PATH = String::CreateFromAscii("%PATH"); + + String sBtn(FwkResId(STR_FULL_DISC_RETRY_BUTTON)); + String sMsg(FwkResId(STR_FULL_DISC_MSG )); + + String sBackupURL(SvtPathOptions().GetBackupPath()); + INetURLObject aConverter(sBackupURL); + sal_Unicode aDelimiter; + String sBackupPath = aConverter.getFSysPath(INetURLObject::FSYS_DETECT, &aDelimiter); + if (sBackupPath.Len()<1) + sBackupPath = sBackupURL; + sMsg.SearchAndReplace(PLACEHOLDER_PATH, sBackupPath); + + ErrorBox dlgError(0, WB_OK, sMsg); + dlgError.SetButtonText(dlgError.GetButtonId(0), sBtn); + dlgError.Execute(); +} + +//----------------------------------------------- +void AutoRecovery::impl_establishProgress(const AutoRecovery::TDocumentInfo& rInfo , + ::comphelper::MediaDescriptor& rArgs , + const css::uno::Reference< css::frame::XFrame >& xNewFrame) +{ + // external well known frame must be preferred (because it was created by ourself + // for loading documents into this frame)! + // But if no frame exists ... we can try to locate it using any frame bound to the provided + // document. Of course we must live without any frame in case the document does not exists at this + // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-) + css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame; + if ( + (!xFrame.is() ) && + (rInfo.Document.is()) + ) + { + css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController(); + if (xController.is()) + xFrame = xController->getFrame(); + } + + // Any outside progress must be used ... + // Only if there is no progress, we can create our own one. + css::uno::Reference< css::task::XStatusIndicator > xInternalProgress; + css::uno::Reference< css::task::XStatusIndicator > xExternalProgress = rArgs.getUnpackedValueOrDefault( + ::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(), + css::uno::Reference< css::task::XStatusIndicator >() ); + + // Normaly a progress is set from outside (e.g. by the CrashSave/Recovery dialog, which uses our dispatch API). + // But for a normal auto save we dont have such "external progress"... because this function is triggered by our own timer then. + // In such case we must create our own progress ! + if ( + (! xExternalProgress.is()) && + (xFrame.is() ) + ) + { + css::uno::Reference< css::task::XStatusIndicatorFactory > xProgressFactory(xFrame, css::uno::UNO_QUERY); + if (xProgressFactory.is()) + xInternalProgress = xProgressFactory->createStatusIndicator(); + } + + // HACK + // An external provided progress (most given by the CrashSave/Recovery dialog) + // must be preferred. But we know that some application filters query it's own progress instance + // at the frame method Frame::createStatusIndicator(). + // So we use a two step mechanism: + // 1) we set the progress inside the MediaDescriptor, which will be provided to the filter + // 2) and we set a special Frame property, which overwrites the normal behaviour of Frame::createStatusIndicator .-) + // But we supress 2) in case we uses an internal progress. Because then it doesnt matter + // if our applications make it wrong. In such case the internal progress resists at the same frame + // and there is no need to forward progress activities to e.g. an outside dialog .-) + if ( + (xExternalProgress.is()) && + (xFrame.is() ) + ) + { + css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY); + if (xFrameProps.is()) + xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(xExternalProgress)); + } + + // But inside the MediaDescriptor we must set our own create progress ... + // in case there is not already anothe rprogress set. + rArgs.createItemIfMissing(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR(), xInternalProgress); +} + +//----------------------------------------------- +void AutoRecovery::impl_forgetProgress(const AutoRecovery::TDocumentInfo& rInfo , + ::comphelper::MediaDescriptor& rArgs , + const css::uno::Reference< css::frame::XFrame >& xNewFrame) +{ + // external well known frame must be preferred (because it was created by ourself + // for loading documents into this frame)! + // But if no frame exists ... we can try to locate it using any frame bound to the provided + // document. Of course we must live without any frame in case the document does not exists at this + // point. But this state shouldnt occure. In such case xNewFrame should be valid ... hopefully .-) + css::uno::Reference< css::frame::XFrame > xFrame = xNewFrame; + if ( + (!xFrame.is() ) && + (rInfo.Document.is()) + ) + { + css::uno::Reference< css::frame::XController > xController = rInfo.Document->getCurrentController(); + if (xController.is()) + xFrame = xController->getFrame(); + } + + // stop progress interception on corresponding frame. + css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY); + if (xFrameProps.is()) + xFrameProps->setPropertyValue(FRAME_PROPNAME_INDICATORINTERCEPTION, css::uno::makeAny(css::uno::Reference< css::task::XStatusIndicator >())); + + // forget progress inside list of arguments. + ::comphelper::MediaDescriptor::iterator pArg = rArgs.find(::comphelper::MediaDescriptor::PROP_STATUSINDICATOR()); + if (pArg != rArgs.end()) + { + rArgs.erase(pArg); + pArg = rArgs.end(); + } +} + +//----------------------------------------------- +void AutoRecovery::impl_flushALLConfigChanges() +{ + try + { + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::uno::XInterface > xRecoveryCfg(m_xRecoveryCFG, css::uno::UNO_QUERY); + aReadLock.unlock(); + // <- SAFE + + if (xRecoveryCfg.is()) + ::comphelper::ConfigurationHelper::flush(xRecoveryCfg); + + // SOLAR SAFE -> + ::vos::OGuard aGuard( Application::GetSolarMutex() ); + ::utl::ConfigManager* pCfgMgr = ::utl::ConfigManager::GetConfigManager(); + if (pCfgMgr) + pCfgMgr->StoreConfigItems(); + } + catch(const css::uno::Exception&) + {} +} + +//----------------------------------------------- +void AutoRecovery::st_impl_removeFile(const ::rtl::OUString& sURL) +{ + if ( ! sURL.getLength()) + return; + + try + { + ::ucbhelper::Content aContent = ::ucbhelper::Content(sURL, css::uno::Reference< css::ucb::XCommandEnvironment >()); + aContent.executeCommand(::rtl::OUString::createFromAscii("delete"), css::uno::makeAny(sal_True)); + } + catch(const css::uno::Exception&) + {} +} + +//----------------------------------------------- +void AutoRecovery::st_impl_removeLockFile() +{ + try + { + ::rtl::OUString sUserURL; + ::utl::Bootstrap::locateUserInstallation( sUserURL ); + + ::rtl::OUStringBuffer sLockURLBuf; + sLockURLBuf.append (sUserURL); + sLockURLBuf.appendAscii("/.lock"); + ::rtl::OUString sLockURL = sLockURLBuf.makeStringAndClear(); + + AutoRecovery::st_impl_removeFile(sLockURL); + } + catch(const css::uno::Exception&) + {} +} + +} // namespace framework diff --git a/framework/source/services/backingcomp.cxx b/framework/source/services/backingcomp.cxx new file mode 100644 index 000000000000..13c5647341e6 --- /dev/null +++ b/framework/source/services/backingcomp.cxx @@ -0,0 +1,874 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +#include "services/backingcomp.hxx" + +#include "backingwindow.hxx" + +//_______________________________________________ +// own includes +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <classes/droptargetlistener.hxx> +#include <helper/acceleratorinfo.hxx> +#include <targets.h> +#include <properties.h> +#include <services.h> + +#ifndef _FRAMEWORK_HELPID_HRC +#include <helpid.hrc> +#endif + +//_______________________________________________ +// interface includes +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/awt/XDataTransferProviderAccess.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/frame/XLayoutManager.hpp> + +//_______________________________________________ +// other includes +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/factory.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/keycod.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/svapp.hxx> +#include <tools/resmgr.hxx> +#include <tools/urlobj.hxx> +#include <rtl/ustrbuf.hxx> + +#ifndef _SOLAR_HRC +#include <svl/solar.hrc> +#endif +#include <svl/urihelper.hxx> +#include <osl/file.hxx> +#include <unotools/configmgr.hxx> + +#ifndef _UTL_BOOTSTRAP_HXX_ +#include <unotools/bootstrap.hxx> +#endif + +namespace framework +{ + +//_______________________________________________ + +//_______________________________________________ + +BackingComp::BackingComp( const css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR ) + : ThreadHelpBase (&Application::GetSolarMutex() ) + , m_xSMGR (xSMGR ) +{ +} + +//_______________________________________________ + +BackingComp::~BackingComp() +{ +} + +//_______________________________________________ + +/** return information about supported interfaces. + + Some interfaces are supported by his class directly, but some other ones are + used by aggregation. An instance of this class must provide some window interfaces. + But it must represent a VCL window behind such interfaces too! So we use an internal + saved window member to ask it for it's interfaces and return it. But we must be aware then, + that it can be destroyed from outside too ... + + @param aType + describe the required interface type + + @return An Any holding the instance, which provides the queried interface. + Note: There exist two possible results ... this instance itself and her window member! + */ + +css::uno::Any SAL_CALL BackingComp::queryInterface( /*IN*/ const css::uno::Type& aType ) + throw(css::uno::RuntimeException) +{ + css::uno::Any aResult; + + // first look for own supported interfaces + aResult = ::cppu::queryInterface( + aType, + static_cast< css::lang::XTypeProvider* >(this), + static_cast< css::lang::XServiceInfo* >(this), + static_cast< css::lang::XInitialization* >(this), + static_cast< css::frame::XController* >(this), + static_cast< css::lang::XComponent* >(this), + static_cast< css::lang::XEventListener* >(this), + static_cast< css::awt::XKeyListener* >(static_cast< css::lang::XEventListener* >(this))); + + // then look for supported window interfaces + // Note: They exist only, if this instance was initialized + // with a valid window reference. It's aggregation on demand ... + if (!aResult.hasValue()) + { + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + if (m_xWindow.is()) + aResult = m_xWindow->queryInterface(aType); + aReadLock.unlock(); + /* } SAFE */ + } + + // look for XWeak and XInterface + if (!aResult.hasValue()) + aResult = OWeakObject::queryInterface(aType); + + return aResult; +} + +//_______________________________________________ + +/** increase ref count of this instance. + */ + +void SAL_CALL BackingComp::acquire() + throw() +{ + OWeakObject::acquire(); +} + +//_______________________________________________ + +/** decrease ref count of this instance. + */ + +void SAL_CALL BackingComp::release() + throw() +{ + OWeakObject::release(); +} + +//_______________________________________________ + +/** return collection about all supported interfaces. + + Optimize this method ! + We initialize a static variable only one time. + And we don't must use a mutex at every call! + For the first call; pTypeCollection is NULL - + for the second call pTypeCollection is different from NULL! + + @return A list of all supported interface types. +*/ + +css::uno::Sequence< css::uno::Type > SAL_CALL BackingComp::getTypes() + throw(css::uno::RuntimeException) +{ + static ::cppu::OTypeCollection* pTypeCollection = NULL; + if (!pTypeCollection) + { + /* GLOBAL SAFE { */ + ::osl::MutexGuard aGlobalLock(::osl::Mutex::getGlobalMutex()); + // Control these pointer again ... it can be, that another instance will be faster then this one! + if (!pTypeCollection) + { + /* LOCAL SAFE { */ + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XTypeProvider > xProvider(m_xWindow, css::uno::UNO_QUERY); + aReadLock.unlock(); + /* } LOCAL SAFE */ + + css::uno::Sequence< css::uno::Type > lWindowTypes; + if (xProvider.is()) + lWindowTypes = xProvider->getTypes(); + + static ::cppu::OTypeCollection aTypeCollection( + ::getCppuType((const ::com::sun::star::uno::Reference< css::lang::XInitialization >*)NULL ), + ::getCppuType((const ::com::sun::star::uno::Reference< css::lang::XTypeProvider >*)NULL ), + ::getCppuType((const ::com::sun::star::uno::Reference< css::lang::XServiceInfo >*)NULL ), + ::getCppuType((const ::com::sun::star::uno::Reference< css::frame::XController >*)NULL ), + ::getCppuType((const ::com::sun::star::uno::Reference< css::lang::XComponent >*)NULL ), + lWindowTypes); + + pTypeCollection = &aTypeCollection; + } + /* } GLOBAL SAFE */ + } + return pTypeCollection->getTypes(); +} + +//_______________________________________________ + +/** create one unique Id for all instances of this class. + + Optimize this method + We initialize a static variable only one time. And we don't must use a mutex at every call! + For the first call; pID is NULL - for the second call pID is different from NULL! + + @return A byte array, which represent the unique id. +*/ + +css::uno::Sequence< sal_Int8 > SAL_CALL BackingComp::getImplementationId() + throw(css::uno::RuntimeException) +{ + static ::cppu::OImplementationId* pID = NULL; + if (!pID) + { + /* GLOBAL SAFE { */ + ::osl::MutexGuard aLock(::osl::Mutex::getGlobalMutex()); + // Control these pointer again ... it can be, that another instance will be faster then this one! + if (!pID) + { + static ::cppu::OImplementationId aID(sal_False); + pID = &aID; + } + /* } GLOBAL SAFE */ + } + return pID->getImplementationId(); +} + +//_______________________________________________ + +/** returns a static implementation name for this UNO service. + + Because this value is needed at different places and our class is used + by some generic macros too, we have to use a static impl method for that! + + @see impl_getStaticImplementationName() + @see IMPLEMENTATIONNAME + + @return The implementation name of this class. +*/ + +::rtl::OUString SAL_CALL BackingComp::getImplementationName() + throw(css::uno::RuntimeException) +{ + return impl_getStaticImplementationName(); +} + +//_______________________________________________ + +/** returns information about supported services. + + Because this value is needed at different places and our class is used + by some generic macros too, we have to use a static impl method for that! + + @see impl_getStaticSupportedServiceNames() + @see SERVICENAME + + @return <TRUE/> if the queried service is supported; + <br><FALSE/> otherwise. +*/ + +sal_Bool SAL_CALL BackingComp::supportsService( /*IN*/ const ::rtl::OUString& sServiceName ) + throw(css::uno::RuntimeException) +{ + return ( + sServiceName.equals(SERVICENAME_STARTMODULE ) || + sServiceName.equals(SERVICENAME_FRAMECONTROLLER) + ); +} + +//_______________________________________________ + +/** returns collection of supported services. + + Because this value is needed at different places and our class is used + by some generic macros too, we have to use a static impl method for that! + + @see impl_getStaticSupportedServiceNames() + @see SERVICENAME + + @return A list of all supported uno service names. +*/ + +css::uno::Sequence< ::rtl::OUString > SAL_CALL BackingComp::getSupportedServiceNames() + throw(css::uno::RuntimeException) +{ + return impl_getStaticSupportedServiceNames(); +} + +//_______________________________________________ + +/** returns static implementation name. + + Because this value is needed at different places and our class is used + by some generic macros too, we have to use a static impl method for that! + + @see impl_getStaticSupportedServiceNames() + @see SERVICENAME + + @return The implementation name of this class. +*/ + +::rtl::OUString BackingComp::impl_getStaticImplementationName() +{ + return IMPLEMENTATIONNAME_STARTMODULE; +} + +//_______________________________________________ + +/** returns static list of supported service names. + + Because this value is needed at different places and our class is used + by some generic macros too, we have to use a static impl method for that! + + @see impl_getStaticSupportedServiceNames() + @see SERVICENAME + + @return A list of all supported uno service names. +*/ + +css::uno::Sequence< ::rtl::OUString > BackingComp::impl_getStaticSupportedServiceNames() +{ + css::uno::Sequence< ::rtl::OUString > lNames(1); + lNames[0] = SERVICENAME_STARTMODULE; + return lNames; +} + +//_______________________________________________ + +/** returns a new instance of this class. + + This factory method is registered inside the UNO runtime + and will be called for every createInstance() request from outside, + which wish to use this service. + + @param xSMGR + reference to the uno service manager, which call us + We use it too, to set it at the new created instance. + + @return A new instance as uno reference. +*/ + +css::uno::Reference< css::uno::XInterface > SAL_CALL BackingComp::impl_createInstance( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) + throw(css::uno::Exception) +{ + BackingComp* pObject = new BackingComp(xSMGR); + return css::uno::Reference< css::uno::XInterface >(static_cast< ::cppu::OWeakObject* >(pObject), css::uno::UNO_QUERY); +} + +//_______________________________________________ + +/** returns a new factory instance for instances of this class. + + It uses a helper class of the cppuhelper project as factory. + It will be initialized with all neccessary informations and + will be able afterwards to create instance of this class. + This factory call us back inside our method impl_createInstance(). + So we can create and initialize ourself. Only filtering of creation + requests will be done by this factory. + + @param xSMGR + reference to the uno service manager, which call us + + @return A new instance of our factory. +*/ + +css::uno::Reference< css::lang::XSingleServiceFactory > BackingComp::impl_createFactory( /*IN*/ const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) +{ + css::uno::Reference< css::lang::XSingleServiceFactory > xReturn( + cppu::createSingleFactory( + xSMGR, + BackingComp::impl_getStaticImplementationName(), + BackingComp::impl_createInstance, + BackingComp::impl_getStaticSupportedServiceNames())); + return xReturn; +} + +//_______________________________________________ + +/** + attach this component to a target frame. + + We has to use the container window of this frame as parent window of our own component window. + But it's not allowed to work with it realy. May another component used it too. + Currently we need it only to create our child component window and support it's + interfaces inside our queryInterface() method. The user of us must have e.g. the + XWindow interface of it to be able to call setComponent(xWindow,xController) at the + frame! + + May he will do the following things: + + <listing> + XController xBackingComp = (XController)UnoRuntime.queryInterface( + XController.class, + xSMGR.createInstance(SERVICENAME_STARTMODULE)); + + // at this time XWindow isn't present at this instance! + XWindow xBackingComp = (XWindow)UnoRuntime.queryInterface( + XWindow.class, + xBackingComp); + + // attach controller to the frame + // We will use it's container window, to create + // the component window. From now we offer the window interfaces! + xBackingComp.attachFrame(xFrame); + + XWindow xBackingComp = (XWindow)UnoRuntime.queryInterface( + XWindow.class, + xBackingComp); + + // Our user can set us at the frame as new component + xFrame.setComponent(xBackingWin, xBackingComp); + + // But that had no effect to our view state. + // We must be started to create our UI elements like e.g. menu, title, background ... + XInitialization xBackingInit = (XInitialization)UnoRuntime.queryInterface( + XInitialization.class, + xBackingComp); + + xBackingInit.initialize(lArgs); + </listing> + + @param xFrame + reference to our new target frame + + @throw com::sun::star::uno::RuntimeException + if the given frame reference is wrong or component window couldn't be created + successfully. + We throw it too, if we already attached to a frame. Because we don't support + reparenting of our component window on demand! +*/ + +void SAL_CALL BackingComp::attachFrame( /*IN*/ const css::uno::Reference< css::frame::XFrame >& xFrame ) + throw (css::uno::RuntimeException) +{ + /* SAFE */ + WriteGuard aWriteLock(m_aLock); + + // check some required states + if (m_xFrame.is()) + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("already attached"), + static_cast< ::cppu::OWeakObject* >(this)); + + if (!xFrame.is()) + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("invalid frame reference"), + static_cast< ::cppu::OWeakObject* >(this)); + + if (!m_xWindow.is()) + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("instance seams to be not or wrong initialized"), + static_cast< ::cppu::OWeakObject* >(this)); + + // safe the frame reference + m_xFrame = xFrame; + + // establish drag&drop mode + ::framework::DropTargetListener* pDropListener = new ::framework::DropTargetListener(m_xSMGR, m_xFrame); + m_xDropTargetListener = css::uno::Reference< css::datatransfer::dnd::XDropTargetListener >(static_cast< ::cppu::OWeakObject* >(pDropListener), css::uno::UNO_QUERY); + + css::uno::Reference< css::awt::XDataTransferProviderAccess > xTransfer(m_xSMGR->createInstance(SERVICENAME_VCLTOOLKIT), css::uno::UNO_QUERY); + if (xTransfer.is()) + { + css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = xTransfer->getDropTarget(m_xWindow); + if (xDropTarget.is()) + { + xDropTarget->addDropTargetListener(m_xDropTargetListener); + xDropTarget->setActive(sal_True); + } + } + + // initialize the component and it's parent window + css::uno::Reference< css::awt::XWindow > xParentWindow = xFrame->getContainerWindow(); + WorkWindow* pParent = (WorkWindow*)VCLUnoHelper::GetWindow(xParentWindow); + Window* pWindow = VCLUnoHelper::GetWindow(m_xWindow); + + // disable full screen mode of the frame! + if (pParent->IsFullScreenMode()) + { + pParent->ShowFullScreenMode(FALSE); + pParent->SetMenuBarMode(MENUBAR_MODE_NORMAL); + } + + // create the menu bar for the backing component + css::uno::Reference< css::beans::XPropertySet > xPropSet(m_xFrame, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xPropSet->getPropertyValue(FRAME_PROPNAME_LAYOUTMANAGER) >>= xLayoutManager; + if (xLayoutManager.is()) + { + xLayoutManager->lock(); + xLayoutManager->createElement( DECLARE_ASCII( "private:resource/menubar/menubar" )); + /* #i85963# new backing window comes withoud standard bar and statusbar + xLayoutManager->createElement( DECLARE_ASCII( "private:resource/toolbar/standardbar" )); + xLayoutManager->createElement( DECLARE_ASCII( "private:resource/statusbar/statusbar" )); + xLayoutManager->showElement ( DECLARE_ASCII( "private:resource/toolbar/standardbar" )); + xLayoutManager->showElement ( DECLARE_ASCII( "private:resource/statusbar/statusbar" )); + */ + xLayoutManager->unlock(); + } + + // set help ID for our canvas + pWindow->SetHelpId(HID_BACKINGWINDOW); + + // inform BackingWindow about frame + BackingWindow* pBack = dynamic_cast<BackingWindow*>(pWindow ); + if( pBack ) + pBack->setOwningFrame( m_xFrame ); + + aWriteLock.unlock(); + /* } SAFE */ +} + +//_______________________________________________ + +/** not supported. + + This component does not know any model. It will be represented by a window and + it's controller only. + + return <FALSE/> everytime. + */ + +sal_Bool SAL_CALL BackingComp::attachModel( /*IN*/ const css::uno::Reference< css::frame::XModel >& ) + throw (css::uno::RuntimeException) +{ + return sal_False; +} + +//_______________________________________________ + +/** not supported. + + This component does not know any model. It will be represented by a window and + it's controller only. + + return An empty reference every time. + */ + +css::uno::Reference< css::frame::XModel > SAL_CALL BackingComp::getModel() + throw (css::uno::RuntimeException) +{ + return css::uno::Reference< css::frame::XModel >(); +} + +//_______________________________________________ + +/** not supported. + + return An empty value. + */ + +css::uno::Any SAL_CALL BackingComp::getViewData() + throw (css::uno::RuntimeException) +{ + return css::uno::Any(); +} + +//_______________________________________________ + +/** not supported. + + @param aData + not used. + */ + +void SAL_CALL BackingComp::restoreViewData( /*IN*/ const css::uno::Any& ) + throw (css::uno::RuntimeException) +{ +} + +//_______________________________________________ + +/** returns the attached frame for this component. + + @see attachFrame() + + @return The internaly saved frame reference. + Can be null, if attachFrame() was not called before. + */ + +css::uno::Reference< css::frame::XFrame > SAL_CALL BackingComp::getFrame() + throw (css::uno::RuntimeException) +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + return m_xFrame; + /* } SAFE */ +} + +//_______________________________________________ + +/** ask controller for it's current working state. + + If somehwere whish to close this component, it must suspend the controller before. + That will be a chance for it to disagree with that AND show any UI for a possible + UI user. + + @param bSuspend + If its set to TRUE this controller should be suspended. + FALSE will resuspend it. + + @return TRUE if the request could be finished successfully; FALSE otherwise. + */ + +sal_Bool SAL_CALL BackingComp::suspend( /*IN*/ sal_Bool ) + throw (css::uno::RuntimeException) +{ + /* FIXME ... implemented by using default :-( */ + return sal_True; +} + +//_______________________________________________ + +/** callback from our window member. + + Our internal saved window wish to die. It will be disposed from outside (may be the frame) + and inform us. We must release its reference only here. Of course we check the given reference + here and reject callback from unknown sources. + + Note: deregistration as listener isnt neccessary here. The broadcaster do it automaticly. + + @param aEvent + describe the broadcaster of this callback + + @throw ::com::sun::star::uno::RuntimeException + if the broadcaster doesn't represent the expected window reference. +*/ + +void SAL_CALL BackingComp::disposing( /*IN*/ const css::lang::EventObject& aEvent ) + throw(css::uno::RuntimeException) +{ + // Attention: dont free m_pAccExec here! see comments inside dtor and + // keyPressed() for further details. + + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + if (!aEvent.Source.is() || aEvent.Source!=m_xWindow || !m_xWindow.is()) + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("unexpected source or called twice"), + static_cast< ::cppu::OWeakObject* >(this)); + + m_xWindow = css::uno::Reference< css::awt::XWindow >(); + + aWriteLock.unlock(); + /* } SAFE */ +} + +//_______________________________________________ + +/** kill this instance. + + It can be called from our owner frame only. But there is no possibility to check the calli. + We have to release all our internal used ressources and die. From this point we can throw + DisposedExceptions for every further interface request ... but current implementation doesn`t do so ... + +*/ + +void SAL_CALL BackingComp::dispose() + throw(css::uno::RuntimeException) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + // kill the menu + css::util::URL aURL; + aURL.Complete = DECLARE_ASCII(".uno:close"); + css::uno::Reference< css::util::XURLTransformer > xParser(m_xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), css::uno::UNO_QUERY); + if (xParser.is()) + xParser->parseStrict(aURL); + + css::uno::Reference< css::frame::XDispatchProvider > xProvider(m_xFrame, css::uno::UNO_QUERY); + if (xProvider.is()) + { + css::uno::Reference< css::frame::XDispatch > xDispatch = xProvider->queryDispatch(aURL, SPECIALTARGET_MENUBAR, 0); + if (xDispatch.is()) + xDispatch->dispatch(aURL, css::uno::Sequence< css::beans::PropertyValue>()); + } + + // deregister drag&drop helper + if (m_xDropTargetListener.is()) + { + css::uno::Reference< css::awt::XDataTransferProviderAccess > xTransfer(m_xSMGR->createInstance(SERVICENAME_VCLTOOLKIT), css::uno::UNO_QUERY); + if (xTransfer.is()) + { + css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = xTransfer->getDropTarget(m_xWindow); + if (xDropTarget.is()) + { + xDropTarget->removeDropTargetListener(m_xDropTargetListener); + xDropTarget->setActive(sal_False); + } + } + m_xDropTargetListener = css::uno::Reference< css::datatransfer::dnd::XDropTargetListener >(); + } + + // stop listening at the window + if (m_xWindow.is()) + { + css::uno::Reference< css::lang::XComponent > xBroadcaster(m_xWindow, css::uno::UNO_QUERY); + if (xBroadcaster.is()) + { + css::uno::Reference< css::lang::XEventListener > xEventThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + xBroadcaster->removeEventListener(xEventThis); + } + css::uno::Reference< css::awt::XKeyListener > xKeyThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + m_xWindow->removeKeyListener(xKeyThis); + m_xWindow = css::uno::Reference< css::awt::XWindow >(); + } + + // forget all other used references + m_xFrame = css::uno::Reference< css::frame::XFrame >(); + m_xSMGR = css::uno::Reference< css::lang::XMultiServiceFactory >(); + + aWriteLock.unlock(); + /* } SAFE */ +} + +//_______________________________________________ + +/** not supported. + + @param xListener + not used. + + @throw ::com::sun::star::uno::RuntimeException + because the listener expect to be holded alive by this container. + We must inform it about this unsupported feature. + */ + +void SAL_CALL BackingComp::addEventListener( /*IN*/ const css::uno::Reference< css::lang::XEventListener >& ) + throw(css::uno::RuntimeException) +{ + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("not supported"), + static_cast< ::cppu::OWeakObject* >(this)); +} + +//_______________________________________________ + +/** not supported. + + Because registration is not supported too, we must do nothing here. Nobody can call this method realy. + + @param xListener + not used. + */ + +void SAL_CALL BackingComp::removeEventListener( /*IN*/ const css::uno::Reference< css::lang::XEventListener >& ) + throw(css::uno::RuntimeException) +{ +} + +//_______________________________________________ + +/** + force initialiation for this component. + + Inside attachFrame() we created our component window. But it was not allowed there, to + initialitze it. E.g. the menu must be set at the container window of the frame, which + is our parent window. But may at that time another component used it. + That's why our creator has to inform us, when it's time to initialize us realy. + Currently only calling of this method must be done. But further implementatoins + can use special in parameter to configure this initialization ... + + @param lArgs + currently not used + + @throw com::sun::star::uno::RuntimeException + if some ressources are missing + Means if may be attachedFrame() wasn't called before. + */ + +void SAL_CALL BackingComp::initialize( /*IN*/ const css::uno::Sequence< css::uno::Any >& lArgs ) + throw(css::uno::Exception, css::uno::RuntimeException) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + if (m_xWindow.is()) + throw css::uno::Exception( + ::rtl::OUString::createFromAscii("already initialized"), + static_cast< ::cppu::OWeakObject* >(this)); + + css::uno::Reference< css::awt::XWindow > xParentWindow; + if ( + (lArgs.getLength()!=1 ) || + (!(lArgs[0] >>= xParentWindow)) || + (!xParentWindow.is() ) + ) + { + throw css::uno::Exception( + ::rtl::OUString::createFromAscii("wrong or corrupt argument list"), + static_cast< ::cppu::OWeakObject* >(this)); + } + + // create the component window + Window* pParent = VCLUnoHelper::GetWindow(xParentWindow); + Window* pWindow = new BackingWindow(pParent); + m_xWindow = VCLUnoHelper::GetInterface(pWindow); + + if (!m_xWindow.is()) + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("couldn't create component window"), + static_cast< ::cppu::OWeakObject* >(this)); + + // start listening for window disposing + // It's set at our owner frame as component window later too. So it will may be disposed there ... + css::uno::Reference< css::lang::XComponent > xBroadcaster(m_xWindow, css::uno::UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->addEventListener(static_cast< css::lang::XEventListener* >(this)); + + m_xWindow->setVisible(sal_True); + + aWriteLock.unlock(); + /* } SAFE */ +} + +//_______________________________________________ + +/** + */ + +void SAL_CALL BackingComp::keyPressed( /*IN*/ const css::awt::KeyEvent& ) + throw(css::uno::RuntimeException) +{ +} + +//_______________________________________________ + +/** + */ + +void SAL_CALL BackingComp::keyReleased( /*IN*/ const css::awt::KeyEvent& ) + throw(css::uno::RuntimeException) +{ + /* Attention + Please use keyPressed() instead of this method. Otherwhise it would be possible, that + - a key input may be first switch to the backing mode + - and this component register itself as key listener too + - and it's first event will be a keyRealeased() for the already well known event, which switched to the backing mode! + So it will be handled twice! document => backing mode => exit app ... + */ +} + +} // namespace framework diff --git a/framework/source/services/backingwindow.cxx b/framework/source/services/backingwindow.cxx new file mode 100644 index 000000000000..8bb5ca5cbeaf --- /dev/null +++ b/framework/source/services/backingwindow.cxx @@ -0,0 +1,1139 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// autogen include statement, do not remove +#include "precompiled_framework.hxx" + +#include "backingwindow.hxx" +#include "framework.hrc" +#include "classes/fwkresid.hxx" +#include <services.h> + +#include "vcl/metric.hxx" +#include "vcl/mnemonic.hxx" +#include "vcl/menu.hxx" +#include "vcl/svapp.hxx" + +#include "tools/urlobj.hxx" + +#include "unotools/dynamicmenuoptions.hxx" +#include "unotools/historyoptions.hxx" +#include "svtools/imagemgr.hxx" +#include "svtools/svtools.hrc" + +#include "comphelper/processfactory.hxx" +#include "comphelper/sequenceashashmap.hxx" +#include "comphelper/configurationhelper.hxx" + +#include "cppuhelper/implbase1.hxx" + +#include "rtl/strbuf.hxx" +#include "rtl/ustrbuf.hxx" +#include "osl/file.h" + +#include "com/sun/star/lang/XMultiServiceFactory.hpp" +#include "com/sun/star/container/XNameAccess.hpp" +#include "com/sun/star/system/XSystemShellExecute.hpp" +#include "com/sun/star/system/SystemShellExecuteFlags.hpp" +#include "com/sun/star/task/XJobExecutor.hpp" +#include "com/sun/star/util/XStringWidth.hpp" + + +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; +using namespace framework; + +#define WRITER_URL "private:factory/swriter" +#define CALC_URL "private:factory/scalc" +#define IMPRESS_WIZARD_URL "private:factory/simpress?slot=6686" +#define DRAW_URL "private:factory/sdraw" +#define BASE_URL "private:factory/sdatabase?Interactive" +#define MATH_URL "private:factory/smath" +#define TEMPLATE_URL "slot:5500" +#define OPEN_URL ".uno:Open" + +DecoToolBox::DecoToolBox( Window* pParent, WinBits nStyle ) : + ToolBox( pParent, nStyle ) +{ + SetBackground(); + SetPaintTransparent( TRUE ); +} + +void DecoToolBox::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( rDCEvt.GetFlags() & SETTINGS_STYLE ) + { + calcMinSize(); + SetBackground(); + SetPaintTransparent( TRUE ); + } +} + +void DecoToolBox::calcMinSize() +{ + ToolBox aTbx( GetParent() ); + USHORT nItems = GetItemCount(); + for( USHORT i = 0; i < nItems; i++ ) + { + USHORT nId = GetItemId( i ); + aTbx.InsertItem( nId, GetItemImage( nId ) ); + } + aTbx.SetOutStyle( TOOLBOX_STYLE_FLAT ); + maMinSize = aTbx.CalcWindowSizePixel(); +} + +Size DecoToolBox::getMinSize() +{ + return maMinSize; +} + +class RecentFilesStringLength : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XStringWidth > +{ + public: + RecentFilesStringLength() {} + virtual ~RecentFilesStringLength() {} + + // XStringWidth + sal_Int32 SAL_CALL queryStringWidth( const ::rtl::OUString& aString ) + throw (::com::sun::star::uno::RuntimeException) + { + return aString.getLength(); + } +}; + +#define STC_BUTTON_STYLE (WB_LEFT | WB_VCENTER | WB_FLATBUTTON | WB_BEVELBUTTON) + +BackingWindow::BackingWindow( Window* i_pParent ) : + Window( i_pParent, FwkResId( DLG_BACKING ) ), + maWelcome( this, WB_LEFT ), + maProduct( this, WB_LEFT ), + maWriterButton( this, STC_BUTTON_STYLE ), + maCalcButton( this, STC_BUTTON_STYLE ), + maImpressButton( this, STC_BUTTON_STYLE ), + maOpenButton( this, STC_BUTTON_STYLE ), + maDrawButton( this, STC_BUTTON_STYLE ), + maDBButton( this, STC_BUTTON_STYLE ), + maMathButton( this, STC_BUTTON_STYLE ), + maTemplateButton( this, STC_BUTTON_STYLE ), + maToolbox( this, WB_DIALOGCONTROL ), + maWelcomeString( FwkResId( STR_BACKING_WELCOME ) ), + maProductString( FwkResId( STR_BACKING_WELCOMEPRODUCT ) ), + maOpenString( FwkResId( STR_BACKING_FILE ) ), + maTemplateString( FwkResId( STR_BACKING_TEMPLATE ) ), + maButtonImageSize( 10, 10 ), + mbInitControls( false ), + mnLayoutStyle( 0 ), + mpAccExec( NULL ), + mnBtnPos( 120 ), + mnBtnTop( 150 ), + mpRecentMenu( NULL ) +{ + mnColumnWidth[0] = mnColumnWidth[1] = 0; + mnTextColumnWidth[0] = mnTextColumnWidth[1] = 0; + + try + { + Reference<lang::XMultiServiceFactory> xConfig( comphelper::getProcessServiceFactory()->createInstance(SERVICENAME_CFGPROVIDER),UNO_QUERY); + if( xConfig.is() ) + { + Sequence<Any> args(1); + PropertyValue val( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("nodepath") ), + 0, + Any(rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("/org.openoffice.Office.Common/Help/StartCenter"))), + PropertyState_DIRECT_VALUE); + args.getArray()[0] <<= val; + Reference<container::XNameAccess> xNameAccess(xConfig->createInstanceWithArguments(SERVICENAME_CFGREADACCESS,args), UNO_QUERY); + if( xNameAccess.is() ) + { + //throws css::container::NoSuchElementException, css::lang::WrappedTargetException + Any value( xNameAccess->getByName(rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("StartCenterLayoutStyle"))) ); + mnLayoutStyle = value.get<sal_Int32>(); + } + } + } + catch (Exception& ) + { + } + + String aExtHelpText( FwkResId( STR_BACKING_EXTHELP ) ); + String aInfoHelpText( FwkResId( STR_BACKING_INFOHELP ) ); + String aTplRepHelpText( FwkResId( STR_BACKING_TPLREP ) ); + + // clean up resource stack + FreeResource(); + + maWelcome.SetPaintTransparent( TRUE ); + maProduct.SetPaintTransparent( TRUE ); + EnableChildTransparentMode(); + + SetStyle( GetStyle() | WB_DIALOGCONTROL ); + + // force tab cycling in toolbox + maToolbox.SetStyle( maToolbox.GetStyle() | WB_FORCETABCYCLE ); + + // insert toolbox items + maToolbox.InsertItem( nItemId_TplRep, Image() ); + maToolbox.SetItemText( nItemId_TplRep, aTplRepHelpText ); + maToolbox.SetQuickHelpText( nItemId_TplRep, aTplRepHelpText ); + maToolbox.SetItemCommand( nItemId_TplRep, String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:TemplateRepository" ) ) ); + maToolbox.ShowItem( nItemId_TplRep ); + + maToolbox.InsertItem( nItemId_Extensions, Image() ); + maToolbox.SetQuickHelpText( nItemId_Extensions, aExtHelpText ); + maToolbox.SetItemText( nItemId_Extensions, aExtHelpText ); + maToolbox.SetItemCommand( nItemId_Extensions, String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:Extensions" ) ) ); + maToolbox.ShowItem( nItemId_Extensions ); + + maToolbox.InsertItem( nItemId_Info, Image() ); + maToolbox.SetItemText( nItemId_Info, aInfoHelpText ); + maToolbox.SetQuickHelpText( nItemId_Info, aInfoHelpText ); + maToolbox.SetItemCommand( nItemId_Info, String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:Info" ) ) ); + maToolbox.ShowItem( nItemId_Info ); + + // get dispatch provider + mxDesktop = Reference<XDesktop>( comphelper::getProcessServiceFactory()->createInstance(SERVICENAME_DESKTOP ),UNO_QUERY ); + if( mxDesktop.is() ) + mxDesktopDispatchProvider = Reference< XDispatchProvider >( mxDesktop, UNO_QUERY ); + + maWriterButton.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:WriterButton" ) ) ) ); + maCalcButton.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:CalcButton" ) ) ) ); + maImpressButton.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:ImpressButton" ) ) ) ); + maDrawButton.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:DrawButton" ) ) ) ); + maDBButton.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:DBButton" ) ) ) ); + maMathButton.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:MathButton" ) ) ) ); + maTemplateButton.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:TemplateButton" ) ) ) ); + maOpenButton.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:OpenButton" ) ) ) ); + maToolbox.SetSmartHelpId( SmartId( String( RTL_CONSTASCII_USTRINGPARAM( ".HelpId:StartCenter:Toolbox" ) ) ) ); + + // init background + initBackground(); + + // add some breathing space for the images + maButtonImageSize.Width() += 12; + maButtonImageSize.Height() += 12; + +} + + +BackingWindow::~BackingWindow() +{ + delete mpRecentMenu; + delete mpAccExec; +} + +void BackingWindow::GetFocus() +{ + if( IsVisible() ) + maWriterButton.GrabFocus(); + Window::GetFocus(); +} + +class ImageContainerRes : public Resource +{ + public: + ImageContainerRes( const ResId& i_rId ) : Resource( i_rId ) {} + ~ImageContainerRes() { FreeResource(); } +}; + +void BackingWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( rDCEvt.GetFlags() & SETTINGS_STYLE ) + { + initBackground(); + Invalidate(); + } +} + +void BackingWindow::prepareRecentFileMenu() +{ + if( ! mpRecentMenu ) + mpRecentMenu = new PopupMenu(); + mpRecentMenu->Clear(); + maRecentFiles.clear(); + + // get recent file list and dispatch arguments + Sequence< Sequence< PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) ); + + sal_Int32 nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength(); + + if( ( nPickListMenuItems > 0 ) ) + { + maRecentFiles.reserve( nPickListMenuItems ); + for ( sal_Int32 i = 0; i < nPickListMenuItems; i++ ) + { + Sequence< PropertyValue >& rPickListEntry = aHistoryList[i]; + rtl::OUString aURL, aFilter, aFilterOpt, aTitle; + + for ( sal_Int32 j = 0; j < rPickListEntry.getLength(); j++ ) + { + const Any& a = rPickListEntry[j].Value; + + if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_URL ) + a >>= aURL; + else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_FILTER ) + { + a >>= aFilter; + sal_Int32 nPos = aFilter.indexOf( '|' ); + if ( nPos >= 0 ) + { + if ( nPos < ( aFilter.getLength() - 1 ) ) + aFilterOpt = aFilter.copy( nPos+1 ); + aFilter = aFilter.copy( 0, nPos-1 ); + } + } + else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_TITLE ) + a >>= aTitle; + } + maRecentFiles.push_back( LoadRecentFile() ); + maRecentFiles.back().aTargetURL = aURL; + + sal_Int32 nArgs = aFilterOpt.getLength() ? 4 : 3; + Sequence< PropertyValue >& rArgsList( maRecentFiles.back().aArgSeq ); + rArgsList.realloc( nArgs ); + + nArgs--; + rArgsList[nArgs].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" )); + rArgsList[nArgs].Value = makeAny( aFilter ); + + if( aFilterOpt.getLength() ) + { + nArgs--; + rArgsList[nArgs].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterOptions" )); + rArgsList[nArgs].Value = makeAny( aFilterOpt ); + } + + // documents in the picklist will never be opened as templates + nArgs--; + rArgsList[nArgs].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate" )); + rArgsList[nArgs].Value = makeAny( (sal_Bool) sal_False ); + + nArgs--; + rArgsList[nArgs].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer" )); + rArgsList[nArgs].Value = makeAny( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:user" ) ) ); + + // and finally create an entry in the popupmenu + rtl::OUString aMenuTitle; + INetURLObject aURLObj( aURL ); + + if ( aURLObj.GetProtocol() == INET_PROT_FILE ) + { + // Do handle file URL differently => convert it to a system + // path and abbreviate it with a special function: + String aFileSystemPath( aURLObj.getFSysPath( INetURLObject::FSYS_DETECT ) ); + + rtl::OUString aSystemPath( aFileSystemPath ); + rtl::OUString aCompactedSystemPath; + + oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL ); + if ( !nError ) + aMenuTitle = String( aCompactedSystemPath ); + else + aMenuTitle = aSystemPath; + } + else + { + // Use INetURLObject to abbreviate all other URLs + Reference< util::XStringWidth > xStringLength( new RecentFilesStringLength() ); + aMenuTitle = aURLObj.getAbbreviated( xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS ); + } + rtl::OUStringBuffer aBuf( aMenuTitle.getLength() + 5 ); + if( i < 9 ) + { + aBuf.append( sal_Unicode( '~' ) ); + aBuf.append( i+1 ); + } + else if( i == 9 ) + aBuf.appendAscii( "1~0" ); + else + aBuf.append( i+1 ); + aBuf.appendAscii( ": " ); + aBuf.append( aMenuTitle ); + mpRecentMenu->InsertItem( static_cast<USHORT>(i+1), aBuf.makeStringAndClear() ); + } + maOpenButton.SetPopupMenu( mpRecentMenu ); + } +} + +void BackingWindow::initBackground() +{ + SetBackground( GetSettings().GetStyleSettings().GetWorkspaceGradient() ); + + bool bDark = GetSettings().GetStyleSettings().GetHighContrastMode(); + if( bDark ) + maWelcomeTextColor = maLabelTextColor = Color( COL_WHITE ); + else if( mnLayoutStyle == 1 ) + maWelcomeTextColor = maLabelTextColor = Color( COL_BLACK ); + else + maWelcomeTextColor = maLabelTextColor = Color( 0x26, 0x35, 0x42 ); + + Color aTextBGColor( bDark ? COL_BLACK : COL_WHITE ); + + // select image set + ImageContainerRes aRes( FwkResId( bDark ? RES_BACKING_IMAGES_HC : RES_BACKING_IMAGES ) ); + + // scale middle segment + Size aMiddleSize; + if( !! maBackgroundMiddle ) + aMiddleSize = maBackgroundMiddle.GetSizePixel(); + // load middle segment + maBackgroundMiddle = BitmapEx( FwkResId( BMP_BACKING_BACKGROUND_MIDDLE ) ); + // and scale it to previous size + if( aMiddleSize.Width() && aMiddleSize.Height() ) + maBackgroundMiddle.Scale( aMiddleSize ); + + if( GetSettings().GetLayoutRTL() ) + { + // replace images by RTL versions + maBackgroundLeft = BitmapEx( FwkResId( BMP_BACKING_BACKGROUND_RTL_RIGHT ) ); + maBackgroundRight = BitmapEx( FwkResId( BMP_BACKING_BACKGROUND_RTL_LEFT) ); + } + else + { + maBackgroundLeft = BitmapEx( FwkResId( BMP_BACKING_BACKGROUND_LEFT ) ); + maBackgroundRight = BitmapEx( FwkResId( BMP_BACKING_BACKGROUND_RIGHT ) ); + } + maToolbox.SetItemImage( nItemId_Extensions, BitmapEx( FwkResId( BMP_BACKING_EXT ) ) ); + maToolbox.SetItemImage( nItemId_Info, BitmapEx( FwkResId( BMP_BACKING_INFO ) ) ); + maToolbox.SetItemImage( nItemId_TplRep, BitmapEx( FwkResId( BMP_BACKING_TPLREP ) ) ); + + maWelcome.SetControlForeground( maWelcomeTextColor ); + maWelcome.SetBackground(); + maProduct.SetControlForeground( maWelcomeTextColor ); + maProduct.SetBackground(); + + if( mnLayoutStyle == 1 ) + { + if( Application::GetSettings().GetLayoutRTL() ) + mnBtnPos = maBackgroundRight.GetSizePixel().Width() + 40; + else + mnBtnPos = maBackgroundLeft.GetSizePixel().Width() + 40; + } + + // get icon images from fwk resource and set them on the appropriate buttons + loadImage( FwkResId( BMP_BACKING_WRITER ), maWriterButton ); + loadImage( FwkResId( BMP_BACKING_CALC ), maCalcButton ); + loadImage( FwkResId( BMP_BACKING_IMPRESS ), maImpressButton ); + loadImage( FwkResId( BMP_BACKING_DRAW ), maDrawButton ); + loadImage( FwkResId( BMP_BACKING_DATABASE ), maDBButton ); + loadImage( FwkResId( BMP_BACKING_FORMULA ), maMathButton ); + loadImage( FwkResId( BMP_BACKING_OPENFILE ), maOpenButton ); + loadImage( FwkResId( BMP_BACKING_OPENTEMPLATE ), maTemplateButton ); + + maOpenButton.SetMenuMode( MENUBUTTON_MENUMODE_TIMED ); + maOpenButton.SetSelectHdl( LINK( this, BackingWindow, SelectHdl ) ); + maOpenButton.SetActivateHdl( LINK( this, BackingWindow, ActivateHdl ) ); +} + +void BackingWindow::initControls() +{ + if( mbInitControls ) + return; + + mbInitControls = true; + + // calculate dialog size + // begin with background bitmap + maControlRect = Rectangle( Point(), maBackgroundLeft.GetSizePixel() ); + maControlRect.Left() += nShadowLeft; + maControlRect.Right() -= nShadowRight; + maControlRect.Top() += nShadowTop; + maControlRect.Bottom() -= nShadowBottom; + + long nYPos = 0; + // set bigger welcome string + maWelcome.SetText( maWelcomeString ); + maTextFont = GetSettings().GetStyleSettings().GetLabelFont(); + maTextFont.SetSize( Size( 0, 18 ) ); + maTextFont.SetWeight( WEIGHT_BOLD ); + maWelcome.SetFont( maTextFont ); + // get metric to get correct width factor and adjust + long nW = (maWelcome.GetFontMetric().GetWidth()*95)/100; + maTextFont.SetSize( Size( nW, 18 ) ); + + maWelcome.SetFont( maTextFont ); + maWelcome.SetControlFont( maTextFont ); + maWelcomeSize = Size( maWelcome.GetTextWidth( maWelcomeString ), maWelcome.GetTextHeight() ); + maWelcomeSize.Width() = (maWelcomeSize.Width() * 20)/19; + + nYPos += (maWelcomeSize.Height()*3)/2; + + if( maControlRect.GetWidth() < mnBtnPos + maWelcomeSize.Width() + 20 ) + maControlRect.Right() = maControlRect.Left() + maWelcomeSize.Width() + mnBtnPos + 20; + + nYPos += maWelcomeSize.Height(); + + // set product string + maTextFont.SetSize( Size( 0, 30 ) ); + maProduct.SetFont( maTextFont ); + + // get metric to get correct width factor and adjust + nW = (maProduct.GetFontMetric().GetWidth()*95)/100; + maTextFont.SetSize( Size( nW, 28 ) ); + + maProduct.SetFont( maTextFont ); + maProduct.SetControlFont( maTextFont ); + maProduct.SetText( maProductString ); + maProductSize = Size( maProduct.GetTextWidth( maProductString ), maProduct.GetTextHeight() ); + maProductSize.Width() = (maProductSize.Width() * 20)/19; + + if( maControlRect.GetWidth() < maProductSize.Width() + mnBtnPos + 10 ) + maControlRect.Right() = maControlRect.Left() + maProductSize.Width() + mnBtnPos + 10; + + if( mnLayoutStyle == 1 ) + { + maWelcome.Show(); + maProduct.Show(); + } + + nYPos += (maProductSize.Height()*3)/2; + + // set a slighly larger font than normal labels on the texts + maTextFont.SetSize( Size( 0, 11 ) ); + maTextFont.SetWeight( WEIGHT_NORMAL ); + + // collect the URLs of the entries in the File/New menu + SvtModuleOptions aModuleOptions; + std::set< rtl::OUString > aFileNewAppsAvailable; + SvtDynamicMenuOptions aOpt; + Sequence < Sequence < PropertyValue > > aNewMenu = aOpt.GetMenu( E_NEWMENU ); + const rtl::OUString sURLKey( RTL_CONSTASCII_USTRINGPARAM( "URL" ) ); + + const Sequence< PropertyValue >* pNewMenu = aNewMenu.getConstArray(); + const Sequence< PropertyValue >* pNewMenuEnd = aNewMenu.getConstArray() + aNewMenu.getLength(); + for ( ; pNewMenu != pNewMenuEnd; ++pNewMenu ) + { + comphelper::SequenceAsHashMap aEntryItems( *pNewMenu ); + rtl::OUString sURL( aEntryItems.getUnpackedValueOrDefault( sURLKey, rtl::OUString() ) ); + if ( sURL.getLength() ) + aFileNewAppsAvailable.insert( sURL ); + } + + // create mnemonics on the fly, preregister the mnemonics of the menu + MnemonicGenerator aMnemns; + maTemplateString = MnemonicGenerator::EraseAllMnemonicChars( maTemplateString ); + maOpenString = MnemonicGenerator::EraseAllMnemonicChars( maOpenString ); + + SystemWindow* pSysWin = GetSystemWindow(); + if( pSysWin ) + { + MenuBar* pMBar = pSysWin->GetMenuBar(); + if( pMBar ) + { + for( USHORT i = 0; i < pMBar->GetItemCount(); i++ ) + { + USHORT nItemId = pMBar->GetItemId( i ); + String aItemText( pMBar->GetItemText( nItemId ) ); + if( aItemText.Len() ) + aMnemns.RegisterMnemonic( aItemText ); + } + } + } + + // layout the buttons + layoutButton( WRITER_URL, 0, aFileNewAppsAvailable, + aModuleOptions, SvtModuleOptions::E_SWRITER, + maWriterButton, aMnemns ); + layoutButton( DRAW_URL, 1, aFileNewAppsAvailable, + aModuleOptions, SvtModuleOptions::E_SDRAW, + maDrawButton, aMnemns ); + nYPos += maButtonImageSize.Height() + 10; + layoutButton( CALC_URL, 0, aFileNewAppsAvailable, + aModuleOptions, SvtModuleOptions::E_SCALC, + maCalcButton, aMnemns ); + layoutButton( BASE_URL, 1, aFileNewAppsAvailable, + aModuleOptions, SvtModuleOptions::E_SDATABASE, + maDBButton, aMnemns ); + nYPos += maButtonImageSize.Height() + 10; + layoutButton( IMPRESS_WIZARD_URL, 0, aFileNewAppsAvailable, + aModuleOptions, SvtModuleOptions::E_SIMPRESS, + maImpressButton, aMnemns ); + layoutButton( MATH_URL, 1, aFileNewAppsAvailable, + aModuleOptions, SvtModuleOptions::E_SMATH, + maMathButton, aMnemns ); + + nYPos += 3*maButtonImageSize.Height() / 2; + + layoutButton( NULL, 0, aFileNewAppsAvailable, + aModuleOptions, SvtModuleOptions::E_SWRITER, + maOpenButton, aMnemns, maOpenString ); + layoutButton( NULL, 1, aFileNewAppsAvailable, + aModuleOptions, SvtModuleOptions::E_SWRITER, + maTemplateButton, aMnemns, maTemplateString ); + nYPos += 10; + + DBG_ASSERT( nYPos < maControlRect.GetHeight(), "misformatting !" ); + if( mnColumnWidth[0] + mnColumnWidth[1] + mnBtnPos + 20 > maControlRect.GetWidth() ) + maControlRect.Right() = maControlRect.Left() + mnColumnWidth[0] + mnColumnWidth[1] + mnBtnPos + 20; + + mnTextColumnWidth[0] = mnColumnWidth[0]; + mnTextColumnWidth[1] = mnColumnWidth[1]; + + if( mnTextColumnWidth[1] > mnTextColumnWidth[0] ) + { + mnColumnWidth[0] = mnColumnWidth[1]; + mnTextColumnWidth[0] = mnTextColumnWidth[1]; + } + else + { + mnColumnWidth[1] = mnColumnWidth[0]; + mnTextColumnWidth[1] = mnTextColumnWidth[0]; + } + if( maControlRect.GetWidth() < maControlRect.GetHeight() * 3 / 2 ) + { + maControlRect.Right() = maControlRect.Left() + maControlRect.GetHeight() * 3 / 2; + long nDelta = (maControlRect.GetWidth() - mnBtnPos - mnColumnWidth[1] - mnColumnWidth[0] - 20); + mnColumnWidth[0] += nDelta/2; + mnColumnWidth[1] += nDelta/2; + } + + maToolbox.SetSelectHdl( LINK( this, BackingWindow, ToolboxHdl ) ); + if( mnLayoutStyle == 0 ) + maToolbox.Show(); + + // scale middle map to formatted width + Size aMiddleSegmentSize( maControlRect.GetSize().Width() + nShadowLeft + nShadowRight, + maBackgroundMiddle.GetSizePixel().Height() ); + + long nLW = maBackgroundLeft.GetSizePixel().Width(); + long nRW = maBackgroundRight.GetSizePixel().Width(); + if( aMiddleSegmentSize.Width() > nLW + nRW ) + { + aMiddleSegmentSize.Width() -= nLW; + aMiddleSegmentSize.Width() -= nRW; + maBackgroundMiddle.Scale( aMiddleSegmentSize ); + } + else + maBackgroundMiddle = BitmapEx(); + + Resize(); + + maWriterButton.GrabFocus(); +} + +void BackingWindow::loadImage( const ResId& i_rId, PushButton& i_rButton ) +{ + BitmapEx aBmp( i_rId ); + Size aImgSize( aBmp.GetSizePixel() ); + if( aImgSize.Width() > maButtonImageSize.Width() ) + maButtonImageSize.Width() = aImgSize.Width(); + if( aImgSize.Height() > maButtonImageSize.Height() ) + maButtonImageSize.Height() = aImgSize.Height(); + i_rButton.SetModeImage( aBmp ); +} + +void BackingWindow::layoutButton( + const char* i_pURL, int nColumn, + const std::set<rtl::OUString>& i_rURLS, + SvtModuleOptions& i_rOpt, SvtModuleOptions::EModule i_eMod, + PushButton& i_rBtn, + MnemonicGenerator& i_rMnemns, + const String& i_rStr + ) +{ + rtl::OUString aURL( rtl::OUString::createFromAscii( i_pURL ? i_pURL : "" ) ); + // setup button + i_rBtn.SetPaintTransparent( TRUE ); + i_rBtn.SetClickHdl( LINK( this, BackingWindow, ClickHdl ) ); + if( i_pURL && (! i_rOpt.IsModuleInstalled( i_eMod ) || i_rURLS.find( aURL ) == i_rURLS.end()) ) + { + i_rBtn.Enable( FALSE ); + } + + // setup text + i_rBtn.SetFont( maTextFont ); + i_rBtn.SetControlFont( maTextFont ); + String aText( i_rStr.Len() ? i_rStr : SvFileInformationManager::GetDescription( INetURLObject( aURL ) ) ); + i_rMnemns.CreateMnemonic( aText ); + i_rBtn.SetText( aText ); + + long nTextWidth = i_rBtn.GetTextWidth( i_rBtn.GetText() ); + + nTextWidth += maButtonImageSize.Width() + 8; // add some fuzz to be on the safe side + if( nColumn >= 0 && nColumn < static_cast<int>(sizeof(mnColumnWidth)/sizeof(mnColumnWidth[0])) ) + { + if( nTextWidth > mnColumnWidth[nColumn] ) + mnColumnWidth[nColumn] = nTextWidth; + } + + i_rBtn.SetImageAlign( IMAGEALIGN_LEFT ); + // show the controls + i_rBtn.Show(); +} + +void BackingWindow::Paint( const Rectangle& ) +{ + + // draw bitmap + if( GetSettings().GetLayoutRTL() ) + { + Point aTL( maControlRect.TopLeft() ); + aTL.X() -= nShadowRight; + aTL.Y() -= nShadowTop; + DrawBitmapEx( aTL, maBackgroundLeft ); + aTL.X() += maBackgroundLeft.GetSizePixel().Width(); + if( !!maBackgroundMiddle ) + { + DrawBitmapEx( aTL, maBackgroundMiddle ); + aTL.X() += maBackgroundMiddle.GetSizePixel().Width(); + } + DrawBitmapEx( aTL, maBackgroundRight ); + } + else + { + Point aTL( maControlRect.TopLeft() ); + aTL.X() -= nShadowLeft; + aTL.Y() -= nShadowTop; + DrawBitmapEx( aTL, maBackgroundLeft ); + aTL.X() += maBackgroundLeft.GetSizePixel().Width(); + if( !!maBackgroundMiddle ) + { + DrawBitmapEx( aTL, maBackgroundMiddle ); + aTL.X() += maBackgroundMiddle.GetSizePixel().Width(); + } + DrawBitmapEx( aTL, maBackgroundRight ); + } +} + +long BackingWindow::Notify( NotifyEvent& rNEvt ) +{ + if( rNEvt.GetType() == EVENT_KEYINPUT ) + { + if( ! mpAccExec ) + { + mpAccExec = svt::AcceleratorExecute::createAcceleratorHelper(); + mpAccExec->init( comphelper::getProcessServiceFactory(), mxFrame); + } + + const KeyEvent* pEvt = rNEvt.GetKeyEvent(); + const KeyCode& rKeyCode(pEvt->GetKeyCode()); + if( pEvt && mpAccExec->execute(rKeyCode) ) + return 1; + // #i110344# extrawurst: specialized arrow key control + if( rKeyCode.GetModifier() == 0 ) + { + if( rKeyCode.GetCode() == KEY_RIGHT ) + { + if( maWriterButton.HasFocus() ) + maDrawButton.GrabFocus(); + else if( maCalcButton.HasFocus() ) + maDBButton.GrabFocus(); + else if( maImpressButton.HasFocus() ) + maMathButton.GrabFocus(); + else if( maOpenButton.HasFocus() ) + maTemplateButton.GrabFocus(); + return 1; + } + else if( rKeyCode.GetCode() == KEY_LEFT ) + { + if( maDrawButton.HasFocus() ) + maWriterButton.GrabFocus(); + else if( maDBButton.HasFocus() ) + maCalcButton.GrabFocus(); + else if( maMathButton.HasFocus() ) + maImpressButton.GrabFocus(); + else if( maTemplateButton.HasFocus() ) + maOpenButton.GrabFocus(); + return 1; + } + else if( rKeyCode.GetCode() == KEY_UP ) + { + // first column + if( maOpenButton.HasFocus() ) + maImpressButton.GrabFocus(); + else if( maImpressButton.HasFocus() ) + maCalcButton.GrabFocus(); + else if( maCalcButton.HasFocus() ) + maWriterButton.GrabFocus(); + // second column + else if( maTemplateButton.HasFocus() ) + maMathButton.GrabFocus(); + else if( maMathButton.HasFocus() ) + maDBButton.GrabFocus(); + else if( maDBButton.HasFocus() ) + maDrawButton.GrabFocus(); + return 1; + } + else if( rKeyCode.GetCode() == KEY_DOWN ) + { + // first column + if( maWriterButton.HasFocus() ) + maCalcButton.GrabFocus(); + else if( maCalcButton.HasFocus() ) + maImpressButton.GrabFocus(); + else if( maImpressButton.HasFocus() ) + maOpenButton.GrabFocus(); + // second column + else if( maDrawButton.HasFocus() ) + maDBButton.GrabFocus(); + else if( maDBButton.HasFocus() ) + maMathButton.GrabFocus(); + else if( maMathButton.HasFocus() ) + maTemplateButton.GrabFocus(); + return 1; + } + } + } + return Window::Notify( rNEvt ); +} + +void BackingWindow::setOwningFrame( const com::sun::star::uno::Reference< com::sun::star::frame::XFrame >& xFrame ) +{ + mxFrame = xFrame; + if( ! mbInitControls ) + initControls(); +} + +void BackingWindow::Resize() +{ + Size aWindowSize( GetSizePixel() ); + Size aControlSize = maControlRect.GetSize(); + maControlRect = Rectangle( Point( (aWindowSize.Width() - aControlSize.Width()) / 2, + (aWindowSize.Height() - aControlSize.Height()) / 2 ), + aControlSize ); + + maToolbox.calcMinSize(); + Size aTBSize( maToolbox.getMinSize() ); + Point aTBPos( maControlRect.Left() + mnBtnPos, + maControlRect.Bottom() - aTBSize.Height() - 10 ); + if( Application::GetSettings().GetLayoutRTL() ) + aTBPos.X() = maControlRect.Right() - aTBSize.Width() - mnBtnPos; + maToolbox.SetPosSizePixel( aTBPos, aTBSize ); + + // #i93631# squeeze controls so they fit into the box + // this can be necessary due to application font height which has small deviations + // from the size set + const long nWDelta = maWelcomeSize.Height(); + const long nW2Delta = (maWelcomeSize.Height()*3)/2; + const long nPDelta = (maProductSize.Height()*3)/2; + const long nBDelta = maButtonImageSize.Height() + 10; + const long nB2Delta = 3*maButtonImageSize.Height()/2; + const long nLastDelta = maButtonImageSize.Height(); + long nDiff = 0; + while( ( maControlRect.Top() + + (nWDelta - nDiff) + + (nW2Delta- nDiff) + + (nPDelta - nDiff) + + 3 * (nBDelta - nDiff) + + (nB2Delta- nDiff) + + nLastDelta + ) > aTBPos.Y() ) + { + nDiff++; + } + + long nYPos = maControlRect.Top(); + nYPos += nW2Delta - nDiff; + maWelcome.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos, nYPos ), + Size( maControlRect.GetWidth() - mnBtnPos - 5, (maWelcomeSize.Height()*20)/19 ) ); + nYPos += nWDelta - nDiff; + maProduct.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos, nYPos ), Size( maControlRect.GetWidth() - mnBtnPos - 5, (maProductSize.Height()*20)/19 ) ); + nYPos += nPDelta - nDiff; + + nYPos += nWDelta/2 - nDiff; + + if( mnLayoutStyle != 1 ) + nYPos = maControlRect.Top() + mnBtnTop; + + maWriterButton.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos, nYPos ), Size( mnTextColumnWidth[0], maButtonImageSize.Height() ) ); + maDrawButton.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos + mnColumnWidth[0], nYPos ), Size( mnTextColumnWidth[1], maButtonImageSize.Height() ) ); + nYPos += nBDelta - nDiff; + maCalcButton.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos, nYPos ), Size( mnTextColumnWidth[0], maButtonImageSize.Height() ) ); + maDBButton.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos + mnColumnWidth[0], nYPos ), Size( mnTextColumnWidth[1], maButtonImageSize.Height() ) ); + nYPos += nBDelta - nDiff; + maImpressButton.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos, nYPos ), Size( mnTextColumnWidth[0], maButtonImageSize.Height() ) ); + maMathButton.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos + mnColumnWidth[0], nYPos ), Size( mnTextColumnWidth[1], maButtonImageSize.Height() ) ); + + nYPos += nB2Delta - nDiff; + maOpenButton.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos, nYPos ), Size( mnTextColumnWidth[0], maButtonImageSize.Height() ) ); + maTemplateButton.SetPosSizePixel( Point( maControlRect.Left() + mnBtnPos + mnColumnWidth[0], nYPos ), Size( mnTextColumnWidth[1], maButtonImageSize.Height() ) ); +} + +IMPL_LINK( BackingWindow, ToolboxHdl, void*, EMPTYARG ) +{ + const char* pNodePath = NULL; + const char* pNode = NULL; + + switch( maToolbox.GetCurItemId() ) + { + case nItemId_Extensions: + pNodePath = "/org.openoffice.Office.Common/Help/StartCenter"; + pNode = "AddFeatureURL"; + break; + case nItemId_Reg: + try + { + // create the Desktop component which can load components + Reference < lang::XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory(); + if( xFactory.is() ) + { + Reference< task::XJobExecutor > xProductRegistration( + xFactory->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.setup.ProductRegistration" ) ) ), + UNO_QUERY_THROW ); + + // tell it that the user wants to register + xProductRegistration->trigger( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "RegistrationRequired" ) ) ); + } + } + catch( const Exception& ) + { + } + break; + case nItemId_Info: + pNodePath = "/org.openoffice.Office.Common/Help/StartCenter"; + pNode = "InfoURL"; + break; + case nItemId_TplRep: + pNodePath = "/org.openoffice.Office.Common/Help/StartCenter"; + pNode = "TemplateRepositoryURL"; + break; + default: + break; + } + if( pNodePath && pNode ) + { + try + { + Reference<lang::XMultiServiceFactory> xConfig( comphelper::getProcessServiceFactory()->createInstance(SERVICENAME_CFGPROVIDER),UNO_QUERY); + if( xConfig.is() ) + { + Sequence<Any> args(1); + PropertyValue val( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("nodepath") ), + 0, + Any(rtl::OUString::createFromAscii(pNodePath)), + PropertyState_DIRECT_VALUE); + args.getArray()[0] <<= val; + Reference<container::XNameAccess> xNameAccess(xConfig->createInstanceWithArguments(SERVICENAME_CFGREADACCESS,args), UNO_QUERY); + if( xNameAccess.is() ) + { + rtl::OUString sURL; + //throws css::container::NoSuchElementException, css::lang::WrappedTargetException + Any value( xNameAccess->getByName(rtl::OUString::createFromAscii(pNode)) ); + sURL = value.get<rtl::OUString> (); + + // extend the URLs with Office locale argument + INetURLObject aURLObj( sURL ); + + rtl::OUString sParam = aURLObj.GetParam(); + rtl::OUStringBuffer aURLBuf( sParam ); + if ( sParam.getLength() > 0 ) + aURLBuf.appendAscii( "&" ); + aURLBuf.appendAscii( "lang=" ); + + // read locale from configuration + ::rtl::OUString sLocale; + ::rtl::OUString sPackage = ::rtl::OUString::createFromAscii("org.openoffice.Setup"); + ::rtl::OUString sRelPath = ::rtl::OUString::createFromAscii("L10N"); + ::rtl::OUString sKey = ::rtl::OUString::createFromAscii("ooLocale"); + + try + { + ::comphelper::ConfigurationHelper::readDirectKey(comphelper::getProcessServiceFactory(), + sPackage, + sRelPath, + sKey, + ::comphelper::ConfigurationHelper::E_READONLY) >>= sLocale; + } + catch(const com::sun::star::uno::RuntimeException& exRun) + { throw exRun; } + catch(const com::sun::star::uno::Exception&) + { sLocale = ::rtl::OUString::createFromAscii("en-US"); } + + aURLBuf.append(sLocale); + + sParam = aURLBuf.makeStringAndClear(); + + aURLObj.SetParam( sParam ); + sURL = aURLObj.GetMainURL( INetURLObject::NO_DECODE ); + + Reference< com::sun::star::system::XSystemShellExecute > xSystemShellExecute( + comphelper::getProcessServiceFactory()->createInstance( + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.system.SystemShellExecute" ) ) ), + UNO_QUERY_THROW); + //throws css::lang::IllegalArgumentException, css::system::SystemShellExecuteException + xSystemShellExecute->execute( sURL, rtl::OUString(), com::sun::star::system::SystemShellExecuteFlags::DEFAULTS); + } + } + } + catch (Exception& ) + { + } + } + + return 0; +} + +IMPL_LINK( BackingWindow, ClickHdl, Button*, pButton ) +{ + // dispatch the appropriate URL and end the dialog + if( pButton == &maWriterButton ) + dispatchURL( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(WRITER_URL) ) ); + else if( pButton == &maCalcButton ) + dispatchURL( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(CALC_URL) ) ); + else if( pButton == &maImpressButton ) + dispatchURL( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(IMPRESS_WIZARD_URL) ) ); + else if( pButton == &maDrawButton ) + dispatchURL( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(DRAW_URL) ) ); + else if( pButton == &maDBButton ) + dispatchURL( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(BASE_URL) ) ); + else if( pButton == &maMathButton ) + dispatchURL( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(MATH_URL) ) ); + else if( pButton == &maOpenButton ) + { + Reference< XDispatchProvider > xFrame( mxFrame, UNO_QUERY ); + + Sequence< com::sun::star::beans::PropertyValue > aArgs(1); + PropertyValue* pArg = aArgs.getArray(); + pArg[0].Name = rtl::OUString::createFromAscii("Referer"); + pArg[0].Value <<= rtl::OUString::createFromAscii("private:user"); + + dispatchURL( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(OPEN_URL) ), rtl::OUString(), xFrame, aArgs ); + } + else if( pButton == &maTemplateButton ) + { + Reference< XDispatchProvider > xFrame( mxFrame, UNO_QUERY ); + + Sequence< com::sun::star::beans::PropertyValue > aArgs(1); + PropertyValue* pArg = aArgs.getArray(); + pArg[0].Name = rtl::OUString::createFromAscii("Referer"); + pArg[0].Value <<= rtl::OUString::createFromAscii("private:user"); + + dispatchURL( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(TEMPLATE_URL) ), rtl::OUString(), xFrame, aArgs ); + } + return 0; +} + +IMPL_LINK( BackingWindow, SelectHdl, Button*, pButton ) +{ + if( pButton == &maOpenButton ) + { + sal_Int32 nItem = sal_Int32(maOpenButton.GetCurItemId())-1; + if( nItem >= 0 && nItem < sal_Int32(maRecentFiles.size()) ) + { + Reference< XDispatchProvider > xFrame( mxFrame, UNO_QUERY ); + dispatchURL( maRecentFiles[nItem].aTargetURL, rtl::OUString(), xFrame, maRecentFiles[nItem].aArgSeq ); + } + } + return 0; +} + +IMPL_LINK( BackingWindow, ActivateHdl, Button*, pButton ) +{ + if( pButton == &maOpenButton ) + prepareRecentFileMenu(); + return 0; +} + +struct ImplDelayedDispatch +{ + Reference< XDispatch > xDispatch; + com::sun::star::util::URL aDispatchURL; + Sequence< PropertyValue > aArgs; + + ImplDelayedDispatch( const Reference< XDispatch >& i_xDispatch, + const com::sun::star::util::URL& i_rURL, + const Sequence< PropertyValue >& i_rArgs ) + : xDispatch( i_xDispatch ), + aDispatchURL( i_rURL ), + aArgs( i_rArgs ) + { + } + ~ImplDelayedDispatch() {} +}; + +static long implDispatchDelayed( void*, void* pArg ) +{ + struct ImplDelayedDispatch* pDispatch = reinterpret_cast<ImplDelayedDispatch*>(pArg); + try + { + pDispatch->xDispatch->dispatch( pDispatch->aDispatchURL, pDispatch->aArgs ); + } + catch( Exception ) + { + } + + // clean up + delete pDispatch; + + return 0; +} + +void BackingWindow::dispatchURL( const rtl::OUString& i_rURL, + const rtl::OUString& rTarget, + const Reference< XDispatchProvider >& i_xProv, + const Sequence< PropertyValue >& i_rArgs ) +{ + // if no special dispatch provider is given, get the desktop + Reference< XDispatchProvider > xProvider( i_xProv.is() ? i_xProv : mxDesktopDispatchProvider ); + + // check for dispatch provider + if( !xProvider.is()) + return; + + // get an URL transformer to clean up the URL + com::sun::star::util::URL aDispatchURL; + aDispatchURL.Complete = i_rURL; + + Reference < com::sun::star::util::XURLTransformer > xURLTransformer( + comphelper::getProcessServiceFactory()->createInstance( rtl::OUString::createFromAscii("com.sun.star.util.URLTransformer") ), + com::sun::star::uno::UNO_QUERY ); + if ( xURLTransformer.is() ) + { + try + { + // clean up the URL + xURLTransformer->parseStrict( aDispatchURL ); + // get a Dispatch for the URL and target + Reference< XDispatch > xDispatch( + xProvider->queryDispatch( aDispatchURL, rTarget, 0 ) + ); + // dispatch the URL + if ( xDispatch.is() ) + { + ImplDelayedDispatch* pDisp = new ImplDelayedDispatch( xDispatch, aDispatchURL, i_rArgs ); + ULONG nEventId = 0; + if( ! Application::PostUserEvent( nEventId, Link( NULL, implDispatchDelayed ), pDisp ) ) + delete pDisp; // event could not be posted for unknown reason, at least don't leak + } + } + catch ( com::sun::star::uno::RuntimeException& ) + { + throw; + } + catch ( com::sun::star::uno::Exception& ) + { + } + } +} + diff --git a/framework/source/services/backingwindow.hxx b/framework/source/services/backingwindow.hxx new file mode 100644 index 000000000000..958ebfbb243e --- /dev/null +++ b/framework/source/services/backingwindow.hxx @@ -0,0 +1,184 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef FRAMEWORK_BACKINGWINDOW_HXX +#define FRAMEWORK_BACKINGWINDOW_HXX + +#include "rtl/ustring.hxx" + +#include "vcl/button.hxx" +#include "vcl/menubtn.hxx" +#include "vcl/fixed.hxx" +#include "vcl/bitmapex.hxx" +#include "vcl/toolbox.hxx" + +#include "unotools/moduleoptions.hxx" +#include "svtools/acceleratorexecute.hxx" + +#include "com/sun/star/frame/XDispatchProvider.hpp" +#include "com/sun/star/frame/XDesktop.hpp" +#include "com/sun/star/frame/XFrame.hpp" +#include "com/sun/star/frame/XTerminateListener.hpp" +#include "com/sun/star/document/XEventListener.hpp" +#include "com/sun/star/document/XEventBroadcaster.hpp" +#include "com/sun/star/util/XURLTransformer.hpp" +#include "com/sun/star/ui/dialogs/XFilePicker.hpp" +#include "com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp" +#include "com/sun/star/ui/dialogs/XFilterManager.hpp" +#include "com/sun/star/ui/dialogs/XFolderPicker.hpp" +#include "com/sun/star/ui/dialogs/TemplateDescription.hpp" +#include "com/sun/star/ui/dialogs/ExecutableDialogResults.hpp" + +#include <set> + +class MnemonicGenerator; + +namespace framework +{ + // To get the transparent mouse-over look, the closer is actually a toolbox + // overload DataChange to handle style changes correctly + class DecoToolBox : public ToolBox + { + Size maMinSize; + + using Window::ImplInit; + public: + DecoToolBox( Window* pParent, WinBits nStyle = 0 ); + DecoToolBox( Window* pParent, const ResId& rResId ); + + void DataChanged( const DataChangedEvent& rDCEvt ); + + void calcMinSize(); + Size getMinSize(); + }; + + class BackingWindow : public Window + { + struct LoadRecentFile + { + rtl::OUString aTargetURL; + com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue > aArgSeq; + }; + + com::sun::star::uno::Reference<com::sun::star::frame::XDesktop> mxDesktop; + com::sun::star::uno::Reference<com::sun::star::frame::XDispatchProvider > mxDesktopDispatchProvider; + com::sun::star::uno::Reference<com::sun::star::frame::XFrame> mxFrame; + com::sun::star::uno::Reference<com::sun::star::document::XEventBroadcaster> mxBroadcaster; + + FixedText maWelcome; + Size maWelcomeSize; + FixedText maProduct; + Size maProductSize; + ImageButton maWriterButton; + ImageButton maCalcButton; + ImageButton maImpressButton; + MenuButton maOpenButton; + ImageButton maDrawButton; + ImageButton maDBButton; + ImageButton maMathButton; + ImageButton maTemplateButton; + + DecoToolBox maToolbox; + + BitmapEx maBackgroundLeft; + BitmapEx maBackgroundMiddle; + BitmapEx maBackgroundRight; + + String maWelcomeString; + String maProductString; + String maCreateString; + String maOpenString; + String maTemplateString; + + Font maTextFont; + Rectangle maControlRect; + + long mnColumnWidth[2]; + long mnTextColumnWidth[2]; + Color maLabelTextColor; + Color maWelcomeTextColor; + + Size maButtonImageSize; + + bool mbInitControls; + sal_Int32 mnLayoutStyle; + svt::AcceleratorExecute* mpAccExec; + long mnBtnPos; + long mnBtnTop; + + PopupMenu* mpRecentMenu; + std::vector< LoadRecentFile > maRecentFiles; + + static const int nItemId_Extensions = 1; + static const int nItemId_Reg = 2; + static const int nItemId_Info = 3; + static const int nItemId_TplRep = 4; + static const int nShadowTop = 32; + static const int nShadowLeft = 35; + static const int nShadowRight = 45; + static const int nShadowBottom = 50; + + void loadImage( const ResId& i_rId, PushButton& i_rButton ); + + void layoutButton( const char* i_pURL, int nColumn, const std::set<rtl::OUString>& i_rURLS, + SvtModuleOptions& i_rOpt, SvtModuleOptions::EModule i_eMod, + PushButton& i_rBtn, + MnemonicGenerator& i_rMnemonicGen, + const String& i_rStr = String() + ); + + void dispatchURL( const rtl::OUString& i_rURL, + const rtl::OUString& i_rTarget = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ), + const com::sun::star::uno::Reference< com::sun::star::frame::XDispatchProvider >& i_xProv = com::sun::star::uno::Reference< com::sun::star::frame::XDispatchProvider >(), + const com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >& = com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >() + ); + + DECL_LINK( ClickHdl, Button* ); + DECL_LINK( SelectHdl, Button* ); + DECL_LINK( ActivateHdl, Button* ); + DECL_LINK( ToolboxHdl, void* ); + + void initControls(); + void initBackground(); + void prepareRecentFileMenu(); + public: + BackingWindow( Window* pParent ); + ~BackingWindow(); + + virtual void Paint( const Rectangle& rRect ); + virtual void Resize(); + virtual long Notify( NotifyEvent& rNEvt ); + virtual void DataChanged( const DataChangedEvent& rDCEvt ); + virtual void GetFocus(); + + void setOwningFrame( const com::sun::star::uno::Reference< com::sun::star::frame::XFrame >& xFrame ); + }; + +} + +#endif + diff --git a/framework/source/services/desktop.cxx b/framework/source/services/desktop.cxx new file mode 100644 index 000000000000..293e03ee655b --- /dev/null +++ b/framework/source/services/desktop.cxx @@ -0,0 +1,2036 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_________________________________________________________________________________________________________________ +// my own includes +//_________________________________________________________________________________________________________________ +#include <loadenv/loadenv.hxx> + +#ifndef __FRAMEWORK_LOADENV_TARGETHELPER_HXX_ +#include <loadenv/targethelper.hxx> +#endif + +#ifndef __FRAMEWORK_DESKTOP_HXX_ +#include <services/desktop.hxx> +#endif +#include <helper/ocomponentaccess.hxx> +#include <dispatch/dispatchprovider.hxx> + +#ifndef __FRAMEWORK_DISPATCH_INTERCEPTIONHELPER_HXX_ +#include <dispatch/interceptionhelper.hxx> +#endif +#include <classes/taskcreator.hxx> +#include <threadhelp/transactionguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <threadhelp/readguard.hxx> +#include <services.h> +#include <general.h> +#include <properties.h> + +#include <classes/resource.hrc> +#include <classes/fwkresid.hxx> + +//_________________________________________________________________________________________________________________ +// interface includes +//_________________________________________________________________________________________________________________ +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/awt/XToolkit.hpp> +#include <com/sun/star/awt/XWindow.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/awt/WindowDescriptor.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/task/XInteractionAbort.hpp> +#include <com/sun/star/task/XInteractionApprove.hpp> +#include <com/sun/star/document/XInteractionFilterSelect.hpp> +#include <com/sun/star/document/AmbigousFilterRequest.hpp> +#include <com/sun/star/task/ErrorCodeRequest.hpp> +#include <com/sun/star/ucb/InteractiveIOException.hpp> +#include <com/sun/star/ucb/InteractiveAugmentedIOException.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> +#include <com/sun/star/frame/DispatchResultState.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/document/MacroExecMode.hpp> +#include <com/sun/star/document/UpdateDocMode.hpp> +#include <com/sun/star/frame/XTerminateListener2.hpp> + +//_________________________________________________________________________________________________________________ +// includes of other projects +//_________________________________________________________________________________________________________________ +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/proptypehlp.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/logfile.hxx> +#include <vcl/svapp.hxx> + +#ifndef __RSC +#include <tools/errinf.hxx> +#endif +#include <comphelper/extract.hxx> + +//_________________________________________________________________________________________________________________ +// namespace +//_________________________________________________________________________________________________________________ + +namespace framework{ + +//_________________________________________________________________________________________________________________ +// non exported const +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// non exported definitions +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// declarations +//_________________________________________________________________________________________________________________ + +//***************************************************************************************************************** +// XInterface, XTypeProvider, XServiceInfo +//***************************************************************************************************************** +DEFINE_XINTERFACE_15 ( Desktop , + OWeakObject , + DIRECT_INTERFACE( css::lang::XTypeProvider ), + DIRECT_INTERFACE( css::lang::XServiceInfo ), + DIRECT_INTERFACE( css::frame::XDesktop ), + DIRECT_INTERFACE( css::frame::XComponentLoader ), + DIRECT_INTERFACE( css::frame::XTasksSupplier ), + DIRECT_INTERFACE( css::frame::XDispatchProvider ), + DIRECT_INTERFACE( css::frame::XDispatchProviderInterception), + DIRECT_INTERFACE( css::frame::XFramesSupplier ), + DIRECT_INTERFACE( css::frame::XFrame ), + DIRECT_INTERFACE( css::lang::XComponent ), + DIRECT_INTERFACE( css::frame::XDispatchResultListener ), + DIRECT_INTERFACE( css::lang::XEventListener ), + DIRECT_INTERFACE( css::task::XInteractionHandler ), + DIRECT_INTERFACE( css::beans::XPropertySet ), + DIRECT_INTERFACE( css::frame::XUntitledNumbers ) + ) + +DEFINE_XTYPEPROVIDER_15 ( Desktop , + css::lang::XTypeProvider , + css::lang::XServiceInfo , + css::frame::XDesktop , + css::frame::XComponentLoader , + css::frame::XTasksSupplier , + css::frame::XDispatchProvider , + css::frame::XDispatchProviderInterception , + css::frame::XFramesSupplier , + css::frame::XFrame , + css::lang::XComponent , + css::frame::XDispatchResultListener , + css::lang::XEventListener , + css::task::XInteractionHandler , + css::beans::XPropertySet , + css::frame::XUntitledNumbers + ) + +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE ( Desktop , + ::cppu::OWeakObject , + SERVICENAME_DESKTOP , + IMPLEMENTATIONNAME_DESKTOP + ) + +DEFINE_INIT_SERVICE ( Desktop, + { + /*Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + + //------------------------------------------------------------------------------------------------------------- + // Initialize a new XFrames-helper-object to handle XIndexAccess and XElementAccess. + // We hold member as reference ... not as pointer too! + // Attention: We share our frame container with this helper. Container is threadsafe himself ... So I think we can do that. + // But look on dispose() for right order of deinitialization. + OFrames* pFramesHelper = new OFrames( m_xFactory, this, &m_aChildTaskContainer ); + m_xFramesHelper = css::uno::Reference< css::frame::XFrames >( static_cast< ::cppu::OWeakObject* >(pFramesHelper), css::uno::UNO_QUERY ); + + //------------------------------------------------------------------------------------------------------------- + // Initialize a new dispatchhelper-object to handle dispatches. + // We use these helper as slave for our interceptor helper ... not directly! + // But he is event listener on THIS instance! + DispatchProvider* pDispatchHelper = new DispatchProvider( m_xFactory, this ); + css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( static_cast< ::cppu::OWeakObject* >(pDispatchHelper), css::uno::UNO_QUERY ); + + //------------------------------------------------------------------------------------------------------------- + // Initialize a new interception helper object to handle dispatches and implement an interceptor mechanism. + // Set created dispatch provider as slowest slave of it. + // Hold interception helper by reference only - not by pointer! + // So it's easiear to destroy it. + InterceptionHelper* pInterceptionHelper = new InterceptionHelper( this, xDispatchProvider ); + m_xDispatchHelper = css::uno::Reference< css::frame::XDispatchProvider >( static_cast< ::cppu::OWeakObject* >(pInterceptionHelper), css::uno::UNO_QUERY ); + + ::rtl::OUStringBuffer sUntitledPrefix (256); + sUntitledPrefix.append (::rtl::OUString( String( FwkResId( STR_UNTITLED_DOCUMENT )))); + sUntitledPrefix.appendAscii (" "); + + ::comphelper::NumberedCollection* pNumbers = new ::comphelper::NumberedCollection (); + m_xTitleNumberGenerator = css::uno::Reference< css::frame::XUntitledNumbers >(static_cast< ::cppu::OWeakObject* >(pNumbers), css::uno::UNO_QUERY_THROW); + pNumbers->setOwner ( static_cast< ::cppu::OWeakObject* >(this) ); + pNumbers->setUntitledPrefix ( sUntitledPrefix.makeStringAndClear () ); + + // Safe impossible cases + // We can't work without this helper! + LOG_ASSERT2( m_xFramesHelper.is ()==sal_False, "Desktop::Desktop()", "Frames helper is not valid. XFrames, XIndexAccess and XElementAcces are not supported!\n") + LOG_ASSERT2( m_xDispatchHelper.is()==sal_False, "Desktop::Desktop()", "Dispatch helper is not valid. XDispatch will not work correctly!\n" ) + + // Enable object for real working! + // Otherwise all calls will be rejected ... + m_aTransactionManager.setWorkingMode( E_WORK ); + } + ) + +/*-************************************************************************************************************//** + @short standard constructor to create instance by factory + @descr This constructor initialize a new instance of this class by valid factory, + and will be set valid values on his member and baseclasses. + + @attention a) Don't use your own reference during an UNO-Service-ctor! There is no guarantee, that you + will get over this. (e.g. using of your reference as parameter to initialize some member) + Do such things in DEFINE_INIT_SERVICE() method, which is called automaticly after your ctor!!! + b) Baseclass OBroadcastHelper is a typedef in namespace cppu! + The microsoft compiler has some problems to handle it right BY using namespace explicitly ::cppu::OBroadcastHelper. + If we write it without a namespace or expand the typedef to OBrodcastHelperVar<...> -> it will be OK!? + I don't know why! (other compiler not tested .. but it works!) + + @seealso method DEFINE_INIT_SERVICE() + + @param "xFactory" is the multi service manager, which create this instance. + The value must be different from NULL! + @return - + + @onerror We throw an ASSERT in debug version or do nothing in relaese version. +*//*-*************************************************************************************************************/ +Desktop::Desktop( const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) + // Init baseclasses first + // Attention: Don't change order of initialization! + // ThreadHelpBase is a struct with a lock as member. We can't use a lock as direct member! + // We must garant right initialization and a valid value of this to initialize other baseclasses! + : ThreadHelpBase ( &Application::GetSolarMutex() ) + , TransactionBase ( ) + , ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType > ( m_aLock.getShareableOslMutex() ) + , ::cppu::OPropertySetHelper ( *(static_cast< ::cppu::OBroadcastHelper* >(this)) ) + , ::cppu::OWeakObject ( ) + // Init member + #ifdef ENABLE_ASSERTIONS + , m_bIsTerminated ( sal_False ) // see dispose() for further informations! + #endif + , m_xFactory ( xFactory ) + , m_aChildTaskContainer ( ) + , m_aListenerContainer ( m_aLock.getShareableOslMutex() ) + , m_xFramesHelper ( ) + , m_xDispatchHelper ( ) + , m_eLoadState ( E_NOTSET ) + , m_xLastFrame ( ) + , m_aInteractionRequest ( ) + , m_bSuspendQuickstartVeto( sal_False ) + , m_aCommandOptions ( ) + , m_sName ( ) + , m_sTitle ( ) + , m_xDispatchRecorderSupplier( ) + , m_xPipeTerminator ( ) + , m_xQuickLauncher ( ) + , m_xSWThreadManager ( ) + , m_xSfxTerminator ( ) + , m_xTitleNumberGenerator ( ) +{ + // Safe impossible cases + // We don't accept all incoming parameter. + LOG_ASSERT2( implcp_ctor( xFactory ), "Desktop::Desktop()", "Invalid parameter detected!") +} + +/*-************************************************************************************************************//** + @short standard destructor + @descr This one do NOTHING! Use dispose() instaed of this. + + @seealso method dispose() + + @param - + @return - + + @onerror - +*//*-*************************************************************************************************************/ +Desktop::~Desktop() +{ + LOG_ASSERT2( m_bIsTerminated ==sal_False, "Desktop::~Desktop()", "Who forgot to terminate the desktop service?" ) + LOG_ASSERT2( m_aTransactionManager.getWorkingMode()!=E_CLOSE , "Desktop::~Desktop()", "Who forgot to dispose this service?" ) +} + +//============================================================================= +sal_Bool SAL_CALL Desktop::terminate() + throw( css::uno::RuntimeException ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + SYNCHRONIZED_START + ReadGuard aReadLock( m_aLock ); + + css::uno::Reference< css::frame::XTerminateListener > xPipeTerminator = m_xPipeTerminator; + css::uno::Reference< css::frame::XTerminateListener > xQuickLauncher = m_xQuickLauncher; + css::uno::Reference< css::frame::XTerminateListener > xSWThreadManager = m_xSWThreadManager; + css::uno::Reference< css::frame::XTerminateListener > xSfxTerminator = m_xSfxTerminator; + + css::lang::EventObject aEvent ( static_cast< ::cppu::OWeakObject* >(this) ); + ::sal_Bool bAskQuickStart = !m_bSuspendQuickstartVeto ; + + aReadLock.unlock(); + SYNCHRONIZED_END + + //------------------------------------------------------------------------------------------------------------- + // Ask normal terminate listener. They could stop terminate without closing any open document. + Desktop::TTerminateListenerList lCalledTerminationListener; + ::sal_Bool bVeto = sal_False; + impl_sendQueryTerminationEvent(lCalledTerminationListener, bVeto); + if ( bVeto ) + { + impl_sendCancelTerminationEvent(lCalledTerminationListener); + return sal_False; + } + + //------------------------------------------------------------------------------------------------------------- + // try to close all open frames. + // Allow using of any UI ... because Desktop.terminate() was designed as UI functionality in the past. + ::sal_Bool bAllowUI = sal_True; + ::sal_Bool bFramesClosed = impl_closeFrames(bAllowUI); + if ( ! bFramesClosed ) + { + impl_sendCancelTerminationEvent(lCalledTerminationListener); + return sal_False; + } + + //------------------------------------------------------------------------------------------------------------- + // Normal listener had no problem ... + // all frames was closed ... + // now it's time to ask our specialized listener. + // They are handled these way because they wish to hinder the office on termination + // but they wish also closing of all frames. + + // Note further: + // We shouldn't ask quicklauncher in case it was allowed from outside only. + // This is special trick to "ignore existing quick starter" for debug purposes. + + // Attention: + // Order of alled listener is important ! + // some of them are harmless .-) + // But some of them can be dangerous. E.g. it would be dangerous if we close our pipe + // and dont terminate in real because another listener throws a veto exception .-) + + ::sal_Bool bTerminate = sal_False; + try + { + if( + ( bAskQuickStart ) && + ( xQuickLauncher.is() ) + ) + { + xQuickLauncher->queryTermination( aEvent ); + lCalledTerminationListener.push_back( xQuickLauncher ); + } + + if ( xSWThreadManager.is() ) + { + xSWThreadManager->queryTermination( aEvent ); + lCalledTerminationListener.push_back( xSWThreadManager ); + } + + if ( xPipeTerminator.is() ) + { + xPipeTerminator->queryTermination( aEvent ); + lCalledTerminationListener.push_back( xPipeTerminator ); + } + + if ( xSfxTerminator.is() ) + { + xSfxTerminator->queryTermination( aEvent ); + lCalledTerminationListener.push_back( xSfxTerminator ); + } + + bTerminate = sal_True; + } + catch(const css::frame::TerminationVetoException&) + { + bTerminate = sal_False; + } + + if ( ! bTerminate ) + impl_sendCancelTerminationEvent(lCalledTerminationListener); + else + { + #ifdef ENABLE_ASSERTIONS + // "Protect" us against dispose before terminate calls! + // see dispose() for further informations. + /* SAFE AREA --------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + m_bIsTerminated = sal_True; + aWriteLock.unlock(); + /* UNSAFE AREA ------------------------------------------------------------------------------------- */ + #endif + + impl_sendNotifyTerminationEvent(); + + if( + ( bAskQuickStart ) && + ( xQuickLauncher.is() ) + ) + { + xQuickLauncher->notifyTermination( aEvent ); + } + + if ( xSWThreadManager.is() ) + xSWThreadManager->notifyTermination( aEvent ); + + if ( xPipeTerminator.is() ) + xPipeTerminator->notifyTermination( aEvent ); + + // Must be realy the last listener to be called. + // Because it shutdown the whole process asynchronous ! + if ( xSfxTerminator.is() ) + xSfxTerminator->notifyTermination( aEvent ); + } + + return bTerminate; +} + + +//============================================================================= +void SAL_CALL Desktop::addTerminateListener( const css::uno::Reference< css::frame::XTerminateListener >& xListener ) + throw( css::uno::RuntimeException ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + css::uno::Reference< css::lang::XServiceInfo > xInfo( xListener, css::uno::UNO_QUERY ); + if ( xInfo.is() ) + { + ::rtl::OUString sImplementationName = xInfo->getImplementationName(); + + // SYCNHRONIZED -> + WriteGuard aWriteLock( m_aLock ); + + if( sImplementationName.equals(IMPLEMENTATIONNAME_SFXTERMINATOR) ) + { + m_xSfxTerminator = xListener; + return; + } + if( sImplementationName.equals(IMPLEMENTATIONNAME_PIPETERMINATOR) ) + { + m_xPipeTerminator = xListener; + return; + } + if( sImplementationName.equals(IMPLEMENTATIONNAME_QUICKLAUNCHER) ) + { + m_xQuickLauncher = xListener; + return; + } + if( sImplementationName.equals(IMPLEMENTATIONNAME_SWTHREADMANAGER) ) + { + m_xSWThreadManager = xListener; + return; + } + + aWriteLock.unlock(); + // <- SYCNHRONIZED + } + + // No lock required ... container is threadsafe by itself. + m_aListenerContainer.addInterface( ::getCppuType( ( const css::uno::Reference< css::frame::XTerminateListener >*) NULL ), xListener ); +} + +//============================================================================= +void SAL_CALL Desktop::removeTerminateListener( const css::uno::Reference< css::frame::XTerminateListener >& xListener ) + throw( css::uno::RuntimeException ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + css::uno::Reference< css::lang::XServiceInfo > xInfo( xListener, css::uno::UNO_QUERY ); + if ( xInfo.is() ) + { + ::rtl::OUString sImplementationName = xInfo->getImplementationName(); + + // SYCNHRONIZED -> + WriteGuard aWriteLock( m_aLock ); + + if( sImplementationName.equals(IMPLEMENTATIONNAME_SFXTERMINATOR) ) + { + m_xSfxTerminator.clear(); + return; + } + + if( sImplementationName.equals(IMPLEMENTATIONNAME_PIPETERMINATOR) ) + { + m_xPipeTerminator.clear(); + return; + } + + if( sImplementationName.equals(IMPLEMENTATIONNAME_QUICKLAUNCHER) ) + { + m_xQuickLauncher.clear(); + return; + } + + if( sImplementationName.equals(IMPLEMENTATIONNAME_SWTHREADMANAGER) ) + { + m_xSWThreadManager.clear(); + return; + } + + aWriteLock.unlock(); + // <- SYCNHRONIZED + } + + // No lock required ... container is threadsafe by itself. + m_aListenerContainer.removeInterface( ::getCppuType( ( const css::uno::Reference< css::frame::XTerminateListener >*) NULL ), xListener ); +} + +/*-************************************************************************************************************//** + @interface XDesktop + @short get access to create enumerations of all current components + @descr You will be the owner of the returned object and must delete it if you don't use it again. + + @seealso class TasksAccess + @seealso class TasksEnumeration + + @param - + @return A reference to an XEnumerationAccess-object. + + @onerror We return a null-reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::container::XEnumerationAccess > SAL_CALL Desktop::getComponents() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // We use a helper class OComponentAccess to have access on all child components. + // Create it on demand and return it as a reference. + OComponentAccess* pAccess = new OComponentAccess( this ); + css::uno::Reference< css::container::XEnumerationAccess > xAccess( static_cast< ::cppu::OWeakObject* >(pAccess), css::uno::UNO_QUERY ); + return xAccess; +} + +/*-************************************************************************************************************//** + @interface XDesktop + @short return the current active component + @descr The most current component is the window, model or the controller of the current active frame. + + @seealso method getCurrentFrame() + @seealso method impl_getFrameComponent() + + @param - + @return A reference to the component. + + @onerror We return a null-reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > SAL_CALL Desktop::getCurrentComponent() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Set return value if method failed. + css::uno::Reference< css::lang::XComponent > xComponent; + + // Get reference to current frame ... + // ... get component of this frame ... (It can be the window, the model or the controller.) + // ... and return the result. + css::uno::Reference< css::frame::XFrame > xCurrentFrame = getCurrentFrame(); + if( xCurrentFrame.is() == sal_True ) + { + xComponent = impl_getFrameComponent( xCurrentFrame ); + } + return xComponent; +} + +/*-************************************************************************************************************//** + @interface XDesktop + @short return the current active frame in hierarchy + @descr There can be more then one different active pathes in our frame hierarchy. But only one of them + could be the most active frame (normal he has the focus). + Don't mix it with getActiveFrame()! That will return our current active frame, which must be + a direct child of us and should be a part(!) of an active path. + + @seealso method getActiveFrame() + + @param - + @return A valid reference, if there is an active frame. + A null reference , otherwise. + + @onerror We return a null reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::getCurrentFrame() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Start search with ouer direct active frame (if it exist!). + // Search on his children for other active frames too. + // Stop if no one could be found and return last of found ones. + css::uno::Reference< css::frame::XFramesSupplier > xLast = css::uno::Reference< css::frame::XFramesSupplier >( getActiveFrame(), css::uno::UNO_QUERY ); + if( xLast.is() == sal_True ) + { + css::uno::Reference< css::frame::XFramesSupplier > xNext = css::uno::Reference< css::frame::XFramesSupplier >( xLast->getActiveFrame(), css::uno::UNO_QUERY ); + while( xNext.is() == sal_True ) + { + xLast = xNext; + xNext = css::uno::Reference< css::frame::XFramesSupplier >( xNext->getActiveFrame(), css::uno::UNO_QUERY ); + } + } + return css::uno::Reference< css::frame::XFrame >( xLast, css::uno::UNO_QUERY ); +} + +/*-************************************************************************************************************//** + @interface XComponentLoader + @short try to load given URL into a task + @descr You can give us some informations about the content, which you will load into a frame. + We search or create this target for you, make a type detection of given URL and try to load it. + As result of this operation we return the new created component or nothing, if loading failed. + + @seealso - + + @param "sURL" , URL, which represant the content + @param "sTargetFrameName" , name of target frame or special value like "_self", "_blank" ... + @param "nSearchFlags" , optional arguments for frame search, if target isn't a special one + @param "lArguments" , optional arguments for loading + @return A valid component reference, if loading was successfully. + A null reference otherwise. + + @onerror We return a null reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > SAL_CALL Desktop::loadComponentFromURL( const ::rtl::OUString& sURL , + const ::rtl::OUString& sTargetFrameName, + sal_Int32 nSearchFlags , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::io::IOException , + css::lang::IllegalArgumentException , + css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + RTL_LOGFILE_CONTEXT( aLog, "framework (as96863) ::Desktop::loadComponentFromURL" ); + + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::frame::XComponentLoader > xThis(static_cast< css::frame::XComponentLoader* >(this), css::uno::UNO_QUERY); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xFactory; + aReadLock.unlock(); + + RTL_LOGFILE_PRODUCT_CONTEXT( aLog2, "PERFORMANCE - Desktop::loadComponentFromURL()" ); + return LoadEnv::loadComponentFromURL(xThis, xSMGR, sURL, sTargetFrameName, nSearchFlags, lArguments); +} + +/*-************************************************************************************************************//** + @interface XTasksSupplier + @short get access to create enumerations of ouer taskchilds + @descr Direct childs of desktop are tasks everytime. + Call these method to could create enumerations of it. + +But; Don't forget - you will be the owner of returned object and must release it! + We use a helper class to implement the access interface. They hold a weakreference to us. + It can be, that the desktop is dead - but not your tasksaccess-object! Then they will do nothing! + You can't create enumerations then. + + @attention Normaly we don't need any lock here. We don't work on internal member! + + @seealso class TasksAccess + + @param - + @return A reference to an accessobject, which can create enumerations of ouer childtasks. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::container::XEnumerationAccess > SAL_CALL Desktop::getTasks() throw( css::uno::RuntimeException ) +{ + LOG_WARNING("Desktop::getTasks()", "Use of obsolete interface XTaskSupplier") + return NULL; + /* + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + OTasksAccess* pTasksAccess = new OTasksAccess( this, &m_aChildTaskContainer ); + css::uno::Reference< css::container::XEnumerationAccess > xAccess( static_cast< ::cppu::OWeakObject* >(pTasksAccess), css::uno::UNO_QUERY ); + return xAccess; + */ +} + +/*-************************************************************************************************************//** + @interface XTasksSupplier + @short return current active task of ouer direct childs + @descr Desktop childs are tasks only ! If we have an active path from desktop + as top to any frame on bottom, we must have an active direct child. His reference is returned here. + + @attention a) Do not confuse it with getCurrentFrame()! The current frame don't must one of ouer direct childs. + It can be every frame in subtree and must have the focus (Is the last one of an active path!). + b) We don't need any lock here. Our container is threadsafe himself and live, if we live! + + @seealso method getCurrentFrame() + + @param - + @return A reference to ouer current active taskchild. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XTask > SAL_CALL Desktop::getActiveTask() throw( css::uno::RuntimeException ) +{ + /* + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + return css::uno::Reference< css::frame::XTask >( m_aChildTaskContainer.getActive(), css::uno::UNO_QUERY ); + */ + LOG_WARNING("Desktop::getActiveTask()", "Use of obsolete interface XTaskSupplier") + return NULL; +} + +/*-************************************************************************************************************//** + @interface XDispatchProvider + @short search a dispatcher for given URL + @descr We use a helper implementation (class DispatchProvider) to do so. + So we don't must implement this algorithm twice! + + @attention We don't need any lock here. Our helper is threadsafe himself and live, if we live! + + @seealso class DispatchProvider + + @param "aURL" , URL to dispatch + @param "sTargetFrameName" , name of target frame, who should dispatch these URL + @param "nSearchFlags" , flags to regulate the search + @param "lQueries" , list of queryDispatch() calls! + @return A reference or list of founded dispatch objects for these URL. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL Desktop::queryDispatch( const css::util::URL& aURL , + const ::rtl::OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) throw( css::uno::RuntimeException ) +{ + const char UNO_PROTOCOL[] = ".uno:"; + + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Remove uno and cmd protocol part as we want to support both of them. We store only the command part + // in our hash map. All other protocols are stored with the protocol part. + String aCommand( aURL.Main ); + if ( aURL.Protocol.equalsIgnoreAsciiCaseAsciiL( UNO_PROTOCOL, sizeof( UNO_PROTOCOL )-1 )) + aCommand = aURL.Path; + + // Make hash_map lookup if the current URL is in the disabled list + if ( m_aCommandOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aCommand ) ) + return css::uno::Reference< css::frame::XDispatch >(); + else + { + // We use a helper to support these interface and an interceptor mechanism. + // Our helper is threadsafe by himself! + return m_xDispatchHelper->queryDispatch( aURL, sTargetFrameName, nSearchFlags ); + } +} + +//***************************************************************************************************************** +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL Desktop::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lQueries ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + return m_xDispatchHelper->queryDispatches( lQueries ); +} + +/*-************************************************************************************************************//** + @interface XDipsatchProviderInterception + @short supports registration/deregistration of interception objects, which + are interested on special dispatches. + + @descr Its realy provided by an internal helper, which is used inside the dispatch api too. + @param xInterceptor + the interceptor object, which wish to be (de)registered. + + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::registerDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) + throw( css::uno::RuntimeException) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper( m_xDispatchHelper, css::uno::UNO_QUERY ); + xInterceptionHelper->registerDispatchProviderInterceptor( xInterceptor ); +} + +//***************************************************************************************************************** +void SAL_CALL Desktop::releaseDispatchProviderInterceptor ( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor) + throw( css::uno::RuntimeException) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper( m_xDispatchHelper, css::uno::UNO_QUERY ); + xInterceptionHelper->releaseDispatchProviderInterceptor( xInterceptor ); +} + +/*-************************************************************************************************************//** + @interface XFramesSupplier + @short return access to append or remove childs on desktop + @descr We don't implement these interface directly. We use a helper class to do this. + If you wish to add or delete childs to/from the container, call these method to get + a reference to the helper. + + @attention Helper is threadsafe himself. So we don't need any lock here. + + @seealso class OFrames + + @param - + @return A reference to the helper. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XFrames > SAL_CALL Desktop::getFrames() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + return m_xFramesHelper; +} + +/*-************************************************************************************************************//** + @interface XFramesSupplier + @short set/get the current active child frame + @descr It must be a task. Direct childs of desktop are tasks only! No frames are accepted. + We don't save this information directly in this class. We use ouer container-helper + to do that. + + @attention Helper is threadsafe himself. So we don't need any lock here. + + @seealso class OFrameContainer + + @param "xFrame", new active frame (must be valid!) + @return A reference to ouer current active childtask, if anyone exist. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::setActiveFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Get old active frame first. + // If nothing will change - do nothing! + // Otherwise set new active frame ... + // and deactivate last frame. + // It's neccessary for our FrameActionEvent listener on a frame! + css::uno::Reference< css::frame::XFrame > xLastActiveChild = m_aChildTaskContainer.getActive(); + if( xLastActiveChild != xFrame ) + { + m_aChildTaskContainer.setActive( xFrame ); + if( xLastActiveChild.is() == sal_True ) + { + xLastActiveChild->deactivate(); + } + } +} + +//***************************************************************************************************************** +css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::getActiveFrame() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + return m_aChildTaskContainer.getActive(); +} + +/*-************************************************************************************************************//** + @interface XFrame + @short non implemented methods! + @descr Some method make no sense for our desktop! He has no window or parent or ... + So we should implement it empty and warn programmer, if he use it! + + @seealso - + + @param - + @return - + + @onerror - + @threadsafe - +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::initialize( const css::uno::Reference< css::awt::XWindow >& ) throw( css::uno::RuntimeException ) +{ +} + +//***************************************************************************************************************** +css::uno::Reference< css::awt::XWindow > SAL_CALL Desktop::getContainerWindow() throw( css::uno::RuntimeException ) +{ + return css::uno::Reference< css::awt::XWindow >(); +} + +//***************************************************************************************************************** +void SAL_CALL Desktop::setCreator( const css::uno::Reference< css::frame::XFramesSupplier >& /*xCreator*/ ) throw( css::uno::RuntimeException ) +{ +} + +//***************************************************************************************************************** +css::uno::Reference< css::frame::XFramesSupplier > SAL_CALL Desktop::getCreator() throw( css::uno::RuntimeException ) +{ + return css::uno::Reference< css::frame::XFramesSupplier >(); +} + +//***************************************************************************************************************** +::rtl::OUString SAL_CALL Desktop::getName() throw( css::uno::RuntimeException ) +{ + /* SAFE { */ + ReadGuard aReadLock( m_aLock ); + return m_sName; + /* } SAFE */ +} + +//***************************************************************************************************************** +void SAL_CALL Desktop::setName( const ::rtl::OUString& sName ) throw( css::uno::RuntimeException ) +{ + /* SAFE { */ + WriteGuard aWriteLock( m_aLock ); + m_sName = sName; + aWriteLock.unlock(); + /* } SAFE */ +} + +//***************************************************************************************************************** +sal_Bool SAL_CALL Desktop::isTop() throw( css::uno::RuntimeException ) +{ + return sal_True; +} + +//***************************************************************************************************************** +void SAL_CALL Desktop::activate() throw( css::uno::RuntimeException ) +{ + // Desktop is activae always ... but sometimes our frames try to activate + // the complete path from bottom to top ... And our desktop is the topest frame :-( + // So - please don't show any assertions here. Do nothing! +} + +//***************************************************************************************************************** +void SAL_CALL Desktop::deactivate() throw( css::uno::RuntimeException ) +{ + // Desktop is activae always ... but sometimes our frames try to deactivate + // the complete path from bottom to top ... And our desktop is the topest frame :-( + // So - please don't show any assertions here. Do nothing! +} + +//***************************************************************************************************************** +sal_Bool SAL_CALL Desktop::isActive() throw( css::uno::RuntimeException ) +{ + return sal_True; +} + +//***************************************************************************************************************** +sal_Bool SAL_CALL Desktop::setComponent( const css::uno::Reference< css::awt::XWindow >& /*xComponentWindow*/ , + const css::uno::Reference< css::frame::XController >& /*xController*/ ) throw( css::uno::RuntimeException ) +{ + return sal_False; +} + +//***************************************************************************************************************** +css::uno::Reference< css::awt::XWindow > SAL_CALL Desktop::getComponentWindow() throw( css::uno::RuntimeException ) +{ + return css::uno::Reference< css::awt::XWindow >(); +} + +//***************************************************************************************************************** +css::uno::Reference< css::frame::XController > SAL_CALL Desktop::getController() throw( css::uno::RuntimeException ) +{ + return css::uno::Reference< css::frame::XController >(); +} + +//***************************************************************************************************************** +void SAL_CALL Desktop::contextChanged() throw( css::uno::RuntimeException ) +{ +} + +//***************************************************************************************************************** +void SAL_CALL Desktop::addFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& ) throw( css::uno::RuntimeException ) +{ +} + +//***************************************************************************************************************** +// css::frame::XFrame +//***************************************************************************************************************** +void SAL_CALL Desktop::removeFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& ) throw( css::uno::RuntimeException ) +{ +} + +/*-************************************************************************************************************//** + @interface XFrame + @short try to find a frame with special parameters + @descr This method searches for a frame with the specified name. + Frames may contain other frames (e.g. a frameset) and may + be contained in other frames. This hierarchie ist searched by + this method. + First some special names are taken into account, i.e. "", + "_self", "_top", "_parent" etc. The FrameSearchFlags are ignored + when comparing these names with aTargetFrameName, further steps are + controlled by the FrameSearchFlags. If allowed, the name of the frame + itself is compared with the desired one, then ( again if allowed ) + the method findFrame is called for all children of the frame. + If no Frame with the given name is found until the top frames container, + a new top Frame is created, if this is allowed by a special + FrameSearchFlag. The new Frame also gets the desired name. + We use a helper to get right search direction and react in a right manner. + + @seealso class TargetFinder + + @param "sTargetFrameName" , name of searched frame + @param "nSearchFlags" , flags to regulate search + @return A reference to an existing frame in hierarchy, if it exist. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL Desktop::findFrame( const ::rtl::OUString& sTargetFrameName , + sal_Int32 nSearchFlags ) throw( css::uno::RuntimeException ) +{ + css::uno::Reference< css::frame::XFrame > xTarget; + + //----------------------------------------------------------------------------------------------------- + // 0) Ignore wrong parameter! + // We doesn't support search for following special targets. + // If we reject this requests - we mustnt check for such names + // in following code again and again. If we do not so -wrong + // search results can occure! + //----------------------------------------------------------------------------------------------------- + if ( + (sTargetFrameName==SPECIALTARGET_DEFAULT ) || // valid for dispatches - not for findFrame()! + (sTargetFrameName==SPECIALTARGET_MENUBAR ) || // valid for dispatches - not for findFrame()! + (sTargetFrameName==SPECIALTARGET_HELPAGENT) || // valid for dispatches - not for findFrame()! + (sTargetFrameName==SPECIALTARGET_PARENT ) || // we have no parent by definition + (sTargetFrameName==SPECIALTARGET_BEAMER ) // beamer frames are allowed as child of tasks only - + // and they exist more then ones. We have no idea which our sub tasks is the right one + ) + { + return NULL; + } + + //----------------------------------------------------------------------------------------------------- + // I) check for special defined targets first which must be handled exclusive. + // force using of "if() else if() ..." + //----------------------------------------------------------------------------------------------------- + + // get threadsafe some neccessary member which are neccessary for following functionality + /* SAFE { */ + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::lang::XMultiServiceFactory > xFactory = m_xFactory; + aReadLock.unlock(); + /* } SAFE */ + + //----------------------------------------------------------------------------------------------------- + // I.I) "_blank" + // create a new task as child of this desktop instance + // Note: Used helper TaskCreator use us automaticly ... + //----------------------------------------------------------------------------------------------------- + if ( sTargetFrameName==SPECIALTARGET_BLANK ) + { + TaskCreator aCreator(xFactory); + xTarget = aCreator.createTask(sTargetFrameName,sal_False); + } + + //----------------------------------------------------------------------------------------------------- + // I.II) "_top" + // We are top by definition + //----------------------------------------------------------------------------------------------------- + else + if ( sTargetFrameName==SPECIALTARGET_TOP ) + { + xTarget = this; + } + + //----------------------------------------------------------------------------------------------------- + // I.III) "_self", "" + // This mean this "frame" in every case. + //----------------------------------------------------------------------------------------------------- + else + if ( + ( sTargetFrameName==SPECIALTARGET_SELF ) || + ( sTargetFrameName.getLength()<1 ) + ) + { + xTarget = this; + } + + else + { + //------------------------------------------------------------------------------------------------- + // II) otherwhise use optional given search flags + // force using of combinations of such flags. means no "else" part of use if() statements. + // But we ust break further searches if target was already found. + // Order of using flags is fix: SELF - CHILDREN - SIBLINGS - PARENT + // TASK and CREATE are handled special. + // But note: Such flags are not valid for the desktop - especialy SIBLINGS or PARENT. + //------------------------------------------------------------------------------------------------- + + // get threadsafe some neccessary member which are neccessary for following functionality + /* SAFE { */ + aReadLock.lock(); + ::rtl::OUString sOwnName = m_sName; + aReadLock.unlock(); + /* } SAFE */ + + //------------------------------------------------------------------------------------------------- + // II.I) SELF + // Check for right name. If it's the searched one return ourself - otherwhise + // ignore this flag. + //------------------------------------------------------------------------------------------------- + if ( + (nSearchFlags & css::frame::FrameSearchFlag::SELF) && + (sOwnName == sTargetFrameName ) + ) + { + xTarget = this; + } + + //------------------------------------------------------------------------------------------------- + // II.II) TASKS + // This is a special flag. Normaly it regulate search inside tasks and forbid access to parent trees. + // But the desktop exists outside such task trees. They are our sub trees. So the desktop implement + // a special feature: We use it to start search on our direct childrens only. That means we supress + // search on ALL child frames. May that can be usefull to get access on opened document tasks + // only without filter out all non realy required sub frames ... + // Used helper method on our container doesn't create any frame - its a search only. + //------------------------------------------------------------------------------------------------- + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::TASKS) + ) + { + xTarget = m_aChildTaskContainer.searchOnDirectChildrens(sTargetFrameName); + } + + //------------------------------------------------------------------------------------------------- + // II.III) CHILDREN + // Search on all children for the given target name. + // An empty name value can't occure here - because it must be already handled as "_self" + // before. Used helper function of container doesn't create any frame. + // It makes a deep search only. + //------------------------------------------------------------------------------------------------- + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN) + ) + { + xTarget = m_aChildTaskContainer.searchOnAllChildrens(sTargetFrameName); + } + + //------------------------------------------------------------------------------------------------- + // II.IV) CREATE + // If we haven't found any valid target frame by using normal flags - but user allowed us to create + // a new one ... we should do that. Used TaskCreator use us automaticly as parent! + //------------------------------------------------------------------------------------------------- + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + ) + { + TaskCreator aCreator(xFactory); + xTarget = aCreator.createTask(sTargetFrameName,sal_False); + } + } + + return xTarget; +} + +//============================================================================= +void SAL_CALL Desktop::dispose() + throw( css::uno::RuntimeException ) +{ + // Safe impossible cases + // It's an programming error if dispose is called before terminate! + LOG_ASSERT2( m_bIsTerminated==sal_False, "Desktop::dispose()", "It's not allowed to dispose the desktop before terminate() is called!" ) + + SYNCHRONIZED_START + WriteGuard aWriteLock( m_aLock ); + + // Look for multiple calls of this method! + // If somewhere call dispose() twice - he will be stopped here realy!!! + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Now - we are alone and its the first call of this method ... + // otherwise call before had thrown a DisposedException / hopefully .-) + // But we dont use the transaction object created before ... we reset it immediatly ... + // two lines of code ... for what ? + // The answer: We wished to synchronize concurrent dispose() calls -> OK + // But next line will wait for all currently running transaction (even if they + // are running within the same thread!) So we would block ourself there if aTransaction + // will stay registered .-) + aTransaction.stop(); + + // Disable this instance for further work. + // This will wait for all current running transactions ... + // and reject all new incoming requests! + m_aTransactionManager.setWorkingMode( E_BEFORECLOSE ); + + aWriteLock.unlock(); + SYNCHRONIZED_END + + // Following lines of code can be called outside a synchronized block ... + // Because our transaction manager will block all new requests to this object. + // So nobody can use us any longer. + // Exception: Only removing of listener will work ... and this code cant be dangerous. + + // First we has to kill all listener connections. + // They might rely on our member and can hinder us on releasing them. + css::uno::Reference< css::uno::XInterface > xThis ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + // Clear our child task container and forget all task references hardly. + // Normaly all open document was already closed by our terminate() function before ... + // New opened frames will have a problem now .-) + m_aChildTaskContainer.clear(); + + // Dispose our helper too. + css::uno::Reference< css::lang::XEventListener > xFramesHelper( m_xFramesHelper, css::uno::UNO_QUERY ); + if( xFramesHelper.is() ) + xFramesHelper->disposing( aEvent ); + + // At least clean up other member references. + m_xDispatchHelper.clear(); + m_xFramesHelper.clear(); + m_xLastFrame.clear(); + m_xFactory.clear(); + + m_xPipeTerminator.clear(); + m_xQuickLauncher.clear(); + m_xSWThreadManager.clear(); + m_xSfxTerminator.clear(); + + // From this point nothing will work further on this object ... + // excepting our dtor() .-) + m_aTransactionManager.setWorkingMode( E_CLOSE ); +} + +/*-************************************************************************************************************//** + @interface XComponent + @short add/remove listener for dispose events + @descr Add an event listener to this object, if you whish to get informations + about our dieing! + You must releas ethis listener reference during your own disposing() method. + + @attention Our container is threadsafe himeslf. So we doesn't need any lock here. + + @seealso - + + @param "xListener", reference to valid listener. We don't accept invalid values! + @return - + + @onerror - + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Safe impossible cases + // Method not defined for all incoming parameter. + LOG_ASSERT2( implcp_addEventListener( xListener ), "Desktop::addEventListener()", "Invalid parameter detected!" ) + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + m_aListenerContainer.addInterface( ::getCppuType( ( const css::uno::Reference< css::lang::XEventListener >*) NULL ), xListener ); +} + +//***************************************************************************************************************** +void SAL_CALL Desktop::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Safe impossible cases + // Method not defined for all incoming parameter. + LOG_ASSERT2( implcp_removeEventListener( xListener ), "Desktop::removeEventListener()", "Invalid parameter detected!" ) + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + m_aListenerContainer.removeInterface( ::getCppuType( ( const css::uno::Reference< css::lang::XEventListener >*) NULL ), xListener ); +} + +/*-************************************************************************************************************//** + @interface XDispatchResultListener + @short callback for dispatches + @descr To support our method "loadComponentFromURL()" we are listener on temp. created dispatcher. + They call us back in this method "statusChanged()". As source of given state event, they give us a + reference to the target frame, in which dispatch was loaded! So we can use it to return his component + to caller! If no target exist ... ??!! + + @seealso method loadComponentFromURL() + + @param "aEvent", state event which (hopefully) valid informations + @return - + + @onerror - + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::dispatchFinished( const css::frame::DispatchResultEvent& aEvent ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ------------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + if( m_eLoadState != E_INTERACTION ) + { + m_xLastFrame = css::uno::Reference< css::frame::XFrame >(); + m_eLoadState = E_FAILED ; + if( aEvent.State == css::frame::DispatchResultState::SUCCESS ) + { + css::uno::Reference < css::frame::XFrame > xFrame; + if ( aEvent.Result >>= m_xLastFrame ) + m_eLoadState = E_SUCCESSFUL; + } + } + /* UNSAFE AREA ----------------------------------------------------------------------------------------- */ +} + +/*-************************************************************************************************************//** + @interface XEventListener + @short not implemented! + @descr We are a status listener ... and so we must be an event listener too ... But we doesn't need it realy! + We are a temp. listener only and our lifetime isn't smaller then of our temp. used dispatcher. + + @seealso method loadComponentFromURL() + + @param - + @return - + + @onerror - + @threadsafe - +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::disposing( const css::lang::EventObject& ) throw( css::uno::RuntimeException ) +{ + LOG_ERROR( "Desktop::disposing()", "Algorithm error! Normaly desktop is temp. listener ... not all the time. So this method shouldn't be called." ) +} + +/*-************************************************************************************************************//** + @interface XInteractionHandler + @short callback for loadComponentFromURL for detected exceptions during load proccess + @descr In this case we must cancel loading and throw these detected exception again as result + of our own called method. + + @attention a) + Normal loop in loadComponentFromURL() breaks on setted member m_eLoadState during callback statusChanged(). + But these interaction feature implements second way to do so! So we must look on different callbacks + for same operation ... and live with it. + b) + Search for given continuations too. If any XInteractionAbort exist ... use it to abort further operations + for currently running operation! + + @seealso method loadComponentFromURL() + @seealso member m_eLoadState + + @param "xRequest", request for interaction - normal a wrapped target exception from bottom services + @return - + + @onerror - + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::handle( const css::uno::Reference< css::task::XInteractionRequest >& xRequest ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Don't check incoming request! + // If somewhere starts interaction without right parameter - he maked something wrong. + // loadComponentFromURL() waits for thjese event - otherwise it yield for ever! + + // get packed request and work on it first + // Attention: Don't set it on internal member BEFORE interaction is finished - because + // "loadComponentFromURL()" yield tills this member is changed. If we do it before + // interaction finish we can't guarantee right functionality. May be we cancel load process to erliear ... + css::uno::Any aRequest = xRequest->getRequest(); + + // extract continuations from request + css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > lContinuations = xRequest->getContinuations(); + css::uno::Reference< css::task::XInteractionAbort > xAbort ; + css::uno::Reference< css::task::XInteractionApprove > xApprove ; + css::uno::Reference< css::document::XInteractionFilterSelect > xFilterSelect ; + sal_Bool bAbort = sal_False; + + sal_Int32 nCount=lContinuations.getLength(); + for( sal_Int32 nStep=0; nStep<nCount; ++nStep ) + { + if( ! xAbort.is() ) + xAbort = css::uno::Reference< css::task::XInteractionAbort >( lContinuations[nStep], css::uno::UNO_QUERY ); + + if( ! xApprove.is() ) + xApprove = css::uno::Reference< css::task::XInteractionApprove >( lContinuations[nStep], css::uno::UNO_QUERY ); + + if( ! xFilterSelect.is() ) + xFilterSelect = css::uno::Reference< css::document::XInteractionFilterSelect >( lContinuations[nStep], css::uno::UNO_QUERY ); + } + + // differ between abortable interactions (error, unknown filter ...) + // and other ones (ambigous but not unknown filter ...) + css::task::ErrorCodeRequest aErrorCodeRequest ; + css::document::AmbigousFilterRequest aAmbigousFilterRequest; + if( aRequest >>= aAmbigousFilterRequest ) + { + if( xFilterSelect.is() ) + { + xFilterSelect->setFilter( aAmbigousFilterRequest.SelectedFilter ); // user selected filter wins! + xFilterSelect->select(); + } + } + else + if( aRequest >>= aErrorCodeRequest ) + { + sal_Bool bWarning = ((aErrorCodeRequest.ErrCode & ERRCODE_WARNING_MASK) == ERRCODE_WARNING_MASK); + if (xApprove.is() && bWarning) + xApprove->select(); + else + if (xAbort.is()) + { + xAbort->select(); + bAbort = sal_True; + } + } + else + if( xAbort.is() ) + { + xAbort->select(); + bAbort = sal_True; + } + + /* SAFE AREA ------------------------------------------------------------------------------------------- */ + // Ok now it's time to break yield loop of loadComponentFromURL(). + // But only for realy aborted requests! + // For example warnings will be approved and we wait for any success story ... + if (bAbort) + { + WriteGuard aWriteLock( m_aLock ); + m_eLoadState = E_INTERACTION; + m_aInteractionRequest = aRequest ; + aWriteLock.unlock(); + } + /* UNSAFE AREA ----------------------------------------------------------------------------------------- */ +} + +//----------------------------------------------------------------------------- +::sal_Int32 SAL_CALL Desktop::leaseNumber( const css::uno::Reference< css::uno::XInterface >& xComponent ) + throw (css::lang::IllegalArgumentException, + css::uno::RuntimeException ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + return m_xTitleNumberGenerator->leaseNumber (xComponent); +} + +//----------------------------------------------------------------------------- +void SAL_CALL Desktop::releaseNumber( ::sal_Int32 nNumber ) + throw (css::lang::IllegalArgumentException, + css::uno::RuntimeException ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + m_xTitleNumberGenerator->releaseNumber (nNumber); +} + +//----------------------------------------------------------------------------- +void SAL_CALL Desktop::releaseNumberForComponent( const css::uno::Reference< css::uno::XInterface >& xComponent ) + throw (css::lang::IllegalArgumentException, + css::uno::RuntimeException ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + m_xTitleNumberGenerator->releaseNumberForComponent (xComponent); +} + +//----------------------------------------------------------------------------- +::rtl::OUString SAL_CALL Desktop::getUntitledPrefix() + throw (css::uno::RuntimeException) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + return m_xTitleNumberGenerator->getUntitledPrefix (); +} + +/*-************************************************************************************************************//** + @short try to convert a property value + @descr This method is called from helperclass "OPropertySetHelper". + Don't use this directly! + You must try to convert the value of given DESKTOP_PROPHANDLE and + return results of this operation. This will be used to ask vetoable + listener. If no listener has a veto, we will change value realy! + ( in method setFastPropertyValue_NoBroadcast(...) ) + + @attention Methods of OPropertySethelper are safed by using our shared osl mutex! (see ctor!) + So we must use different locks to make our implementation threadsafe. + + @seealso class OPropertySetHelper + @seealso method setFastPropertyValue_NoBroadcast() + + @param "aConvertedValue" new converted value of property + @param "aOldValue" old value of property + @param "nHandle" handle of property + @param "aValue" new value of property + @return sal_True if value will be changed, sal_FALSE otherway + + @onerror IllegalArgumentException, if you call this with an invalid argument + @threadsafe yes +*//*-*************************************************************************************************************/ +sal_Bool SAL_CALL Desktop::convertFastPropertyValue( css::uno::Any& aConvertedValue , + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) throw( css::lang::IllegalArgumentException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Initialize state with FALSE !!! + // (Handle can be invalid) + sal_Bool bReturn = sal_False; + + switch( nHandle ) + { + case DESKTOP_PROPHANDLE_SUSPENDQUICKSTARTVETO: + bReturn = PropHelper::willPropertyBeChanged( + css::uno::makeAny(m_bSuspendQuickstartVeto), + aValue, + aOldValue, + aConvertedValue); + break; + case DESKTOP_PROPHANDLE_DISPATCHRECORDERSUPPLIER : + bReturn = PropHelper::willPropertyBeChanged( + css::uno::makeAny(m_xDispatchRecorderSupplier), + aValue, + aOldValue, + aConvertedValue); + break; + case DESKTOP_PROPHANDLE_TITLE : + bReturn = PropHelper::willPropertyBeChanged( + css::uno::makeAny(m_sTitle), + aValue, + aOldValue, + aConvertedValue); + break; + } + + // Return state of operation. + return bReturn ; +} + +/*-************************************************************************************************************//** + @short set value of a transient property + @descr This method is calling from helperclass "OPropertySetHelper". + Don't use this directly! + Handle and value are valid everyway! You must set the new value only. + After this, baseclass send messages to all listener automaticly. + + @seealso class OPropertySetHelper + + @param "nHandle" handle of property to change + @param "aValue" new value of property + @return - + + @onerror An exception is thrown. + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle , + const css::uno::Any& aValue ) throw( css::uno::Exception ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + switch( nHandle ) + { + case DESKTOP_PROPHANDLE_SUSPENDQUICKSTARTVETO: aValue >>= m_bSuspendQuickstartVeto; + break; + case DESKTOP_PROPHANDLE_DISPATCHRECORDERSUPPLIER: aValue >>= m_xDispatchRecorderSupplier; + break; + case DESKTOP_PROPHANDLE_TITLE: aValue >>= m_sTitle; + break; + } +} + +/*-************************************************************************************************************//** + @short get value of a transient property + @descr This method is calling from helperclass "OPropertySetHelper". + Don't use this directly! + + @attention We don't need any mutex or lock here ... We use threadsafe container or methods here only! + + @seealso class OPropertySetHelper + + @param "nHandle" handle of property to change + @param "aValue" current value of property + @return - + + @onerror - + @threadsafe yes +*//*-*************************************************************************************************************/ +void SAL_CALL Desktop::getFastPropertyValue( css::uno::Any& aValue , + sal_Int32 nHandle ) const +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + switch( nHandle ) + { + case DESKTOP_PROPHANDLE_ACTIVEFRAME : aValue <<= m_aChildTaskContainer.getActive(); + break; + case DESKTOP_PROPHANDLE_ISPLUGGED : aValue <<= sal_False; + break; + case DESKTOP_PROPHANDLE_SUSPENDQUICKSTARTVETO: aValue <<= m_bSuspendQuickstartVeto; + break; + case DESKTOP_PROPHANDLE_DISPATCHRECORDERSUPPLIER: aValue <<= m_xDispatchRecorderSupplier; + break; + case DESKTOP_PROPHANDLE_TITLE: aValue <<= m_sTitle; + break; + } +} + +/*-************************************************************************************************************//** + @short return structure and information about transient properties + @descr This method is calling from helperclass "OPropertySetHelper". + Don't use this directly! + + @attention You must use global lock (method use static variable) ... and it must be the shareable osl mutex of it. + Because; our baseclass use this mutex to make his code threadsafe. We use our lock! + So we could have two different mutex/lock mechanism at the same object. + + @seealso class OPropertySetHelper + + @param - + @return structure with property-informations + + @onerror - + @threadsafe yes +*//*-*************************************************************************************************************/ +::cppu::IPropertyArrayHelper& SAL_CALL Desktop::getInfoHelper() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Optimize this method ! + // We initialize a static variable only one time. And we don't must use a mutex at every call! + // For the first call; pInfoHelper is NULL - for the second call pInfoHelper is different from NULL! + static ::cppu::OPropertyArrayHelper* pInfoHelper = NULL; + + if( pInfoHelper == NULL ) + { + // Ready for multithreading + ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() ); + // Control this pointer again, another instance can be faster then these! + if( pInfoHelper == NULL ) + { + // Define static member to give structure of properties to baseclass "OPropertySetHelper". + // "impl_getStaticPropertyDescriptor" is a non exported and static funtion, who will define a static propertytable. + // "sal_True" say: Table is sorted by name. + static ::cppu::OPropertyArrayHelper aInfoHelper( impl_getStaticPropertyDescriptor(), sal_True ); + pInfoHelper = &aInfoHelper; + } + } + + return(*pInfoHelper); +} + +/*-************************************************************************************************************//** + @short return propertysetinfo + @descr You can call this method to get information about transient properties + of this object. + + @attention You must use global lock (method use static variable) ... and it must be the shareable osl mutex of it. + Because; our baseclass use this mutex to make his code threadsafe. We use our lock! + So we could have two different mutex/lock mechanism at the same object. + + @seealso class OPropertySetHelper + @seealso interface XPropertySet + @seealso interface XMultiPropertySet + + @param - + @return reference to object with information [XPropertySetInfo] + + @onerror - + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL Desktop::getPropertySetInfo() throw (::com::sun::star::uno::RuntimeException) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Optimize this method ! + // We initialize a static variable only one time. And we don't must use a mutex at every call! + // For the first call; pInfo is NULL - for the second call pInfo is different from NULL! + static css::uno::Reference< css::beans::XPropertySetInfo >* pInfo = NULL; + + if( pInfo == NULL ) + { + // Ready for multithreading + ::osl::MutexGuard aGuard( LockHelper::getGlobalLock().getShareableOslMutex() ); + // Control this pointer again, another instance can be faster then these! + if( pInfo == NULL ) + { + // Create structure of propertysetinfo for baseclass "OPropertySetHelper". + // (Use method "getInfoHelper()".) + static css::uno::Reference< css::beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + pInfo = &xInfo; + } + } + + return (*pInfo); +} + +/*-************************************************************************************************************//** + @short return current component of current frame + @descr The desktop himself has no component. But every frame in subtree. + If somewhere call getCurrentComponent() at this class, we try to find the right frame and + then we try to become his component. It can be a VCL-component, the model or the controller + of founded frame. + + @attention We don't work on internal member ... so we doesn't need any lock here. + + @seealso method getCurrentComponent(); + + @param "xFrame", reference to valid frame in hierarchy. Method is not defined for invalid values. + But we don't check these. Its an IMPL-method and caller must use it right! + @return A reference to found component. + + @onerror A null reference is returned. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > Desktop::impl_getFrameComponent( const css::uno::Reference< css::frame::XFrame >& xFrame ) const +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Set default return value, if method failed. + css::uno::Reference< css::lang::XComponent > xComponent; + // Does no controller exists? + css::uno::Reference< css::frame::XController > xController = xFrame->getController(); + if( xController.is() == sal_False ) + { + // Controller not exist - use the VCL-component. + xComponent = css::uno::Reference< css::lang::XComponent >( xFrame->getComponentWindow(), css::uno::UNO_QUERY ); + } + else + { + // Does no model exists? + css::uno::Reference< css::frame::XModel > xModel( xController->getModel(), css::uno::UNO_QUERY ); + if( xModel.is() == sal_True ) + { + // Model exist - use the model as component. + xComponent = css::uno::Reference< css::lang::XComponent >( xModel, css::uno::UNO_QUERY ); + } + else + { + // Model not exist - use the controller as component. + xComponent = css::uno::Reference< css::lang::XComponent >( xController, css::uno::UNO_QUERY ); + } + } + + return xComponent; +} + +/*-************************************************************************************************************//** + @short create table with information about properties + @descr We use a helper class to support properties. These class need some information about this. + These method create a new static description table with name, type, r/w-flags and so on ... + + @seealso class OPropertySetHelper + @seealso method getInfoHelper() + + @param - + @return Static table with information about properties. + + @onerror - + @threadsafe yes +*//*-*************************************************************************************************************/ +const css::uno::Sequence< css::beans::Property > Desktop::impl_getStaticPropertyDescriptor() +{ + // Create a new static property array to initialize sequence! + // Table of all predefined properties of this class. Its used from OPropertySetHelper-class! + // Don't forget to change the defines (see begin of this file), if you add, change or delete a property in this list!!! + // It's necessary for methods of OPropertySetHelper. + // ATTENTION: + // YOU MUST SORT FOLLOW TABLE BY NAME ALPHABETICAL !!! + + static const css::beans::Property pProperties[] = + { + css::beans::Property( DESKTOP_PROPNAME_ACTIVEFRAME , DESKTOP_PROPHANDLE_ACTIVEFRAME , ::getCppuType((const css::uno::Reference< css::lang::XComponent >*)NULL) , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( DESKTOP_PROPNAME_DISPATCHRECORDERSUPPLIER , DESKTOP_PROPHANDLE_DISPATCHRECORDERSUPPLIER, ::getCppuType((const css::uno::Reference< css::frame::XDispatchRecorderSupplier >*)NULL), css::beans::PropertyAttribute::TRANSIENT ), + css::beans::Property( DESKTOP_PROPNAME_ISPLUGGED , DESKTOP_PROPHANDLE_ISPLUGGED , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY ), + css::beans::Property( DESKTOP_PROPNAME_SUSPENDQUICKSTARTVETO , DESKTOP_PROPHANDLE_SUSPENDQUICKSTARTVETO , ::getBooleanCppuType() , css::beans::PropertyAttribute::TRANSIENT ), + css::beans::Property( DESKTOP_PROPNAME_TITLE , DESKTOP_PROPHANDLE_TITLE , ::getCppuType((const ::rtl::OUString*)NULL) , css::beans::PropertyAttribute::TRANSIENT ), + }; + // Use it to initialize sequence! + static const css::uno::Sequence< css::beans::Property > lPropertyDescriptor( pProperties, DESKTOP_PROPCOUNT ); + // Return static "PropertyDescriptor" + return lPropertyDescriptor; +} + +//============================================================================= +void Desktop::impl_sendQueryTerminationEvent(Desktop::TTerminateListenerList& lCalledListener, + ::sal_Bool& bVeto ) +{ + bVeto = sal_False; + + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer( ::getCppuType( ( const css::uno::Reference< css::frame::XTerminateListener >*) NULL ) ); + if ( ! pContainer ) + return; + + css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) ); + + ::cppu::OInterfaceIteratorHelper aIterator( *pContainer ); + while ( aIterator.hasMoreElements() ) + { + try + { + css::uno::Reference< css::frame::XTerminateListener > xListener(aIterator.next(), css::uno::UNO_QUERY); + if ( ! xListener.is() ) + continue; + xListener->queryTermination( aEvent ); + lCalledListener.push_back(xListener); + } + catch( const css::frame::TerminationVetoException& ) + { + // first veto will stop notification loop. + bVeto = sal_True; + return; + } + catch( const css::uno::Exception& ) + { + // clean up container. + // E.g. dead remote listener objects can make trouble otherwise. + // Iterator implementation allows removing objects during it's used ! + aIterator.remove(); + } + } +} + +//============================================================================= +void Desktop::impl_sendCancelTerminationEvent(const Desktop::TTerminateListenerList& lCalledListener) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) ); + Desktop::TTerminateListenerList::const_iterator pIt; + for ( pIt = lCalledListener.begin(); + pIt != lCalledListener.end (); + ++pIt ) + { + try + { + // Note: cancelTermination() is a new and optional interface method ! + css::uno::Reference< css::frame::XTerminateListener > xListener = *pIt; + css::uno::Reference< css::frame::XTerminateListener2 > xListenerGeneration2(xListener, css::uno::UNO_QUERY); + if ( ! xListenerGeneration2.is() ) + continue; + xListenerGeneration2->cancelTermination( aEvent ); + } + catch( const css::uno::Exception& ) + {} + } +} + +//============================================================================= +void Desktop::impl_sendNotifyTerminationEvent() +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer( ::getCppuType( ( const css::uno::Reference< css::frame::XTerminateListener >*) NULL ) ); + if ( ! pContainer ) + return; + + css::lang::EventObject aEvent( static_cast< ::cppu::OWeakObject* >(this) ); + + ::cppu::OInterfaceIteratorHelper aIterator( *pContainer ); + while ( aIterator.hasMoreElements() ) + { + try + { + css::uno::Reference< css::frame::XTerminateListener > xListener(aIterator.next(), css::uno::UNO_QUERY); + if ( ! xListener.is() ) + continue; + xListener->notifyTermination( aEvent ); + } + catch( const css::uno::Exception& ) + { + // clean up container. + // E.g. dead remote listener objects can make trouble otherwise. + // Iterator implementation allows removing objects during it's used ! + aIterator.remove(); + } + } +} + +//============================================================================= +::sal_Bool Desktop::impl_closeFrames(::sal_Bool bAllowUI) +{ + SYNCHRONIZED_START + ReadGuard aReadLock( m_aLock ); + css::uno::Sequence< css::uno::Reference< css::frame::XFrame > > lFrames = m_aChildTaskContainer.getAllElements(); + aReadLock.unlock(); + SYNCHRONIZED_END + + ::sal_Int32 c = lFrames.getLength(); + ::sal_Int32 i = 0; + ::sal_Int32 nNonClosedFrames = 0; + + for( i=0; i<c; ++i ) + { + try + { + css::uno::Reference< css::frame::XFrame > xFrame = lFrames[i]; + + // XController.suspend() will show an UI ... + // Use it in case it was allowed from outside only. + sal_Bool bSuspended = sal_False; + css::uno::Reference< css::frame::XController > xController( xFrame->getController(), css::uno::UNO_QUERY ); + if ( + ( bAllowUI ) && + ( xController.is() ) + ) + { + bSuspended = xController->suspend( sal_True ); + if ( ! bSuspended ) + { + ++nNonClosedFrames; + continue; + } + } + + // Try to close frame (in case no UI was allowed without calling XController->suspend() before!) + // But don't deliver ownership to any other one! + // This method can be called again. + css::uno::Reference< css::util::XCloseable > xClose( xFrame, css::uno::UNO_QUERY ); + if ( xClose.is() ) + { + try + { + xClose->close(sal_False); + } + catch(const css::util::CloseVetoException&) + { + // Any internal process of this frame disagree with our request. + // Safe this state but dont break these loop. Other frames has to be closed! + ++nNonClosedFrames; + + // Reactivate controller. + // It can happen that XController.suspend() returned true ... but a registered close listener + // throwed these veto exception. Then the controller has to be reactivated. Otherwise + // these document doesnt work any more. + if ( + (bSuspended ) && + (xController.is()) + ) + xController->suspend(sal_False); + } + + // If interface XClosable interface exists and was used ... + // it's not allowed to use XComponent->dispose() also ! + continue; + } + + // XClosable not supported ? + // Then we have to dispose these frame hardly. + css::uno::Reference< css::lang::XComponent > xDispose( xFrame, css::uno::UNO_QUERY ); + if ( xDispose.is() ) + xDispose->dispose(); + + // Don't remove these frame from our child container! + // A frame do it by itself inside close()/dispose() method. + } + catch(const css::lang::DisposedException&) + { + // Dispose frames are closed frames. + // So we can count it here .-) + } + } + + return (nNonClosedFrames < 1); +} + +//_________________________________________________________________________________________________________________ +// debug methods +//_________________________________________________________________________________________________________________ + +/*----------------------------------------------------------------------------------------------------------------- + The follow methods checks the parameter for other functions. If a parameter or his value is non valid, + we return "sal_True". (otherwise sal_False) This mechanism is used to throw an ASSERT! +-----------------------------------------------------------------------------------------------------------------*/ + +#ifdef ENABLE_ASSERTIONS + +//***************************************************************************************************************** +// We work with valid servicemanager only. +sal_Bool Desktop::implcp_ctor( const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) +{ + return( + ( &xFactory == NULL ) || + ( xFactory.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +// We work with valid listener only. +sal_Bool Desktop::implcp_addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + return( + ( &xListener == NULL ) || + ( xListener.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +// We work with valid listener only. +sal_Bool Desktop::implcp_removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + return( + ( &xListener == NULL ) || + ( xListener.is() == sal_False ) + ); +} + +#endif // #ifdef ENABLE_ASSERTIONS + +} // namespace framework diff --git a/framework/source/services/dispatchhelper.cxx b/framework/source/services/dispatchhelper.cxx new file mode 100644 index 000000000000..be8e666ee9db --- /dev/null +++ b/framework/source/services/dispatchhelper.cxx @@ -0,0 +1,227 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_______________________________________________ +// my own includes +#include <services/dispatchhelper.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <services.h> + +//_______________________________________________ +// interface includes +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/frame/XNotifyingDispatch.hpp> + +//_______________________________________________ +// includes of other projects + +//_______________________________________________ +// namespace + +namespace framework{ + +//_______________________________________________ +// non exported const + +//_______________________________________________ +// non exported definitions + +//_______________________________________________ +// declarations + +//_______________________________________________ +// XInterface, XTypeProvider, XServiceInfo + +DEFINE_XSERVICEINFO_MULTISERVICE(DispatchHelper , + ::cppu::OWeakObject , + SERVICENAME_DISPATCHHELPER , + IMPLEMENTATIONNAME_DISPATCHHELPER) + +DEFINE_INIT_SERVICE( DispatchHelper, {} ) + +//_______________________________________________ + +/** ctor. + + @param xSMGR the global uno service manager, which can be used to create own needed services. +*/ +DispatchHelper::DispatchHelper( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) + : ThreadHelpBase( ) + // Init member + , m_xSMGR (xSMGR) +{ +} + +//_______________________________________________ + +/** dtor. +*/ +DispatchHelper::~DispatchHelper() +{ +} + +//_______________________________________________ + +/** capsulate all steps of a dispatch request and provide so an easy way for dispatches. + + @param xDispatchProvider + identifies the object, which provides may be valid dispatch objects for this execute. + + @param sURL + describes the requested feature. + + @param sTargetFrameName + points to the frame, which must be used (or may be created) for this dispatch. + + @param nSearchFlags + in case the <var>sTargetFrameName</var> isn't unique, these flags regulate further searches. + + @param lArguments + optional arguments for this request. + + @return An Any which capsulate a possible result of the internal wrapped dispatch. + */ +css::uno::Any SAL_CALL DispatchHelper::executeDispatch( + const css::uno::Reference< css::frame::XDispatchProvider >& xDispatchProvider , + const ::rtl::OUString& sURL , + const ::rtl::OUString& sTargetFrameName , + sal_Int32 nSearchFlags , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) + throw(css::uno::RuntimeException) +{ + css::uno::Reference< css::uno::XInterface > xTHIS(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + + // check for valid parameters + if ( + (!xDispatchProvider.is()) || + (sURL.getLength()<1 ) + ) + { + return css::uno::Any(); + } + + // parse given URL + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::util::XURLTransformer > xParser(m_xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), css::uno::UNO_QUERY); + aReadLock.unlock(); + /* } SAFE */ + + css::util::URL aURL; + aURL.Complete = sURL; + xParser->parseStrict(aURL); + + // search dispatcher + css::uno::Reference< css::frame::XDispatch > xDispatch = xDispatchProvider->queryDispatch(aURL, sTargetFrameName, nSearchFlags); + css::uno::Reference< css::frame::XNotifyingDispatch > xNotifyDispatch (xDispatch, css::uno::UNO_QUERY); + + // make sure that synchronous execution is used (if possible) + css::uno::Sequence< css::beans::PropertyValue > aArguments( lArguments ); + sal_Int32 nLength = lArguments.getLength(); + aArguments.realloc( nLength + 1 ); + aArguments[ nLength ].Name = ::rtl::OUString::createFromAscii("SynchronMode"); + aArguments[ nLength ].Value <<= (sal_Bool) sal_True; + + css::uno::Any aResult; + if (xNotifyDispatch.is()) + { + // dispatch it with guaranteed notification + // Here we can hope for a result ... instead of the normal dispatch. + css::uno::Reference< css::frame::XDispatchResultListener > xListener(xTHIS, css::uno::UNO_QUERY); + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + m_xBroadcaster = css::uno::Reference< css::uno::XInterface >(xNotifyDispatch, css::uno::UNO_QUERY); + m_aResult = css::uno::Any(); + m_aBlock.reset(); + aWriteLock.unlock(); + /* } SAFE */ + + // dispatch it and wait for a notification + // TODO/MBA: waiting in main thread?! + xNotifyDispatch->dispatchWithNotification(aURL, aArguments, xListener); + //m_aBlock.wait(); + aResult = m_aResult; + } + else + if (xDispatch.is()) + { + // dispatch it without any chance to get a result + xDispatch->dispatch( aURL, aArguments ); + } + + return aResult; +} + +//_______________________________________________ + +/** callback for started dispatch with guaranteed notifications. + + We must save the result, so the method executeDispatch() can return it. + Further we must release the broadcaster (otherwhise it can't die) + and unblock the waiting executeDispatch() request. + + @param aResult + describes the result of the dispatch operation + */ +void SAL_CALL DispatchHelper::dispatchFinished( const css::frame::DispatchResultEvent& aResult ) + throw(css::uno::RuntimeException) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + m_aResult <<= aResult; + m_aBlock.set(); + m_xBroadcaster.clear(); + + /* } SAFE */ +} + +//_______________________________________________ + +/** we has to realease our broadcaster reference. + + @param aEvent + describe the source of this event and MUST be our save broadcaster! + */ +void SAL_CALL DispatchHelper::disposing( const css::lang::EventObject& ) + throw(css::uno::RuntimeException) +{ + /* SAFE { */ + WriteGuard aWriteLock(m_aLock); + + m_aResult.clear(); + m_aBlock.set(); + m_xBroadcaster.clear(); + + /* } SAFE */ +} + +} diff --git a/framework/source/services/frame.cxx b/framework/source/services/frame.cxx new file mode 100644 index 000000000000..08a7c522831b --- /dev/null +++ b/framework/source/services/frame.cxx @@ -0,0 +1,3311 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_________________________________________________________________________________________________________________ +// my own includes +//_________________________________________________________________________________________________________________ +#include <services/frame.hxx> +#include <dispatch/dispatchprovider.hxx> + +#ifndef __FRAMEWORK_DISPATCH_INTERCEPTIONHELPER_HXX_ +#include <dispatch/interceptionhelper.hxx> +#endif +#include <dispatch/closedispatcher.hxx> +#include <dispatch/windowcommanddispatch.hxx> +#include <loadenv/loadenv.hxx> +#include <helper/oframes.hxx> +#include <helper/statusindicatorfactory.hxx> +#include <helper/titlehelper.hxx> +#include <classes/droptargetlistener.hxx> +#include <classes/taskcreator.hxx> +#include <loadenv/targethelper.hxx> +#include <classes/framelistanalyzer.hxx> +#include <helper/dockingareadefaultacceptor.hxx> +#include <dispatch/dispatchinformationprovider.hxx> +#include <threadhelp/transactionguard.hxx> +#include <pattern/window.hxx> +#include <services.h> +#include <properties.h> + +//_________________________________________________________________________________________________________________ +// interface includes +//_________________________________________________________________________________________________________________ +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/task/XJobExecutor.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/awt/XDevice.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/awt/XWindowPeer.hpp> +#include <com/sun/star/awt/XVclWindowPeer.hpp> +#include <com/sun/star/task/XStatusIndicatorSupplier.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/awt/XDataTransferProviderAccess.hpp> +#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> + +#ifndef _COM_SUN_STAR_FRAME_XTITLECHANGEBROADCASTER_HPP_ +#include <com/sun/star/frame/XTitleChangeBroadcaster.hpp> +#endif + +//_________________________________________________________________________________________________________________ +// includes of other projects +//_________________________________________________________________________________________________________________ +#include <comphelper/sequenceashashmap.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/proptypehlp.hxx> +#include <rtl/ustrbuf.hxx> +#include <vcl/window.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/svapp.hxx> + +#ifndef _TOOLKIT_HELPER_VCLUNOHELPER_HXX_ +#include <toolkit/unohlp.hxx> +#endif +#include <toolkit/awt/vclxwindow.hxx> +#include <comphelper/processfactory.hxx> +#include <unotools/moduleoptions.hxx> + +#ifdef ENABLE_ASSERTIONS + #ifndef _RTL_STRBUF_HXX_ + #include <rtl/strbuf.hxx> + #endif +#endif + +#include <vcl/menu.hxx> + +//_________________________________________________________________________________________________________________ +// namespace +//_________________________________________________________________________________________________________________ + +namespace framework{ + +//_________________________________________________________________________________________________________________ +// non exported const +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// non exported definitions +//_________________________________________________________________________________________________________________ + +css::uno::WeakReference< css::frame::XFrame > Frame::m_xCloserFrame = css::uno::WeakReference< css::frame::XFrame >(); + +//_________________________________________________________________________________________________________________ +// declarations +//_________________________________________________________________________________________________________________ + +//***************************************************************************************************************** +// XInterface, XTypeProvider, XServiceInfo +//***************************************************************************************************************** +DEFINE_XINTERFACE_21 ( Frame , + OWeakObject , + DIRECT_INTERFACE(css::lang::XTypeProvider ), + DIRECT_INTERFACE(css::lang::XServiceInfo ), + DIRECT_INTERFACE(css::frame::XFramesSupplier ), + DIRECT_INTERFACE(css::frame::XFrame ), + DIRECT_INTERFACE(css::lang::XComponent ), + DIRECT_INTERFACE(css::task::XStatusIndicatorFactory ), + DIRECT_INTERFACE(css::frame::XDispatchProvider ), + DIRECT_INTERFACE(css::frame::XDispatchInformationProvider ), + DIRECT_INTERFACE(css::frame::XDispatchProviderInterception ), + DIRECT_INTERFACE(css::beans::XPropertySet ), + DIRECT_INTERFACE(css::beans::XPropertySetInfo ), + DIRECT_INTERFACE(css::awt::XWindowListener ), + DIRECT_INTERFACE(css::awt::XTopWindowListener ), + DIRECT_INTERFACE(css::awt::XFocusListener ), + DERIVED_INTERFACE(css::lang::XEventListener, css::awt::XWindowListener ), + DIRECT_INTERFACE(css::document::XActionLockable ), + DIRECT_INTERFACE(css::util::XCloseable ), + DIRECT_INTERFACE(css::util::XCloseBroadcaster ), + DIRECT_INTERFACE(css::frame::XComponentLoader ), + DIRECT_INTERFACE(css::frame::XTitle ), + DIRECT_INTERFACE(css::frame::XTitleChangeBroadcaster ) + ) + +DEFINE_XTYPEPROVIDER_20 ( Frame , + css::lang::XTypeProvider , + css::lang::XServiceInfo , + css::frame::XFramesSupplier , + css::frame::XFrame , + css::lang::XComponent , + css::task::XStatusIndicatorFactory , + css::beans::XPropertySet , + css::beans::XPropertySetInfo , + css::frame::XDispatchProvider , + css::frame::XDispatchInformationProvider , + css::frame::XDispatchProviderInterception , + css::awt::XWindowListener , + css::awt::XTopWindowListener , + css::awt::XFocusListener , + css::lang::XEventListener , + css::util::XCloseable , + css::util::XCloseBroadcaster , + css::frame::XComponentLoader , + css::frame::XTitle , + css::frame::XTitleChangeBroadcaster + ) + +DEFINE_XSERVICEINFO_MULTISERVICE ( Frame , + ::cppu::OWeakObject , + SERVICENAME_FRAME , + IMPLEMENTATIONNAME_FRAME + ) + +DEFINE_INIT_SERVICE ( Frame, + { + /*Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY_THROW); + + //------------------------------------------------------------------------------------------------------------- + // Initialize a new dispatchhelper-object to handle dispatches. + // We use these helper as slave for our interceptor helper ... not directly! + // But he is event listener on THIS instance! + DispatchProvider* pDispatchHelper = new DispatchProvider( m_xFactory, this ); + css::uno::Reference< css::frame::XDispatchProvider > xDispatchProvider( static_cast< ::cppu::OWeakObject* >(pDispatchHelper), css::uno::UNO_QUERY ); + + //------------------------------------------------------------------------------------------------------------- + DispatchInformationProvider* pInfoHelper = new DispatchInformationProvider(m_xFactory, this); + m_xDispatchInfoHelper = css::uno::Reference< css::frame::XDispatchInformationProvider >( static_cast< ::cppu::OWeakObject* >(pInfoHelper), css::uno::UNO_QUERY ); + + //------------------------------------------------------------------------------------------------------------- + // Initialize a new interception helper object to handle dispatches and implement an interceptor mechanism. + // Set created dispatch provider as slowest slave of it. + // Hold interception helper by reference only - not by pointer! + // So it's easiear to destroy it. + InterceptionHelper* pInterceptionHelper = new InterceptionHelper( this, xDispatchProvider ); + m_xDispatchHelper = css::uno::Reference< css::frame::XDispatchProvider >( static_cast< ::cppu::OWeakObject* >(pInterceptionHelper), css::uno::UNO_QUERY ); + + //------------------------------------------------------------------------------------------------------------- + // Initialize a new XFrames-helper-object to handle XIndexAccess and XElementAccess. + // We hold member as reference ... not as pointer too! + // Attention: We share our frame container with this helper. Container is threadsafe himself ... So I think we can do that. + // But look on dispose() for right order of deinitialization. + OFrames* pFramesHelper = new OFrames( m_xFactory, this, &m_aChildFrameContainer ); + m_xFramesHelper = css::uno::Reference< css::frame::XFrames >( static_cast< ::cppu::OWeakObject* >(pFramesHelper), css::uno::UNO_QUERY ); + + //------------------------------------------------------------------------------------------------------------- + // Initialize a the drop target listener. + // We hold member as reference ... not as pointer too! + DropTargetListener* pDropListener = new DropTargetListener( m_xFactory, this ); + m_xDropTargetListener = css::uno::Reference< css::datatransfer::dnd::XDropTargetListener >( static_cast< ::cppu::OWeakObject* >(pDropListener), css::uno::UNO_QUERY ); + + // Safe impossible cases + // We can't work without these helpers! + LOG_ASSERT2( xDispatchProvider.is ()==sal_False, "Frame::impl_initService()", "Slowest slave for dispatch- and interception helper isn't valid. XDispatchProvider, XDispatch, XDispatchProviderInterception are not full supported!" ) + LOG_ASSERT2( m_xDispatchHelper.is ()==sal_False, "Frame::impl_initService()", "Interception helper isn't valid. XDispatchProvider, XDispatch, XDispatchProviderInterception are not full supported!" ) + LOG_ASSERT2( m_xFramesHelper.is ()==sal_False, "Frame::impl_initService()", "Frames helper isn't valid. XFrames, XIndexAccess and XElementAcces are not supported!" ) + LOG_ASSERT2( m_xDropTargetListener.is()==sal_False, "Frame::impl_initService()", "DropTarget helper isn't valid. Drag and drop without functionality!" ) + + //------------------------------------------------------------------------------------------------------------- + // establish notifies for changing of "disabled commands" configuration during runtime + m_aCommandOptions.EstablisFrameCallback(this); + + //------------------------------------------------------------------------------------------------------------- + // Create an initial layout manager + // Create layout manager and connect it to the newly created frame + m_xLayoutManager = css::uno::Reference< css::frame::XLayoutManager >(m_xFactory->createInstance(SERVICENAME_LAYOUTMANAGER), css::uno::UNO_QUERY); + + //------------------------------------------------------------------------------------------------------------- + // set information about all supported properties at the base class helper PropertySetHelper + impl_initializePropInfo(); + } + ) + +/*-****************************************************************************************************//** + @short standard constructor to create instance by factory + @descr This constructor initialize a new instance of this class by valid factory, + and will be set valid values on his member and baseclasses. + + @attention a) Don't use your own reference during an UNO-Service-ctor! There is no guarantee, that you + will get over this. (e.g. using of your reference as parameter to initialize some member) + Do such things in DEFINE_INIT_SERVICE() method, which is called automaticly after your ctor!!! + b) Baseclass OBroadcastHelper is a typedef in namespace cppu! + The microsoft compiler has some problems to handle it right BY using namespace explicitly ::cppu::OBroadcastHelper. + If we write it without a namespace or expand the typedef to OBrodcastHelperVar<...> -> it will be OK!? + I don't know why! (other compiler not tested .. but it works!) + + @seealso method DEFINE_INIT_SERVICE() + + @param "xFactory" is the multi service manager, which create this instance. + The value must be different from NULL! + @return - + + @onerror ASSERT in debug version or nothing in relaese version. +*//*-*****************************************************************************************************/ +Frame::Frame( const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) + : ThreadHelpBase ( &Application::GetSolarMutex() ) + , TransactionBase ( ) + , PropertySetHelper ( xFactory, + &m_aLock, + &m_aTransactionManager, + sal_False) // FALSE => dont release shared mutex on calling us! + , ::cppu::OWeakObject ( ) + // init member + , m_xFactory ( xFactory ) + , m_aListenerContainer ( m_aLock.getShareableOslMutex() ) + , m_xParent ( ) + , m_xContainerWindow ( ) + , m_xComponentWindow ( ) + , m_xController ( ) + , m_eActiveState ( E_INACTIVE ) + , m_sName ( ) + , m_bIsFrameTop ( sal_True ) // I think we are top without a parent ... and there is no parent yet! + , m_bConnected ( sal_False ) // There exist no component inside of use => sal_False, we are not connected! + , m_nExternalLockCount ( 0 ) + , m_bSelfClose ( sal_False ) // Important! + , m_bIsHidden ( sal_True ) + , m_xTitleHelper ( ) + , m_aChildFrameContainer ( ) +{ + // Check incoming parameter to avoid against wrong initialization. + LOG_ASSERT2( implcp_ctor( xFactory ), "Frame::Frame()", "Invalid parameter detected!" ) + + /* Please have a look on "@attentions" of description before! */ +} + +/*-****************************************************************************************************//** + @short standard destructor + @descr This one do NOTHING! Use dispose() instaed of this. + + @seealso method dispose() + + @param - + @return - + + @onerror - +*//*-*****************************************************************************************************/ +Frame::~Frame() +{ + LOG_ASSERT2( m_aTransactionManager.getWorkingMode()!=E_CLOSE, "Frame::~Frame()", "Who forgot to dispose this service?" ) +} + +/*-************************************************************************************************************//** + @interface XComponentLoader + @short try to load given URL into a task + @descr You can give us some informations about the content, which you will load into a frame. + We search or create this target for you, make a type detection of given URL and try to load it. + As result of this operation we return the new created component or nothing, if loading failed. + + @seealso - + + @param "sURL" , URL, which represant the content + @param "sTargetFrameName" , name of target frame or special value like "_self", "_blank" ... + @param "nSearchFlags" , optional arguments for frame search, if target isn't a special one + @param "lArguments" , optional arguments for loading + @return A valid component reference, if loading was successfully. + A null reference otherwise. + + @onerror We return a null reference. + @threadsafe yes +*//*-*************************************************************************************************************/ +css::uno::Reference< css::lang::XComponent > SAL_CALL Frame::loadComponentFromURL( const ::rtl::OUString& sURL , + const ::rtl::OUString& sTargetFrameName, + sal_Int32 nSearchFlags , + const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::io::IOException , + css::lang::IllegalArgumentException , + css::uno::RuntimeException ) +{ + { + // If the frame is closed the call might lead to crash even with target "_blank", + // so the DisposedException should be thrown in this case + // It still looks to be too dangerous to set the transaction for the whole loading process + // so the guard is used in scopes to let the standard check be used + + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + } + + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::frame::XComponentLoader > xThis(static_cast< css::frame::XComponentLoader* >(this), css::uno::UNO_QUERY); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xFactory; + aReadLock.unlock(); + + return LoadEnv::loadComponentFromURL(xThis, xSMGR, sURL, sTargetFrameName, nSearchFlags, lArguments); +} + +/*-****************************************************************************************************//** + @short return access to append or remove childs on desktop + @descr We don't implement these interface directly. We use a helper class to do this. + If you wish to add or delete childs to/from the container, call these method to get + a reference to the helper. + + @seealso class OFrames + + @param - + @return A reference to the helper which answer your queries. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrames > SAL_CALL Frame::getFrames() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + +/*TODO + This is a temp. HACK! + Our parent (a Task!) stand in close/dispose and set working mode to E_BEFOERECLOSE + and call dispose on us! We tra to get this xFramesHelper and are reject by an "already closed" pranet instance .... + => We use SOFTEXCEPTIONS here ... but we should make it right in further times .... + */ + + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + // Return access to all child frames to caller. + // Ouer childframe container is implemented in helper class OFrames and used as a reference m_xFramesHelper! + return m_xFramesHelper; +} + +/*-****************************************************************************************************//** + @short get the current active child frame + @descr It must be a frameto. Direct childs of a frame are frames only! No task or desktop is accepted. + We don't save this information directly in this class. We use ouer container-helper + to do that. + + @seealso class OFrameContainer + @seealso method setActiveFrame() + + @param - + @return A reference to ouer current active childframe, if anyone exist. + @return A null reference, if nobody is active. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL Frame::getActiveFrame() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + // Return current active frame. + // This information is avaliable on the container. + return m_aChildFrameContainer.getActive(); +} + +/*-****************************************************************************************************//** + @short set the new active direct child frame + @descr It must be a frame to. Direct childs of frame are frames only! No task or desktop is accepted. + We don't save this information directly in this class. We use ouer container-helper + to do that. + + @seealso class OFrameContainer + @seealso method getActiveFrame() + + @param "xFrame", reference to new active child. It must be an already existing child! + @return - + + @onerror An assertion is thrown and element is ignored, if given frame is'nt already a child of us. +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::setActiveFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameters. + LOG_ASSERT2( implcp_setActiveFrame( xFrame ), "Frame::setActiveFrame()", "Invalid parameter detected!" ) + // Look for rejected calls! + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + + // Copy neccessary member for threadsafe access! + // m_aChildFrameContainer is threadsafe himself and he live if we live!!! + // ...and our transaction is non breakable too ... + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive(); + EActiveState eActiveState = m_eActiveState ; + + aWriteLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // Don't work, if "new" active frame is'nt different from current one! + // (xFrame==NULL is allowed to UNSET it!) + if( xActiveChild != xFrame ) + { + // ... otherwise set new and deactivate old one. + m_aChildFrameContainer.setActive( xFrame ); + if ( + ( eActiveState != E_INACTIVE ) && + ( xActiveChild.is() == sal_True ) + ) + { + xActiveChild->deactivate(); + } + } + + if( xFrame.is() == sal_True ) + { + // If last active frame had focus ... + // ... reset state to ACTIVE and send right FrameActionEvent for focus lost. + if( eActiveState == E_FOCUS ) + { + aWriteLock.lock(); + eActiveState = E_ACTIVE ; + m_eActiveState = eActiveState; + aWriteLock.unlock(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_DEACTIVATING ); + } + + // If last active frame was active ... + // but new one isn't it ... + // ... set it as active one. + if ( + ( eActiveState == E_ACTIVE ) && + ( xFrame->isActive() == sal_False ) + ) + { + xFrame->activate(); + } + } + else + // If this frame is active and has no active subframe anymore it is UI active too + if( eActiveState == E_ACTIVE ) + { + aWriteLock.lock(); + eActiveState = E_FOCUS ; + m_eActiveState = eActiveState; + aWriteLock.unlock(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_ACTIVATED ); + } +} + +/*-****************************************************************************************************//** + initialize new created layout manager +**/ +void lcl_enableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager >& xLayoutManager, + const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + // Provide container window to our layout manager implementation + xLayoutManager->attachFrame(xFrame); + + css::uno::Reference< css::frame::XFrameActionListener > xListen(xLayoutManager, css::uno::UNO_QUERY_THROW); + xFrame->addFrameActionListener(xListen); + + DockingAreaDefaultAcceptor* pAcceptor = new DockingAreaDefaultAcceptor(xFrame); + css::uno::Reference< css::ui::XDockingAreaAcceptor > xDockingAreaAcceptor( static_cast< ::cppu::OWeakObject* >(pAcceptor), css::uno::UNO_QUERY_THROW); + xLayoutManager->setDockingAreaAcceptor(xDockingAreaAcceptor); +} + +/*-****************************************************************************************************//** + deinitialize layout manager +**/ +void lcl_disableLayoutManager(const css::uno::Reference< css::frame::XLayoutManager >& xLayoutManager, + const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + css::uno::Reference< css::frame::XFrameActionListener > xListen(xLayoutManager, css::uno::UNO_QUERY_THROW); + xFrame->removeFrameActionListener(xListen); + xLayoutManager->setDockingAreaAcceptor(css::uno::Reference< css::ui::XDockingAreaAcceptor >()); + xLayoutManager->attachFrame(css::uno::Reference< css::frame::XFrame >()); +} + +/*-****************************************************************************************************//** + @short initialize frame instance + @descr A frame needs a window. This method set a new one ... but should called one times only! + We use this window to listen for window events and forward it to our set component. + Its used as parent of component window too. + + @seealso method getContainerWindow() + @seealso method setComponent() + @seealso member m_xContainerWindow + + @param "xWindow", reference to new container window - must be valid! + @return - + + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::initialize( const css::uno::Reference< css::awt::XWindow >& xWindow ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + if (!xWindow.is()) + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("Frame::initialize() called without a valid container window reference."), + static_cast< css::frame::XFrame* >(this)); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + + if ( m_xContainerWindow.is() ) + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("Frame::initialized() is called more then once, which isnt usefull nor allowed."), + static_cast< css::frame::XFrame* >(this)); + + // Look for rejected calls first! + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + // Enable object for real working ... so follow impl methods don't must handle it special! (e.g. E_SOFTEXCEPTIONS for rejected calls) + m_aTransactionManager.setWorkingMode( E_WORK ); + + // This must be the first call of this method! + // We should initialize our object and open it for working. + // Set the new window. + LOG_ASSERT2( m_xContainerWindow.is()==sal_True, "Frame::initialize()", "Leak detected! This state should never occure ..." ) + m_xContainerWindow = xWindow; + + // if window is initially visible, we will never get a windowShowing event + Window* pWindow = VCLUnoHelper::GetWindow(xWindow); + if (pWindow && pWindow->IsVisible()) + m_bIsHidden = sal_False; + + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xFactory; + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager = m_xLayoutManager; + + // Release lock ... because we call some impl methods, which are threadsafe by himself. + // If we hold this lock - we will produce our own deadlock! + aWriteLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if (xLayoutManager.is()) + lcl_enableLayoutManager(xLayoutManager, this); + + // create progress helper + css::uno::Reference< css::frame::XFrame > xThis (static_cast< css::frame::XFrame* >(this) , css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::task::XStatusIndicatorFactory > xIndicatorFactory(xSMGR->createInstance(IMPLEMENTATIONNAME_STATUSINDICATORFACTORY), css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::lang::XInitialization > xIndicatorInit (xIndicatorFactory , css::uno::UNO_QUERY_THROW); + css::uno::Sequence< css::uno::Any > lArgs(2); + css::beans::NamedValue aArg; + aArg.Name = STATUSINDICATORFACTORY_PROPNAME_FRAME; + aArg.Value <<= xThis; + lArgs[0] <<= aArg; + aArg.Name = STATUSINDICATORFACTORY_PROPNAME_ALLOWPARENTSHOW; + aArg.Value <<= sal_True; + lArgs[1] <<= aArg; + xIndicatorInit->initialize(lArgs); + + // SAFE -> ---------------------------------- + aWriteLock.lock(); + m_xIndicatorFactoryHelper = xIndicatorFactory; + aWriteLock.unlock(); + // <- SAFE ---------------------------------- + + // Start listening for events after setting it on helper class ... + // So superflous messages are filtered to NULL :-) + implts_startWindowListening(); + + impl_enablePropertySet(); + + // create WindowCommandDispatch; it is supposed to release itself at frame destruction + (void)new WindowCommandDispatch(xSMGR, this); + + // Initialize title functionality + TitleHelper* pTitleHelper = new TitleHelper(xSMGR); + m_xTitleHelper = css::uno::Reference< css::frame::XTitle >(static_cast< ::cppu::OWeakObject* >(pTitleHelper), css::uno::UNO_QUERY_THROW); + pTitleHelper->setOwner(xThis); +} + +/*-****************************************************************************************************//** + @short returns current set container window + @descr The ContainerWindow property is used as a container for the component + in this frame. So this object implements a container interface too. + The instantiation of the container window is done by the user of this class. + The frame is the owner of its container window. + + @seealso method initialize() + + @param - + @return A reference to current set containerwindow. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::awt::XWindow > SAL_CALL Frame::getContainerWindow() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + return m_xContainerWindow; +} + +/*-****************************************************************************************************//** + @short set parent frame + @descr We need a parent to support some functionality! e.g. findFrame() + By the way we use the chance to set an internal information about our top state. + So we must not check this information during every isTop() call. + We are top, if our parent is the desktop instance or we havent any parent. + + @seealso getCreator() + @seealso findFrame() + @seealso isTop() + @seealos m_bIsFrameTop + + @param xCreator + valid reference to our new owner frame, which should implement a supplier interface + + @threadsafe yes + @modified 08.05.2002 09:35, as96863 +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::setCreator( const css::uno::Reference< css::frame::XFramesSupplier >& xCreator ) throw( css::uno::RuntimeException ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE { */ + WriteGuard aWriteLock( m_aLock ); + m_xParent = xCreator; + aWriteLock.unlock(); + /* } SAFE */ + + css::uno::Reference< css::frame::XDesktop > xIsDesktop( xCreator, css::uno::UNO_QUERY ); + m_bIsFrameTop = ( xIsDesktop.is() || ! xCreator.is() ); +} + +/*-****************************************************************************************************//** + @short returns current parent frame + @descr The Creator is the parent frame container. If it is NULL, the frame is the uppermost one. + + @seealso method setCreator() + + @param - + @return A reference to current set parent frame container. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFramesSupplier > SAL_CALL Frame::getCreator() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + return m_xParent; +} + +/*-****************************************************************************************************//** + @short returns current set name of frame + @descr This name is used to find target of findFrame() or queryDispatch() calls. + + @seealso method setName() + + @param - + @return Current set name of frame. + + @onerror An empty string is returned. +*//*-*****************************************************************************************************/ +::rtl::OUString SAL_CALL Frame::getName() throw( css::uno::RuntimeException ) +{ + /* SAFE { */ + ReadGuard aReadLock( m_aLock ); + return m_sName; + /* } SAFE */ +} + +/*-****************************************************************************************************//** + @short set new name for frame + @descr This name is used to find target of findFrame() or queryDispatch() calls. + + @attention Special names like "_blank", "_self" aren't allowed ... + "_beamer" or "_menubar" excepts this rule! + + @seealso method getName() + + @param "sName", new frame name. + @return - + + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::setName( const ::rtl::OUString& sName ) throw( css::uno::RuntimeException ) +{ + /* SAFE { */ + WriteGuard aWriteLock( m_aLock ); + // Set new name ... but look for invalid special target names! + // They are not allowed to set. + if (TargetHelper::isValidNameForFrame(sName)) + m_sName = sName; + aWriteLock.unlock(); + /* } SAFE */ +} + +/*-****************************************************************************************************//** + @short search for frames + @descr This method searches for a frame with the specified name. + Frames may contain other frames (e.g. a frameset) and may + be contained in other frames. This hierarchie ist searched by + this method. + First some special names are taken into account, i.e. "", + "_self", "_top", "_blank" etc. The nSearchFlags are ignored + when comparing these names with sTargetFrameName, further steps are + controlled by the search flags. If allowed, the name of the frame + itself is compared with the desired one, then ( again if allowed ) + the method findFrame() is called for all children, for siblings + and as last for the parent frame. + If no frame with the given name is found until the top frames container, + a new top one is created, if this is allowed by a special + flag. The new frame also gets the desired name. + + @param sTargetFrameName + special names (_blank, _self) or real name of target frame + @param nSearchFlags + optional flags which regulate search for non special target frames + + @return A reference to found or may be new created frame. + @threadsafe yes + @modified 16.05.2002 11:08, as96863 +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XFrame > SAL_CALL Frame::findFrame( const ::rtl::OUString& sTargetFrameName, + sal_Int32 nSearchFlags ) throw( css::uno::RuntimeException ) +{ + css::uno::Reference< css::frame::XFrame > xTarget; + + //----------------------------------------------------------------------------------------------------- + // 0) Ignore wrong parameter! + // We doesn't support search for following special targets. + // If we reject this requests - we mustnt check for such names + // in following code again and again. If we do not so -wrong + // search results can occure! + //----------------------------------------------------------------------------------------------------- + if ( + (sTargetFrameName==SPECIALTARGET_DEFAULT ) || // valid for dispatches - not for findFrame()! + (sTargetFrameName==SPECIALTARGET_MENUBAR ) || // valid for dispatches - not for findFrame()! + (sTargetFrameName==SPECIALTARGET_HELPAGENT) // valid for dispatches - not for findFrame()! + ) + { + return NULL; + } + + //----------------------------------------------------------------------------------------------------- + // I) check for special defined targets first which must be handled exclusive. + // force using of "if() else if() ..." + //----------------------------------------------------------------------------------------------------- + + // get threadsafe some neccessary member which are neccessary for following functionality + /* SAFE { */ + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::frame::XFrame > xParent ( m_xParent, css::uno::UNO_QUERY ); + css::uno::Reference< css::lang::XMultiServiceFactory > xFactory = m_xFactory; + sal_Bool bIsTopFrame = m_bIsFrameTop; + sal_Bool bIsTopWindow = WindowHelper::isTopWindow(m_xContainerWindow); + aReadLock.unlock(); + /* } SAFE */ + + //----------------------------------------------------------------------------------------------------- + // I.I) "_blank" + // Not allowed for a normal frame - but for the desktop. + // Use helper class to do so. It use the desktop automaticly. + //----------------------------------------------------------------------------------------------------- + if ( sTargetFrameName==SPECIALTARGET_BLANK ) + { + TaskCreator aCreator(xFactory); + xTarget = aCreator.createTask(sTargetFrameName,sal_False); + } + + //----------------------------------------------------------------------------------------------------- + // I.II) "_parent" + // It doesn't matter if we have a valid parent or not. User ask for him and get it. + // An empty result is a valid result too. + //----------------------------------------------------------------------------------------------------- + else + if ( sTargetFrameName==SPECIALTARGET_PARENT ) + { + xTarget = xParent; + } + + //----------------------------------------------------------------------------------------------------- + // I.III) "_top" + // If we are not the top frame in this hierarchy, we must forward request to our parent. + // Otherwhise we must return ourself. + //----------------------------------------------------------------------------------------------------- + else + if ( sTargetFrameName==SPECIALTARGET_TOP ) + { + if (bIsTopFrame) + xTarget = this; + else + if (xParent.is()) // If we are not top - the parent MUST exist. But may it's better to check it again .-) + xTarget = xParent->findFrame(SPECIALTARGET_TOP,0); + } + + //----------------------------------------------------------------------------------------------------- + // I.IV) "_self", "" + // This mean this frame in every case. + //----------------------------------------------------------------------------------------------------- + else + if ( + ( sTargetFrameName==SPECIALTARGET_SELF ) || + ( sTargetFrameName.getLength()<1 ) + ) + { + xTarget = this; + } + + //----------------------------------------------------------------------------------------------------- + // I.V) "_beamer" + // This is a special sub frame of any task. We must return it if we found it on our direct childrens + // or create it there if it not already exists. + // Note: Such beamer exists for task(top) frames only! + //----------------------------------------------------------------------------------------------------- + else + if ( sTargetFrameName==SPECIALTARGET_BEAMER ) + { + // We are a task => search or create the beamer + if (bIsTopWindow) + { + xTarget = m_aChildFrameContainer.searchOnDirectChildrens(SPECIALTARGET_BEAMER); + if ( ! xTarget.is() ) + { + /* TODO + Creation not supported yet! + Wait for new layout manager service because we can't plug it + inside already opened document of this frame ... + */ + } + } + // We arent a task => forward request to our parent or ignore it. + else + if (xParent.is()) + xTarget = xParent->findFrame(SPECIALTARGET_BEAMER,0); + } + + else + { + //------------------------------------------------------------------------------------------------- + // II) otherwhise use optional given search flags + // force using of combinations of such flags. means no "else" part of use if() statements. + // But we ust break further searches if target was already found. + // Order of using flags is fix: SELF - CHILDREN - SIBLINGS - PARENT + // TASK and CREATE are handled special. + //------------------------------------------------------------------------------------------------- + + // get threadsafe some neccessary member which are neccessary for following functionality + /* SAFE { */ + aReadLock.lock(); + ::rtl::OUString sOwnName = m_sName; + aReadLock.unlock(); + /* } SAFE */ + + //------------------------------------------------------------------------------------------------- + // II.I) SELF + // Check for right name. If it's the searched one return ourself - otherwhise + // ignore this flag. + //------------------------------------------------------------------------------------------------- + if ( + (nSearchFlags & css::frame::FrameSearchFlag::SELF) && + (sOwnName == sTargetFrameName ) + ) + { + xTarget = this; + } + + //------------------------------------------------------------------------------------------------- + // II.II) CHILDREN + // Search on all children for the given target name. + // An empty name value can't occure here - because it must be already handled as "_self" + // before. Used helper function of container doesn't create any frame. + // It makes a deep search only. + //------------------------------------------------------------------------------------------------- + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN) + ) + { + xTarget = m_aChildFrameContainer.searchOnAllChildrens(sTargetFrameName); + } + + //------------------------------------------------------------------------------------------------- + // II.III) TASKS + // This is a special flag. It regulate search on this task tree only or allow search on + // all other ones (which are sibling trees of us) too. + // Upper search must stop at this frame if we are the topest one and the TASK flag isn't set + // or we can ignore it if we have no valid parent. + //------------------------------------------------------------------------------------------------- + if ( + ( bIsTopFrame && (nSearchFlags & css::frame::FrameSearchFlag::TASKS) ) || + ( ! bIsTopFrame ) + ) + { + //------------------------------------------------------------------------------------------------- + // II.III.I) SIBLINGS + // Search on all our direct siblings - means all childrens of our parent. + // Use this flag in combination with TASK. We must supress such upper search if + // user has not set it and if we are a top frame. + // + // Attention: Don't forward this request to our parent as a findFrame() call. + // In such case we must protect us against recursive calls. + // Use snapshot of our parent. But don't use queryFrames() of XFrames interface. + // Because it's return all siblings and all her childrens including our children too + // if we call it with the CHILDREN flag. We doesn't need that - we need the direct container + // items of our parent only to start searches there. So we must use the container interface + // XIndexAccess instead of XFrames. + //------------------------------------------------------------------------------------------------- + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::SIBLINGS) && + ( xParent.is() ) // search on siblings is impossible without a parent + ) + { + css::uno::Reference< css::frame::XFramesSupplier > xSupplier( xParent, css::uno::UNO_QUERY ); + if (xSupplier.is()) + { + css::uno::Reference< css::container::XIndexAccess > xContainer( xSupplier->getFrames(), css::uno::UNO_QUERY ); + if (xContainer.is()) + { + sal_Int32 nCount = xContainer->getCount(); + for( sal_Int32 i=0; i<nCount; ++i ) + { + css::uno::Reference< css::frame::XFrame > xSibling; + if ( + ( !(xContainer->getByIndex(i)>>=xSibling) ) || // control unpacking + ( ! xSibling.is() ) || // check for valid items + ( xSibling==static_cast< ::cppu::OWeakObject* >(this) ) // ignore ourself! (We are a part of this container too - but search on our children was already done.) + ) + { + continue; + } + + // Don't allow upper search here! Use rigth flags to regulate it. + // And allow deep search on children only - if it was allowed for us too. + sal_Int32 nRightFlags = css::frame::FrameSearchFlag::SELF; + if (nSearchFlags & css::frame::FrameSearchFlag::CHILDREN) + nRightFlags |= css::frame::FrameSearchFlag::CHILDREN; + xTarget = xSibling->findFrame(sTargetFrameName, nRightFlags ); + // perform search be breaking further search if a result exist. + if (xTarget.is()) + break; + } + } + } + } + + //------------------------------------------------------------------------------------------------- + // II.III.II) PARENT + // Forward search to our parent (if he exists.) + // To prevent us against recursive and superflous calls (which can occure if we allow him + // to search on his childrens too) we must change used search flags. + //------------------------------------------------------------------------------------------------- + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::PARENT) && + ( xParent.is() ) + ) + { + if (xParent->getName() == sTargetFrameName) + xTarget = xParent; + else + { + sal_Int32 nRightFlags = nSearchFlags; + nRightFlags &= ~css::frame::FrameSearchFlag::CHILDREN; + xTarget = xParent->findFrame(sTargetFrameName, nRightFlags); + } + } + } + + //------------------------------------------------------------------------------------------------- + // II.IV) CREATE + // If we haven't found any valid target frame by using normal flags - but user allowed us to create + // a new one ... we should do that. Used TaskCreator use Desktop instance automaticly as parent! + //------------------------------------------------------------------------------------------------- + if ( + ( ! xTarget.is() ) && + (nSearchFlags & css::frame::FrameSearchFlag::CREATE) + ) + { + TaskCreator aCreator(xFactory); + xTarget = aCreator.createTask(sTargetFrameName,sal_False); + } + } + + return xTarget; +} + +/*-****************************************************************************************************//** + @short - + @descr Returns sal_True, if this frame is a "top frame", otherwise sal_False. + The "m_bIsFrameTop" member must be set in the ctor or setCreator() method. + A top frame is a member of the top frame container or a member of the + task frame container. Both containers can create new frames if the findFrame() + method of their css::frame::XFrame interface is called with a frame name not yet known. + + @seealso ctor + @seealso method setCreator() + @seealso method findFrame() + + @param - + @return true, if is it a top frame ... false otherwise. + + @onerror No error should occure! +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL Frame::isTop() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + // This information is set in setCreator(). + // We are top, if ouer parent is a task or the desktop or if no parent exist! + return m_bIsFrameTop; +} + +/*-****************************************************************************************************//** + @short activate frame in hierarchy + @descr This feature is used to mark active pathes in our frame hierarchy. + You can be a listener for this event to react for it ... change some internal states or something else. + + @seealso method deactivate() + @seealso method isActivate() + @seealso enum EActiveState + @seealso listener mechanism + + @param - + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::activate() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + + // Copy neccessary member and free the lock. + // It's not neccessary for m_aChildFrameContainer ... because + // he is threadsafe himself and live if we live. + // We use a registered transaction to prevent us against + // breaks during this operation! + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive() ; + css::uno::Reference< css::frame::XFramesSupplier > xParent ( m_xParent, css::uno::UNO_QUERY ) ; + css::uno::Reference< css::frame::XFrame > xThis ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XWindow > xComponentWindow( m_xComponentWindow, css::uno::UNO_QUERY ) ; + EActiveState eState = m_eActiveState ; + + aWriteLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + //_________________________________________________________________________________________________________ + // 1) If I'am not active before ... + if( eState == E_INACTIVE ) + { + // ... do it then. + aWriteLock.lock(); + eState = E_ACTIVE; + m_eActiveState = eState; + aWriteLock.unlock(); + // Deactivate sibling path and forward activation to parent ... if any parent exist! + if( xParent.is() == sal_True ) + { + // Everytime set THIS frame as active child of parent and activate it. + // We MUST have a valid path from bottom to top as active path! + // But we must deactivate the old active sibling path first. + + // Attention: Deactivation of an active path, deactivate the whole path ... from bottom to top! + // But we wish to deactivate founded sibling-tree only. + // [ see deactivate() / step 4) for further informations! ] + + xParent->setActiveFrame( xThis ); + + // Then we can activate from here to top. + // Attention: We are ACTIVE now. And the parent will call activate() at us! + // But we do nothing then! We are already activated. + xParent->activate(); + } + // Its neccessary to send event NOW - not before. + // Activation goes from bottom to top! + // Thats the reason to activate parent first and send event now. + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_ACTIVATED ); + } + + //_________________________________________________________________________________________________________ + // 2) I was active before or current activated and there is a path from here to bottom, who CAN be active. + // But ouer direct child of path is not active yet. + // (It can be, if activation occur in the middle of a current path!) + // In these case we activate path to bottom to set focus on right frame! + if ( + ( eState == E_ACTIVE ) && + ( xActiveChild.is() == sal_True ) && + ( xActiveChild->isActive() == sal_False ) + ) + { + xActiveChild->activate(); + } + + //_________________________________________________________________________________________________________ + // 3) I was active before or current activated. But if I have no active child => I will get the focus! + if ( + ( eState == E_ACTIVE ) && + ( xActiveChild.is() == sal_False ) + ) + { + aWriteLock.lock(); + eState = E_FOCUS; + m_eActiveState = eState; + aWriteLock.unlock(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_ACTIVATED ); + } +} + +/*-****************************************************************************************************//** + @short deactivate frame in hierarchy + @descr This feature is used to deactive pathes in our frame hierarchy. + You can be a listener for this event to react for it ... change some internal states or something else. + + @seealso method activate() + @seealso method isActivate() + @seealso enum EActiveState + @seealso listener mechanism + + @param - + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::deactivate() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + + // Copy neccessary member and free the lock. + css::uno::Reference< css::frame::XFrame > xActiveChild = m_aChildFrameContainer.getActive() ; + css::uno::Reference< css::frame::XFramesSupplier > xParent ( m_xParent, css::uno::UNO_QUERY ) ; + css::uno::Reference< css::frame::XFrame > xThis ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + EActiveState eState = m_eActiveState ; + + aWriteLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + // Work only, if there something to do! + if( eState != E_INACTIVE ) + { + //_____________________________________________________________________________________________________ + // 1) Deactivate all active childs. + if ( + ( xActiveChild.is() == sal_True ) && + ( xActiveChild->isActive() == sal_True ) + ) + { + xActiveChild->deactivate(); + } + + //_____________________________________________________________________________________________________ + // 2) If I have the focus - I will lost it now. + if( eState == E_FOCUS ) + { + // Set new state INACTIVE(!) and send message to all listener. + // Don't set ACTIVE as new state. This frame is deactivated for next time - due to activate(). + aWriteLock.lock(); + eState = E_ACTIVE; + m_eActiveState = eState ; + aWriteLock.unlock(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_UI_DEACTIVATING ); + } + + //_____________________________________________________________________________________________________ + // 3) If I'am active - I will be deactivated now. + if( eState == E_ACTIVE ) + { + // Set new state and send message to all listener. + aWriteLock.lock(); + eState = E_INACTIVE; + m_eActiveState = eState ; + aWriteLock.unlock(); + implts_sendFrameActionEvent( css::frame::FrameAction_FRAME_DEACTIVATING ); + } + + //_____________________________________________________________________________________________________ + // 4) If there is a path from here to my parent ... + // ... I'am on the top or in the middle of deactivated subtree and action was started here. + // I must deactivate all frames from here to top, which are members of current path. + // Stop, if THESE frame not the active frame of ouer parent! + if ( + ( xParent.is() == sal_True ) && + ( xParent->getActiveFrame() == xThis ) + ) + { + // We MUST break the path - otherwise we will get the focus - not ouer parent! ... + // Attention: Ouer parent don't call us again - WE ARE NOT ACTIVE YET! + // [ see step 3 and condition "if ( m_eActiveState!=INACTIVE ) ..." in this method! ] + xParent->deactivate(); + } + } +} + +/*-****************************************************************************************************//** + @short returns active state + @descr Call it to get informations about current active state of this frame. + + @seealso method activate() + @seealso method deactivate() + @seealso enum EActiveState + + @param - + @return true if active, false otherwise. + + @onerror No error should occure. +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL Frame::isActive() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + return ( + ( m_eActiveState == E_ACTIVE ) || + ( m_eActiveState == E_FOCUS ) + ); +} + +/*-****************************************************************************************************//** + @short ??? + @descr - + + @seealso - + + @param - + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::contextChanged() throw( css::uno::RuntimeException ) +{ + // Look for rejected calls! + // Sometimes called during closing object... => soft exceptions + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + // Impl-method is threadsafe himself! + // Send event to all listener for frame actions. + implts_sendFrameActionEvent( css::frame::FrameAction_CONTEXT_CHANGED ); +} + +/*-****************************************************************************************************//** + @short set new component inside the frame + @descr A frame is a container for a component. Use this method to set, change or realease it! + We accept null references! The xComponentWindow will be a child of our container window + and get all window events from us. + + @attention (a) A current set component can disagree with the suspend() request! + We don't set the new one and return with false then. + (b) It's possible to set: + (b1) a simple component here which supports the window only - no controller; + (b2) a full featured component which supports window and controller; + (b3) or both to NULL if outside code which to forget this component. + + @seealso method getComponentWindow() + @seealso method getController() + + @param xComponentWindow + valid reference to new component window which will be a child of internal container window + May <NULL/> for releasing. + @param xController + reference to new component controller + (may <NULL/> for relasing or setting of a simple component) + + @return <TRUE/> if operation was successful, <FALSE/> otherwise. + + @onerror We return <FALSE/>. + @threadsafe yes + @modified 06.05.2002 11:39, as96863 +*//*-*****************************************************************************************************/ +sal_Bool SAL_CALL Frame::setComponent( const css::uno::Reference< css::awt::XWindow >& xComponentWindow , + const css::uno::Reference< css::frame::XController >& xController ) throw( css::uno::RuntimeException ) +{ + //_____________________________________________________________________________________________________ + // Ignore this HACK of sfx2! + // He call us with an valid controller without a valid window ... Thats not allowed! + if ( xController.is() && ! xComponentWindow.is() ) + return sal_True; + + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + //_____________________________________________________________________________________________________ + // Get threadsafe some copies of used members. + /* SAFE { */ + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + css::uno::Reference< css::awt::XWindow > xOldComponentWindow = m_xComponentWindow; + css::uno::Reference< css::frame::XController > xOldController = m_xController; + Window* pOwnWindow = VCLUnoHelper::GetWindow( xContainerWindow ); + sal_Bool bHadFocus = pOwnWindow->HasChildPathFocus(); + sal_Bool bWasConnected = m_bConnected; + aReadLock.unlock(); + /* } SAFE */ + + //_____________________________________________________________________________________________________ + // stop listening on old window + // May it produce some trouble. + // But don't forget to listen on new window again ... or reactivate listening + // if we reject this setComponent() request and leave this method without changing the old window. + implts_stopWindowListening(); + + // Notify all listener, that this component (if current one exist) will be unloaded. + if (bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_DETACHING ); + + //_____________________________________________________________________________________________________ + // otherwhise release old component first + // Always release controller before releasing window, + // because controller may want to access its window! + // But check for real changes - may the new controller is the old one. + if ( + (xOldController.is() ) && + (xOldController != xController) + ) + { + /* ATTENTION + Don't suspend the old controller here. Because the outside caller must do that + by definition. We have to dispose it here only. + */ + + // Before we dispose this controller we should hide it inside this frame instance. + // We hold it alive for next calls by using xOldController! + /* SAFE {*/ + WriteGuard aWriteLock( m_aLock ); + m_xController = NULL; + aWriteLock.unlock(); + /* } SAFE */ + + css::uno::Reference< css::lang::XComponent > xDisposable( xOldController, css::uno::UNO_QUERY ); + if (xDisposable.is()) + { + try + { + xDisposable->dispose(); + } + catch(const css::lang::DisposedException&) + {} + } + xOldController = NULL; + } + + //_____________________________________________________________________________________________________ + // Now it's time to release the component window. + // If controller wasn't released successfully - this code line shouldn't be reached. + // Because in case of "suspend()==false" we return immediately with false ... + // see before + // Check for real changes too. + if ( + (xOldComponentWindow.is() ) && + (xOldComponentWindow != xComponentWindow) + ) + { + /* SAFE { */ + WriteGuard aWriteLock( m_aLock ); + m_xComponentWindow = NULL; + aWriteLock.unlock(); + /* } SAFE */ + + css::uno::Reference< css::lang::XComponent > xDisposable( xOldComponentWindow, css::uno::UNO_QUERY ); + if (xDisposable.is()) + { + try + { + xDisposable->dispose(); + } + catch(const css::lang::DisposedException&) + {} + } + xOldComponentWindow = NULL; + } + + //_____________________________________________________________________________________________________ + // Now it's time to set the new component ... + // By the way - find out our new "load state" - means if we have a valid component inside. + /* SAFE { */ + WriteGuard aWriteLock( m_aLock ); + m_xComponentWindow = xComponentWindow; + m_xController = xController ; + m_bConnected = (m_xComponentWindow.is() || m_xController.is()); + sal_Bool bIsConnected = m_bConnected; + aWriteLock.unlock(); + /* } SAFE */ + + //_____________________________________________________________________________________________________ + // notifies all interest listener, that current component was changed or a new one was loaded + if (bIsConnected && bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_REATTACHED ); + else + if (bIsConnected && !bWasConnected) + implts_sendFrameActionEvent( css::frame::FrameAction_COMPONENT_ATTACHED ); + + //_____________________________________________________________________________________________________ + // A new component window doesn't know anything about current active/focus states. + // Set this information on it! + if ( + (bHadFocus ) && + (xComponentWindow.is()) + ) + { + xComponentWindow->setFocus(); + } + + // If it was a new component window - we must resize it to fill out + // our container window. + implts_resizeComponentWindow(); + // New component should change our current icon ... + implts_setIconOnWindow(); + // OK - start listening on new window again - or do nothing if it is an empty one. + implts_startWindowListening(); + + /* SAFE { */ + aWriteLock.lock(); + impl_checkMenuCloser(); + aWriteLock.unlock(); + /* } SAFE */ + + return sal_True; +} + +/*-****************************************************************************************************//** + @short returns current set component window + @descr Frames are used to display components. The actual displayed component is + held by the m_xComponentWindow property. If the component implements only a + XComponent interface, the communication between the frame and the + component is very restricted. Better integration is achievable through a + XController interface. + If the component wants other objects to be able to get information about its + ResourceDescriptor it has to implement a XModel interface. + This frame is the owner of the component window. + + @seealso method setComponent() + + @param - + @return css::uno::Reference to current set component window. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::awt::XWindow > SAL_CALL Frame::getComponentWindow() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Register transaction and reject wrong calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + return m_xComponentWindow; +} + +/*-****************************************************************************************************//** + @short returns current set controller + @descr Frames are used to display components. The actual displayed component is + held by the m_xComponentWindow property. If the component implements only a + XComponent interface, the communication between the frame and the + component is very restricted. Better integration is achievable through a + XController interface. + If the component wants other objects to be able to get information about its + ResourceDescriptor it has to implement a XModel interface. + This frame is the owner of the component window. + + @seealso method setComponent() + + @param - + @return css::uno::Reference to current set controller. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XController > SAL_CALL Frame::getController() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // It seems to be unavoidable that disposed frames allow to ask for a Controller (#111452) + // Register transaction and reject wrong calls. + // TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + return m_xController; +} + +/*-****************************************************************************************************//** + @short add/remove listener for activate/deactivate/contextChanged events + @descr - + + @seealso method activate() + @seealso method deactivate() + @seealso method contextChanged() + + @param "xListener" reference to your listener object + @return - + + @onerror Listener is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::addFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_addFrameActionListener( xListener ), "Frame::addFrameActionListener()", "Invalid parameter detected." ) + // Listener container is threadsafe by himself ... but we must look for rejected calls! + // Our OMenuDispatch-helper (is a member of ODispatchProvider!) is create at startup of this frame BEFORE initialize! + // => soft exceptions! + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.addInterface( ::getCppuType( (const css::uno::Reference< css::frame::XFrameActionListener >*)NULL ), xListener ); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::removeFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_removeFrameActionListener( xListener ), "Frame::removeFrameActionListener()", "Invalid parameter detected." ) + // Listener container is threadsafe by himself ... but we must look for rejected calls after disposing! + // But we must work with E_SOFTEXCEPTIONS ... because sometimes we are called from our listeners + // during dispose! Our work mode is E_BEFORECLOSE then ... and E_HARDEXCEPTIONS whould throw a DisposedException. + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.removeInterface( ::getCppuType( (const css::uno::Reference< css::frame::XFrameActionListener >*)NULL ), xListener ); +} + +/*-****************************************************************************************************//** + @short support two way mechanism to release a frame + @descr This method ask internal component (controller) if he accept this close request. + In case of <TRUE/> nothing will be happen (from point of caller of this close method). + In case of <FALSE/> a CloseVetoException is thrown. After such exception given parameter + <var>bDeliverOwnerShip</var> regulate which will be the new owner of this instance. + + @attention It's the replacement for XTask::close() which is marked as obsolete method. + + @param bDeliverOwnerShip + If parameter is set to <FALSE/> the original caller will be the owner after thrown + veto exception and must try to close this frame at later time again. Otherwhise the + source of throwed exception is the right one. May it will be the frame himself. + + @thrown CloseVetoException + if any internal things willn't be closed + + @threadsafe yes + @modified 06.05.2002 08:33, as96863 +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::close( sal_Bool bDeliverOwnerShip ) throw( css::util::CloseVetoException, + css::uno::RuntimeException ) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // At the end of this method may we must dispose ourself ... + // and may nobody from outside hold a reference to us ... + // then it's a good idea to do that by ourself. + css::uno::Reference< css::uno::XInterface > xSelfHold( static_cast< ::cppu::OWeakObject* >(this) ); + + // Check any close listener before we look for currently running internal processes. + // Because if a listener disagree with this close() request - we hace time to finish this + // internal operations too ... + // Note: container is threadsafe himself. + css::lang::EventObject aSource (static_cast< ::cppu::OWeakObject*>(this)); + ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer( ::getCppuType( ( const css::uno::Reference< css::util::XCloseListener >*) NULL ) ); + if (pContainer!=NULL) + { + ::cppu::OInterfaceIteratorHelper pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + ((css::util::XCloseListener*)pIterator.next())->queryClosing( aSource, bDeliverOwnerShip ); + } + catch( css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + // Ok - no listener disagreed with this close() request + // check if this frame is used for any load process currently + if (isActionLocked()) + { + if (bDeliverOwnerShip) + { + /* SAFE */ + WriteGuard aWriteLock( m_aLock ); + m_bSelfClose = sal_True; + aWriteLock.unlock(); + /* SAFE */ + } + + throw css::util::CloseVetoException(DECLARE_ASCII("Frame in use for loading document ..."),static_cast< ::cppu::OWeakObject*>(this)); + } + + if ( ! setComponent(NULL,NULL) ) + throw css::util::CloseVetoException(DECLARE_ASCII("Component couldn't be deattached ..."),static_cast< ::cppu::OWeakObject*>(this)); + + // If closing is allowed ... inform all istener and dispose this frame! + pContainer = m_aListenerContainer.getContainer( ::getCppuType( ( const css::uno::Reference< css::util::XCloseListener >*) NULL ) ); + if (pContainer!=NULL) + { + ::cppu::OInterfaceIteratorHelper pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + ((css::util::XCloseListener*)pIterator.next())->notifyClosing( aSource ); + } + catch( css::uno::RuntimeException& ) + { + pIterator.remove(); + } + } + } + + /* SAFE { */ + WriteGuard aWriteLock( m_aLock ); + m_bIsHidden = sal_True; + aWriteLock.unlock(); + /* } SAFE */ + impl_checkMenuCloser(); + + // Attention: We must release our own registered transaction here. Otherwhise following dispose() call + // wait for us too .... + aTransaction.stop(); + dispose(); +} + +/*-****************************************************************************************************//** + @short be a listener for close events! + @descr Adds/remove a CloseListener at this frame instance. If the close() method is called on + this object, the such listener are informed and can disagree with that by throwing + a CloseVetoException. + + @seealso Frame::close() + + @param xListener + reference to your listener object + + @onerror Listener is ignored. + + @threadsafe yes + @modified 06.05.2002 10:03, as96863 +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::addCloseListener( const css::uno::Reference< css::util::XCloseListener >& xListener ) throw (css::uno::RuntimeException) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + // We doesn't need any lock here ... + // Container lives if we live and is threadsafe by himself. + m_aListenerContainer.addInterface( ::getCppuType( ( const css::uno::Reference< css::util::XCloseListener >* ) NULL ), xListener ); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::removeCloseListener( const css::uno::Reference< css::util::XCloseListener >& xListener ) throw (css::uno::RuntimeException) +{ + // Use soft exception mode - moslty this method is called during disposing of this frame ... + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + // We doesn't need any lock here ... + // Container lives if we live and is threadsafe by himself. + m_aListenerContainer.removeInterface( ::getCppuType( ( const css::uno::Reference< css::util::XCloseListener >* ) NULL ), xListener ); +} + +//***************************************************************************************************************** +::rtl::OUString SAL_CALL Frame::getTitle() + throw (css::uno::RuntimeException) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::frame::XTitle > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW); + aReadLock.unlock(); + // <- SAFE + + return xTitle->getTitle(); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::setTitle( const ::rtl::OUString& sTitle ) + throw (css::uno::RuntimeException) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::frame::XTitle > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW); + aReadLock.unlock(); + // <- SAFE + + xTitle->setTitle(sTitle); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::addTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener) + throw (css::uno::RuntimeException) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW); + aReadLock.unlock(); + // <- SAFE + + xTitle->addTitleChangeListener(xListener); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::removeTitleChangeListener( const css::uno::Reference< css::frame::XTitleChangeListener >& xListener ) + throw (css::uno::RuntimeException) +{ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::frame::XTitleChangeBroadcaster > xTitle(m_xTitleHelper, css::uno::UNO_QUERY_THROW); + aReadLock.unlock(); + // <- SAFE + + xTitle->removeTitleChangeListener(xListener); +} + +/*-****************************************************************************************************/ +void Frame::implts_forgetSubFrames() +{ + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::container::XIndexAccess > xContainer(m_xFramesHelper, css::uno::UNO_QUERY_THROW); + aReadLock.unlock(); + // <- SAFE + + sal_Int32 c = xContainer->getCount(); + sal_Int32 i = 0; + + for (i=0; i<c; ++i) + { + try + { + css::uno::Reference< css::frame::XFrame > xFrame; + xContainer->getByIndex(i) >>= xFrame; + if (xFrame.is()) + xFrame->setCreator(css::uno::Reference< css::frame::XFramesSupplier >()); + } + catch(const css::uno::Exception&) + { + // Ignore errors here. + // Nobody can guarantee a stable index in multi threaded environments .-) + } + } + + // SAFE -> + WriteGuard aWriteLock(m_aLock); + m_xFramesHelper.clear(); // clear uno reference + m_aChildFrameContainer.clear(); // clear container content + aWriteLock.unlock(); + // <- SAFE +} + +/*-****************************************************************************************************//** + @short destroy instance + @descr The owner of this object calles the dispose method if the object + should be destroyed. All other objects and components, that are registered + as an EventListener are forced to release their references to this object. + Furthermore this frame is removed from its parent frame container to release + this reference. The reference attributes are disposed and released also. + + @attention Look for globale description at beginning of file too! + (DisposedException, FairRWLock ..., initialize, dispose) + + @seealso method initialize() + @seealso baseclass FairRWLockBase! + + @param - + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::dispose() throw( css::uno::RuntimeException ) +{ + // We should hold a reference to ourself ... + // because our owner dispose us and release our reference ... + // May be we will die before we could finish this method ... + css::uno::Reference< css::frame::XFrame > xThis( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + + LOG_DISPOSEEVENT( "Frame", sName ) + + // First operation should be ... "stopp all listening for window events on our container window". + // These events are superflous but can make trouble! + // We will die, die and die ... + implts_stopWindowListening(); + + // Send message to all listener and forget her references. + css::lang::EventObject aEvent( xThis ); + m_aListenerContainer.disposeAndClear( aEvent ); + + // set "end of live" for our property set helper + impl_disablePropertySet(); + + // interception/dispatch chain must be destructed explicitly + // Otherwhise some dispatches and/or interception objects wont die. + css::uno::Reference< css::lang::XEventListener > xDispatchHelper(m_xDispatchHelper, css::uno::UNO_QUERY_THROW); + xDispatchHelper->disposing(aEvent); + xDispatchHelper.clear(); + + // Disable this instance for further work. + // This will wait for all current running ones ... + // and reject all further requests! + m_aTransactionManager.setWorkingMode( E_BEFORECLOSE ); + + // Don't show any dialogs, errors or something else any more! + // If somewhere called dispose() whitout close() before - normaly no dialogs + // should exist. Otherwhise it's the problem of the outside caller. + // Note: + // (a) Do it after stopWindowListening(). May that force some active/deactive + // notifications which we doesn't need here realy. + // (b) Don't forget to save the old value of IsDialogCancelEnabled() to + // restore it afterwards. We cannot call EnableDialogCancel( sal_False ) + // as we would kill the headless mode! + sal_Bool bCancelDialogs( Application::IsDialogCancelEnabled() ); + Application::EnableDialogCancel( sal_True ); + + // We should be alone for ever and further dispose calls are rejected by lines before ... + // I hope it :-) + + // Free references of our frame tree. + // Force parent container to forget this frame too ... + // ( It's contained in m_xParent and so no css::lang::XEventListener for m_xParent! ) + // It's important to do that before we free some other internal structures. + // Because if our parent gets an activate and found us as last possible active frame + // he try to deactivate us ... and we run into some trouble (DisposedExceptions!). + if( m_xParent.is() == sal_True ) + { + m_xParent->getFrames()->remove( xThis ); + m_xParent = css::uno::Reference< css::frame::XFramesSupplier >(); + } + + /* } SAFE */ + // Forget our internal component and her window first. + // So we can release our container window later without problems. + // Because this container window is the parent of the component window ... + // Note: Dispose it hard - because suspending must be done inside close() call! + // But try to dispose the controller first befor you destroy the window. + // Because the window is used by the controller too ... + if (m_xController.is()) + { + css::uno::Reference< css::lang::XComponent > xDisposable( m_xController, css::uno::UNO_QUERY ); + if (xDisposable.is()) + xDisposable->dispose(); + } + + if (m_xComponentWindow.is()) + { + css::uno::Reference< css::lang::XComponent > xDisposable( m_xComponentWindow, css::uno::UNO_QUERY ); + if (xDisposable.is()) + xDisposable->dispose(); + } + + impl_checkMenuCloser(); + + impl_disposeContainerWindow( m_xContainerWindow ); + + /*ATTENTION + Clear container after successful removing from parent container ... + because our parent could be the desktop which stand in dispose too! + If we have already cleared our own container we lost our child before this could be + remove himself at this instance ... + Release m_xFramesHelper after that ... it's the same problem between parent and child! + "m_xParent->getFrames()->remove( xThis );" needs this helper ... + Otherwise we get a null reference and could finish removing successfuly. + => You see: Order of calling operations is important!!! + */ + implts_forgetSubFrames(); + + // Release some other references. + // This calls should be easy ... I hope it :-) + m_xDispatchHelper.clear(); + m_xFactory.clear(); + m_xDropTargetListener.clear(); + m_xDispatchRecorderSupplier.clear(); + m_xLayoutManager.clear(); + m_xIndicatorFactoryHelper.clear(); + + // It's important to set default values here! + // If may be later somewhere change the disposed-behaviour of this implementation + // and doesn't throw any DisposedExceptions we must guarantee best matching default values ... + m_eActiveState = E_INACTIVE; + m_sName = ::rtl::OUString(); + m_bIsFrameTop = sal_False; + m_bConnected = sal_False; + m_nExternalLockCount = 0; + m_bSelfClose = sal_False; + m_bIsHidden = sal_True; + + // Disable this instance for further working realy! + m_aTransactionManager.setWorkingMode( E_CLOSE ); + + // Don't forget it restore old value - + // otherwhise no dialogs can be shown anymore in other frames. + Application::EnableDialogCancel( bCancelDialogs ); +} + +/*-****************************************************************************************************//** + @short Be a listener for dispose events! + @descr Adds/remove an EventListener to this object. If the dispose method is called on + this object, the disposing method of the listener is called. + + @seealso - + + @param "xListener" reference to your listener object. + @return - + + @onerror Listener is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_addEventListener( xListener ), "Frame::addEventListener()", "Invalid parameter detected." ) + // Look for rejected calls only! + // Container is threadsafe. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.addInterface( ::getCppuType( ( const css::uno::Reference< css::lang::XEventListener >* ) NULL ), xListener ); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_removeEventListener( xListener ), "Frame::removeEventListener()", "Invalid parameter detected." ) + // Look for rejected calls only! + // Container is threadsafe. + // Use E_SOFTEXCEPTIONS to allow removing listeners during dispose call! + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + m_aListenerContainer.removeInterface( ::getCppuType( ( const css::uno::Reference< css::lang::XEventListener >* ) NULL ), xListener ); +} + +/*-****************************************************************************************************//** + @short create new status indicator + @descr Use returned status indicator to show progresses and some text informations. + All created objects share the same dialog! Only the last one can show his information. + + @seealso class StatusIndicatorFactory + @seealso class StatusIndicator + + @param - + @return A reference to created object. + + @onerror We return a null reference. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::task::XStatusIndicator > SAL_CALL Frame::createStatusIndicator() throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA ----------------------------------------------------------------------------------------- */ + // Look for rejected calls! + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + // Make snapshot of neccessary member and define default return value. + css::uno::Reference< css::task::XStatusIndicator > xExternal(m_xIndicatorInterception.get(), css::uno::UNO_QUERY); + css::uno::Reference< css::task::XStatusIndicatorFactory > xFactory = m_xIndicatorFactoryHelper; + + aReadLock.unlock(); + /* UNSAFE AREA ----------------------------------------------------------------------------------------- */ + + // Was set from outside to intercept any progress activities! + if (xExternal.is()) + return xExternal; + + // Or use our own factory as fallback, to create such progress. + if (xFactory.is()) + return xFactory->createStatusIndicator(); + + return css::uno::Reference< css::task::XStatusIndicator >(); +} + +/*-****************************************************************************************************//** + @short search for target to load URL + @descr This method searches for a dispatch for the specified DispatchDescriptor. + The FrameSearchFlags and the FrameName of the DispatchDescriptor are + treated as described for findFrame. + + @seealso method findFrame() + @seealso method queryDispatches() + @seealso method set/getName() + @seealso class TargetFinder + + @param "aURL" , URL for loading + @param "sTargetFrameName" , name of target frame + @param "nSearchFlags" , additional flags to regulate search if sTargetFrameName isn't clear + @return css::uno::Reference to dispatch handler. + + @onerror A null reference is returned. +*//*-*****************************************************************************************************/ +css::uno::Reference< css::frame::XDispatch > SAL_CALL Frame::queryDispatch( const css::util::URL& aURL , + const ::rtl::OUString& sTargetFrameName, + sal_Int32 nSearchFlags ) throw( css::uno::RuntimeException ) +{ + const char UNO_PROTOCOL[] = ".uno:"; + + // Don't check incoming parameter here! Our helper do it for us and it isn't a good idea to do it more then ones! + // But look for rejected calls! + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // Remove uno and cmd protocol part as we want to support both of them. We store only the command part + // in our hash map. All other protocols are stored with the protocol part. + String aCommand( aURL.Main ); + if ( aURL.Protocol.equalsIgnoreAsciiCaseAsciiL( UNO_PROTOCOL, sizeof( UNO_PROTOCOL )-1 )) + aCommand = aURL.Path; + + // Make hash_map lookup if the current URL is in the disabled list + if ( m_aCommandOptions.Lookup( SvtCommandOptions::CMDOPTION_DISABLED, aCommand ) ) + return css::uno::Reference< css::frame::XDispatch >(); + else + { + // We use a helper to support these interface and an interceptor mechanism. + // Our helper is threadsafe by himself! + return m_xDispatchHelper->queryDispatch( aURL, sTargetFrameName, nSearchFlags ); + } +} + +/*-****************************************************************************************************//** + @short handle more then ones dispatches at same call + @descr Returns a sequence of dispatches. For details see the queryDispatch method. + For failed dispatches we return empty items in list! + + @seealso method queryDispatch() + + @param "lDescriptor" list of dispatch arguments for queryDispatch()! + @return List of dispatch references. Some elements can be NULL! + + @onerror An empty list is returned. +*//*-*****************************************************************************************************/ +css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL Frame::queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& lDescriptor ) throw( css::uno::RuntimeException ) +{ + // Don't check incoming parameter here! Our helper do it for us and it isn't a good idea to do it more then ones! + // But look for rejected calls! + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + // We use a helper to support these interface and an interceptor mechanism. + // Our helper is threadsafe by himself! + return m_xDispatchHelper->queryDispatches( lDescriptor ); +} + +/*-****************************************************************************************************//** + @short register/unregister interceptor for dispatch calls + @descr If you whish to handle some dispatches by himself ... you should be + an interceptor for it. Please see class OInterceptionHelper for further informations. + + @seealso class OInterceptionHelper + + @param "xInterceptor", reference to your interceptor implementation. + @return - + + @onerror Interceptor is ignored. +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::registerDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor ) throw( css::uno::RuntimeException ) +{ + // We use a helper to support these interface and an interceptor mechanism. + // This helper is threadsafe himself and check incoming parameter too. + // I think we don't need any lock here! + // But we must look for rejected calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper( m_xDispatchHelper, css::uno::UNO_QUERY ); + xInterceptionHelper->registerDispatchProviderInterceptor( xInterceptor ); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::releaseDispatchProviderInterceptor( const css::uno::Reference< css::frame::XDispatchProviderInterceptor >& xInterceptor ) throw( css::uno::RuntimeException ) +{ + // We use a helper to support these interface and an interceptor mechanism. + // This helper is threadsafe himself and check incoming parameter too. + // I think we don't need any lock here! + // But we must look for rejected calls ... + // Sometimes we are called during our dispose() method ... => soft exceptions! + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + css::uno::Reference< css::frame::XDispatchProviderInterception > xInterceptionHelper( m_xDispatchHelper, css::uno::UNO_QUERY ); + xInterceptionHelper->releaseDispatchProviderInterceptor( xInterceptor ); +} + +/*-****************************************************************************************************//** + @short provides information about all possible dispatch functions + inside the currnt frame environment +*//*-*****************************************************************************************************/ +css::uno::Sequence< sal_Int16 > SAL_CALL Frame::getSupportedCommandGroups() + throw(css::uno::RuntimeException) +{ + return m_xDispatchInfoHelper->getSupportedCommandGroups(); +} + +//***************************************************************************************************************** +css::uno::Sequence< css::frame::DispatchInformation > SAL_CALL Frame::getConfigurableDispatchInformation(sal_Int16 nCommandGroup) + throw(css::uno::RuntimeException) +{ + return m_xDispatchInfoHelper->getConfigurableDispatchInformation(nCommandGroup); +} + +/*-****************************************************************************************************//** + @short notifications for window events + @descr We are a listener on our container window to forward it to our component window. + + @seealso method setComponent() + @seealso member m_xContainerWindow + @seealso member m_xComponentWindow + + @param "aEvent" describe source of detected event + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::windowResized( const css::awt::WindowEvent& +#if OSL_DEBUG_LEVEL > 0 +aEvent +#endif +) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_windowResized( aEvent ), "Frame::windowResized()", "Invalid parameter detected." ) + // Look for rejected calls. + // Part of dispose-mechanism => soft exceptions + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Impl-method is threadsafe! + // If we have a current component window - we must resize it! + implts_resizeComponentWindow(); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::focusGained( const css::awt::FocusEvent& +#if OSL_DEBUG_LEVEL > 0 +aEvent +#endif +) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_focusGained( aEvent ), "Frame::focusGained()", "Invalid parameter detected." ) + // Look for rejected calls. + // Part of dispose() mechanism ... => soft exceptions! + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + // Make snapshot of member! + css::uno::Reference< css::awt::XWindow > xComponentWindow = m_xComponentWindow; + aReadLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( xComponentWindow.is() == sal_True ) + { + xComponentWindow->setFocus(); + } +} + +/*-****************************************************************************************************//** + @short notifications for window events + @descr We are a listener on our container window to forward it to our component window ... + but a XTopWindowListener we are only if we are a top frame! + + @seealso method setComponent() + @seealso member m_xContainerWindow + @seealso member m_xComponentWindow + + @param "aEvent" describe source of detected event + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::windowActivated( const css::lang::EventObject& +#if OSL_DEBUG_LEVEL > 0 +aEvent +#endif +) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_windowActivated( aEvent ), "Frame::windowActivated()", "Invalid parameter detected." ) + // Look for rejected calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + // Make snapshot of member! + EActiveState eState = m_eActiveState; + aReadLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Activate the new active path from here to top. + if( eState == E_INACTIVE ) + { +// CheckMenuCloser_Impl(); + setActiveFrame( css::uno::Reference< css::frame::XFrame >() ); + activate(); + } +} + +//***************************************************************************************************************** +void SAL_CALL Frame::windowDeactivated( const css::lang::EventObject& +#if OSL_DEBUG_LEVEL > 0 +aEvent +#endif +) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_windowDeactivated( aEvent ), "Frame::windowDeactivated()", "Invalid parameter detected." ) + // Look for rejected calls. + // Sometimes called during dispose() => soft exceptions + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + + css::uno::Reference< css::frame::XFrame > xParent ( m_xParent, css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow; + EActiveState eActiveState = m_eActiveState ; + + aReadLock.unlock(); + + if( eActiveState != E_INACTIVE ) + { + // Deactivation is always done implicitely by activation of another frame. + // Only if no activation is done, deactivations have to be processed if the activated window + // is a parent window of the last active Window! + ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() ); +// CheckMenuCloser_Impl(); + Window* pFocusWindow = Application::GetFocusWindow(); + if ( + ( xContainerWindow.is() == sal_True ) && + ( xParent.is() == sal_True ) && + ( (css::uno::Reference< css::frame::XDesktop >( xParent, css::uno::UNO_QUERY )).is() == sal_False ) + ) + { + css::uno::Reference< css::awt::XWindow > xParentWindow = xParent->getContainerWindow() ; + Window* pParentWindow = VCLUnoHelper::GetWindow( xParentWindow ); + //#i70261#: dialogs opend from an OLE object will cause a deactivate on the frame of the OLE object + // on Solaris/Linux at that time pFocusWindow is still NULL because the focus handling is different; right after + // the deactivation the focus will be set into the dialog! + // currently I see no case where a sub frame could get a deactivate with pFocusWindow being NULL permanently + // so for now this case is omitted from handled deactivations + if( pFocusWindow && pParentWindow->IsChild( pFocusWindow ) ) + { + css::uno::Reference< css::frame::XFramesSupplier > xSupplier( xParent, css::uno::UNO_QUERY ); + if( xSupplier.is() == sal_True ) + { + aSolarGuard.clear(); + xSupplier->setActiveFrame( css::uno::Reference< css::frame::XFrame >() ); + } + } + } + } +} + +//***************************************************************************************************************** +void SAL_CALL Frame::windowClosing( const css::lang::EventObject& ) throw( css::uno::RuntimeException ) +{ + /* #i62088# + Some interceptor objects intercept our "internaly asynchronoues implemented" dispatch call. + And they close this frame directly (means synchronous then). + Means: Frame::windowClosing()->Frame::close() + In such situation its not a good idea to hold this transaction count alive .-) + */ + { + // Look for rejected calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + // deactivate this frame ... + deactivate(); + } + + // ... and try to close it + // But do it asynchron inside the main thread. + // VCL has no fun to do such things outside his main thread :-( + // Note: The used dispatch make it asynchronous for us .-) + + /*ATTENTION! + Don't try to suspend the controller here! Because it's done inside used dispatch(). + Otherwhise the dialog "would you save your changes?" will be shown more then once ... + */ + + /* SAFE */ + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::lang::XMultiServiceFactory > xFactory = m_xFactory; + aReadLock.unlock(); + /* SAFE */ + + css::util::URL aURL; + aURL.Complete = DECLARE_ASCII(".uno:CloseFrame"); + css::uno::Reference< css::util::XURLTransformer > xParser(xFactory->createInstance(SERVICENAME_URLTRANSFORMER), css::uno::UNO_QUERY_THROW); + xParser->parseStrict(aURL); + + css::uno::Reference< css::frame::XDispatch > xCloser = queryDispatch(aURL, SPECIALTARGET_SELF, 0); + if (xCloser.is()) + xCloser->dispatch(aURL, css::uno::Sequence< css::beans::PropertyValue >()); + + // Attention: If this dispatch works synchronous ... and full fill its job ... + // this line of code will never be reached ... + // Or if it will be reached it will be for sure that all your member are gone .-) +} + +/*-****************************************************************************************************//** + @short react for a show event for the internal container window + @descr Normaly we doesn't need this information realy. But we can use it to + implement the special feature "trigger first visible task". + + Algorithm: - first we have to check if we are a top (task) frame + It's not enough to be a top frame! Because we MUST have the desktop as parent. + But frames without a parent are top too. So it's not possible to check isTop() here! + We have to look for the type of our parent. + - if we are a task frame, then we have to check if we are the first one. + We use a static variable to do so. They will be reset to afterwards be shure + that further calls of this method doesn't do anything then. + - Then we have to trigger the right event string on the global job executor. + + @seealso css::task::JobExecutor + + @param aEvent + describes the source of this event + We are not interested on this information. We are interested on the visible state only. + + @threadsafe yes + @modified 31.07.2002 07:56, as96863 +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::windowShown( const css::lang::EventObject& ) throw(css::uno::RuntimeException) +{ + static sal_Bool bFirstVisibleTask = sal_True; + + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::frame::XDesktop > xDesktopCheck( m_xParent, css::uno::UNO_QUERY ); + css::uno::Reference< css::lang::XMultiServiceFactory > xFactory = m_xFactory; + m_bIsHidden = sal_False; + aReadLock.unlock(); + /* } SAFE */ + + impl_checkMenuCloser(); + + if (xDesktopCheck.is()) + { + /* STATIC SAFE { */ + WriteGuard aStaticWriteLock( LockHelper::getGlobalLock() ); + sal_Bool bMustBeTriggered = bFirstVisibleTask; + bFirstVisibleTask = sal_False; + aStaticWriteLock.unlock(); + /* } STATIC SAFE */ + + if (bMustBeTriggered) + { + css::uno::Reference< css::task::XJobExecutor > xExecutor( xFactory->createInstance( SERVICENAME_JOBEXECUTOR ), css::uno::UNO_QUERY ); + if (xExecutor.is()) + { + xExecutor->trigger( DECLARE_ASCII("onFirstVisibleTask") ); + } + } + } +} + +void SAL_CALL Frame::windowHidden( const css::lang::EventObject& ) throw(css::uno::RuntimeException) +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + m_bIsHidden = sal_True; + aReadLock.unlock(); + /* } SAFE */ + + impl_checkMenuCloser(); +} + +/*-****************************************************************************************************//** + @short called by dispose of our windows! + @descr This object is forced to release all references to the interfaces given + by the parameter source. We are a listener at our container window and + should listen for his diposing. + + @seealso XWindowListener + @seealso XTopWindowListener + @seealso XFocusListener + + @param - + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void SAL_CALL Frame::disposing( const css::lang::EventObject& aEvent ) throw( css::uno::RuntimeException ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Check incoming parameter. + LOG_ASSERT2( implcp_disposing( aEvent ), "Frame::disposing()", "Invalid parameter detected." ) + // Look for rejected calls. + // May be we are called during releasing our windows in our in dispose call!? => soft exceptions + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + + if( aEvent.Source == m_xContainerWindow ) + { + // NECCESSARY: Impl-method is threadsafe by himself! + aWriteLock.unlock(); + implts_stopWindowListening(); + aWriteLock.lock(); + m_xContainerWindow = css::uno::Reference< css::awt::XWindow >(); + } +} + +/*-************************************************************************************************************//** + @interface com.sun.star.document.XActionLockable + @short implement locking of frame/task from outside + @descr Sometimes we have problems to decide if closing of task is allowed. Because; frame/task + could be used for pending loading jobs. So you can lock this object from outside and + prevent instance against closing during using! But - don't do it in a wrong or expensive manner. + Otherwise task couldn't die anymore!!! + + @seealso interface XActionLockable + @seeelso method BaseDispatcher::implts_loadIt() + @seeelso method Desktop::loadComponentFromURL() + + @param - + @return true if frame/task is locked + false otherwise + + @onerror - + @threadsafe yes +*//*-*************************************************************************************************************/ +sal_Bool SAL_CALL Frame::isActionLocked() throw( css::uno::RuntimeException ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ReadGuard aReadLock( m_aLock ); + return( m_nExternalLockCount!=0); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::addActionLock() throw( css::uno::RuntimeException ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + ++m_nExternalLockCount; +} + +//***************************************************************************************************************** +void SAL_CALL Frame::removeActionLock() throw( css::uno::RuntimeException ) +{ + // Register no transaction here! Otherwhise we wait for ever inside possible + // implts_checkSuicide()/dispose() request ... + + /* SAFE AREA */{ + WriteGuard aWriteLock( m_aLock ); + LOG_ASSERT2( m_nExternalLockCount<=0, "Frame::removeActionLock()", "Frame isn't locked! Possible multithreading problem detected." ) + --m_nExternalLockCount; + }/* SAFE */ + + implts_checkSuicide(); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::setActionLocks( sal_Int16 nLock ) throw( css::uno::RuntimeException ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + WriteGuard aWriteLock( m_aLock ); + // Attention: If somewhere called resetActionLocks() before and get e.g. 5 locks ... + // and tried to set these 5 ones here after his operations ... + // we can't ignore setted requests during these two calls! + // So we must add(!) these 5 locks here. + m_nExternalLockCount = m_nExternalLockCount + nLock; +} + +//***************************************************************************************************************** +sal_Int16 SAL_CALL Frame::resetActionLocks() throw( css::uno::RuntimeException ) +{ + // Register no transaction here! Otherwhise we wait for ever inside possible + // implts_checkSuicide()/dispose() request ... + + sal_Int16 nCurrentLocks = 0; + /* SAFE */{ + WriteGuard aWriteLock( m_aLock ); + nCurrentLocks = m_nExternalLockCount; + m_nExternalLockCount = 0; + }/* SAFE */ + + // Attention: + // external lock count is 0 here every time ... but if + // member m_bSelfClose is set to true too .... we call our own close()/dispose(). + // See close() for further informations + implts_checkSuicide(); + + return nCurrentLocks; +} + +//***************************************************************************************************************** +void Frame::impl_initializePropInfo() +{ + impl_setPropertyChangeBroadcaster(static_cast< css::frame::XFrame* >(this)); + + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_DISPATCHRECORDERSUPPLIER, + FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER, + ::getCppuType((const css::uno::Reference< css::frame::XDispatchRecorderSupplier >*)NULL), + css::beans::PropertyAttribute::TRANSIENT)); + + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_INDICATORINTERCEPTION, + FRAME_PROPHANDLE_INDICATORINTERCEPTION, + ::getCppuType((const css::uno::Reference< css::task::XStatusIndicator >*)NULL), + css::beans::PropertyAttribute::TRANSIENT)); + + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_ISHIDDEN, + FRAME_PROPHANDLE_ISHIDDEN, + ::getBooleanCppuType(), + css::beans::PropertyAttribute::TRANSIENT | css::beans::PropertyAttribute::READONLY)); + + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_LAYOUTMANAGER, + FRAME_PROPHANDLE_LAYOUTMANAGER, + ::getCppuType((const css::uno::Reference< ::com::sun::star::frame::XLayoutManager >*)NULL), + css::beans::PropertyAttribute::TRANSIENT)); + + impl_addPropertyInfo( + css::beans::Property( + FRAME_PROPNAME_TITLE, + FRAME_PROPHANDLE_TITLE, + ::getCppuType((const ::rtl::OUString*)NULL), + css::beans::PropertyAttribute::TRANSIENT)); +} + +//***************************************************************************************************************** +void SAL_CALL Frame::impl_setPropertyValue(const ::rtl::OUString& /*sProperty*/, + sal_Int32 nHandle , + const css::uno::Any& aValue ) + +{ + static ::rtl::OUString MATERIALPROP_TITLE = ::rtl::OUString::createFromAscii("title"); + + /* There is no need to lock any mutex here. Because we share the + solar mutex with our base class. And we said to our base class: "dont release it on calling us" .-) + see ctor of PropertySetHelper for further informations. + */ + + /* Attention: You can use nHandle only, if you are sure that all supported + properties has an unique handle. That must be guaranteed + inside method impl_initializePropInfo()! + */ + switch (nHandle) + { + case FRAME_PROPHANDLE_TITLE : + { + ::rtl::OUString sExternalTitle; + aValue >>= sExternalTitle; + setTitle (sExternalTitle); + } + break; + + case FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER : + aValue >>= m_xDispatchRecorderSupplier; + break; + + case FRAME_PROPHANDLE_LAYOUTMANAGER : + { + css::uno::Reference< css::frame::XLayoutManager > xOldLayoutManager = m_xLayoutManager; + css::uno::Reference< css::frame::XLayoutManager > xNewLayoutManager; + aValue >>= xNewLayoutManager; + + if (xOldLayoutManager != xNewLayoutManager) + { + m_xLayoutManager = xNewLayoutManager; + if (xOldLayoutManager.is()) + lcl_disableLayoutManager(xOldLayoutManager, this); + if (xNewLayoutManager.is()) + lcl_enableLayoutManager(xNewLayoutManager, this); + } + } + break; + + case FRAME_PROPHANDLE_INDICATORINTERCEPTION : + { + css::uno::Reference< css::task::XStatusIndicator > xProgress; + aValue >>= xProgress; + m_xIndicatorInterception = xProgress; + } + break; + + #ifdef ENABLE_WARNINGS + default : + LOG_WARNING( "Frame::setFastPropertyValue_NoBroadcast()", "Invalid handle detected!" ) + break; + #endif + } +} + +//***************************************************************************************************************** +css::uno::Any SAL_CALL Frame::impl_getPropertyValue(const ::rtl::OUString& /*sProperty*/, + sal_Int32 nHandle ) +{ + /* There is no need to lock any mutex here. Because we share the + solar mutex with our base class. And we said to our base class: "dont release it on calling us" .-) + see ctor of PropertySetHelper for further informations. + */ + + /* Attention: You can use nHandle only, if you are sure that all supported + properties has an unique handle. That must be guaranteed + inside method impl_initializePropInfo()! + */ + css::uno::Any aValue; + switch (nHandle) + { + case FRAME_PROPHANDLE_TITLE : + aValue <<= getTitle (); + break; + + case FRAME_PROPHANDLE_DISPATCHRECORDERSUPPLIER : + aValue <<= m_xDispatchRecorderSupplier; + break; + + case FRAME_PROPHANDLE_ISHIDDEN : + aValue <<= m_bIsHidden; + break; + + case FRAME_PROPHANDLE_LAYOUTMANAGER : + aValue <<= m_xLayoutManager; + break; + + case FRAME_PROPHANDLE_INDICATORINTERCEPTION : + { + css::uno::Reference< css::task::XStatusIndicator > xProgress(m_xIndicatorInterception.get(), css::uno::UNO_QUERY); + aValue = css::uno::makeAny(xProgress); + } + break; + + #ifdef ENABLE_WARNINGS + default : + LOG_WARNING( "Frame::getFastPropertyValue()", "Invalid handle detected!" ) + break; + #endif + } + + return aValue; +} + +/*-****************************************************************************************************//** + @short dispose old container window and forget his reference + @descr Sometimes we must repair our "modal dialog parent mechanism" too! + + @seealso - + + @param "xWindow", reference to old container window to dispose it + @return An empty reference. + + @onerror - + @threadsafe NO! +*//*-*****************************************************************************************************/ +void Frame::impl_disposeContainerWindow( css::uno::Reference< css::awt::XWindow >& xWindow ) +{ + if( xWindow.is() == sal_True ) + { + xWindow->setVisible( sal_False ); + // All VclComponents are XComponents; so call dispose before discarding + // a css::uno::Reference< XVclComponent >, because this frame is the owner of the window + xWindow->dispose(); + xWindow = css::uno::Reference< css::awt::XWindow >(); + } +} + +/*-****************************************************************************************************//** + @short send frame action event to our listener + @descr This method is threadsafe AND can be called by our dispose method too! + + @seealso - + + @param "aAction", describe the event for sending + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void Frame::implts_sendFrameActionEvent( const css::frame::FrameAction& aAction ) +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Sometimes used by dispose() => soft exceptions! + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + // Log informations about order of events to file! + // (only activated in debug version!) + LOG_FRAMEACTIONEVENT( "Frame", m_sName, aAction ) + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Send css::frame::FrameAction event to all listener. + // Get container for right listener. + // FOLLOW LINES ARE THREADSAFE!!! + // ( OInterfaceContainerHelper is synchronized with m_aListenerContainer! ) + ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer( ::getCppuType( ( const css::uno::Reference< css::frame::XFrameActionListener >*) NULL ) ); + + if( pContainer != NULL ) + { + // Build action event. + css::frame::FrameActionEvent aFrameActionEvent( static_cast< ::cppu::OWeakObject* >(this), this, aAction ); + + // Get iterator for access to listener. + ::cppu::OInterfaceIteratorHelper aIterator( *pContainer ); + // Send message to all listener. + while( aIterator.hasMoreElements() == sal_True ) + { + try + { + ((css::frame::XFrameActionListener*)aIterator.next())->frameAction( aFrameActionEvent ); + } + catch( css::uno::RuntimeException& ) + { + aIterator.remove(); + } + } + } +} + +/*-****************************************************************************************************//** + @short helper to resize our component window + @descr A frame contains 2 windows - a container ~ and a component window. + This method resize inner component window to full size of outer container window. + This method is threadsafe AND can be called by our dispose method too! + + @seealso - + + @param - + @return - + + @onerror - +*//*-*****************************************************************************************************/ +void Frame::implts_resizeComponentWindow() +{ + // usually the LayoutManager does the resizing + // in case there is no LayoutManager resizing has to be done here + if ( !m_xLayoutManager.is() ) + { + css::uno::Reference< css::awt::XWindow > xComponentWindow( getComponentWindow() ); + if( xComponentWindow.is() == sal_True ) + { + css::uno::Reference< css::awt::XDevice > xDevice( getContainerWindow(), css::uno::UNO_QUERY ); + + // Convert relativ size to output size. + css::awt::Rectangle aRectangle = getContainerWindow()->getPosSize(); + css::awt::DeviceInfo aInfo = xDevice->getInfo(); + css::awt::Size aSize ( aRectangle.Width - aInfo.LeftInset - aInfo.RightInset , + aRectangle.Height - aInfo.TopInset - aInfo.BottomInset ); + + // Resize our component window. + xComponentWindow->setPosSize( 0, 0, aSize.Width, aSize.Height, css::awt::PosSize::POSSIZE ); + } + } +} + +/*-****************************************************************************************************//** + @short helper to set icon on our container window (if it is a system window!) + @descr We use our internal set controller (if it exist) to specify which factory he represanted. + These information can be used to find right icon. But our controller can say it us directly + too ... we should ask his optional property set first ... + + @seealso method Window::SetIcon() + + @param - + @return - + + @onerror We do nothing. +*//*-*****************************************************************************************************/ +void Frame::implts_setIconOnWindow() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Look for rejected calls. + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of neccessary members and release lock. + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::awt::XWindow > xContainerWindow( m_xContainerWindow, css::uno::UNO_QUERY ); + css::uno::Reference< css::frame::XController > xController ( m_xController , css::uno::UNO_QUERY ); + aReadLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( + ( xContainerWindow.is() == sal_True ) && + ( xController.is() == sal_True ) + ) + { + //------------------------------------------------------------------------------------------------------------- + // a) set default value to an invalid one. So we can start further searches for right icon id, if + // first steps failed! + // We must reset it to any fallback value - if no search step returns a valid result. + sal_Int32 nIcon = -1; + + //------------------------------------------------------------------------------------------------------------- + // b) try to find information on controller propertyset directly + // Don't forget to catch possible exceptions - because these property is an optional one! + css::uno::Reference< css::beans::XPropertySet > xSet( xController, css::uno::UNO_QUERY ); + if( xSet.is() == sal_True ) + { + try + { + xSet->getPropertyValue( DECLARE_ASCII("IconId") )>>= nIcon; + } + catch( css::beans::UnknownPropertyException& ) + { + } + } + + //------------------------------------------------------------------------------------------------------------- + // c) if b) failed ... analyze argument list of currently loaded document insde the frame to find the filter. + // He can be used to detect right factory - and these can be used to match factory to icon ... + if( nIcon == -1 ) + { + css::uno::Reference< css::frame::XModel > xModel = xController->getModel(); + if( xModel.is() == sal_True ) + { + SvtModuleOptions::EFactory eFactory = SvtModuleOptions::ClassifyFactoryByModel(xModel); + if (eFactory != SvtModuleOptions::E_UNKNOWN_FACTORY) + nIcon = SvtModuleOptions().GetFactoryIcon( eFactory ); + } + } + + //------------------------------------------------------------------------------------------------------------- + // d) if all steps failed - use fallback! + if( nIcon == -1 ) + { + nIcon = 0; + } + + //------------------------------------------------------------------------------------------------------------- + // e) set icon on container window now + // Don't forget SolarMutex! We use vcl directly :-( + // Check window pointer for right WorkWindow class too!!! + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() ); + Window* pWindow = (VCLUnoHelper::GetWindow( xContainerWindow )); + if( + ( pWindow != NULL ) && + ( pWindow->GetType() == WINDOW_WORKWINDOW ) + ) + { + WorkWindow* pWorkWindow = (WorkWindow*)pWindow; + pWorkWindow->SetIcon( (sal_uInt16)nIcon ); + } + aSolarGuard.clear(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + } +} + +/*-************************************************************************************************************//** + @short helper to start/stop listeneing for window events on container window + @descr If we get a new container window, we must set it on internal memeber ... + and stop listening at old one ... and start listening on new one! + But sometimes (in dispose() call!) it's neccessary to stop listeneing without starting + on new connections. So we split this functionality to make it easier at use. + + @seealso method initialize() + @seealso method dispose() + + @param - + @return - + + @onerror We do nothing! + @threadsafe yes +*//*-*************************************************************************************************************/ +void Frame::implts_startWindowListening() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + TransactionGuard aTransaction( m_aTransactionManager, E_HARDEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of neccessary member! + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow ; + css::uno::Reference< css::lang::XMultiServiceFactory > xFactory = m_xFactory ; + css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > xDragDropListener = m_xDropTargetListener; + css::uno::Reference< css::awt::XWindowListener > xWindowListener ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XFocusListener > xFocusListener ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XTopWindowListener > xTopWindowListener ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + aReadLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( xContainerWindow.is() == sal_True ) + { + xContainerWindow->addWindowListener( xWindowListener); + xContainerWindow->addFocusListener ( xFocusListener ); + + css::uno::Reference< css::awt::XTopWindow > xTopWindow( xContainerWindow, css::uno::UNO_QUERY ); + if( xTopWindow.is() == sal_True ) + { + xTopWindow->addTopWindowListener( xTopWindowListener ); + + css::uno::Reference< css::awt::XDataTransferProviderAccess > xTransfer( xFactory->createInstance( SERVICENAME_VCLTOOLKIT ), css::uno::UNO_QUERY ); + if( xTransfer.is() == sal_True ) + { + css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = xTransfer->getDropTarget( xContainerWindow ); + if( xDropTarget.is() == sal_True ) + { + xDropTarget->addDropTargetListener( xDragDropListener ); + xDropTarget->setActive( sal_True ); + } + } + } + } +} + +//***************************************************************************************************************** +void Frame::implts_stopWindowListening() +{ + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + // Sometimes used by dispose() => soft exceptions! + TransactionGuard aTransaction( m_aTransactionManager, E_SOFTEXCEPTIONS ); + + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + // Make snapshot of neccessary member! + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::awt::XWindow > xContainerWindow = m_xContainerWindow ; + css::uno::Reference< css::lang::XMultiServiceFactory > xFactory = m_xFactory ; + css::uno::Reference< css::datatransfer::dnd::XDropTargetListener > xDragDropListener = m_xDropTargetListener; + css::uno::Reference< css::awt::XWindowListener > xWindowListener ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XFocusListener > xFocusListener ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XTopWindowListener > xTopWindowListener ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + aReadLock.unlock(); + /* UNSAFE AREA --------------------------------------------------------------------------------------------- */ + + if( xContainerWindow.is() == sal_True ) + { + xContainerWindow->removeWindowListener( xWindowListener); + xContainerWindow->removeFocusListener ( xFocusListener ); + + css::uno::Reference< css::awt::XTopWindow > xTopWindow( xContainerWindow, css::uno::UNO_QUERY ); + if( xTopWindow.is() == sal_True ) + { + xTopWindow->removeTopWindowListener( xTopWindowListener ); + + css::uno::Reference< css::awt::XDataTransferProviderAccess > xTransfer( xFactory->createInstance( SERVICENAME_VCLTOOLKIT ), css::uno::UNO_QUERY ); + if( xTransfer.is() == sal_True ) + { + css::uno::Reference< css::datatransfer::dnd::XDropTarget > xDropTarget = xTransfer->getDropTarget( xContainerWindow ); + if( xDropTarget.is() == sal_True ) + { + xDropTarget->removeDropTargetListener( xDragDropListener ); + xDropTarget->setActive( sal_False ); + } + } + } + } +} + +/*-****************************************************************************************************//** + @short helper to force breaked close() request again + @descr If we self disagree with a close() request, and detect that all external locks are gone ... + then we must try to close this frame again. + + @seealso XCloseable::close() + @seealso Frame::close() + @seealso Frame::removeActionLock() + @seealso Frame::resetActionLock() + @seealso m_bSelfClose + @seealso m_nExternalLockCount + + @threadsafe yes + @modified 06.05.2002 09:31, as96863 +*//*-*****************************************************************************************************/ +void Frame::implts_checkSuicide() +{ + /* SAFE */ + ReadGuard aReadLock(m_aLock); + // in case of lock==0 and safed state of previous close() request m_bSelfClose + // we must force close() again. Because we had disagreed with that before. + sal_Bool bSuicide = (m_nExternalLockCount==0 && m_bSelfClose); + m_bSelfClose = sal_False; + aReadLock.unlock(); + /* } SAFE */ + // force close and deliver owner ship to source of possible throwed veto exception + // Attention: Because this method isn't designed to throw such exception we must supress + // it for outside code! + try + { + if (bSuicide) + close(sal_True); + } + catch(const css::util::CloseVetoException&) + {} + catch(const css::lang::DisposedException&) + {} +} + +//_______________________________________________________________ + +/** little helper to enable/disable the menu closer at the menubar of the given frame. + + @param xFrame + we use its layout manager to set/reset a special callback. + Its existence regulate visibility of this closer item. + + @param bState + <TRUE/> enable; <FALSE/> disable this state + */ + +void Frame::impl_setCloser( /*IN*/ const css::uno::Reference< css::frame::XFrame >& xFrame , + /*IN*/ sal_Bool bState ) +{ + // Note: If start module isnt installed - no closer has to be shown! + if (!SvtModuleOptions().IsModuleInstalled(SvtModuleOptions::E_SSTARTMODULE)) + return; + + try + { + css::uno::Reference< css::beans::XPropertySet > xFrameProps(xFrame, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::frame::XLayoutManager > xLayoutManager; + xFrameProps->getPropertyValue(FRAME_PROPNAME_LAYOUTMANAGER) >>= xLayoutManager; + css::uno::Reference< css::beans::XPropertySet > xLayoutProps(xLayoutManager, css::uno::UNO_QUERY_THROW); + xLayoutProps->setPropertyValue(LAYOUTMANAGER_PROPNAME_MENUBARCLOSER, css::uno::makeAny(bState)); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + {} +} + +//_______________________________________________________________ + +/** it checks, which of the top level task frames must have the special menu closer for + switching to the backing window mode. + + It analyze the current list of visible top level frames. Only the last real document + frame can have this symbol. Not the help frame nor the backing task itself. + Here we do anything related to this closer. We remove it from the old frame and set it + for the new one. + */ + +void Frame::impl_checkMenuCloser() +{ + /* SAFE { */ + ReadGuard aReadLock(m_aLock); + + // only top frames, which are part of our desktop hierarchy, can + // do so! By the way - we need the desktop instance to have acess + // to all other top level frames too. + css::uno::Reference< css::frame::XDesktop > xDesktop (m_xParent, css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XFramesSupplier > xTaskSupplier(xDesktop , css::uno::UNO_QUERY); + if ( !xDesktop.is() || !xTaskSupplier.is() ) + return; + + aReadLock.unlock(); + /* } SAFE */ + + // analyze the list of current open tasks + // Suppress search for other views to the same model ... + // It's not needed here and can be very expensive. + FrameListAnalyzer aAnalyzer( + xTaskSupplier, + this, + FrameListAnalyzer::E_HIDDEN | FrameListAnalyzer::E_HELP | FrameListAnalyzer::E_BACKINGCOMPONENT); + + // specify the new frame, which must have this special state ... + css::uno::Reference< css::frame::XFrame > xNewCloserFrame; + + // ----------------------------- + // a) + // If there exist ate least one other frame - there are two frames currently open. + // But we can enable this closer only, if one of these two tasks includes the help module. + // The "other frame" couldn't be the help. Because then it wouldn't be part of this "other list". + // In such case it will be seperated to the reference aAnalyzer.m_xHelp! + // But we must check, if weself includes the help ... + // Check aAnalyzer.m_bReferenceIsHelp! + if ( + (aAnalyzer.m_lOtherVisibleFrames.getLength()==1) && + ( + (aAnalyzer.m_bReferenceIsHelp ) || + (aAnalyzer.m_bReferenceIsHidden) + ) + ) + { + // others[0] can't be the backing component! + // Because it's set at the special member aAnalyzer.m_xBackingComponent ... :-) + xNewCloserFrame = aAnalyzer.m_lOtherVisibleFrames[0]; + } + else + // ----------------------------- + // b) + // There is no other frame ... means no other document frame. The help module + // will be handled seperatly and must(!) be ignored here ... excepting weself includes the help. + if ( + (aAnalyzer.m_lOtherVisibleFrames.getLength()==0) && + (!aAnalyzer.m_bReferenceIsHelp ) && + (!aAnalyzer.m_bReferenceIsHidden ) && + (!aAnalyzer.m_bReferenceIsBacking ) + ) + { + xNewCloserFrame = this; + } + + // Look for neccessary actions ... + // Only if the closer state must be moved from one frame to another one + // or must be enabled/disabled at all. + /* STATIC SAFE { */ + WriteGuard aStaticWriteLock(LockHelper::getGlobalLock()); + css::uno::Reference< css::frame::XFrame > xCloserFrame (m_xCloserFrame.get(), css::uno::UNO_QUERY); + if (xCloserFrame!=xNewCloserFrame) + { + if (xCloserFrame.is()) + impl_setCloser(xCloserFrame, sal_False); + if (xNewCloserFrame.is()) + impl_setCloser(xNewCloserFrame, sal_True); + m_xCloserFrame = xNewCloserFrame; + } + aStaticWriteLock.unlock(); + /* } STATIC SAFE */ +} + +//_________________________________________________________________________________________________________________ +// debug methods +//_________________________________________________________________________________________________________________ + +/*----------------------------------------------------------------------------------------------------------------- + The follow methods checks the parameter for other functions. If a parameter or his value is non valid, + we return "sal_True". (otherwise sal_False) This mechanism is used to throw an ASSERT! +-----------------------------------------------------------------------------------------------------------------*/ + +#ifdef ENABLE_ASSERTIONS + +//***************************************************************************************************************** +// We don't accept null pointer or references! +sal_Bool Frame::implcp_ctor( const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) +{ + return ( + ( &xFactory == NULL ) || + ( xFactory.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +// Its allowed to reset the active frame membervariable with a NULL-css::uno::Reference but not with a NULL-pointer! +// And we accept frames only! No tasks and desktops! +sal_Bool Frame::implcp_setActiveFrame( const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + return ( + ( &xFrame == NULL ) || + ( css::uno::Reference< css::frame::XDesktop >( xFrame, css::uno::UNO_QUERY ).is() == sal_True ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_addFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener ) +{ + return ( + ( &xListener == NULL ) || + ( xListener.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_removeFrameActionListener( const css::uno::Reference< css::frame::XFrameActionListener >& xListener ) +{ + return ( + ( &xListener == NULL ) || + ( xListener.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + return ( + ( &xListener == NULL ) || + ( xListener.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) +{ + return ( + ( &xListener == NULL ) || + ( xListener.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_windowResized( const css::awt::WindowEvent& aEvent ) +{ + return ( + ( &aEvent == NULL ) || + ( aEvent.Source.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_focusGained( const css::awt::FocusEvent& aEvent ) +{ + return ( + ( &aEvent == NULL ) || + ( aEvent.Source.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_windowActivated( const css::lang::EventObject& aEvent ) +{ + return ( + ( &aEvent == NULL ) || + ( aEvent.Source.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_windowDeactivated( const css::lang::EventObject& aEvent ) +{ + return ( + ( &aEvent == NULL ) || + ( aEvent.Source.is() == sal_False ) + ); +} + +//***************************************************************************************************************** +sal_Bool Frame::implcp_disposing( const css::lang::EventObject& aEvent ) +{ + return ( + ( &aEvent == NULL ) || + ( aEvent.Source.is() == sal_False ) + ); +} + +#endif // #ifdef ENABLE_ASSERTIONS + +} // namespace framework diff --git a/framework/source/services/fwk_services.src b/framework/source/services/fwk_services.src new file mode 100644 index 000000000000..ef037c75778a --- /dev/null +++ b/framework/source/services/fwk_services.src @@ -0,0 +1,227 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include "framework.hrc" + +Window DLG_BACKING +{ + OutputSize = TRUE ; + SVLook = TRUE ; + Border = FALSE; + + String STR_BACKING_WELCOME + { + Text [ en-US ] = "Welcome to"; + }; + String STR_BACKING_WELCOMEPRODUCT + { + Text [ en-US ] = "%PRODUCTNAME"; + }; + String STR_BACKING_CREATE + { + Text [ en-US ] = "Create a new document"; + }; + String STR_BACKING_TEMPLATE + { + Text [ en-US ] = "~Templates..."; + }; + String STR_BACKING_FILE + { + Text [ en-US ] = "~Open..."; + }; + String STR_BACKING_EXTHELP + { + Text [ en-US ] = "Add new features to %PRODUCTNAME"; + }; + String STR_BACKING_REGHELP + { + Text [ en-US ] = "Register your %PRODUCTNAME"; + }; + String STR_BACKING_INFOHELP + { + Text [ en-US ] = "Get more information about %PRODUCTNAME"; + }; + String STR_BACKING_TPLREP + { + Text [ en-US ] = "Get more templates for %PRODUCTNAME"; + }; +}; + +Resource RES_BACKING_IMAGES +{ + Bitmap BMP_BACKING_BACKGROUND_LEFT + { + File = "backing.png"; + }; + Bitmap BMP_BACKING_BACKGROUND_MIDDLE + { + File = "backing_space.png"; + }; + Bitmap BMP_BACKING_BACKGROUND_RIGHT + { + File = "backing_right.png"; + }; + Bitmap BMP_BACKING_BACKGROUND_RTL_LEFT + { + File = "backing_rtl_left.png"; + }; + Bitmap BMP_BACKING_BACKGROUND_RTL_RIGHT + { + File = "backing_rtl_right.png"; + }; + Bitmap BMP_BACKING_EXT + { + File = "extension.png"; + }; + Bitmap BMP_BACKING_REG + { + File = "register_32.png"; + }; + Bitmap BMP_BACKING_INFO + { + File = "info_26.png"; + }; + Bitmap BMP_BACKING_TPLREP + { + File = "addtemplate_32.png"; + }; + Bitmap BMP_BACKING_WRITER + { + File = "odt_32.png"; + }; + Bitmap BMP_BACKING_CALC + { + File = "ods_32.png"; + }; + Bitmap BMP_BACKING_IMPRESS + { + File = "odp_32.png"; + }; + Bitmap BMP_BACKING_DRAW + { + File = "odg_32.png"; + }; + Bitmap BMP_BACKING_DATABASE + { + File = "odb_32.png"; + }; + Bitmap BMP_BACKING_FORMULA + { + File = "odf_32.png"; + }; + Bitmap BMP_BACKING_OPENFILE + { + File = "folder_32.png"; + }; + Bitmap BMP_BACKING_OPENTEMPLATE + { + File = "templates_32.png"; + }; +}; + +Resource RES_BACKING_IMAGES_HC +{ + Bitmap BMP_BACKING_BACKGROUND_LEFT + { + File = "backing_hc.png"; + }; + Bitmap BMP_BACKING_BACKGROUND_MIDDLE + { + File = "backing_space_hc.png"; + }; + Bitmap BMP_BACKING_BACKGROUND_RIGHT + { + File = "backing_right_hc.png"; + }; + Bitmap BMP_BACKING_BACKGROUND_RTL_LEFT + { + File = "backing_rtl_left_hc.png"; + }; + Bitmap BMP_BACKING_BACKGROUND_RTL_RIGHT + { + File = "backing_rtl_right_hc.png"; + }; + Bitmap BMP_BACKING_EXT + { + File = "extension_hc.png"; + }; + Bitmap BMP_BACKING_REG + { + File = "register_hc.png"; + }; + Bitmap BMP_BACKING_INFO + { + File = "info_hc.png"; + }; + Bitmap BMP_BACKING_TPLREP + { + File = "template_hc.png"; + }; + Bitmap BMP_BACKING_WRITER + { + File = "odt_32_hc.png"; + }; + Bitmap BMP_BACKING_CALC + { + File = "ods_32_hc.png"; + }; + Bitmap BMP_BACKING_IMPRESS + { + File = "odp_32_hc.png"; + }; + Bitmap BMP_BACKING_DRAW + { + File = "odg_32_hc.png"; + }; + Bitmap BMP_BACKING_DATABASE + { + File = "odb_32_hc.png"; + }; + Bitmap BMP_BACKING_FORMULA + { + File = "odf_32_hc.png"; + }; + Bitmap BMP_BACKING_OPENFILE + { + File = "folder_32_hc.png"; + }; + Bitmap BMP_BACKING_OPENTEMPLATE + { + File = "templates_32_hc.png"; + }; +}; + +Window WIN_TABWINDOW +{ + OutputSize = TRUE ; + SVLook = TRUE ; + TabControl TC_TABCONTROL + { + OutputSize = TRUE ; + }; +}; + diff --git a/framework/source/services/license.cxx b/framework/source/services/license.cxx new file mode 100644 index 000000000000..18f3e21bf67d --- /dev/null +++ b/framework/source/services/license.cxx @@ -0,0 +1,631 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_________________________________________________________________________________________________________________ +// my own includes +//_________________________________________________________________________________________________________________ +#include <services/license.hxx> +#include <threadhelp/resetableguard.hxx> +#include <macros/debug.hxx> +#include <services.h> + +// local header for UI implementation +#include "services/licensedlg.hxx" +#include "classes/resource.hrc" + +//_________________________________________________________________________________________________________________ +// interface includes +//_________________________________________________________________________________________________________________ + +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/lang/XComponent.hpp> + + +//_________________________________________________________________________________________________________________ +// includes of other projects +//_________________________________________________________________________________________________________________ + +#include <rtl/ustrbuf.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/ustring.hxx> +#include <rtl/string.hxx> +#include <unotools/bootstrap.hxx> +#include <osl/file.hxx> +#include <svtools/xtextedt.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/processfactory.hxx> +#include <tools/date.hxx> +#include <tools/time.hxx> +#include <tools/datetime.hxx> +#include <osl/file.hxx> +#include <osl/time.h> + +//_________________________________________________________________________________________________________________ +// namespace +//_________________________________________________________________________________________________________________ + +namespace framework{ +using namespace utl; +using namespace ::osl ; +using namespace ::cppu ; +using namespace ::com::sun::star::uno ; +using namespace ::com::sun::star::beans ; +using namespace ::com::sun::star::lang ; +using namespace ::com::sun::star::util ; +using namespace ::com::sun::star::frame ; + +//_________________________________________________________________________________________________________________ +// non exported const +//_________________________________________________________________________________________________________________ + +// license file name +static const char *szLicensePath = "/share/readme"; +#ifdef UNX +static const char *szUNXLicenseName = "/LICENSE"; +static const char *szUNXLicenseExt = ""; +#elif defined(WNT) || defined(OS2) +static const char *szWNTLicenseName = "/license"; +static const char *szWNTLicenseExt = ".txt"; +#endif + +//_________________________________________________________________________________________________________________ +// non exported definitions +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// declarations +//_________________________________________________________________________________________________________________ + +//***************************************************************************************************************** +// constructor +//***************************************************************************************************************** +License::License( const Reference< XMultiServiceFactory >& xFactory ) + // Init baseclasses first + // Attention: + // Don't change order of initialization! + // ThreadHelpBase is a struct with a mutex as member. We can't use a mutex as member, while + // we must garant right initialization and a valid value of this! First initialize + // baseclasses and then members. And we need the mutex for other baseclasses !!! + : ThreadHelpBase ( &Application::GetSolarMutex() ) + , OWeakObject ( ) + // Init member + , m_xFactory ( xFactory ) + , m_bTerminate ( sal_False ) +{ +} + +//***************************************************************************************************************** +// destructor +//***************************************************************************************************************** +License::~License() +{ +} + +//***************************************************************************************************************** +// XInterface, XTypeProvider, XServiceInfo +//***************************************************************************************************************** + +DEFINE_XINTERFACE_4 ( License , + OWeakObject , + DIRECT_INTERFACE(XTypeProvider ), + DIRECT_INTERFACE(XServiceInfo ), + DIRECT_INTERFACE(XJob ), + DIRECT_INTERFACE(XCloseable ) + ) + +DEFINE_XTYPEPROVIDER_4 ( License , + XTypeProvider , + XServiceInfo , + XJob , + XCloseable + ) + +DEFINE_XSERVICEINFO_MULTISERVICE ( License, + OWeakObject , + SERVICENAME_LICENSE , + IMPLEMENTATIONNAME_LICENSE + ) + +DEFINE_INIT_SERVICE ( License, + { + } + ) + + +#if 0 +IMPL_STATIC_LINK_NOINSTANCE( License, Terminate, void*, EMPTYARG ) +{ + /* + Reference< XMultiServiceFactory > xFactory = comphelper::getProcessServiceFactory(); + Reference< XDesktop > xDesktop(xFactory->createInstance( + ::rtl::OUString::createFromAscii("com.sun.star.frame.Desktop")), UNO_QUERY); + if (xDesktop.is()) + xDesktop->terminate(); + */ + /* + _exit(0); + */ + return 0; +} +#endif + +static DateTime _oslDateTimeToDateTime(const oslDateTime& aDateTime) +{ + return DateTime( + Date(aDateTime.Day, aDateTime.Month, aDateTime.Year), + Time(aDateTime.Hours, aDateTime.Minutes, aDateTime.Seconds)); +} + +static ::rtl::OUString _makeDateTimeString (const DateTime& aDateTime, sal_Bool bUTC = sal_False) +{ + ::rtl::OStringBuffer aDateTimeString; + aDateTimeString.append((sal_Int32)aDateTime.GetYear()); + aDateTimeString.append("-"); + if (aDateTime.GetMonth()<10) aDateTimeString.append("0"); + aDateTimeString.append((sal_Int32)aDateTime.GetMonth()); + aDateTimeString.append("-"); + if (aDateTime.GetDay()<10) aDateTimeString.append("0"); + aDateTimeString.append((sal_Int32)aDateTime.GetDay()); + aDateTimeString.append("T"); + if (aDateTime.GetHour()<10) aDateTimeString.append("0"); + aDateTimeString.append((sal_Int32)aDateTime.GetHour()); + aDateTimeString.append(":"); + if (aDateTime.GetMin()<10) aDateTimeString.append("0"); + aDateTimeString.append((sal_Int32)aDateTime.GetMin()); + aDateTimeString.append(":"); + if (aDateTime.GetSec()<10) aDateTimeString.append("0"); + aDateTimeString.append((sal_Int32)aDateTime.GetSec()); + if (bUTC) aDateTimeString.append("Z"); + + return OStringToOUString(aDateTimeString.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US); +} + +static sal_Bool _parseDateTime(const ::rtl::OUString& aString, DateTime& aDateTime) +{ + // take apart a canonical literal xsd:dateTime string + //CCYY-MM-DDThh:mm:ss(Z) + + ::rtl::OUString aDateTimeString = aString.trim(); + + // check length + if (aDateTimeString.getLength() < 19 || aDateTimeString.getLength() > 20) + return sal_False; + + sal_Int32 nDateLength = 10; + sal_Int32 nTimeLength = 8; + + ::rtl::OUString aDateTimeSep = ::rtl::OUString::createFromAscii("T"); + ::rtl::OUString aDateSep = ::rtl::OUString::createFromAscii("-"); + ::rtl::OUString aTimeSep = ::rtl::OUString::createFromAscii(":"); + ::rtl::OUString aUTCString = ::rtl::OUString::createFromAscii("Z"); + + ::rtl::OUString aDateString = aDateTimeString.copy(0, nDateLength); + ::rtl::OUString aTimeString = aDateTimeString.copy(nDateLength+1, nTimeLength); + + sal_Int32 nIndex = 0; + sal_Int32 nYear = aDateString.getToken(0, '-', nIndex).toInt32(); + sal_Int32 nMonth = aDateString.getToken(0, '-', nIndex).toInt32(); + sal_Int32 nDay = aDateString.getToken(0, '-', nIndex).toInt32(); + nIndex = 0; + sal_Int32 nHour = aTimeString.getToken(0, ':', nIndex).toInt32(); + sal_Int32 nMinute = aTimeString.getToken(0, ':', nIndex).toInt32(); + sal_Int32 nSecond = aTimeString.getToken(0, ':', nIndex).toInt32(); + + Date tmpDate((USHORT)nDay, (USHORT)nMonth, (USHORT)nYear); + Time tmpTime(nHour, nMinute, nSecond); + DateTime tmpDateTime(tmpDate, tmpTime); + if (aString.indexOf(aUTCString) < 0) + tmpDateTime.ConvertToUTC(); + + aDateTime = tmpDateTime; + return sal_True; +} + +static ::rtl::OUString _getCurrentDateString() +{ + ::rtl::OUString aString; + return _makeDateTimeString(DateTime()); +} + +// execution of license check... +css::uno::Any SAL_CALL License::execute(const css::uno::Sequence< css::beans::NamedValue >& ) + throw( css::lang::IllegalArgumentException, css::uno::Exception) +{ + // return value + Any aRet; aRet <<= sal_False; + + try + { + ::rtl::OUString aBaseInstallPath; + Bootstrap::PathStatus aBaseLocateResult = + Bootstrap::locateBaseInstallation(aBaseInstallPath); + if (aBaseLocateResult != Bootstrap::PATH_EXISTS) + { + // base install noit found + // prepare termination + // m_bTerminate = sal_True; + // Application::PostUserEvent( STATIC_LINK( 0, License, Terminate ) ); + aRet <<= sal_False; + return aRet; + } + // determine the filename of the license to show + ::rtl::OUString aLangString; + ::com::sun::star::lang::Locale aLocale; + ::rtl::OString aMgrName = ::rtl::OString("fwe"); + AllSettings aSettings(Application::GetSettings()); + aLocale = aSettings.GetUILocale(); + ResMgr* pResMgr = ResMgr::SearchCreateResMgr(aMgrName, aLocale); + + aLangString = aLocale.Language; + if ( aLocale.Country.getLength() != 0 ) + { + aLangString += ::rtl::OUString::createFromAscii("-"); + aLangString += aLocale.Country; + if ( aLocale.Variant.getLength() != 0 ) + { + aLangString += ::rtl::OUString::createFromAscii("-"); + aLangString += aLocale.Variant; + } + } +#if defined(WNT) || defined(OS2) + ::rtl::OUString aLicensePath = + aBaseInstallPath + ::rtl::OUString::createFromAscii(szLicensePath) + + ::rtl::OUString::createFromAscii(szWNTLicenseName) + + ::rtl::OUString::createFromAscii("_") + + aLangString + + ::rtl::OUString::createFromAscii(szWNTLicenseExt); +#else + ::rtl::OUString aLicensePath = + aBaseInstallPath + ::rtl::OUString::createFromAscii(szLicensePath) + + ::rtl::OUString::createFromAscii(szUNXLicenseName) + + ::rtl::OUString::createFromAscii("_") + + aLangString + + ::rtl::OUString::createFromAscii(szUNXLicenseExt); +#endif + // check if we need to show the license at all + // open org.openoffice.Setup/Office/ooLicenseAcceptDate + ::rtl::OUString sConfigSrvc = SERVICENAME_CFGPROVIDER; + ::rtl::OUString sAccessSrvc = ::rtl::OUString::createFromAscii("com.sun.star.configuration.ConfigurationUpdateAccess"); + ::rtl::OUString sReadSrvc = SERVICENAME_CFGREADACCESS; + + // get configuration provider + Reference< XMultiServiceFactory > theConfigProvider = Reference< XMultiServiceFactory >( + m_xFactory->createInstance(sConfigSrvc), UNO_QUERY_THROW); + Sequence< Any > theArgs(1); + NamedValue v; + v.Name = ::rtl::OUString::createFromAscii("NodePath"); + v.Value <<= ::rtl::OUString::createFromAscii("org.openoffice.Setup/Office"); + theArgs[0] <<= v; + Reference< XPropertySet > pset = Reference< XPropertySet >( + theConfigProvider->createInstanceWithArguments(sAccessSrvc, theArgs), UNO_QUERY_THROW); + + // if we find a date there, compare it to baseinstall license date + ::rtl::OUString aAcceptDate; + if (pset->getPropertyValue(::rtl::OUString::createFromAscii("ooLicenseAcceptDate")) >>= aAcceptDate) + { + // get LicenseFileDate from base install + ::rtl::OUString aLicenseURL = aLicensePath; + /* + if (FileBase::getFileURLFromSystemPath(aLicensePath, aLicenseURL) != FileBase::E_None) + return makeAny(sal_False); + */ + DirectoryItem aDirItem; + if (DirectoryItem::get(aLicenseURL, aDirItem) != FileBase::E_None) + return makeAny(sal_False); + FileStatus aStatus(FileStatusMask_All); + if (aDirItem.getFileStatus(aStatus) != FileBase::E_None) + return makeAny(sal_False); + TimeValue aTimeVal = aStatus.getModifyTime(); + oslDateTime aDateTimeVal; + if (!osl_getDateTimeFromTimeValue(&aTimeVal, &aDateTimeVal)) + return makeAny(sal_False); + + // compare dates + DateTime aLicenseDateTime = _oslDateTimeToDateTime(aDateTimeVal); + DateTime aAcceptDateTime; + if (!_parseDateTime(aAcceptDate, aAcceptDateTime)) + return makeAny(sal_False); + + if ( aAcceptDateTime > aLicenseDateTime ) + return makeAny(sal_True); + } + // prepare to show + // display license dialog + LicenseDialog* pDialog = new LicenseDialog(aLicensePath, pResMgr); + sal_Bool bAgreed = (pDialog->Execute() == 1); + delete pDialog; + + if (bAgreed) { + + // write org.openoffice.Setup/ooLicenseAcceptDate + aAcceptDate = _getCurrentDateString(); + pset->setPropertyValue(::rtl::OUString::createFromAscii("ooLicenseAcceptDate"), makeAny(aAcceptDate)); + Reference< XChangesBatch >(pset, UNO_QUERY_THROW)->commitChanges(); + + // enable quickstarter + sal_Bool bQuickstart( sal_True ); + sal_Bool bAutostart( sal_True ); + Sequence< Any > aSeq( 2 ); + aSeq[0] <<= bQuickstart; + aSeq[1] <<= bAutostart; + + Reference < XInitialization > xQuickstart( ::comphelper::getProcessServiceFactory()->createInstance( + ::rtl::OUString::createFromAscii( "com.sun.star.office.Quickstart" )),UNO_QUERY ); + if ( xQuickstart.is() ) + xQuickstart->initialize( aSeq ); + + aRet <<= sal_True; + } + else + { + // license was not accepted + // prepare termination + // m_bTerminate = sal_True; + // Application::PostUserEvent( STATIC_LINK( 0, License, Terminate ) ); + aRet <<= sal_False; + } + } + catch (RuntimeException&) + { + // license could not be verified + aRet <<= sal_False; + } + return aRet; +} + +void SAL_CALL License::close(sal_Bool /*bDeliverOwnership*/) throw (css::util::CloseVetoException) +{ + if (!m_bTerminate) + throw CloseVetoException(); +} +void SAL_CALL License::addCloseListener(const css::uno::Reference< css::util::XCloseListener >&) + throw (css::uno::RuntimeException) +{ +} +void SAL_CALL License::removeCloseListener(const css::uno::Reference< css::util::XCloseListener >&) + throw (css::uno::RuntimeException) +{ +} + + +//************************************************************************ +// License Dialog +//************************************************************************ + +LicenseDialog::LicenseDialog(const ::rtl::OUString & aLicensePath, ResMgr *pResMgr) : + ModalDialog(NULL, ResId(DLG_LICENSE, *pResMgr)), + aLicenseML(this, ResId(ML_LICENSE, *pResMgr)), + aInfo1FT(this, ResId(FT_INFO1, *pResMgr)), + aInfo2FT(this, ResId(FT_INFO2, *pResMgr)), + aInfo3FT(this, ResId(FT_INFO3, *pResMgr)), + aInfo2_1FT(this, ResId(FT_INFO2_1, *pResMgr)), + aInfo3_1FT(this, ResId(FT_INFO3_1, *pResMgr)), + aFixedLine(this, ResId(FL_DIVIDE, *pResMgr)), + aPBPageDown(this, ResId(PB_PAGEDOWN, *pResMgr)), + aPBDecline( this, ResId(PB_DECLINE, *pResMgr) ), + aPBAccept( this, ResId(PB_ACCEPT, *pResMgr) ), + aArrow(this, ResId(IMG_ARROW, *pResMgr)), + aStrAccept( ResId(LICENSE_ACCEPT, *pResMgr) ), + aStrNotAccept( ResId(LICENSE_NOTACCEPT, *pResMgr) ), + bEndReached(FALSE) +{ + FreeResource(); + + aLicenseML.SetEndReachedHdl( LINK(this, LicenseDialog, EndReachedHdl) ); + aLicenseML.SetScrolledHdl( LINK(this, LicenseDialog, ScrolledHdl) ); + + aPBPageDown.SetClickHdl( LINK(this, LicenseDialog, PageDownHdl) ); + aPBDecline.SetClickHdl( LINK(this, LicenseDialog, DeclineBtnHdl) ); + aPBAccept.SetClickHdl( LINK(this, LicenseDialog, AcceptBtnHdl) ); + + // We want a automatic repeating page down button + WinBits aStyle = aPBPageDown.GetStyle(); + aStyle |= WB_REPEAT; + aPBPageDown.SetStyle( aStyle ); + + String aText = aInfo2FT.GetText(); + aText.SearchAndReplaceAll( UniString::CreateFromAscii("%PAGEDOWN"), aPBPageDown.GetText() ); + aInfo2FT.SetText( aText ); + + aPBDecline.SetText( aStrNotAccept ); + aPBAccept.SetText( aStrAccept ); + + aPBAccept.Disable(); + + // load license text + File aLicenseFile(aLicensePath); + if ( aLicenseFile.open(OpenFlag_Read) == FileBase::E_None) + { + DirectoryItem d; + DirectoryItem::get(aLicensePath, d); + FileStatus fs(FileStatusMask_FileSize); + d.getFileStatus(fs); + sal_uInt64 nBytesRead = 0; + sal_uInt64 nPosition = 0; + sal_uInt32 nBytes = (sal_uInt32)fs.getFileSize(); + sal_Char *pBuffer = new sal_Char[nBytes]; + // FileBase RC r = FileBase::E_None; + while (aLicenseFile.read(pBuffer+nPosition, nBytes-nPosition, nBytesRead) == FileBase::E_None + && nPosition + nBytesRead < nBytes) + { + nPosition += nBytesRead; + } + ::rtl::OUString aLicenseString(pBuffer, nBytes, RTL_TEXTENCODING_UTF8, + OSTRING_TO_OUSTRING_CVTFLAGS | RTL_TEXTTOUNICODE_FLAGS_GLOBAL_SIGNATURE); + delete[] pBuffer; + aLicenseML.SetText(aLicenseString); + } + +} + +LicenseDialog::~LicenseDialog() +{ +} + +IMPL_LINK( LicenseDialog, PageDownHdl, PushButton *, EMPTYARG ) +{ + aLicenseML.ScrollDown( SCROLL_PAGEDOWN ); + return 0; +} + +IMPL_LINK( LicenseDialog, EndReachedHdl, LicenseView *, EMPTYARG ) +{ + bEndReached = TRUE; + + EnableControls(); + + return 0; +} + +IMPL_LINK( LicenseDialog, ScrolledHdl, LicenseView *, EMPTYARG ) +{ + EnableControls(); + + return 0; +} + +IMPL_LINK( LicenseDialog, DeclineBtnHdl, PushButton *, EMPTYARG ) +{ + EndDialog(0); + return 0; +} +IMPL_LINK( LicenseDialog, AcceptBtnHdl, PushButton *, EMPTYARG ) +{ + EndDialog(1); + return 0; +} + + +void LicenseDialog::EnableControls() +{ + if( !bEndReached && + ( aLicenseML.IsEndReached() || !aLicenseML.GetText().Len() ) ) + bEndReached = TRUE; + + if ( bEndReached ) + { + Point aPos( aInfo1FT.GetPosPixel().X(), + aInfo3_1FT.GetPosPixel().Y() ); + aArrow.SetPosPixel( aPos ); + aPBAccept.Enable(); + } + else + { + Point aPos( aInfo1FT.GetPosPixel().X(), + aInfo2_1FT.GetPosPixel().Y() ); + aArrow.SetPosPixel( aPos ); + aPBAccept.Disable(); + } + + if ( aLicenseML.IsEndReached() ) + aPBPageDown.Disable(); + else + aPBPageDown.Enable(); + +} + + +LicenseView::LicenseView( Window* pParent, const ResId& rResId ) + : MultiLineEdit( pParent, rResId ) +{ + SetLeftMargin( 5 ); + + mbEndReached = IsEndReached(); + + StartListening( *GetTextEngine() ); +} + +LicenseView::~LicenseView() +{ + maEndReachedHdl = Link(); + maScrolledHdl = Link(); + + EndListeningAll(); +} + +void LicenseView::ScrollDown( ScrollType eScroll ) +{ + ScrollBar* pScroll = GetVScrollBar(); + + if ( pScroll ) + pScroll->DoScrollAction( eScroll ); +} + +BOOL LicenseView::IsEndReached() const +{ + BOOL bEndReached; + + ExtTextView* pView = GetTextView(); + ExtTextEngine* pEdit = GetTextEngine(); + ULONG nHeight = pEdit->GetTextHeight(); + Size aOutSize = pView->GetWindow()->GetOutputSizePixel(); + Point aBottom( 0, aOutSize.Height() ); + + if ( (ULONG) pView->GetDocPos( aBottom ).Y() >= nHeight - 1 ) + bEndReached = TRUE; + else + bEndReached = FALSE; + + return bEndReached; +} + +void LicenseView::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.IsA( TYPE(TextHint) ) ) + { + BOOL bLastVal = EndReached(); + ULONG nId = ((const TextHint&)rHint).GetId(); + + if ( nId == TEXT_HINT_PARAINSERTED ) + { + if ( bLastVal ) + mbEndReached = IsEndReached(); + } + else if ( nId == TEXT_HINT_VIEWSCROLLED ) + { + if ( ! mbEndReached ) + mbEndReached = IsEndReached(); + maScrolledHdl.Call( this ); + } + + if ( EndReached() && !bLastVal ) + { + maEndReachedHdl.Call( this ); + } + } +} + +} // namespace framework + diff --git a/framework/source/services/makefile.mk b/framework/source/services/makefile.mk new file mode 100644 index 000000000000..2978d9aacf5e --- /dev/null +++ b/framework/source/services/makefile.mk @@ -0,0 +1,64 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* +PRJ=..$/.. + +PRJNAME= framework +TARGET= fwk_services +USE_DEFFILE= TRUE +ENABLE_EXCEPTIONS= TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE : settings.mk + +# --- Generate ----------------------------------------------------- + +SLOFILES=\ + $(SLO)$/desktop.obj \ + $(SLO)$/frame.obj \ + $(SLO)$/urltransformer.obj \ + $(SLO)$/mediatypedetectionhelper.obj \ + $(SLO)$/substitutepathvars.obj \ + $(SLO)$/pathsettings.obj \ + $(SLO)$/backingcomp.obj \ + $(SLO)$/backingwindow.obj \ + $(SLO)$/dispatchhelper.obj \ + $(SLO)$/license.obj \ + $(SLO)$/modulemanager.obj \ + $(SLO)$/autorecovery.obj \ + $(SLO)$/sessionlistener.obj \ + $(SLO)$/taskcreatorsrv.obj \ + $(SLO)$/uriabbreviation.obj \ + $(SLO)$/tabwindowservice.obj + +SRS1NAME=$(TARGET) +SRC1FILES= fwk_services.src + +# --- Targets ------------------------------------------------------ + +.INCLUDE : target.mk + diff --git a/framework/source/services/mediatypedetectionhelper.cxx b/framework/source/services/mediatypedetectionhelper.cxx new file mode 100644 index 000000000000..1033cbda7913 --- /dev/null +++ b/framework/source/services/mediatypedetectionhelper.cxx @@ -0,0 +1,111 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_________________________________________________________________________________________________________________ +// my own includes +//_________________________________________________________________________________________________________________ +#include <services/mediatypedetectionhelper.hxx> +#include <services.h> +#include <svl/inettype.hxx> +#include <tools/string.hxx> +#include <rtl/logfile.hxx> + +//_________________________________________________________________________________________________________________ +// interface includes +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// namespace +//_________________________________________________________________________________________________________________ + +namespace framework +{ + +using namespace ::com::sun::star ; +using namespace ::rtl ; + +//_________________________________________________________________________________________________________________ +// declarations +//_________________________________________________________________________________________________________________ + +//***************************************************************************************************************** +// constructor +//***************************************************************************************************************** +MediaTypeDetectionHelper::MediaTypeDetectionHelper( const uno::Reference< lang::XMultiServiceFactory >& xFactory ) + : m_xFactory( xFactory ) +{ +} + +//***************************************************************************************************************** +// destructor +//***************************************************************************************************************** +MediaTypeDetectionHelper::~MediaTypeDetectionHelper() +{ +} + +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE ( MediaTypeDetectionHelper + , ::cppu::OWeakObject + , SERVICENAME_MEDIATYPEDETECTIONHELPER + , IMPLEMENTATIONNAME_MEDIATYPEDETECTIONHELPER + ) + +DEFINE_INIT_SERVICE ( MediaTypeDetectionHelper, + { + } + ) + +//***************************************************************************************************************** +// XStringMapping +//***************************************************************************************************************** + +//virtual +sal_Bool SAL_CALL MediaTypeDetectionHelper::mapStrings( + uno::Sequence< OUString >& rSeq ) + throw(uno::RuntimeException) +{ + sal_Bool bModified = sal_False; + for( sal_Int32 i = rSeq.getLength(); i--; ) + { + + OUString& rUrl = rSeq[i]; + INetContentType eType = INetContentTypes::GetContentTypeFromURL( rUrl ); + + UniString aType( INetContentTypes::GetContentType( eType ) ); + if( aType.Len() ) + { + rUrl = aType; + bModified = sal_True; + } + } + return bModified; +} + +} // namespace framework + diff --git a/framework/source/services/menudocumenthandler.cxx b/framework/source/services/menudocumenthandler.cxx new file mode 100644 index 000000000000..8741fff39320 --- /dev/null +++ b/framework/source/services/menudocumenthandler.cxx @@ -0,0 +1,903 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +#include <stdio.h> +#include <services/menudocumenthandler.hxx> +#include <classes/menuconfiguration.hxx> +#include <classes/addonmenu.hxx> + +#ifndef __FRAMEWORK_SERVICES_ATTRIBUTELIST_HXX_ +#include <services/attributelist.hxx> +#endif + +#ifndef __COM_SUN_STAR_XML_SAX_XEXTENDEDDOCUMENTHANDLER_HPP_ +#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp> +#endif + + +using namespace ::rtl; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::xml::sax; + +const int ITEMID_START_VALUE = 1000; + +#define XMLNS_MENU "http://openoffice.org/2001/menu" +#define XMLNS_PREFIX "menu:" + +#define ELEMENT_MENUBAR "http://openoffice.org/2001/menu^menubar" +#define ELEMENT_MENU "http://openoffice.org/2001/menu^menu" +#define ELEMENT_MENUPOPUP "http://openoffice.org/2001/menu^menupopup" +#define ELEMENT_MENUITEM "http://openoffice.org/2001/menu^menuitem" +#define ELEMENT_MENUSEPARATOR "http://openoffice.org/2001/menu^menuseparator" + +#define ELEMENT_NS_MENUBAR "menu:menubar" +#define ELEMENT_NS_MENU "menu:menu" +#define ELEMENT_NS_MENUPOPUP "menu:menupopup" +#define ELEMENT_NS_MENUITEM "menu:menuitem" +#define ELEMENT_NS_MENUSEPARATOR "menu:menuseparator" + +#define ATTRIBUTE_ID "http://openoffice.org/2001/menu^id" +#define ATTRIBUTE_LABEL "http://openoffice.org/2001/menu^label" +#define ATTRIBUTE_HELPID "http://openoffice.org/2001/menu^helpid" +#define ATTRIBUTE_LINEBREAK "http://openoffice.org/2001/menu^linebreak" + +#define ATTRIBUTE_NS_ID "menu:id" +#define ATTRIBUTE_NS_LABEL "menu:label" +#define ATTRIBUTE_NS_HELPID "menu:helpid" +#define ATTRIBUTE_NS_LINEBREAK "menu:linebreak" + +#define ATTRIBUTE_XMLNS_MENU "xmlns:menu" + +#define ATTRIBUTE_TYPE_CDATA "CDATA" + +#define MENUBAR_DOCTYPE "<!DOCTYPE menu:menubar PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"menubar.dtd\">" + + +// special popup menus (filled during runtime) must be saved as a menuitem!!! +// same as in menumanager.cxx - you have to change both files!!! +#define SID_SFX_START 5000 +#define SID_NEWDOCDIRECT (SID_SFX_START + 537) +#define SID_AUTOPILOTMENU (SID_SFX_START + 1381) +#define SID_FORMATMENU (SID_SFX_START + 780) + +const ::rtl::OUString aSlotProtocol( RTL_CONSTASCII_USTRINGPARAM( "slot:" )); +const ::rtl::OUString aSlotNewDocDirect( RTL_CONSTASCII_USTRINGPARAM( "slot:5537" )); +const ::rtl::OUString aSlotAutoPilot( RTL_CONSTASCII_USTRINGPARAM( "slot:6381" )); + +const ::rtl::OUString aSpecialFileMenu( RTL_CONSTASCII_USTRINGPARAM( "file" )); +const ::rtl::OUString aSpecialWindowMenu( RTL_CONSTASCII_USTRINGPARAM( "window" )); + +const ULONG MENU_SAVE_LABEL = 0x00000001; + +namespace framework +{ + +// ----------------------------------------------------------------------------- +// Base class implementation + +ReadMenuDocumentHandlerBase::ReadMenuDocumentHandlerBase() : + m_xLocator( 0 ), + m_xReader( 0 ) +{ +} + +ReadMenuDocumentHandlerBase::~ReadMenuDocumentHandlerBase() +{ +} + +Any SAL_CALL ReadMenuDocumentHandlerBase::queryInterface( + const Type & rType ) +throw( RuntimeException ) +{ + Any a = ::cppu::queryInterface( + rType , + SAL_STATIC_CAST( XDocumentHandler*, this )); + if ( a.hasValue() ) + return a; + + return OWeakObject::queryInterface( rType ); +} + +void SAL_CALL ReadMenuDocumentHandlerBase::ignorableWhitespace( + const OUString& aWhitespaces ) +throw( SAXException, RuntimeException ) +{ +} + +void SAL_CALL ReadMenuDocumentHandlerBase::processingInstruction( + const OUString& aTarget, const OUString& aData ) +throw( SAXException, RuntimeException ) +{ +} + +void SAL_CALL ReadMenuDocumentHandlerBase::setDocumentLocator( + const Reference< XLocator > &xLocator) +throw( SAXException, RuntimeException ) +{ + m_xLocator = xLocator; +} + +::rtl::OUString ReadMenuDocumentHandlerBase::getErrorLineString() +{ + char buffer[32]; + + if ( m_xLocator.is() ) + { + snprintf( buffer, sizeof(buffer), "Line: %ld - ", static_cast<long>( m_xLocator->getLineNumber() )); + return OUString::createFromAscii( buffer ); + } + else + return OUString(); +} + +// ----------------------------------------------------------------------------- + +// #110897# +OReadMenuDocumentHandler::OReadMenuDocumentHandler( + const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xServiceFactory, + MenuBar* pMenuBar ) +: // #110897# + mxServiceFactory(xServiceFactory), + m_pMenuBar( pMenuBar ), + m_nElementDepth( 0 ), + m_bMenuBarMode( sal_False ), + m_nItemId( ITEMID_START_VALUE ) +{ +} + +// #110897# +const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& OReadMenuDocumentHandler::getServiceFactory() +{ + // #110897# + return mxServiceFactory; +} + +OReadMenuDocumentHandler::~OReadMenuDocumentHandler() +{ +} + + +void SAL_CALL OReadMenuDocumentHandler::startDocument(void) + throw ( SAXException, RuntimeException ) +{ +} + + +void SAL_CALL OReadMenuDocumentHandler::endDocument(void) + throw( SAXException, RuntimeException ) +{ + if ( m_nElementDepth > 0 ) + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "A closing element is missing!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + + +void SAL_CALL OReadMenuDocumentHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttrList ) +throw( SAXException, RuntimeException ) +{ + if ( m_bMenuBarMode ) + { + ++m_nElementDepth; + m_xReader->startElement( aName, xAttrList ); + } + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUBAR ))) + { + ++m_nElementDepth; + m_bMenuBarMode = sal_True; + + // #110897# m_xReader = Reference< XDocumentHandler >( new OReadMenuBarHandler( m_pMenuBar, &m_nItemId )); + m_xReader = Reference< XDocumentHandler >( new OReadMenuBarHandler( getServiceFactory(), m_pMenuBar, &m_nItemId )); + + m_xReader->startDocument(); + } +} + + +void SAL_CALL OReadMenuDocumentHandler::characters(const rtl::OUString& aChars) +throw( SAXException, RuntimeException ) +{ +} + + +void SAL_CALL OReadMenuDocumentHandler::endElement( const OUString& aName ) + throw( SAXException, RuntimeException ) +{ + if ( m_bMenuBarMode ) + { + --m_nElementDepth; + m_xReader->endElement( aName ); + if ( 0 == m_nElementDepth ) + { + m_xReader->endDocument(); + m_xReader = Reference< XDocumentHandler >(); + m_bMenuBarMode = sal_False; + if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUBAR ))) + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menubar expected!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + } +} + + +// ----------------------------------------------------------------------------- + + +// #110897# +OReadMenuBarHandler::OReadMenuBarHandler( + const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& xServiceFactory, + MenuBar* pMenuBar, USHORT* pItemId ) +: // #110897# + mxServiceFactory( xServiceFactory ), + m_pMenuBar( pMenuBar ), + m_nElementDepth( 0 ), + m_bMenuMode( sal_False ), + m_pItemId( pItemId ) +{ +} + +// #110897# +const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& OReadMenuBarHandler::getServiceFactory() +{ + // #110897# + return mxServiceFactory; +} + +OReadMenuBarHandler::~OReadMenuBarHandler() +{ +} + + +void SAL_CALL OReadMenuBarHandler::startDocument(void) + throw ( SAXException, RuntimeException ) +{ +} + + +void SAL_CALL OReadMenuBarHandler::endDocument(void) + throw( SAXException, RuntimeException ) +{ +} + + +void SAL_CALL OReadMenuBarHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttrList ) +throw( SAXException, RuntimeException ) +{ + if ( m_bMenuMode ) + { + ++m_nElementDepth; + m_xReader->startElement( aName, xAttrList ); + } + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENU ))) + { + ++m_nElementDepth; + + ULONG nHelpId = 0; + OUString aCommandId; + OUString aLabel; + + m_bMenuMode = sal_True; + PopupMenu* pMenu = new PopupMenu(); + + // read attributes for menu + for ( int i=0; i< xAttrList->getLength(); i++ ) + { + OUString aName = xAttrList->getNameByIndex( i ); + OUString aValue = xAttrList->getValueByIndex( i ); + if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ID ))) + aCommandId = aValue; + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_LABEL ))) + aLabel = aValue; + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_HELPID ))) + nHelpId = aValue.toInt32(); + } + + if ( aCommandId.getLength() > 0 ) + { + USHORT nItemId; + if ( aCommandId.compareTo( aSlotProtocol, aSlotProtocol.getLength() ) == 0 ) + nItemId = (USHORT) aCommandId.copy( aSlotProtocol.getLength() ).toInt32(); + else + nItemId = ++(*m_pItemId); + + m_pMenuBar->InsertItem( nItemId, String() ); + m_pMenuBar->SetPopupMenu( nItemId, pMenu ); + m_pMenuBar->SetItemCommand( nItemId, aCommandId ); + if ( nHelpId > 0 ) + m_pMenuBar->SetHelpId( nItemId, nHelpId ); + if ( aLabel.getLength() > 0 ) + { + m_pMenuBar->SetItemText( nItemId, aLabel ); + m_pMenuBar->SetUserValue( nItemId, MENU_SAVE_LABEL ); + } + else + { + m_pMenuBar->SetUserValue( nItemId, 0 ); + } + } + else + { + delete pMenu; + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "attribute id for element menu required!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_xReader = Reference< XDocumentHandler >( new OReadMenuHandler( pMenu, m_pItemId )); + m_xReader->startDocument(); + } + else + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "element menu expected!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + + +void SAL_CALL OReadMenuBarHandler::characters(const rtl::OUString& aChars) +throw( SAXException, RuntimeException ) +{ +} + + +void OReadMenuBarHandler::endElement( const OUString& aName ) + throw( SAXException, RuntimeException ) +{ + if ( m_bMenuMode ) + { + --m_nElementDepth; + if ( 0 == m_nElementDepth ) + { + m_xReader->endDocument(); + m_xReader = Reference< XDocumentHandler >(); + m_bMenuMode = sal_False; + if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENU ))) + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menu expected!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + else + m_xReader->endElement( aName ); + } +} + + +// ----------------------------------------------------------------------------- + + +OReadMenuHandler::OReadMenuHandler( Menu* pMenu, USHORT* pItemId ) : + m_pMenu( pMenu ), + m_nElementDepth( 0 ), + m_bMenuPopupMode( sal_False ), + m_pItemId( pItemId ) +{ +} + + +OReadMenuHandler::~OReadMenuHandler() +{ +} + + +void SAL_CALL OReadMenuHandler::startDocument(void) + throw ( SAXException, RuntimeException ) +{ +} + + +void SAL_CALL OReadMenuHandler::endDocument(void) + throw( SAXException, RuntimeException) +{ +} + + +void SAL_CALL OReadMenuHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttrList ) +throw( SAXException, RuntimeException ) +{ + if ( m_bMenuPopupMode ) + { + ++m_nElementDepth; + m_xReader->startElement( aName, xAttrList ); + } + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUPOPUP ))) + { + ++m_nElementDepth; + m_bMenuPopupMode = sal_True; + m_xReader = Reference< XDocumentHandler >( new OReadMenuPopupHandler( m_pMenu, m_pItemId )); + m_xReader->startDocument(); + } + else + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "unknown element found!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + + +void SAL_CALL OReadMenuHandler::characters(const rtl::OUString& aChars) +throw( SAXException, RuntimeException ) +{ +} + + +void SAL_CALL OReadMenuHandler::endElement( const OUString& aName ) + throw( SAXException, RuntimeException ) +{ + if ( m_bMenuPopupMode ) + { + --m_nElementDepth; + if ( 0 == m_nElementDepth ) + { + m_xReader->endDocument(); + m_xReader = Reference< XDocumentHandler >(); + m_bMenuPopupMode = sal_False; + if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUPOPUP ))) + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menupopup expected!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + else + m_xReader->endElement( aName ); + } +} + + +// ----------------------------------------------------------------------------- + + +OReadMenuPopupHandler::OReadMenuPopupHandler( Menu* pMenu, USHORT* pItemId ) : + m_pMenu( pMenu ), + m_nElementDepth( 0 ), + m_bMenuMode( sal_False ), + m_pItemId( pItemId ), + m_nNextElementExpected( ELEM_CLOSE_NONE ) +{ +} + + +OReadMenuPopupHandler::~OReadMenuPopupHandler() +{ +} + + +void SAL_CALL OReadMenuPopupHandler::startDocument(void) + throw ( SAXException, RuntimeException ) +{ +} + + +void SAL_CALL OReadMenuPopupHandler::endDocument(void) + throw( SAXException, RuntimeException) +{ +} + + +void SAL_CALL OReadMenuPopupHandler::startElement( + const OUString& aName, const Reference< XAttributeList > &xAttrList ) +throw( SAXException, RuntimeException ) +{ + ++m_nElementDepth; + + if ( m_bMenuMode ) + m_xReader->startElement( aName, xAttrList ); + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENU ))) + { + ULONG nHelpId = 0; + OUString aCommandId; + OUString aLabel; + + m_bMenuMode = sal_True; + PopupMenu* pMenu = new PopupMenu(); + + // read attributes for menu + for ( int i=0; i< xAttrList->getLength(); i++ ) + { + OUString aName = xAttrList->getNameByIndex( i ); + OUString aValue = xAttrList->getValueByIndex( i ); + if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ID ))) + aCommandId = aValue; + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_LABEL ))) + aLabel = aValue; + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_HELPID ))) + nHelpId = aValue.toInt32(); + } + + if ( aCommandId.getLength() > 0 ) + { + USHORT nItemId; + if ( aCommandId.compareTo( aSlotProtocol, aSlotProtocol.getLength() ) == 0 ) + nItemId = (USHORT) aCommandId.copy( aSlotProtocol.getLength() ).toInt32(); + else + nItemId = ++(*m_pItemId); + + m_pMenu->InsertItem( nItemId, String() ); + m_pMenu->SetPopupMenu( nItemId, pMenu ); + m_pMenu->SetItemCommand( nItemId, aCommandId ); + if ( nHelpId > 0 ) + m_pMenu->SetHelpId( nItemId, nHelpId ); + if ( aLabel.getLength() > 0 ) + { + m_pMenu->SetItemText( nItemId, aLabel ); + m_pMenu->SetUserValue( nItemId, MENU_SAVE_LABEL ); + } + else + { + m_pMenu->SetUserValue( nItemId, 0 ); + } + } + else + { + delete pMenu; + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "attribute id for element menu required!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + + m_xReader = Reference< XDocumentHandler >( new OReadMenuHandler( pMenu, m_pItemId )); + m_xReader->startDocument(); + } + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUITEM ))) + { + ULONG nHelpId = 0; + OUString aCommandId; + OUString aLabel; + + // read attributes for menu item + for ( int i=0; i< xAttrList->getLength(); i++ ) + { + OUString aName = xAttrList->getNameByIndex( i ); + OUString aValue = xAttrList->getValueByIndex( i ); + if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_ID ))) + aCommandId = aValue; + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_LABEL ))) + aLabel = aValue; + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ATTRIBUTE_HELPID ))) + nHelpId = aValue.toInt32(); + } + + if ( aCommandId.getLength() > 0 ) + { + USHORT nItemId; + if ( aCommandId.compareTo( aSlotProtocol, aSlotProtocol.getLength() ) == 0 ) + nItemId = (USHORT) aCommandId.copy( aSlotProtocol.getLength() ).toInt32(); + else + nItemId = ++(*m_pItemId); + + m_pMenu->InsertItem( nItemId, String() ); + m_pMenu->SetItemCommand( nItemId, aCommandId ); + if ( nHelpId > 0 ) + m_pMenu->SetHelpId( nItemId, nHelpId ); + if ( aLabel.getLength() > 0 ) + { + m_pMenu->SetItemText( nItemId, aLabel ); + m_pMenu->SetUserValue( nItemId, MENU_SAVE_LABEL ); + } + else + { + m_pMenu->SetUserValue( nItemId, 0 ); + } + } + + m_nNextElementExpected = ELEM_CLOSE_MENUITEM; + } + else if ( aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUSEPARATOR ))) + { + m_pMenu->InsertSeparator(); + m_nNextElementExpected = ELEM_CLOSE_MENUSEPARATOR; + } + else + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "unknown element found!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } +} + + +void SAL_CALL OReadMenuPopupHandler::characters(const rtl::OUString& aChars) +throw( SAXException, RuntimeException ) +{ +} + + +void SAL_CALL OReadMenuPopupHandler::endElement( const OUString& aName ) + throw( SAXException, RuntimeException ) +{ + --m_nElementDepth; + if ( m_bMenuMode ) + { + if ( 0 == m_nElementDepth ) + { + m_xReader->endDocument(); + m_xReader = Reference< XDocumentHandler >(); + m_bMenuMode = sal_False; + if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENU ))) + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menu expected!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + else + m_xReader->endElement( aName ); + } + else + { + if ( m_nNextElementExpected == ELEM_CLOSE_MENUITEM ) + { + if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUITEM ))) + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menuitem expected!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + else if ( m_nNextElementExpected == ELEM_CLOSE_MENUSEPARATOR ) + { + if ( !aName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( ELEMENT_MENUSEPARATOR ))) + { + OUString aErrorMessage = getErrorLineString(); + aErrorMessage += OUString( RTL_CONSTASCII_USTRINGPARAM( "closing element menuseparator expected!" )); + throw SAXException( aErrorMessage, Reference< XInterface >(), Any() ); + } + } + + m_nNextElementExpected = ELEM_CLOSE_NONE; + } +} + + +// --------------------------------- Write XML --------------------------------- + + +OWriteMenuDocumentHandler::OWriteMenuDocumentHandler( MenuBar* pMenu, Reference< XDocumentHandler > rxWriteDocumentHandler ) : + m_pMenuBar( pMenu ), + m_xWriteDocumentHandler( rxWriteDocumentHandler ) +{ + m_xEmptyList = Reference< XAttributeList >( (XAttributeList *)new AttributeListImpl, UNO_QUERY ); + m_aAttributeType = OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_TYPE_CDATA )); +} + + +OWriteMenuDocumentHandler::~OWriteMenuDocumentHandler() +{ +} + + +void OWriteMenuDocumentHandler::WriteMenuDocument() +throw ( SAXException, RuntimeException ) +{ + AttributeListImpl* pList = new AttributeListImpl; + Reference< XAttributeList > rList( (XAttributeList *) pList , UNO_QUERY ); + + m_xWriteDocumentHandler->startDocument(); + + // write DOCTYPE line! + Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY ); + if ( xExtendedDocHandler.is() ) + { + xExtendedDocHandler->unknown( OUString( RTL_CONSTASCII_USTRINGPARAM( MENUBAR_DOCTYPE )) ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + } + + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_XMLNS_MENU )), + m_aAttributeType, + OUString( RTL_CONSTASCII_USTRINGPARAM( XMLNS_MENU )) ); + + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_ID )), + m_aAttributeType, + OUString( RTL_CONSTASCII_USTRINGPARAM( "menubar" )) ); + + m_xWriteDocumentHandler->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUBAR )), pList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + WriteMenu( m_pMenuBar ); + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUBAR )) ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endDocument(); +} + + +void OWriteMenuDocumentHandler::WriteMenu( Menu* pMenu ) +throw ( SAXException, RuntimeException ) +{ + USHORT nItemCount = pMenu->GetItemCount(); + BOOL bSeparator = FALSE; + + for ( USHORT nItemPos = 0; nItemPos < nItemCount; nItemPos++ ) + { + USHORT nItemId = pMenu->GetItemId( nItemPos ); + + PopupMenu* pPopupMenu = pMenu->GetPopupMenu( nItemId ); + if ( pPopupMenu ) + { + OUString aItemCommand = pMenu->GetItemCommand( nItemId ); + + if ( nItemId == SID_NEWDOCDIRECT || + nItemId == SID_AUTOPILOTMENU ) + { + // special popup menus (filled during runtime) must be saved as a menuitem!!! + WriteMenuItem( pMenu, nItemId ); + bSeparator = FALSE; + } + else if ( nItemId == SID_FORMATMENU ) + { + // special popup menu - must be written as empty popup! + AttributeListImpl* pListMenu = new AttributeListImpl; + Reference< XAttributeList > xListMenu( (XAttributeList *)pListMenu , UNO_QUERY ); + + String aCommand( pMenu->GetItemCommand( nItemId ) ); + if ( !aCommand.Len() ) + { + aCommand = String::CreateFromAscii("slot:"); + aCommand += String::CreateFromInt32( nItemId ); + } + + pListMenu->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_ID )), + m_aAttributeType, + aCommand ); + +// if ( pMenu->GetUserValue( nItemId ) & MENU_SAVE_LABEL ) + pListMenu->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_LABEL )), + m_aAttributeType, + pMenu->GetItemText( nItemId ) ); + + m_xWriteDocumentHandler->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENU )), xListMenu ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUPOPUP )), m_xEmptyList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUPOPUP )) ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENU )) ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + bSeparator = FALSE; + } + else if ( !AddonPopupMenu::IsCommandURLPrefix ( aItemCommand )) + { + AttributeListImpl* pListMenu = new AttributeListImpl; + Reference< XAttributeList > xListMenu( (XAttributeList *)pListMenu , UNO_QUERY ); + + String aCommand( pMenu->GetItemCommand( nItemId ) ); + if ( !aCommand.Len() ) + { + aCommand = String::CreateFromAscii("slot:"); + aCommand += String::CreateFromInt32( nItemId ); + } + + pListMenu->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_ID )), + m_aAttributeType, + aCommand ); + +// if ( pMenu->GetUserValue( nItemId ) & MENU_SAVE_LABEL ) + pListMenu->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_LABEL )), + m_aAttributeType, + pMenu->GetItemText( nItemId ) ); + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENU )), xListMenu ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUPOPUP )), m_xEmptyList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + + WriteMenu( pPopupMenu ); + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUPOPUP )) ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENU )) ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + bSeparator = FALSE; + } + } + else + { + if ( pMenu->GetItemType( nItemPos ) != MENUITEM_SEPARATOR ) + { + // don't save special menu items for (window list and pickup list, add-ons ) + if ( !MenuConfiguration::IsPickListItemId( nItemId ) && + !MenuConfiguration::IsWindowListItemId( nItemId ) && + !AddonMenuManager::IsAddonMenuId( nItemId )) + { + bSeparator = FALSE; + WriteMenuItem( pMenu, nItemId ); + } + } + else if ( !bSeparator ) + { + // Don't write two separators together + WriteMenuSeparator(); + bSeparator = TRUE; + } + } + } +} + + +void OWriteMenuDocumentHandler::WriteMenuItem( Menu* pMenu, USHORT nItemId ) +{ + AttributeListImpl* pList = new AttributeListImpl; + Reference< XAttributeList > xList( (XAttributeList *) pList , UNO_QUERY ); + + String aCommand( pMenu->GetItemCommand( nItemId ) ); + if ( !aCommand.Len() ) + { + aCommand = String::CreateFromAscii("slot:"); + aCommand += String::CreateFromInt32( nItemId ); + } + + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_ID )), + m_aAttributeType, + aCommand ); + + ULONG nHelpId = pMenu->GetHelpId( nItemId ); + if ( nHelpId > 0 ) + { + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_HELPID )), + m_aAttributeType, + OUString::valueOf( sal_Int64( nHelpId )) ); + } + +// if ( pMenu->GetUserValue( nItemId ) & MENU_SAVE_LABEL ) + pList->addAttribute( OUString( RTL_CONSTASCII_USTRINGPARAM( ATTRIBUTE_NS_LABEL )), + m_aAttributeType, + pMenu->GetItemText( nItemId )); + + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUITEM )), xList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUITEM )) ); +} + + +void OWriteMenuDocumentHandler::WriteMenuSeparator() +{ + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->startElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUSEPARATOR )), m_xEmptyList ); + m_xWriteDocumentHandler->ignorableWhitespace( OUString() ); + m_xWriteDocumentHandler->endElement( OUString( RTL_CONSTASCII_USTRINGPARAM( ELEMENT_NS_MENUSEPARATOR )) ); +} + +} // namespace framework diff --git a/framework/source/services/modulemanager.cxx b/framework/source/services/modulemanager.cxx new file mode 100644 index 000000000000..396a9650e3f7 --- /dev/null +++ b/framework/source/services/modulemanager.cxx @@ -0,0 +1,425 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +#include "services/modulemanager.hxx" +#include "services/frame.hxx" + +//_______________________________________________ +// own includes +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <services.h> + +//_______________________________________________ +// interface includes +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XModule.hpp> +#include <comphelper/configurationhelper.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/sequenceasvector.hxx> +#include <comphelper/enumhelper.hxx> + +//_______________________________________________ +// other includes +#include <rtl/logfile.hxx> + +namespace framework +{ + +static const ::rtl::OUString CFGPATH_FACTORIES = ::rtl::OUString::createFromAscii("/org.openoffice.Setup/Office/Factories"); +static const ::rtl::OUString MODULEPROP_IDENTIFIER = ::rtl::OUString::createFromAscii("ooSetupFactoryModuleIdentifier" ); + +/*----------------------------------------------- + 04.12.2003 09:32 +-----------------------------------------------*/ +DEFINE_XINTERFACE_7(ModuleManager , + OWeakObject , + DIRECT_INTERFACE(css::lang::XTypeProvider ), + DIRECT_INTERFACE(css::lang::XServiceInfo ), + DIRECT_INTERFACE(css::container::XNameReplace ), + DIRECT_INTERFACE(css::container::XNameAccess ), + DIRECT_INTERFACE(css::container::XElementAccess ), + DIRECT_INTERFACE(css::container::XContainerQuery), + DIRECT_INTERFACE(css::frame::XModuleManager )) + +/*----------------------------------------------- + 04.12.2003 09:32 +-----------------------------------------------*/ +DEFINE_XTYPEPROVIDER_7(ModuleManager , + css::lang::XTypeProvider , + css::lang::XServiceInfo , + css::container::XNameReplace , + css::container::XNameAccess , + css::container::XElementAccess , + css::container::XContainerQuery, + css::frame::XModuleManager ) + +/*----------------------------------------------- + 04.12.2003 09:35 +-----------------------------------------------*/ +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE(ModuleManager , + ::cppu::OWeakObject , + SERVICENAME_MODULEMANAGER , + IMPLEMENTATIONNAME_MODULEMANAGER) + +/*----------------------------------------------- + 04.12.2003 09:35 +-----------------------------------------------*/ +DEFINE_INIT_SERVICE( + ModuleManager, + { + /*Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + } + ) + +/*----------------------------------------------- + 04.12.2003 09:30 +-----------------------------------------------*/ +ModuleManager::ModuleManager(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR) + : ThreadHelpBase( ) + , m_xSMGR (xSMGR) +{ +} + +/*----------------------------------------------- + 10.12.2003 11:59 +-----------------------------------------------*/ +ModuleManager::~ModuleManager() +{ + if (m_xCFG.is()) + m_xCFG.clear(); +} + +/*----------------------------------------------- + 10.12.2003 11:02 +-----------------------------------------------*/ +::rtl::OUString SAL_CALL ModuleManager::identify(const css::uno::Reference< css::uno::XInterface >& xModule) + throw(css::lang::IllegalArgumentException, + css::frame::UnknownModuleException, + css::uno::RuntimeException ) +{ + // valid parameter? + css::uno::Reference< css::frame::XFrame > xFrame (xModule, css::uno::UNO_QUERY); + css::uno::Reference< css::awt::XWindow > xWindow (xModule, css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XController > xController(xModule, css::uno::UNO_QUERY); + css::uno::Reference< css::frame::XModel > xModel (xModule, css::uno::UNO_QUERY); + + if ( + (!xFrame.is() ) && + (!xWindow.is() ) && + (!xController.is()) && + (!xModel.is() ) + ) + { + throw css::lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii("Given module is not a frame nor a window, controller or model."), + static_cast< ::cppu::OWeakObject* >(this), + 1); + } + + if (xFrame.is()) + { + xController = xFrame->getController(); + xWindow = xFrame->getComponentWindow(); + } + if (xController.is()) + xModel = xController->getModel(); + + // modules are implemented by the deepest component in hierarchy ... + // Means: model -> controller -> window + // No fallbacks to higher components are allowed ! + // Note : A frame provides access to module components only ... but it's not a module by himself. + + ::rtl::OUString sModule; + if (xModel.is()) + sModule = implts_identify(xModel); + else + if (xController.is()) + sModule = implts_identify(xController); + else + if (xWindow.is()) + sModule = implts_identify(xWindow); + + if (sModule.getLength() < 1) + throw css::frame::UnknownModuleException( + ::rtl::OUString::createFromAscii("Cant find suitable module for the given component."), + static_cast< ::cppu::OWeakObject* >(this)); + + return sModule; +} + +/*----------------------------------------------- + 08.03.2007 09:55 +-----------------------------------------------*/ +void SAL_CALL ModuleManager::replaceByName(const ::rtl::OUString& sName , + const css::uno::Any& aValue) + throw (css::lang::IllegalArgumentException , + css::container::NoSuchElementException, + css::lang::WrappedTargetException , + css::uno::RuntimeException ) +{ + ::comphelper::SequenceAsHashMap lProps(aValue); + if (lProps.empty() ) + { + throw css::lang::IllegalArgumentException( + ::rtl::OUString::createFromAscii("No properties given to replace part of module."), + static_cast< css::container::XNameAccess* >(this), + 2); + } + + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + // get access to the element + // Note: Dont use impl_getConfig() method here. Because it creates a readonly access only, further + // it cache it as a member of this module manager instance. If we change some props there ... but dont + // flush changes (because an error occured) we will read them later. If we use a different config access + // we can close it without a flush ... and our read data wont be affected .-) + css::uno::Reference< css::uno::XInterface > xCfg = ::comphelper::ConfigurationHelper::openConfig( + xSMGR, + CFGPATH_FACTORIES, + ::comphelper::ConfigurationHelper::E_STANDARD); + css::uno::Reference< css::container::XNameAccess > xModules (xCfg, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::container::XNameReplace > xModule ; + + xModules->getByName(sName) >>= xModule; + if (!xModule.is()) + { + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("Was not able to get write access to the requested module entry inside configuration."), + static_cast< css::container::XNameAccess* >(this)); + } + + ::comphelper::SequenceAsHashMap::const_iterator pProp; + for ( pProp = lProps.begin(); + pProp != lProps.end() ; + ++pProp ) + { + const ::rtl::OUString& sPropName = pProp->first; + const css::uno::Any& aPropValue = pProp->second; + + // let "NoSuchElementException" out ! We support the same API ... + // and without a flush() at the end all changed data before will be ignored ! + xModule->replaceByName(sPropName, aPropValue); + } + + ::comphelper::ConfigurationHelper::flush(xCfg); +} + +/*----------------------------------------------- + 10.12.2003 12:05 +-----------------------------------------------*/ +css::uno::Any SAL_CALL ModuleManager::getByName(const ::rtl::OUString& sName) + throw(css::container::NoSuchElementException, + css::lang::WrappedTargetException , + css::uno::RuntimeException ) +{ + // get access to the element + css::uno::Reference< css::container::XNameAccess > xCFG = implts_getConfig(); + css::uno::Reference< css::container::XNameAccess > xModule; + xCFG->getByName(sName) >>= xModule; + if (!xModule.is()) + { + throw css::uno::RuntimeException( + ::rtl::OUString::createFromAscii("Was not able to get write access to the requested module entry inside configuration."), + static_cast< css::container::XNameAccess* >(this)); + } + + // convert it to seq< PropertyValue > + const css::uno::Sequence< ::rtl::OUString > lPropNames = xModule->getElementNames(); + ::comphelper::SequenceAsHashMap lProps ; + sal_Int32 c = lPropNames.getLength(); + sal_Int32 i = 0; + + lProps[MODULEPROP_IDENTIFIER] <<= sName; + for (i=0; i<c; ++i) + { + const ::rtl::OUString& sPropName = lPropNames[i]; + lProps[sPropName] = xModule->getByName(sPropName); + } + + return css::uno::makeAny(lProps.getAsConstPropertyValueList()); +} + +/*----------------------------------------------- + 10.12.2003 11:58 +-----------------------------------------------*/ +css::uno::Sequence< ::rtl::OUString > SAL_CALL ModuleManager::getElementNames() + throw(css::uno::RuntimeException) +{ + css::uno::Reference< css::container::XNameAccess > xCFG = implts_getConfig(); + return xCFG->getElementNames(); +} + +/*----------------------------------------------- + 10.12.2003 11:57 +-----------------------------------------------*/ +sal_Bool SAL_CALL ModuleManager::hasByName(const ::rtl::OUString& sName) + throw(css::uno::RuntimeException) +{ + css::uno::Reference< css::container::XNameAccess > xCFG = implts_getConfig(); + return xCFG->hasByName(sName); +} + +/*----------------------------------------------- + 10.12.2003 11:35 +-----------------------------------------------*/ +css::uno::Type SAL_CALL ModuleManager::getElementType() + throw(css::uno::RuntimeException) +{ + return ::getCppuType((const css::uno::Sequence< css::beans::PropertyValue >*)0); +} + +/*----------------------------------------------- + 10.12.2003 11:56 +-----------------------------------------------*/ +sal_Bool SAL_CALL ModuleManager::hasElements() + throw(css::uno::RuntimeException) +{ + css::uno::Reference< css::container::XNameAccess > xCFG = implts_getConfig(); + return xCFG->hasElements(); +} + +/*----------------------------------------------- + 07.03.2007 12:55 +-----------------------------------------------*/ +css::uno::Reference< css::container::XEnumeration > SAL_CALL ModuleManager::createSubSetEnumerationByQuery(const ::rtl::OUString&) + throw(css::uno::RuntimeException) +{ + return css::uno::Reference< css::container::XEnumeration >(); +} + +/*----------------------------------------------- + 07.03.2007 12:55 +-----------------------------------------------*/ +css::uno::Reference< css::container::XEnumeration > SAL_CALL ModuleManager::createSubSetEnumerationByProperties(const css::uno::Sequence< css::beans::NamedValue >& lProperties) + throw(css::uno::RuntimeException) +{ + ::comphelper::SequenceAsHashMap lSearchProps (lProperties); + css::uno::Sequence< ::rtl::OUString > lModules = getElementNames(); + sal_Int32 c = lModules.getLength(); + sal_Int32 i = 0; + ::comphelper::SequenceAsVector< css::uno::Any > lResult ; + + for (i=0; i<c; ++i) + { + try + { + const ::rtl::OUString& sModule = lModules[i]; + ::comphelper::SequenceAsHashMap lModuleProps = getByName(sModule); + + if (lModuleProps.match(lSearchProps)) + lResult.push_back(css::uno::makeAny(lModuleProps.getAsConstPropertyValueList())); + } + catch(const css::uno::Exception&) + {} + } + + ::comphelper::OAnyEnumeration* pEnum = new ::comphelper::OAnyEnumeration(lResult.getAsConstList()); + css::uno::Reference< css::container::XEnumeration > xEnum(static_cast< css::container::XEnumeration* >(pEnum), css::uno::UNO_QUERY_THROW); + return xEnum; +} + +/*----------------------------------------------- + 14.12.2003 09:45 +-----------------------------------------------*/ +css::uno::Reference< css::container::XNameAccess > ModuleManager::implts_getConfig() + throw(css::uno::RuntimeException) +{ + // SAFE -> ---------------------------------- + ReadGuard aReadLock(m_aLock); + if (m_xCFG.is()) + return m_xCFG; + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE ---------------------------------- + + css::uno::Reference< css::uno::XInterface > xCfg; + try + { + xCfg = ::comphelper::ConfigurationHelper::openConfig( + xSMGR, + CFGPATH_FACTORIES, + ::comphelper::ConfigurationHelper::E_READONLY); + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::uno::Exception&) + { xCfg.clear(); } + + // SAFE -> ---------------------------------- + WriteGuard aWriteLock(m_aLock); + m_xCFG = css::uno::Reference< css::container::XNameAccess >(xCfg, css::uno::UNO_QUERY_THROW); + return m_xCFG; + // <- SAFE ---------------------------------- +} + +/*----------------------------------------------- + 30.01.2004 07:54 +-----------------------------------------------*/ +::rtl::OUString ModuleManager::implts_identify(const css::uno::Reference< css::uno::XInterface >& xComponent) +{ + // Search for an optional (!) interface XModule first. + // Its used to overrule an existing service name. Used e.g. by our database form designer + // which uses a writer module internaly. + css::uno::Reference< css::frame::XModule > xModule(xComponent, css::uno::UNO_QUERY); + if (xModule.is()) + return xModule->getIdentifier(); + + // detect modules in a generic way ... + // comparing service names with configured entries ... + css::uno::Reference< css::lang::XServiceInfo > xInfo(xComponent, css::uno::UNO_QUERY); + if (!xInfo.is()) + return ::rtl::OUString(); + + const css::uno::Sequence< ::rtl::OUString > lKnownModules = getElementNames(); + const ::rtl::OUString* pKnownModules = lKnownModules.getConstArray(); + sal_Int32 c = lKnownModules.getLength(); + sal_Int32 i = 0; + + for (i=0; i<c; ++i) + { + if (xInfo->supportsService(pKnownModules[i])) + return pKnownModules[i]; + } + + return ::rtl::OUString(); +} + +} // namespace framework diff --git a/framework/source/services/pathsettings.cxx b/framework/source/services/pathsettings.cxx new file mode 100644 index 000000000000..5227cb9f302c --- /dev/null +++ b/framework/source/services/pathsettings.cxx @@ -0,0 +1,1175 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" +// ______________________________________________ +// my own includes + +/** Attention: stl headers must(!) be included at first. Otherwhise it can make trouble + with solaris headers ... +*/ +#include <vector> +#include <services/pathsettings.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <services.h> + +// ______________________________________________ +// interface includes +#include <com/sun/star/beans/Property.hpp> +#include <com/sun/star/beans/XProperty.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/util/XChangesNotifier.hpp> + +// ______________________________________________ +// includes of other projects +#include <tools/urlobj.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/logfile.hxx> + +#include <comphelper/configurationhelper.hxx> +#include <unotools/configpathes.hxx> + +// ______________________________________________ +// non exported const + +#define CFG_READONLY_DEFAULT sal_False + +const ::rtl::OUString CFGPROP_INTERNALPATHES = ::rtl::OUString::createFromAscii("InternalPaths"); +const ::rtl::OUString CFGPROP_USERPATHES = ::rtl::OUString::createFromAscii("UserPaths" ); +const ::rtl::OUString CFGPROP_WRITEPATH = ::rtl::OUString::createFromAscii("WritePath" ); +const ::rtl::OUString CFGPROP_ISSINGLEPATH = ::rtl::OUString::createFromAscii("IsSinglePath" ); + +/* + 0 : old style "Template" string using ";" as seperator + 1 : internal paths "Template_internal" string list + 2 : user paths "Template_user" string list + 3 : write path "Template_write" string + */ + +const ::rtl::OUString POSTFIX_INTERNAL_PATHES = ::rtl::OUString::createFromAscii("_internal"); +const ::rtl::OUString POSTFIX_USER_PATHES = ::rtl::OUString::createFromAscii("_user" ); +const ::rtl::OUString POSTFIX_WRITE_PATH = ::rtl::OUString::createFromAscii("_writable"); + +const sal_Int32 IDGROUP_OLDSTYLE = 0; +const sal_Int32 IDGROUP_INTERNAL_PATHES = 1; +const sal_Int32 IDGROUP_USER_PATHES = 2; +const sal_Int32 IDGROUP_WRITE_PATH = 3; + +const sal_Int32 IDGROUP_COUNT = 4; + +sal_Int32 impl_getPropGroup(sal_Int32 nID) +{ + return (nID % IDGROUP_COUNT); +} + +// ______________________________________________ +// namespace + +namespace framework +{ + +//----------------------------------------------------------------------------- +// XInterface, XTypeProvider, XServiceInfo + +DEFINE_XINTERFACE_7 ( PathSettings , + OWeakObject , + DIRECT_INTERFACE ( css::lang::XTypeProvider ), + DIRECT_INTERFACE ( css::lang::XServiceInfo ), + DERIVED_INTERFACE( css::lang::XEventListener, css::util::XChangesListener), + DIRECT_INTERFACE ( css::util::XChangesListener ), + DIRECT_INTERFACE ( css::beans::XPropertySet ), + DIRECT_INTERFACE ( css::beans::XFastPropertySet ), + DIRECT_INTERFACE ( css::beans::XMultiPropertySet ) + ) + +DEFINE_XTYPEPROVIDER_7 ( PathSettings , + css::lang::XTypeProvider , + css::lang::XServiceInfo , + css::lang::XEventListener , + css::util::XChangesListener , + css::beans::XPropertySet , + css::beans::XFastPropertySet , + css::beans::XMultiPropertySet + ) + +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE ( PathSettings , + ::cppu::OWeakObject , + SERVICENAME_PATHSETTINGS , + IMPLEMENTATIONNAME_PATHSETTINGS + ) + +DEFINE_INIT_SERVICE ( PathSettings, + { + /*Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + + // fill cache + impl_readAll(); + } + ) + +//----------------------------------------------------------------------------- +PathSettings::PathSettings( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) + // Init baseclasses first + // Attention: Don't change order of initialization! + // ThreadHelpBase is a struct with a lock as member. We can't use a lock as direct member! + // We must garant right initialization and a valid value of this to initialize other baseclasses! + : ThreadHelpBase() + , ::cppu::OBroadcastHelperVar< ::cppu::OMultiTypeInterfaceContainerHelper, ::cppu::OMultiTypeInterfaceContainerHelper::keyType >(m_aLock.getShareableOslMutex()) + , ::cppu::OPropertySetHelper(*(static_cast< ::cppu::OBroadcastHelper* >(this))) + , ::cppu::OWeakObject() + // Init member + , m_xSMGR (xSMGR) + , m_pPropHelp(0 ) + , m_bIgnoreEvents(sal_False) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::PathSettings" ); +} + +//----------------------------------------------------------------------------- +PathSettings::~PathSettings() +{ + if (m_pPropHelp) + delete m_pPropHelp; +} + +//----------------------------------------------------------------------------- +void SAL_CALL PathSettings::changesOccurred(const css::util::ChangesEvent& aEvent) + throw (css::uno::RuntimeException) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::changesOccurred" ); + /* + if (m_bIgnoreEvents) + return; + */ + + sal_Int32 c = aEvent.Changes.getLength(); + sal_Int32 i = 0; + sal_Bool bUpdateDescriptor = sal_False; + + for (i=0; i<c; ++i) + { + const css::util::ElementChange& aChange = aEvent.Changes[i]; + + ::rtl::OUString sChanged; + aChange.Accessor >>= sChanged; + + ::rtl::OUString sPath = ::utl::extractFirstFromConfigurationPath(sChanged); + if (sPath.getLength()) + { + PathSettings::EChangeOp eOp = impl_updatePath(sPath, sal_True); + if ( + (eOp == PathSettings::E_ADDED ) || + (eOp == PathSettings::E_REMOVED) + ) + bUpdateDescriptor = sal_True; + } + } + + if (bUpdateDescriptor) + impl_rebuildPropertyDescriptor(); +} + +//----------------------------------------------------------------------------- +void SAL_CALL PathSettings::disposing(const css::lang::EventObject& aSource) + throw(css::uno::RuntimeException) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::disposing" ); + // SAFE -> + WriteGuard aWriteLock(m_aLock); + + if (aSource.Source == m_xCfgNew) + m_xCfgNew.clear(); + + aWriteLock.unlock(); + // <- SAFE +} + +//----------------------------------------------------------------------------- +void PathSettings::impl_readAll() +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::impl_readAll" ); + RTL_LOGFILE_CONTEXT(aLog, "framework (as96863) ::PathSettings::load config (all)"); + + // TODO think about me + css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew(); + css::uno::Sequence< ::rtl::OUString > lPaths = xCfg->getElementNames(); + + sal_Int32 c = lPaths.getLength(); + sal_Int32 i = 0; + + for (i=0; i<c; ++i) + { + const ::rtl::OUString& sPath = lPaths[i]; + impl_updatePath(sPath, sal_False); + } + + impl_rebuildPropertyDescriptor(); +} + +//----------------------------------------------------------------------------- +// NO substitution here ! It's done outside ... +OUStringList PathSettings::impl_readOldFormat(const ::rtl::OUString& sPath) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::impl_readOldFormat" ); + css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgOld(); + css::uno::Any aVal = xCfg->getByName(sPath); + + ::rtl::OUString sStringVal; + css::uno::Sequence< ::rtl::OUString > lStringListVal; + OUStringList aPathVal; + + if (aVal >>= sStringVal) + { + aPathVal.push_back(sStringVal); + } + else + if (aVal >>= lStringListVal) + { + aPathVal << lStringListVal; + } + + return aPathVal; +} + +//----------------------------------------------------------------------------- +// NO substitution here ! It's done outside ... +PathSettings::PathInfo PathSettings::impl_readNewFormat(const ::rtl::OUString& sPath) +{ + css::uno::Reference< css::container::XNameAccess > xCfg = fa_getCfgNew(); + + // get access to the "queried" path + css::uno::Reference< css::container::XNameAccess > xPath; + xCfg->getByName(sPath) >>= xPath; + + PathSettings::PathInfo aPathVal; + + // read internal path list + css::uno::Reference< css::container::XNameAccess > xIPath; + xPath->getByName(CFGPROP_INTERNALPATHES) >>= xIPath; + aPathVal.lInternalPaths << xIPath->getElementNames(); + + // read user defined path list + aPathVal.lUserPaths << xPath->getByName(CFGPROP_USERPATHES); + + // read the writeable path + xPath->getByName(CFGPROP_WRITEPATH) >>= aPathVal.sWritePath; + + // read state props + xPath->getByName(CFGPROP_ISSINGLEPATH) >>= aPathVal.bIsSinglePath; + + // analyze finalized/mandatory states + aPathVal.bIsReadonly = sal_False; + css::uno::Reference< css::beans::XProperty > xInfo(xPath, css::uno::UNO_QUERY); + if (xInfo.is()) + { + css::beans::Property aInfo = xInfo->getAsProperty(); + sal_Bool bFinalized = ((aInfo.Attributes & css::beans::PropertyAttribute::READONLY ) == css::beans::PropertyAttribute::READONLY ); + //sal_Bool bMandatory = ((aInfo.Attributes & css::beans::PropertyAttribute::REMOVEABLE) != css::beans::PropertyAttribute::REMOVEABLE); + + // Note: Till we support finalized / mandatory on our API more in detail we handle + // all states simple as READONLY ! But because all realy needed pathes are "mandatory" by default + // we have to handle "finalized" as the real "readonly" indicator . + aPathVal.bIsReadonly = bFinalized; + } + + return aPathVal; +} + +//----------------------------------------------------------------------------- +void PathSettings::impl_storePath(const PathSettings::PathInfo& aPath) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::impl_storePath" ); + m_bIgnoreEvents = sal_True; + + css::uno::Reference< css::container::XNameAccess > xCfgNew = fa_getCfgNew(); + css::uno::Reference< css::container::XNameAccess > xCfgOld = fa_getCfgOld(); + + // try to replace path-parts with well known and uspported variables. + // So an office can be moved easialy to another location without loosing + // it's related pathes. + PathInfo aResubstPath(aPath); + impl_subst(aResubstPath, sal_True); + + // update new configuration + if (! aResubstPath.bIsSinglePath) + { + ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew, + aResubstPath.sPathName, + CFGPROP_USERPATHES, + css::uno::makeAny(aResubstPath.lUserPaths.getAsConstList())); + } + + ::comphelper::ConfigurationHelper::writeRelativeKey(xCfgNew, + aResubstPath.sPathName, + CFGPROP_WRITEPATH, + css::uno::makeAny(aResubstPath.sWritePath)); + + ::comphelper::ConfigurationHelper::flush(xCfgNew); + + // remove the whole path from the old configuration ! + // Otherwise we cant make sure that the diff between new and old configuration + // on loading time realy represent an user setting !!! + + // Check if the given path exists inside the old configuration. + // Because our new configuration knows more then the list of old pathes ... ! + if (xCfgOld->hasByName(aResubstPath.sPathName)) + { + css::uno::Reference< css::beans::XPropertySet > xProps(xCfgOld, css::uno::UNO_QUERY_THROW); + xProps->setPropertyValue(aResubstPath.sPathName, css::uno::Any()); + ::comphelper::ConfigurationHelper::flush(xCfgOld); + } + + m_bIgnoreEvents = sal_False; +} + +//----------------------------------------------------------------------------- +#ifdef MIGRATE_OLD_USER_PATHES +void PathSettings::impl_mergeOldUserPaths( PathSettings::PathInfo& rPath, + const OUStringList& lOld ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "PathSettings::impl_mergeOldUserPaths" ); + OUStringList::const_iterator pIt; + for ( pIt = lOld.begin(); + pIt != lOld.end() ; + ++pIt ) + { + const ::rtl::OUString& sOld = *pIt; + + if (rPath.bIsSinglePath) + { + LOG_ASSERT2(lOld.size()>1, "PathSettings::impl_mergeOldUserPaths()", "Single path has more then one path value inside old configuration (Common.xcu)!") + if (! rPath.sWritePath.equals(sOld)) + rPath.sWritePath = sOld; + } + else + { + if ( + ( rPath.lInternalPaths.findConst(sOld) == rPath.lInternalPaths.end()) && + ( rPath.lUserPaths.findConst(sOld) == rPath.lUserPaths.end() ) && + (! rPath.sWritePath.equals(sOld) ) + ) + rPath.lUserPaths.push_back(sOld); + } + } +} +#endif // MIGRATE_OLD_USER_PATHES + +//----------------------------------------------------------------------------- +PathSettings::EChangeOp PathSettings::impl_updatePath(const ::rtl::OUString& sPath , + sal_Bool bNotifyListener) +{ + // SAFE -> + WriteGuard aWriteLock(m_aLock); + + PathSettings::PathInfo* pPathOld = 0; + PathSettings::PathInfo* pPathNew = 0; + PathSettings::EChangeOp eOp = PathSettings::E_UNDEFINED; + PathSettings::PathInfo aPath; + + try + { + aPath = impl_readNewFormat(sPath); + aPath.sPathName = sPath; + // replace all might existing variables with real values + // Do it before these old pathes will be compared against the + // new path configuration. Otherwise some striungs uses different variables ... but substitution + // will produce strings with same content (because some variables are redundant!) + impl_subst(aPath, sal_False); + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + catch(const css::container::NoSuchElementException&) + { eOp = PathSettings::E_REMOVED; } + catch(const css::uno::Exception& exAny) + { throw exAny; } + + #ifdef MIGRATE_OLD_USER_PATHES + try + { + // migration of old user defined values on demand + // can be disabled for a new major + OUStringList lOldVals = impl_readOldFormat(sPath); + // replace all might existing variables with real values + // Do it before these old pathes will be compared against the + // new path configuration. Otherwise some striungs uses different variables ... but substitution + // will produce strings with same content (because some variables are redundant!) + impl_subst(lOldVals, fa_getSubstitution(), sal_False); + impl_mergeOldUserPaths(aPath, lOldVals); + } + catch(const css::uno::RuntimeException& exRun) + { throw exRun; } + // Normal(!) exceptions can be ignored! + // E.g. in case an addon installs a new path, which was not well known for an OOo 1.x installation + // we cant find a value for it inside the "old" configuration. So a NoSuchElementException + // will be normal .-) + catch(const css::uno::Exception&) + {} + #endif // MIGRATE_OLD_USER_PATHES + + PathSettings::PathHash::iterator pPath = m_lPaths.find(sPath); + if (eOp == PathSettings::E_UNDEFINED) + { + if (pPath != m_lPaths.end()) + eOp = PathSettings::E_CHANGED; + else + eOp = PathSettings::E_ADDED; + } + + switch(eOp) + { + case PathSettings::E_ADDED : + { + if (bNotifyListener) + { + pPathOld = 0; + pPathNew = &aPath; + impl_notifyPropListener(eOp, sPath, pPathOld, pPathNew); + } + m_lPaths[sPath] = aPath; + } + break; + + case PathSettings::E_CHANGED : + { + if (bNotifyListener) + { + pPathOld = &(pPath->second); + pPathNew = &aPath; + impl_notifyPropListener(eOp, sPath, pPathOld, pPathNew); + } + m_lPaths[sPath] = aPath; + } + break; + + case PathSettings::E_REMOVED : + { + if (pPath != m_lPaths.end()) + { + if (bNotifyListener) + { + pPathOld = &(pPath->second); + pPathNew = 0; + impl_notifyPropListener(eOp, sPath, pPathOld, pPathNew); + } + m_lPaths.erase(pPath); + } + } + break; + + default: // to let compiler be happy + break; + } + + return eOp; +} + +//----------------------------------------------------------------------------- +css::uno::Sequence< sal_Int32 > PathSettings::impl_mapPathName2IDList(const ::rtl::OUString& sPath) +{ + ::rtl::OUString sOldStyleProp = sPath; + ::rtl::OUString sInternalProp = sPath+POSTFIX_INTERNAL_PATHES; + ::rtl::OUString sUserProp = sPath+POSTFIX_USER_PATHES; + ::rtl::OUString sWriteProp = sPath+POSTFIX_WRITE_PATH; + + // Attention: The default set of IDs is fix and must follow these schema. + // Otherwhise the outside code ant work for new added properties. + // Why ? + // The outside code must fire N events for every changed property. + // And the knowing about packaging of variables of the structure PathInfo + // follow these group IDs ! But if such ID isnt in the range of [0..IDGROUP_COUNT] + // the outside cant determine the right group ... and cant fire the right events .-) + + css::uno::Sequence< sal_Int32 > lIDs(IDGROUP_COUNT); + lIDs[0] = IDGROUP_OLDSTYLE ; + lIDs[1] = IDGROUP_INTERNAL_PATHES; + lIDs[2] = IDGROUP_USER_PATHES ; + lIDs[3] = IDGROUP_WRITE_PATH ; + + sal_Int32 c = m_lPropDesc.getLength(); + sal_Int32 i = 0; + for (i=0; i<c; ++i) + { + const css::beans::Property& rProp = m_lPropDesc[i]; + + if (rProp.Name.equals(sOldStyleProp)) + lIDs[IDGROUP_OLDSTYLE] = rProp.Handle; + else + if (rProp.Name.equals(sInternalProp)) + lIDs[IDGROUP_INTERNAL_PATHES] = rProp.Handle; + else + if (rProp.Name.equals(sUserProp)) + lIDs[IDGROUP_USER_PATHES] = rProp.Handle; + else + if (rProp.Name.equals(sWriteProp)) + lIDs[IDGROUP_WRITE_PATH] = rProp.Handle; + } + + return lIDs; +} + +//----------------------------------------------------------------------------- +void PathSettings::impl_notifyPropListener( PathSettings::EChangeOp /*eOp*/ , + const ::rtl::OUString& sPath , + const PathSettings::PathInfo* pPathOld, + const PathSettings::PathInfo* pPathNew) +{ + css::uno::Sequence< sal_Int32 > lHandles(1); + css::uno::Sequence< css::uno::Any > lOldVals(1); + css::uno::Sequence< css::uno::Any > lNewVals(1); + + css::uno::Sequence< sal_Int32 > lIDs = impl_mapPathName2IDList(sPath); + sal_Int32 c = lIDs.getLength(); + sal_Int32 i = 0; + sal_Int32 nMaxID = m_lPropDesc.getLength()-1; + for (i=0; i<c; ++i) + { + sal_Int32 nID = lIDs[i]; + + if ( + (nID < 0 ) || + (nID > nMaxID) + ) + continue; + + lHandles[0] = nID; + switch(impl_getPropGroup(nID)) + { + case IDGROUP_OLDSTYLE : + { + if (pPathOld) + { + ::rtl::OUString sVal = impl_convertPath2OldStyle(*pPathOld); + lOldVals[0] <<= sVal; + } + if (pPathNew) + { + ::rtl::OUString sVal = impl_convertPath2OldStyle(*pPathNew); + lNewVals[0] <<= sVal; + } + } + break; + + case IDGROUP_INTERNAL_PATHES : + { + if (pPathOld) + lOldVals[0] <<= pPathOld->lInternalPaths.getAsConstList(); + if (pPathNew) + lNewVals[0] <<= pPathNew->lInternalPaths.getAsConstList(); + } + break; + + case IDGROUP_USER_PATHES : + { + if (pPathOld) + lOldVals[0] <<= pPathOld->lUserPaths.getAsConstList(); + if (pPathNew) + lNewVals[0] <<= pPathNew->lUserPaths.getAsConstList(); + } + break; + + case IDGROUP_WRITE_PATH : + { + if (pPathOld) + lOldVals[0] <<= pPathOld->sWritePath; + if (pPathNew) + lNewVals[0] <<= pPathNew->sWritePath; + } + break; + } + + fire(lHandles.getArray(), + lNewVals.getArray(), + lOldVals.getArray(), + 1, + sal_False); + } +} + +//----------------------------------------------------------------------------- +void PathSettings::impl_subst( OUStringList& lVals , + const css::uno::Reference< css::util::XStringSubstitution >& xSubst , + sal_Bool bReSubst) +{ + OUStringList::iterator pIt; + + for ( pIt = lVals.begin(); + pIt != lVals.end() ; + ++pIt ) + { + const ::rtl::OUString& sOld = *pIt; + ::rtl::OUString sNew ; + if (bReSubst) + sNew = xSubst->reSubstituteVariables(sOld); + else + sNew = xSubst->substituteVariables(sOld, sal_False); + + *pIt = sNew; + } +} + +//----------------------------------------------------------------------------- +void PathSettings::impl_subst(PathSettings::PathInfo& aPath , + sal_Bool bReSubst) +{ + css::uno::Reference< css::util::XStringSubstitution > xSubst = fa_getSubstitution(); + + impl_subst(aPath.lInternalPaths, xSubst, bReSubst); + impl_subst(aPath.lUserPaths , xSubst, bReSubst); + if (bReSubst) + aPath.sWritePath = xSubst->reSubstituteVariables(aPath.sWritePath); + else + aPath.sWritePath = xSubst->substituteVariables(aPath.sWritePath, sal_False); +} + +//----------------------------------------------------------------------------- +::rtl::OUString PathSettings::impl_convertPath2OldStyle(const PathSettings::PathInfo& rPath) const +{ + OUStringList::const_iterator pIt; + OUStringList lTemp; + lTemp.reserve(rPath.lInternalPaths.size() + rPath.lUserPaths.size() + 1); + + for ( pIt = rPath.lInternalPaths.begin(); + pIt != rPath.lInternalPaths.end() ; + ++pIt ) + { + lTemp.push_back(*pIt); + } + for ( pIt = rPath.lUserPaths.begin(); + pIt != rPath.lUserPaths.end() ; + ++pIt ) + { + lTemp.push_back(*pIt); + } + + if (rPath.sWritePath.getLength() > 0) + lTemp.push_back(rPath.sWritePath); + + ::rtl::OUStringBuffer sPathVal(256); + for ( pIt = lTemp.begin(); + pIt != lTemp.end() ; + ) + { + sPathVal.append(*pIt); + ++pIt; + if (pIt != lTemp.end()) + sPathVal.appendAscii(";"); + } + + return sPathVal.makeStringAndClear(); +} + +//----------------------------------------------------------------------------- +OUStringList PathSettings::impl_convertOldStyle2Path(const ::rtl::OUString& sOldStylePath) const +{ + OUStringList lList; + sal_Int32 nToken = 0; + do + { + ::rtl::OUString sToken = sOldStylePath.getToken(0, ';', nToken); + if (sToken.getLength()) + lList.push_back(sToken); + } + while(nToken >= 0); + + return lList; +} + +//----------------------------------------------------------------------------- +void PathSettings::impl_purgeKnownPaths(const PathSettings::PathInfo& rPath, + OUStringList& lList) +{ + OUStringList::const_iterator pIt; + for ( pIt = rPath.lInternalPaths.begin(); + pIt != rPath.lInternalPaths.end() ; + ++pIt ) + { + const ::rtl::OUString& rItem = *pIt; + OUStringList::iterator pItem = lList.find(rItem); + if (pItem != lList.end()) + lList.erase(pItem); + } + for ( pIt = rPath.lUserPaths.begin(); + pIt != rPath.lUserPaths.end() ; + ++pIt ) + { + const ::rtl::OUString& rItem = *pIt; + OUStringList::iterator pItem = lList.find(rItem); + if (pItem != lList.end()) + lList.erase(pItem); + } + + OUStringList::iterator pItem = lList.find(rPath.sWritePath); + if (pItem != lList.end()) + lList.erase(pItem); +} + +//----------------------------------------------------------------------------- +void PathSettings::impl_rebuildPropertyDescriptor() +{ + // SAFE -> + WriteGuard aWriteLock(m_aLock); + + sal_Int32 c = (sal_Int32)m_lPaths.size(); + sal_Int32 i = 0; + m_lPropDesc.realloc(c*IDGROUP_COUNT); + + PathHash::const_iterator pIt; + for ( pIt = m_lPaths.begin(); + pIt != m_lPaths.end() ; + ++pIt ) + { + const PathSettings::PathInfo& rPath = pIt->second; + css::beans::Property* pProp = 0; + + pProp = &(m_lPropDesc[i]); + pProp->Name = rPath.sPathName; + pProp->Handle = i; + pProp->Type = ::getCppuType((::rtl::OUString*)0); + pProp->Attributes = css::beans::PropertyAttribute::BOUND; + if (rPath.bIsReadonly) + pProp->Attributes |= css::beans::PropertyAttribute::READONLY; + ++i; + + pProp = &(m_lPropDesc[i]); + pProp->Name = rPath.sPathName+POSTFIX_INTERNAL_PATHES; + pProp->Handle = i; + pProp->Type = ::getCppuType((css::uno::Sequence< ::rtl::OUString >*)0); + pProp->Attributes = css::beans::PropertyAttribute::BOUND | + css::beans::PropertyAttribute::READONLY; + ++i; + + pProp = &(m_lPropDesc[i]); + pProp->Name = rPath.sPathName+POSTFIX_USER_PATHES; + pProp->Handle = i; + pProp->Type = ::getCppuType((css::uno::Sequence< ::rtl::OUString >*)0); + pProp->Attributes = css::beans::PropertyAttribute::BOUND; + if (rPath.bIsReadonly) + pProp->Attributes |= css::beans::PropertyAttribute::READONLY; + ++i; + + pProp = &(m_lPropDesc[i]); + pProp->Name = rPath.sPathName+POSTFIX_WRITE_PATH; + pProp->Handle = i; + pProp->Type = ::getCppuType((::rtl::OUString*)0); + pProp->Attributes = css::beans::PropertyAttribute::BOUND; + if (rPath.bIsReadonly) + pProp->Attributes |= css::beans::PropertyAttribute::READONLY; + ++i; + } + + if (m_pPropHelp) + delete m_pPropHelp; + m_pPropHelp = new ::cppu::OPropertyArrayHelper(m_lPropDesc, sal_False); // false => not sorted ... must be done inside helper + + aWriteLock.unlock(); + // <- SAFE +} + +//----------------------------------------------------------------------------- +css::uno::Any PathSettings::impl_getPathValue(sal_Int32 nID) const +{ + const PathSettings::PathInfo* pPath = impl_getPathAccessConst(nID); + if (! pPath) + throw css::container::NoSuchElementException(); + + css::uno::Any aVal; + switch(impl_getPropGroup(nID)) + { + case IDGROUP_OLDSTYLE : + { + ::rtl::OUString sVal = impl_convertPath2OldStyle(*pPath); + aVal <<= sVal; + } + break; + + case IDGROUP_INTERNAL_PATHES : + { + aVal <<= pPath->lInternalPaths.getAsConstList(); + } + break; + + case IDGROUP_USER_PATHES : + { + aVal <<= pPath->lUserPaths.getAsConstList(); + } + break; + + case IDGROUP_WRITE_PATH : + { + aVal <<= pPath->sWritePath; + } + break; + } + + return aVal; +} + +//----------------------------------------------------------------------------- +void PathSettings::impl_setPathValue( sal_Int32 nID , + const css::uno::Any& aVal) +{ + PathSettings::PathInfo* pOrgPath = impl_getPathAccess(nID); + if (! pOrgPath) + throw css::container::NoSuchElementException(); + + // We work on a copied path ... so we can be sure that errors during this operation + // does not make our internal cache invalid .-) + PathSettings::PathInfo aChangePath(*pOrgPath); + + switch(impl_getPropGroup(nID)) + { + case IDGROUP_OLDSTYLE : + { + ::rtl::OUString sVal; + aVal >>= sVal; + OUStringList lList = impl_convertOldStyle2Path(sVal); + impl_subst(lList, fa_getSubstitution(), sal_False); + impl_purgeKnownPaths(aChangePath, lList); + if (! impl_isValidPath(lList)) + throw css::lang::IllegalArgumentException(); + + if (aChangePath.bIsSinglePath) + { + LOG_ASSERT2(lList.size()>1, "PathSettings::impl_setPathValue()", "You try to set more then path value for a defined SINGLE_PATH!") + if ( !lList.empty() ) + aChangePath.sWritePath = *(lList.begin()); + else + aChangePath.sWritePath = ::rtl::OUString(); + } + else + { + OUStringList::const_iterator pIt; + for ( pIt = lList.begin(); + pIt != lList.end() ; + ++pIt ) + { + aChangePath.lUserPaths.push_back(*pIt); + } + } + } + break; + + case IDGROUP_INTERNAL_PATHES : + { + if (aChangePath.bIsSinglePath) + { + ::rtl::OUStringBuffer sMsg(256); + sMsg.appendAscii("The path '" ); + sMsg.append (aChangePath.sPathName); + sMsg.appendAscii("' is defined as SINGLE_PATH. It's sub set of internal pathes cant be set."); + throw css::uno::Exception(sMsg.makeStringAndClear(), + static_cast< ::cppu::OWeakObject* >(this)); + } + + OUStringList lList; + lList << aVal; + if (! impl_isValidPath(lList)) + throw css::lang::IllegalArgumentException(); + aChangePath.lInternalPaths = lList; + } + break; + + case IDGROUP_USER_PATHES : + { + if (aChangePath.bIsSinglePath) + { + ::rtl::OUStringBuffer sMsg(256); + sMsg.appendAscii("The path '" ); + sMsg.append (aChangePath.sPathName); + sMsg.appendAscii("' is defined as SINGLE_PATH. It's sub set of internal pathes cant be set."); + throw css::uno::Exception(sMsg.makeStringAndClear(), + static_cast< ::cppu::OWeakObject* >(this)); + } + + OUStringList lList; + lList << aVal; + if (! impl_isValidPath(lList)) + throw css::lang::IllegalArgumentException(); + aChangePath.lUserPaths = lList; + } + break; + + case IDGROUP_WRITE_PATH : + { + ::rtl::OUString sVal; + aVal >>= sVal; + if (! impl_isValidPath(sVal)) + throw css::lang::IllegalArgumentException(); + aChangePath.sWritePath = sVal; + } + break; + } + + // TODO check if path has at least one path value set + // At least it depends from the feature using this path, if an empty path list is allowed. + /* + if (impl_isPathEmpty(aChangePath)) + { + ::rtl::OUStringBuffer sMsg(256); + sMsg.appendAscii("The path '" ); + sMsg.append (aChangePath.sPathName); + sMsg.appendAscii("' is empty now ... Not a real good idea."); + throw css::uno::Exception(sMsg.makeStringAndClear(), + static_cast< ::cppu::OWeakObject* >(this)); + } + */ + + // first we should try to store the changed (copied!) path ... + // In case an error occure on saving time an exception is thrown ... + // If no exception occures we can update our internal cache (means + // we can overwrite pOrgPath ! + impl_storePath(aChangePath); + pOrgPath->takeOver(aChangePath); +} + +//----------------------------------------------------------------------------- +sal_Bool PathSettings::impl_isValidPath(const OUStringList& lPath) const +{ + OUStringList::const_iterator pIt; + for ( pIt = lPath.begin(); + pIt != lPath.end() ; + ++pIt ) + { + const ::rtl::OUString& rVal = *pIt; + if (! impl_isValidPath(rVal)) + return sal_False; + } + + return sal_True; +} + +//----------------------------------------------------------------------------- +sal_Bool PathSettings::impl_isValidPath(const ::rtl::OUString& sPath) const +{ + return (! INetURLObject(sPath).HasError()); +} + +//----------------------------------------------------------------------------- +::rtl::OUString impl_extractBaseFromPropName(const ::rtl::OUString& sPropName) +{ + sal_Int32 i = -1; + + i = sPropName.indexOf(POSTFIX_INTERNAL_PATHES); + if (i > -1) + return sPropName.copy(0, i); + i = sPropName.indexOf(POSTFIX_USER_PATHES); + if (i > -1) + return sPropName.copy(0, i); + i = sPropName.indexOf(POSTFIX_WRITE_PATH); + if (i > -1) + return sPropName.copy(0, i); + + return sPropName; +} + +//----------------------------------------------------------------------------- +PathSettings::PathInfo* PathSettings::impl_getPathAccess(sal_Int32 nHandle) +{ + // SAFE -> + ReadGuard aReadLock(m_aLock); + + if (nHandle > (m_lPropDesc.getLength()-1)) + return 0; + + const css::beans::Property& rProp = m_lPropDesc[nHandle]; + ::rtl::OUString sProp = impl_extractBaseFromPropName(rProp.Name); + PathSettings::PathHash::iterator rPath = m_lPaths.find(sProp); + + if (rPath != m_lPaths.end()) + return &(rPath->second); + + return 0; + // <- SAFE +} + +//----------------------------------------------------------------------------- +const PathSettings::PathInfo* PathSettings::impl_getPathAccessConst(sal_Int32 nHandle) const +{ + // SAFE -> + ReadGuard aReadLock(m_aLock); + + if (nHandle > (m_lPropDesc.getLength()-1)) + return 0; + + const css::beans::Property& rProp = m_lPropDesc[nHandle]; + ::rtl::OUString sProp = impl_extractBaseFromPropName(rProp.Name); + PathSettings::PathHash::const_iterator rPath = m_lPaths.find(sProp); + + if (rPath != m_lPaths.end()) + return &(rPath->second); + + return 0; + // <- SAFE +} + +//----------------------------------------------------------------------------- +sal_Bool SAL_CALL PathSettings::convertFastPropertyValue( css::uno::Any& aConvertedValue, + css::uno::Any& aOldValue , + sal_Int32 nHandle , + const css::uno::Any& aValue ) + throw(css::lang::IllegalArgumentException) +{ + // throws NoSuchElementException ! + css::uno::Any aCurrentVal = impl_getPathValue(nHandle); + + return PropHelper::willPropertyBeChanged( + aCurrentVal, + aValue, + aOldValue, + aConvertedValue); +} + +//----------------------------------------------------------------------------- +void SAL_CALL PathSettings::setFastPropertyValue_NoBroadcast( sal_Int32 nHandle, + const css::uno::Any& aValue ) + throw(css::uno::Exception) +{ + // throws NoSuchElement- and IllegalArgumentException ! + impl_setPathValue(nHandle, aValue); +} + +//----------------------------------------------------------------------------- +void SAL_CALL PathSettings::getFastPropertyValue(css::uno::Any& aValue , + sal_Int32 nHandle) const +{ + aValue = impl_getPathValue(nHandle); +} + +//----------------------------------------------------------------------------- +::cppu::IPropertyArrayHelper& SAL_CALL PathSettings::getInfoHelper() +{ + return *m_pPropHelp; +} + +//----------------------------------------------------------------------------- +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL PathSettings::getPropertySetInfo() + throw(css::uno::RuntimeException) +{ + return css::uno::Reference< css::beans::XPropertySetInfo >(createPropertySetInfo(getInfoHelper())); +} + +//----------------------------------------------------------------------------- +css::uno::Reference< css::util::XStringSubstitution > PathSettings::fa_getSubstitution() +{ + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + css::uno::Reference< css::util::XStringSubstitution > xSubst = m_xSubstitution; + aReadLock.unlock(); + // <- SAFE + + if (! xSubst.is()) + { + // create the needed substitution service. + // We must replace all used variables inside readed path values. + // In case we can't do so ... the whole office can't work realy. + // That's why it seams to be OK to throw a RuntimeException then. + xSubst = css::uno::Reference< css::util::XStringSubstitution >( + xSMGR->createInstance(SERVICENAME_SUBSTITUTEPATHVARIABLES), + css::uno::UNO_QUERY_THROW); + + // SAFE -> + WriteGuard aWriteLock(m_aLock); + m_xSubstitution = xSubst; + aWriteLock.unlock(); + } + + return xSubst; +} + +//----------------------------------------------------------------------------- +css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgOld() +{ + const static ::rtl::OUString CFG_NODE_OLD = ::rtl::OUString::createFromAscii("org.openoffice.Office.Common/Path/Current"); + + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + css::uno::Reference< css::container::XNameAccess > xCfg = m_xCfgOld; + aReadLock.unlock(); + // <- SAFE + + if (! xCfg.is()) + { + xCfg = css::uno::Reference< css::container::XNameAccess >( + ::comphelper::ConfigurationHelper::openConfig( + xSMGR, + CFG_NODE_OLD, + ::comphelper::ConfigurationHelper::E_STANDARD), // not readonly! Somtimes we need write access there !!! + css::uno::UNO_QUERY_THROW); + + // SAFE -> + WriteGuard aWriteLock(m_aLock); + m_xCfgOld = xCfg; + aWriteLock.unlock(); + } + + return xCfg; +} + +//----------------------------------------------------------------------------- +css::uno::Reference< css::container::XNameAccess > PathSettings::fa_getCfgNew() +{ + const static ::rtl::OUString CFG_NODE_NEW = ::rtl::OUString::createFromAscii("org.openoffice.Office.Paths/Paths"); + + // SAFE -> + ReadGuard aReadLock(m_aLock); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + css::uno::Reference< css::container::XNameAccess > xCfg = m_xCfgNew; + aReadLock.unlock(); + // <- SAFE + + if (! xCfg.is()) + { + xCfg = css::uno::Reference< css::container::XNameAccess >( + ::comphelper::ConfigurationHelper::openConfig( + xSMGR, + CFG_NODE_NEW, + ::comphelper::ConfigurationHelper::E_STANDARD), + css::uno::UNO_QUERY_THROW); + + // SAFE -> + WriteGuard aWriteLock(m_aLock); + m_xCfgNew = xCfg; + aWriteLock.unlock(); + + css::uno::Reference< css::util::XChangesNotifier > xBroadcaster(xCfg, css::uno::UNO_QUERY_THROW); + xBroadcaster->addChangesListener(static_cast< css::util::XChangesListener* >(this)); + } + + return xCfg; +} + +} // namespace framework diff --git a/framework/source/services/sessionlistener.cxx b/framework/source/services/sessionlistener.cxx new file mode 100644 index 000000000000..42a2637f823e --- /dev/null +++ b/framework/source/services/sessionlistener.cxx @@ -0,0 +1,368 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" +//_______________________________________________ +// my own includes + +#ifndef __FRAMEWORK_SERVICES_TYPEDETECTION_HXX_ +#include <services/sessionlistener.hxx> +#endif +#include <threadhelp/readguard.hxx> +#include <threadhelp/resetableguard.hxx> +#include <protocols.h> +#include <services.h> + +#include <osl/thread.h> + + +#include <vcl/svapp.hxx> +#include <tools/urlobj.hxx> +#include <tools/tempfile.hxx> +#include <unotools/tempfile.hxx> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/PropertyState.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/XFramesSupplier.hpp> +#include <com/sun/star/frame/XStorable.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/util/XChangesBatch.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> +#include <com/sun/star/util/URL.hpp> +#include <osl/time.h> +#include <comphelper/processfactory.hxx> +#include <unotools/pathoptions.hxx> +#include <unotools/internaloptions.hxx> +#include <stdio.h> +//_______________________________________________ +// interface includes +#include <com/sun/star/uno/Any.hxx> + +#include <com/sun/star/uno/Sequence.hxx> +//_______________________________________________ +// includes of other projects + +//_______________________________________________ +// namespace + +using namespace com::sun::star::uno; +using namespace com::sun::star::util; +using namespace com::sun::star::frame; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; + +using namespace rtl; + +namespace framework{ + +//_______________________________________________ +// non exported const + +//_______________________________________________ +// non exported definitions + +//_______________________________________________ +// declarations + +//*********************************************** +// XInterface, XTypeProvider, XServiceInfo + +DEFINE_XINTERFACE_6( + SessionListener, + OWeakObject, + DIRECT_INTERFACE(css::lang::XTypeProvider), + DIRECT_INTERFACE(css::lang::XInitialization), + DIRECT_INTERFACE(css::frame::XSessionManagerListener), + DIRECT_INTERFACE(css::frame::XSessionManagerListener2), + DIRECT_INTERFACE(css::frame::XStatusListener), + DIRECT_INTERFACE(css::lang::XServiceInfo)) + +DEFINE_XTYPEPROVIDER_5( + SessionListener, + css::lang::XTypeProvider, + css::lang::XInitialization, + css::frame::XSessionManagerListener2, + css::frame::XStatusListener, + css::lang::XServiceInfo) + +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE( + SessionListener, + cppu::OWeakObject, + SERVICENAME_SESSIONLISTENER, + IMPLEMENTATIONNAME_SESSIONLISTENER) + +DEFINE_INIT_SERVICE(SessionListener, + { + /* Add special code for initialization here, if you have to use your own instance + during your ctor is still in progress! */ + } + ) + +SessionListener::SessionListener(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) + : ThreadHelpBase (&Application::GetSolarMutex()) + , OWeakObject ( ) + , m_xSMGR (xSMGR ) + , m_bRestored( sal_False ) + , m_bSessionStoreRequested( sal_False ) + , m_bAllowUserInteractionOnQuit( sal_False ) + , m_bTerminated( sal_False ) +{ +} + +SessionListener::~SessionListener() +{ + if (m_rSessionManager.is()) + { + css::uno::Reference< XSessionManagerListener> me(this); + m_rSessionManager->removeSessionManagerListener(me); + } +} + +void SessionListener::StoreSession( sal_Bool bAsync ) +{ + ResetableGuard aGuard(m_aLock); + try + { + // xd create SERVICENAME_AUTORECOVERY -> XDispatch + // xd->dispatch("vnd.sun.star.autorecovery:/doSessionSave, async=bAsync + // on stop event m_rSessionManager->saveDone(this); in case of asynchronous call + // in case of synchronous call the caller should do saveDone() call himself! + + css::uno::Reference< XDispatch > xDispatch(m_xSMGR->createInstance(SERVICENAME_AUTORECOVERY), UNO_QUERY_THROW); + css::uno::Reference< XURLTransformer > xURLTransformer(m_xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), UNO_QUERY_THROW); + URL aURL; + aURL.Complete = OUString::createFromAscii("vnd.sun.star.autorecovery:/doSessionSave"); + xURLTransformer->parseStrict(aURL); + + // in case of asynchronous call the notification will trigger saveDone() + if ( bAsync ) + xDispatch->addStatusListener(this, aURL); + + Sequence< PropertyValue > args(1); + args[0] = PropertyValue(OUString::createFromAscii("DispatchAsynchron"),-1,makeAny(bAsync),PropertyState_DIRECT_VALUE); + xDispatch->dispatch(aURL, args); + } catch (com::sun::star::uno::Exception& e) { + OString aMsg = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8); + OSL_ENSURE(sal_False, aMsg.getStr()); + // save failed, but tell manager to go on if we havent yet dispatched the request + // in case of synchronous saving the notification is done by the caller + if ( bAsync && m_rSessionManager.is() ) + m_rSessionManager->saveDone(this); + } +} + +void SessionListener::QuitSessionQuietly() +{ + ResetableGuard aGuard(m_aLock); + try + { + // xd create SERVICENAME_AUTORECOVERY -> XDispatch + // xd->dispatch("vnd.sun.star.autorecovery:/doSessionQuietQuit, async=false + // it is done synchronously to avoid conflict with normal quit process + + css::uno::Reference< XDispatch > xDispatch(m_xSMGR->createInstance(SERVICENAME_AUTORECOVERY), UNO_QUERY_THROW); + css::uno::Reference< XURLTransformer > xURLTransformer(m_xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), UNO_QUERY_THROW); + URL aURL; + aURL.Complete = OUString::createFromAscii("vnd.sun.star.autorecovery:/doSessionQuietQuit"); + xURLTransformer->parseStrict(aURL); + + Sequence< PropertyValue > args(1); + args[0] = PropertyValue(OUString::createFromAscii("DispatchAsynchron"),-1,makeAny(sal_False),PropertyState_DIRECT_VALUE); + xDispatch->dispatch(aURL, args); + } catch (com::sun::star::uno::Exception& e) { + OString aMsg = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8); + OSL_ENSURE(sal_False, aMsg.getStr()); + } +} + +void SAL_CALL SessionListener::disposing(const com::sun::star::lang::EventObject&) throw (RuntimeException) +{ +} + +void SAL_CALL SessionListener::initialize(const Sequence< Any >& args) + throw (RuntimeException) +{ + + OUString aSMgr = OUString::createFromAscii("com.sun.star.frame.SessionManagerClient"); + if (args.getLength() > 0) + { + NamedValue v; + for (int i = 0; i < args.getLength(); i++) + { + if (args[i] >>= v) + { + if (v.Name.equalsAscii("SessionManagerName")) + v.Value >>= aSMgr; + else if (v.Name.equalsAscii("SessionManager")) + v.Value >>= m_rSessionManager; + else if (v.Name.equalsAscii("AllowUserInteractionOnQuit")) + v.Value >>= m_bAllowUserInteractionOnQuit; + } + } + } + if (!m_rSessionManager.is()) + m_rSessionManager = css::uno::Reference< XSessionManagerClient > + (m_xSMGR->createInstance(aSMgr), UNO_QUERY); + + if (m_rSessionManager.is()) + { + m_rSessionManager->addSessionManagerListener(this); + } +} + +void SAL_CALL SessionListener::statusChanged(const FeatureStateEvent& event) + throw (css::uno::RuntimeException) +{ + if (event.FeatureURL.Complete.equalsAscii("vnd.sun.star.autorecovery:/doSessionRestore")) + { + if (event.FeatureDescriptor.compareToAscii("update")==0) + m_bRestored = sal_True; // a document was restored + // if (event.FeatureDescriptor.compareToAscii("stop")==0) + + } + else if (event.FeatureURL.Complete.equalsAscii("vnd.sun.star.autorecovery:/doSessionSave")) + { + if (event.FeatureDescriptor.compareToAscii("stop")==0) + { + if (m_rSessionManager.is()) + m_rSessionManager->saveDone(this); // done with save + } + } +} + + +sal_Bool SAL_CALL SessionListener::doRestore() + throw (RuntimeException) +{ + ResetableGuard aGuard(m_aLock); + m_bRestored = sal_False; + try { + css::uno::Reference< XDispatch > xDispatch(m_xSMGR->createInstance(SERVICENAME_AUTORECOVERY), UNO_QUERY_THROW); + + URL aURL; + aURL.Complete = OUString::createFromAscii("vnd.sun.star.autorecovery:/doSessionRestore"); + css::uno::Reference< XURLTransformer > xURLTransformer(m_xSMGR->createInstance(SERVICENAME_URLTRANSFORMER), UNO_QUERY_THROW); + xURLTransformer->parseStrict(aURL); + Sequence< PropertyValue > args; + xDispatch->addStatusListener(this, aURL); + xDispatch->dispatch(aURL, args); + m_bRestored = sal_True; + + } catch (com::sun::star::uno::Exception& e) { + OString aMsg = OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8); + OSL_ENSURE(sal_False, aMsg.getStr()); + } + + return m_bRestored; +} + + +void SAL_CALL SessionListener::doSave( sal_Bool bShutdown, sal_Bool /*bCancelable*/ ) + throw (RuntimeException) +{ + if (bShutdown) + { + m_bSessionStoreRequested = sal_True; // there is no need to protect it with mutex + if ( m_bAllowUserInteractionOnQuit && m_rSessionManager.is() ) + m_rSessionManager->queryInteraction( static_cast< css::frame::XSessionManagerListener* >( this ) ); + else + StoreSession( sal_True ); + } + // we don't have anything to do so tell the session manager we're done + else if( m_rSessionManager.is() ) + m_rSessionManager->saveDone( this ); +} + +void SAL_CALL SessionListener::approveInteraction( sal_Bool bInteractionGranted ) + throw (RuntimeException) +{ + // do AutoSave as the first step + ResetableGuard aGuard(m_aLock); + + if ( bInteractionGranted ) + { + // close the office documents in normal way + try + { + // first of all let the session be stored to be sure that we lose no information + StoreSession( sal_False ); + + css::uno::Reference< css::frame::XDesktop > xDesktop( m_xSMGR->createInstance(SERVICENAME_DESKTOP), css::uno::UNO_QUERY_THROW); + m_bTerminated = xDesktop->terminate(); + + if ( m_rSessionManager.is() ) + { + // false means that the application closing has been cancelled + if ( !m_bTerminated ) + m_rSessionManager->cancelShutdown(); + else + m_rSessionManager->interactionDone( this ); + } + } + catch( css::uno::Exception& ) + { + StoreSession( sal_True ); + m_rSessionManager->interactionDone( this ); + } + + if ( m_rSessionManager.is() ) + m_rSessionManager->saveDone(this); + } + else + { + StoreSession( sal_True ); + } +} + +void SessionListener::shutdownCanceled() + throw (RuntimeException) +{ + // set the state back + m_bSessionStoreRequested = sal_False; // there is no need to protect it with mutex +} + +void SessionListener::doQuit() + throw (RuntimeException) +{ + if ( m_bSessionStoreRequested && !m_bTerminated ) + { + // let the session be closed quietly in this case + QuitSessionQuietly(); + } +} + +} diff --git a/framework/source/services/substitutepathvars.cxx b/framework/source/services/substitutepathvars.cxx new file mode 100644 index 000000000000..90c460b74298 --- /dev/null +++ b/framework/source/services/substitutepathvars.cxx @@ -0,0 +1,1249 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_________________________________________________________________________________________________________________ +// my own includes +//_________________________________________________________________________________________________________________ +#include "services/substitutepathvars.hxx" +#include <threadhelp/resetableguard.hxx> +#include <helper/networkdomain.hxx> +#include "services.h" + +//_________________________________________________________________________________________________________________ +// interface includes +//_________________________________________________________________________________________________________________ +#include <com/sun/star/beans/XPropertySet.hpp> + +//_________________________________________________________________________________________________________________ +// includes of other projects +//_________________________________________________________________________________________________________________ +#include <unotools/configitem.hxx> +#include <unotools/localfilehelper.hxx> +#include <unotools/configmgr.hxx> + +#ifndef _UTL_BOOTSTRAP_HXX_ +#include <unotools/bootstrap.hxx> +#endif +#include <osl/mutex.hxx> +#include <osl/file.hxx> +#include <osl/security.hxx> +#include <osl/socket.hxx> +#include <vos/process.hxx> +#include <i18npool/mslangid.hxx> +#include <tools/urlobj.hxx> +#include <tools/resmgr.hxx> +#include <tools/debug.hxx> +#include <tools/wldcrd.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/bootstrap.hxx> + +#include <comphelper/configurationhelper.hxx> + +#include <string.h> + +//_________________________________________________________________________________________________________________ +// Defines +//_________________________________________________________________________________________________________________ +// + +#define STRPOS_NOTFOUND (sal_Int32)-1 + +#define ASCII_STR( val ) rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( val )) + +#define SEARCHPATH_DELIMITER ';' + +// Variable start/end characters +#define SIGN_STARTVARIABLE ASCII_STR("$(") +#define SIGN_ENDVARIABLE ASCII_STR(")") + +// Length of SUBSTITUTE_... to replace it with real values. +#define REPLACELENGTH_INST 7 +#define REPLACELENGTH_PROG 7 +#define REPLACELENGTH_USER 7 +#define REPLACELENGTH_WORK 7 +#define REPLACELENGTH_HOME 7 +#define REPLACELENGTH_TEMP 7 +#define REPLACELENGTH_PATH 7 +#define REPLACELENGTH_INSTPATH 11 +#define REPLACELENGTH_PROGPATH 11 +#define REPLACELENGTH_USERPATH 11 +#define REPLACELENGTH_INSTURL 10 +#define REPLACELENGTH_PROGURL 10 +#define REPLACELENGTH_USERURL 10 +#define REPLACELENGTH_PATH 7 +#define REPLACELENGTH_LANG 7 +#define REPLACELENGTH_LANGID 9 +#define REPLACELENGTH_VLANG 8 +#define REPLACELENGTH_WORKDIRURL 13 +// --> PB 2004-10-27 #i32656# - new variable of hierachy service +#define REPLACELENGTH_BASEINSTURL 14 +#define REPLACELENGTH_USERDATAURL 14 +// <-- + +// Name of the pre defined path variables +#define VARIABLE_INST "$(inst)" +#define VARIABLE_PROG "$(prog)" +#define VARIABLE_USER "$(user)" +#define VARIABLE_WORK "$(work)" +#define VARIABLE_HOME "$(home)" +#define VARIABLE_TEMP "$(temp)" +#define VARIABLE_PATH "$(path)" +#define VARIABLE_LANG "$(lang)" +#define VARIABLE_LANGID "$(langid)" +#define VARIABLE_VLANG "$(vlang)" +#define VARIABLE_INSTPATH "$(instpath)" +#define VARIABLE_PROGPATH "$(progpath)" +#define VARIABLE_USERPATH "$(userpath)" +#define VARIABLE_INSTURL "$(insturl)" +#define VARIABLE_PROGURL "$(progurl)" +#define VARIABLE_USERURL "$(userurl)" +#define VARIABLE_WORKDIRURL "$(workdirurl)" +// --> PB 2004-10-27 #i32656# - new variable of hierachy service +#define VARIABLE_BASEINSTURL "$(baseinsturl)" +#define VARIABLE_USERDATAURL "$(userdataurl)" +// <-- + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; +using namespace com::sun::star::util; +using namespace com::sun::star::lang; +using namespace com::sun::star::container; + +//_________________________________________________________________________________________________________________ +// Namespace +//_________________________________________________________________________________________________________________ +// + +namespace framework +{ + +struct FixedVariable +{ + const char* pVarName; + PreDefVariable nEnumValue; + int nStrLen; +}; + +struct TableEntry +{ + const char* pOSString; + int nStrLen; +}; + +// Table with valid operating system strings +// Name of the os as char* and the length +// of the string +static TableEntry aOSTable[OS_COUNT] = +{ + { "WINDOWS" , 7 }, + { "UNIX" , 4 }, + { "SOLARIS" , 7 }, + { "LINUX" , 5 }, + { "" , 0 } // unknown +}; + +// Table with valid environment variables +// Name of the environment type as a char* and +// the length of the string. +static TableEntry aEnvTable[ET_COUNT] = +{ + { "HOST" , 4 }, + { "YPDOMAIN" , 8 }, + { "DNSDOMAIN" , 9 }, + { "NTDOMAIN" , 8 }, + { "OS" , 2 }, + { "" , 0 } // unknown +}; + +// Priority table for the environment types. Lower numbers define +// a higher priority. Equal numbers has the same priority that means +// that the first match wins!! +static sal_Int16 aEnvPrioTable[ET_COUNT] = +{ + 1, // ET_HOST + 2, // ET_IPDOMAIN + 2, // ET_DNSDOMAIN + 2, // ET_NTDOMAIN + 3, // ET_OS + 99, // ET_UNKNOWN +}; + +// Table with all fixed/predefined variables supported. +static FixedVariable aFixedVarTable[] = +{ + { VARIABLE_INST, PREDEFVAR_INST, REPLACELENGTH_INST }, + { VARIABLE_PROG, PREDEFVAR_PROG, REPLACELENGTH_PROG }, + { VARIABLE_USER, PREDEFVAR_USER, REPLACELENGTH_USER }, + { VARIABLE_WORK, PREDEFVAR_WORK, REPLACELENGTH_WORK }, // Special variable (transient)! + { VARIABLE_HOME, PREDEFVAR_HOME, REPLACELENGTH_HOME }, + { VARIABLE_TEMP, PREDEFVAR_TEMP, REPLACELENGTH_TEMP }, + { VARIABLE_PATH, PREDEFVAR_PATH, REPLACELENGTH_PATH }, + { VARIABLE_LANG, PREDEFVAR_LANG, REPLACELENGTH_LANG }, + { VARIABLE_LANGID, PREDEFVAR_LANGID, REPLACELENGTH_LANGID }, + { VARIABLE_VLANG, PREDEFVAR_VLANG, REPLACELENGTH_VLANG }, + { VARIABLE_INSTPATH, PREDEFVAR_INSTPATH, REPLACELENGTH_INSTPATH }, + { VARIABLE_PROGPATH, PREDEFVAR_PROGPATH, REPLACELENGTH_PROGPATH }, + { VARIABLE_USERPATH, PREDEFVAR_USERPATH, REPLACELENGTH_USERPATH }, + { VARIABLE_INSTURL, PREDEFVAR_INSTURL, REPLACELENGTH_INSTURL }, + { VARIABLE_PROGURL, PREDEFVAR_PROGURL, REPLACELENGTH_PROGURL }, + { VARIABLE_USERURL, PREDEFVAR_USERURL, REPLACELENGTH_USERURL }, + { VARIABLE_WORKDIRURL, PREDEFVAR_WORKDIRURL, REPLACELENGTH_WORKDIRURL }, // Special variable (transient) and don't use for resubstitution! + // --> PB 2004-10-27 #i32656# - new variable of hierachy service + { VARIABLE_BASEINSTURL, PREDEFVAR_BASEINSTURL, REPLACELENGTH_BASEINSTURL }, + { VARIABLE_USERDATAURL, PREDEFVAR_USERDATAURL, REPLACELENGTH_USERDATAURL }, + // <-- + { "$(brandbaseurl)", PREDEFVAR_BRANDBASEURL, + RTL_CONSTASCII_LENGTH("$(brandbaseurl)") } +}; + +//_________________________________________________________________________________________________________________ +// Implementation helper classes +//_________________________________________________________________________________________________________________ +// + +OperatingSystem SubstitutePathVariables_Impl::GetOperatingSystemFromString( const rtl::OUString& aOSString ) +{ + for ( int i = 0; i < OS_COUNT; i++ ) + { + if ( aOSString.equalsIgnoreAsciiCaseAsciiL( aOSTable[i].pOSString, aOSTable[i].nStrLen )) + return (OperatingSystem)i; + } + + return OS_UNKNOWN; +} + +EnvironmentType SubstitutePathVariables_Impl::GetEnvTypeFromString( const rtl::OUString& aEnvTypeString ) +{ + for ( int i = 0; i < ET_COUNT; i++ ) + { + if ( aEnvTypeString.equalsIgnoreAsciiCaseAsciiL( aEnvTable[i].pOSString, aEnvTable[i].nStrLen )) + return (EnvironmentType)i; + } + + return ET_UNKNOWN; +} + +SubstitutePathVariables_Impl::SubstitutePathVariables_Impl( const Link& aNotifyLink ) : + utl::ConfigItem( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Office.Substitution" ))), + m_bYPDomainRetrieved( sal_False ), + m_bDNSDomainRetrieved( sal_False ), + m_bNTDomainRetrieved( sal_False ), + m_bHostRetrieved( sal_False ), + m_bOSRetrieved( sal_False ), + m_aListenerNotify( aNotifyLink ), + m_aSharePointsNodeName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SharePoints" ))), + m_aDirPropertyName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/Directory" ))), + m_aEnvPropertyName( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/Environment" ))), + m_aLevelSep( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/" ))) +{ + // Enable notification mechanism + // We need it to get information about changes outside these class on our configuration branch + Sequence< rtl::OUString > aNotifySeq( 1 ); + aNotifySeq[0] = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SharePoints" )); + EnableNotification( aNotifySeq, sal_True ); +} + +SubstitutePathVariables_Impl::~SubstitutePathVariables_Impl() +{ +} + +void SubstitutePathVariables_Impl::GetSharePointsRules( SubstituteVariables& aSubstVarMap ) +{ + Sequence< rtl::OUString > aSharePointNames; + ReadSharePointsFromConfiguration( aSharePointNames ); + + if ( aSharePointNames.getLength() > 0 ) + { + sal_Int32 nSharePoints = 0; + + // Read SharePoints container from configuration + while ( nSharePoints < aSharePointNames.getLength() ) + { + rtl::OUString aSharePointNodeName( m_aSharePointsNodeName ); + aSharePointNodeName += rtl::OUString::createFromAscii( "/" ); + aSharePointNodeName += aSharePointNames[ nSharePoints ]; + + SubstituteRuleVector aRuleSet; + ReadSharePointRuleSetFromConfiguration( aSharePointNames[ nSharePoints ], aSharePointNodeName, aRuleSet ); + if ( !aRuleSet.empty() ) + { + // We have at minimum one rule. Filter the correct rule out of the rule set + // and put into our SubstituteVariable map + SubstituteRule aActiveRule; + if ( FilterRuleSet( aRuleSet, aActiveRule )) + { + // We have found an active rule + aActiveRule.aSubstVariable = aSharePointNames[ nSharePoints ]; + aSubstVarMap.insert( SubstituteVariables::value_type( + aActiveRule.aSubstVariable, aActiveRule )); + } + } + + ++nSharePoints; + } + } +} + +void SubstitutePathVariables_Impl::Notify( const com::sun::star::uno::Sequence< rtl::OUString >& /*aPropertyNames*/ ) +{ + // NOT implemented yet! +} + +void SubstitutePathVariables_Impl::Commit() +{ +} + + +//_________________________________________________________________________________________________________________ +// private methods +//_________________________________________________________________________________________________________________ +// + +OperatingSystem SubstitutePathVariables_Impl::GetOperatingSystem() +{ + if ( !m_bOSRetrieved ) + { +#ifdef SOLARIS + m_eOSType = OS_SOLARIS; +#elif defined LINUX + m_eOSType = OS_LINUX; +#elif defined WIN32 + m_eOSType = OS_WINDOWS; +#elif defined UNIX + m_eOSType = OS_UNIX; +#else + m_eOSType = OS_UNKNOWN; +#endif + m_bOSRetrieved = sal_True; + } + + return m_eOSType; +} + +const rtl::OUString& SubstitutePathVariables_Impl::GetYPDomainName() +{ + if ( !m_bYPDomainRetrieved ) + { + m_aYPDomain = NetworkDomain::GetYPDomainName().toAsciiLowerCase(); + m_bYPDomainRetrieved = sal_True; + } + + return m_aYPDomain; +} + +const rtl::OUString& SubstitutePathVariables_Impl::GetDNSDomainName() +{ + if ( !m_bDNSDomainRetrieved ) + { + rtl::OUString aTemp; + osl::SocketAddr aSockAddr; + oslSocketResult aResult; + + rtl::OUString aHostName = GetHostName(); + osl::SocketAddr::resolveHostname( aHostName, aSockAddr ); + aTemp = aSockAddr.getHostname( &aResult ); + + // DNS domain name begins after the first "." + sal_Int32 nIndex = aTemp.indexOf( '.' ); + if ( nIndex >= 0 && aTemp.getLength() > nIndex+1 ) + m_aDNSDomain = aTemp.copy( nIndex+1 ).toAsciiLowerCase(); + else + m_aDNSDomain = rtl::OUString(); + + m_bDNSDomainRetrieved = sal_True; + } + + return m_aDNSDomain; +} + +const rtl::OUString& SubstitutePathVariables_Impl::GetNTDomainName() +{ + if ( !m_bNTDomainRetrieved ) + { + m_aNTDomain = NetworkDomain::GetNTDomainName().toAsciiLowerCase(); + m_bNTDomainRetrieved = sal_True; + } + + return m_aNTDomain; +} + +const rtl::OUString& SubstitutePathVariables_Impl::GetHostName() +{ + if ( !m_bHostRetrieved ) + { + rtl::OUString aHostName; + oslSocketResult aSocketResult; + + m_aHost = osl::SocketAddr::getLocalHostname( &aSocketResult ).toAsciiLowerCase(); + } + + return m_aHost; +} + +sal_Bool SubstitutePathVariables_Impl::FilterRuleSet( const SubstituteRuleVector& aRuleSet, SubstituteRule& aActiveRule ) +{ + sal_Bool bResult = sal_False; + + if ( !aRuleSet.empty() ) + { + sal_Int16 nPrioCurrentRule = aEnvPrioTable[ ET_UNKNOWN ]; + const sal_uInt32 nCount = aRuleSet.size(); + for ( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + const SubstituteRule& aRule = aRuleSet[nIndex]; + EnvironmentType eEnvType = aRule.aEnvType; + + // Check if environment type has a higher priority than current one! + if ( nPrioCurrentRule > aEnvPrioTable[eEnvType] ) + { + switch ( eEnvType ) + { + case ET_HOST: + { + rtl::OUString aHost = GetHostName(); + rtl::OUString aHostStr; + aRule.aEnvValue >>= aHostStr; + aHostStr = aHostStr.toAsciiLowerCase(); + + // Pattern match if domain environment match + WildCard aPattern(aHostStr); + sal_Bool bMatch = aPattern.Matches(aHost); + if ( bMatch ) + { + aActiveRule = aRule; + bResult = sal_True; + nPrioCurrentRule = aEnvPrioTable[eEnvType]; + } + } + break; + + case ET_YPDOMAIN: + case ET_DNSDOMAIN: + case ET_NTDOMAIN: + { + rtl::OUString aDomain; + rtl::OUString aDomainStr; + aRule.aEnvValue >>= aDomainStr; + aDomainStr = aDomainStr.toAsciiLowerCase(); + + // Retrieve the correct domain value + if ( eEnvType == ET_YPDOMAIN ) + aDomain = GetYPDomainName(); + else if ( eEnvType == ET_DNSDOMAIN ) + aDomain = GetDNSDomainName(); + else + aDomain = GetNTDomainName(); + + // Pattern match if domain environment match + WildCard aPattern(aDomainStr); + sal_Bool bMatch = aPattern.Matches(aDomain); + if ( bMatch ) + { + aActiveRule = aRule; + bResult = sal_True; + nPrioCurrentRule = aEnvPrioTable[eEnvType]; + } + } + break; + + case ET_OS: + { + // No pattern matching for OS type + OperatingSystem eOSType = GetOperatingSystem(); + + sal_Int16 nValue = 0; + aRule.aEnvValue >>= nValue; + + sal_Bool bUnix = ( eOSType == OS_LINUX ) || ( eOSType == OS_SOLARIS ); + OperatingSystem eRuleOSType = (OperatingSystem)nValue; + + // Match if OS identical or rule is set to UNIX and OS is LINUX/SOLARIS! + if (( eRuleOSType == eOSType ) || ( eRuleOSType == OS_UNIX && bUnix )) + { + aActiveRule = aRule; + bResult = sal_True; + nPrioCurrentRule = aEnvPrioTable[eEnvType]; + } + } + break; + + case ET_UNKNOWN: // nothing to do + break; + + default: + break; + } + } + } + } + + return bResult; +} + +void SubstitutePathVariables_Impl::ReadSharePointsFromConfiguration( Sequence< rtl::OUString >& aSharePointsSeq ) +{ + //returns all the names of all share point nodes + aSharePointsSeq = GetNodeNames( m_aSharePointsNodeName ); +} + +void SubstitutePathVariables_Impl::ReadSharePointRuleSetFromConfiguration( + const rtl::OUString& aSharePointName, + const rtl::OUString& aSharePointNodeName, + SubstituteRuleVector& rRuleSet ) +{ + Sequence< rtl::OUString > aSharePointMappingsNodeNames = GetNodeNames( aSharePointNodeName, utl::CONFIG_NAME_LOCAL_PATH ); + + sal_Int32 nSharePointMapping = 0; + while ( nSharePointMapping < aSharePointMappingsNodeNames.getLength() ) + { + rtl::OUString aSharePointMapping( aSharePointNodeName ); + aSharePointMapping += m_aLevelSep; + aSharePointMapping += aSharePointMappingsNodeNames[ nSharePointMapping ]; + + // Read SharePointMapping + rtl::OUString aDirValue; + rtl::OUString aDirProperty( aSharePointMapping ); + aDirProperty += m_aDirPropertyName; + + // Read only the directory property + Sequence< rtl::OUString > aDirPropertySeq( 1 ); + aDirPropertySeq[0] = aDirProperty; + + Sequence< Any > aValueSeq = GetProperties( aDirPropertySeq ); + if ( aValueSeq.getLength() == 1 ) + aValueSeq[0] >>= aDirValue; + + // Read the environment setting + rtl::OUString aEnvUsed; + rtl::OUString aEnvProperty( aSharePointMapping ); + aEnvProperty += m_aEnvPropertyName; + Sequence< rtl::OUString > aEnvironmentVariable = GetNodeNames( aEnvProperty ); + + // Filter the property which has a value set + Sequence< rtl::OUString > aEnvUsedPropertySeq( aEnvironmentVariable.getLength() ); + + rtl::OUString aEnvUsePropNameTemplate( aEnvProperty ); + aEnvUsePropNameTemplate += m_aLevelSep; + + for ( sal_Int32 nProperty = 0; nProperty < aEnvironmentVariable.getLength(); nProperty++ ) + aEnvUsedPropertySeq[nProperty] = rtl::OUString( aEnvUsePropNameTemplate + aEnvironmentVariable[nProperty] ); + + Sequence< Any > aEnvUsedValueSeq; + aEnvUsedValueSeq = GetProperties( aEnvUsedPropertySeq ); + + rtl::OUString aEnvUsedValue; + for ( sal_Int32 nIndex = 0; nIndex < aEnvironmentVariable.getLength(); nIndex++ ) + { + if ( aEnvUsedValueSeq[nIndex] >>= aEnvUsedValue ) + { + aEnvUsed = aEnvironmentVariable[nIndex]; + break; + } + } + + // Decode the environment and optional the operatng system settings + Any aEnvValue; + EnvironmentType eEnvType = GetEnvTypeFromString( aEnvUsed ); + if ( eEnvType == ET_OS ) + { + OperatingSystem eOSType = GetOperatingSystemFromString( aEnvUsedValue ); + aEnvValue <<= (sal_Int16)eOSType; + } + else + aEnvValue <<= aEnvUsedValue; + + // Create rule struct and push it into the rule set + SubstituteRule aRule( aSharePointName, aDirValue, aEnvValue, eEnvType ); + rRuleSet.push_back( aRule ); + + ++nSharePointMapping; + } +} + +//***************************************************************************************************************** +// XInterface, XTypeProvider, XServiceInfo +//***************************************************************************************************************** +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE ( SubstitutePathVariables , + ::cppu::OWeakObject , + SERVICENAME_SUBSTITUTEPATHVARIABLES , + IMPLEMENTATIONNAME_SUBSTITUTEPATHVARIABLES + ) + +DEFINE_INIT_SERVICE ( SubstitutePathVariables, {} ) + + +SubstitutePathVariables::SubstitutePathVariables( const Reference< XMultiServiceFactory >& xServiceManager ) : + ThreadHelpBase(), + m_aVarStart( SIGN_STARTVARIABLE ), + m_aVarEnd( SIGN_ENDVARIABLE ), + m_aImpl( LINK( this, SubstitutePathVariables, implts_ConfigurationNotify )), + m_xServiceManager( xServiceManager ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::SubstitutePathVariables" ); + int i; + + SetPredefinedPathVariables( m_aPreDefVars ); + m_aImpl.GetSharePointsRules( m_aSubstVarMap ); + + // Init the predefined/fixed variable to index hash map + for ( i = 0; i < PREDEFVAR_COUNT; i++ ) + { + // Store variable name into struct of predefined/fixed variables + m_aPreDefVars.m_FixedVarNames[i] = rtl::OUString::createFromAscii( aFixedVarTable[i].pVarName ); + + // Create hash map entry + m_aPreDefVarMap.insert( VarNameToIndexMap::value_type( + m_aPreDefVars.m_FixedVarNames[i], aFixedVarTable[i].nEnumValue ) ); + } + + // Sort predefined/fixed variable to path length + for ( i = 0; i < PREDEFVAR_COUNT; i++ ) + { + if (( i != PREDEFVAR_WORKDIRURL ) && + ( i != PREDEFVAR_PATH )) + { + // Special path variables, don't include into automatic resubstituion search! + // $(workdirurl) is not allowed to resubstitute! This variable is the value of path settings entry + // and it could be possible that it will be resubstituted by itself!! + // Example: WORK_PATH=c:\test, $(workdirurl)=WORK_PATH => WORK_PATH=$(workdirurl) and this cannot be substituted! + ReSubstFixedVarOrder aFixedVar; + aFixedVar.eVariable = aFixedVarTable[i].nEnumValue; + aFixedVar.nVarValueLength = m_aPreDefVars.m_FixedVar[(sal_Int32)aFixedVar.eVariable].getLength(); + m_aReSubstFixedVarOrder.push_back( aFixedVar ); + } + } + m_aReSubstFixedVarOrder.sort(); + + // Sort user variables to path length + SubstituteVariables::const_iterator pIter; + for ( pIter = m_aSubstVarMap.begin(); pIter != m_aSubstVarMap.end(); pIter++ ) + { + ReSubstUserVarOrder aUserOrderVar; + rtl::OUStringBuffer aStrBuffer( pIter->second.aSubstVariable.getLength() ); + aStrBuffer.append( m_aVarStart ); + aStrBuffer.append( pIter->second.aSubstVariable ); + aStrBuffer.append( m_aVarEnd ); + aUserOrderVar.aVarName = aStrBuffer.makeStringAndClear(); + aUserOrderVar.nVarValueLength = pIter->second.aSubstVariable.getLength(); + m_aReSubstUserVarOrder.push_back( aUserOrderVar ); + } + m_aReSubstUserVarOrder.sort(); +} + +SubstitutePathVariables::~SubstitutePathVariables() +{ +} + +// XStringSubstitution +rtl::OUString SAL_CALL SubstitutePathVariables::substituteVariables( const ::rtl::OUString& aText, sal_Bool bSubstRequired ) +throw ( NoSuchElementException, RuntimeException ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::substituteVariables" ); + ResetableGuard aLock( m_aLock ); + return impl_substituteVariable( aText, bSubstRequired ); +} + +rtl::OUString SAL_CALL SubstitutePathVariables::reSubstituteVariables( const ::rtl::OUString& aText ) +throw ( RuntimeException ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::reSubstituteVariables" ); + ResetableGuard aLock( m_aLock ); + return impl_reSubstituteVariables( aText ); +} + +rtl::OUString SAL_CALL SubstitutePathVariables::getSubstituteVariableValue( const ::rtl::OUString& aVariable ) +throw ( NoSuchElementException, RuntimeException ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::getSubstituteVariableValue" ); + ResetableGuard aLock( m_aLock ); + return impl_getSubstituteVariableValue( aVariable ); +} + +//_________________________________________________________________________________________________________________ +// protected methods +//_________________________________________________________________________________________________________________ +// + +IMPL_LINK( SubstitutePathVariables, implts_ConfigurationNotify, SubstitutePathNotify*, EMPTYARG ) +{ + /* SAFE AREA ----------------------------------------------------------------------------------------------- */ + ResetableGuard aLock( m_aLock ); + + return 0; +} + +rtl::OUString SubstitutePathVariables::ConvertOSLtoUCBURL( const rtl::OUString& aOSLCompliantURL ) const +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::ConvertOSLtoUCBURL" ); + String aResult; + rtl::OUString aTemp; + + osl::FileBase::getSystemPathFromFileURL( aOSLCompliantURL, aTemp ); + utl::LocalFileHelper::ConvertPhysicalNameToURL( aTemp, aResult ); + + // Not all OSL URL's can be mapped to UCB URL's! + if ( aResult.Len() == 0 ) + return aOSLCompliantURL; + else + return rtl::OUString( aResult ); +} + +rtl::OUString SubstitutePathVariables::GetWorkPath() const +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::GetWorkPath" ); + rtl::OUString aWorkPath; + ::comphelper::ConfigurationHelper::readDirectKey( + m_xServiceManager, + ::rtl::OUString::createFromAscii("org.openoffice.Office.Paths"), + ::rtl::OUString::createFromAscii("Paths/Work"), + ::rtl::OUString::createFromAscii("WritePath"), + ::comphelper::ConfigurationHelper::E_READONLY) >>= aWorkPath; + return aWorkPath; +} + +rtl::OUString SubstitutePathVariables::GetWorkVariableValue() const +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::GetWorkVariableValue" ); + ::rtl::OUString aWorkPath; + ::comphelper::ConfigurationHelper::readDirectKey( + m_xServiceManager, + ::rtl::OUString::createFromAscii("org.openoffice.Office.Paths"), + ::rtl::OUString::createFromAscii("Variables"), + ::rtl::OUString::createFromAscii("Work"), + ::comphelper::ConfigurationHelper::E_READONLY) >>= aWorkPath; + + // fallback to $HOME in case platform dependend config layer does not return + // an usuable work dir value. + if (aWorkPath.getLength() < 1) + { + osl::Security aSecurity; + aSecurity.getHomeDir( aWorkPath ); + } + return ConvertOSLtoUCBURL( aWorkPath ); +} + +rtl::OUString SubstitutePathVariables::GetHomeVariableValue() const +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::GetHomeVariableValue" ); + osl::Security aSecurity; + rtl::OUString aHomePath; + + aSecurity.getHomeDir( aHomePath ); + return ConvertOSLtoUCBURL( aHomePath ); +} + +rtl::OUString SubstitutePathVariables::GetPathVariableValue() const +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::GetPathVariableValue" ); + const int PATH_EXTEND_FACTOR = 120; + + rtl::OUString aRetStr; + const char* pEnv = getenv( "PATH" ); + + if ( pEnv ) + { + rtl::OUString aTmp; + rtl::OUString aPathList( pEnv, strlen( pEnv ), gsl_getSystemTextEncoding() ); + rtl::OUStringBuffer aPathStrBuffer( aPathList.getLength() * PATH_EXTEND_FACTOR / 100 ); + + sal_Bool bAppendSep = sal_False; + sal_Int32 nToken = 0; + do + { + ::rtl::OUString sToken = aPathList.getToken(0, SAL_PATHSEPARATOR, nToken); + if (sToken.getLength()) + { + osl::FileBase::getFileURLFromSystemPath( sToken, aTmp ); + if ( bAppendSep ) + aPathStrBuffer.appendAscii( ";" ); // Office uses ';' as path separator + aPathStrBuffer.append( aTmp ); + bAppendSep = sal_True; + } + } + while(nToken>=0); + + aRetStr = aPathStrBuffer.makeStringAndClear(); + } + + return aRetStr; +} + +rtl::OUString SubstitutePathVariables::impl_substituteVariable( const ::rtl::OUString& rText, sal_Bool bSubstRequired ) +throw ( NoSuchElementException, RuntimeException ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::impl_substituteVariable" ); + // This is maximal recursive depth supported! + const sal_Int32 nMaxRecursiveDepth = 8; + + rtl::OUString aWorkText = rText; + rtl::OUString aResult; + + // Use vector with strings to detect endless recursions! + std::vector< rtl::OUString > aEndlessRecursiveDetector; + + // Search for first occure of "$(...". + sal_Int32 nDepth = 0; + sal_Int32 bSubstitutionCompleted = sal_False; + sal_Int32 nPosition = aWorkText.indexOf( m_aVarStart ); // = first position of "$(" in string + sal_Int32 nLength = 0; // = count of letters from "$(" to ")" in string + sal_Bool bVarNotSubstituted = sal_False; + + // Have we found any variable like "$(...)"? + if ( nPosition != STRPOS_NOTFOUND ) + { + // Yes; Get length of found variable. + // If no ")" was found - nLength is set to 0 by default! see before. + sal_Int32 nEndPosition = aWorkText.indexOf( m_aVarEnd, nPosition ); + if ( nEndPosition != STRPOS_NOTFOUND ) + nLength = nEndPosition - nPosition + 1; + } + + // Is there something to replace ? + sal_Bool bWorkRetrieved = sal_False; + sal_Bool bWorkDirURLRetrieved = sal_False; + while ( !bSubstitutionCompleted && nDepth < nMaxRecursiveDepth ) + { + while ( ( nPosition != STRPOS_NOTFOUND ) && ( nLength > 3 ) ) // "$(" ")" + { + // YES; Get the next variable for replace. + sal_Int32 nReplaceLength = 0; + rtl::OUString aReplacement; + rtl::OUString aSubString = aWorkText.copy( nPosition, nLength ); + rtl::OUString aSubVarString; + + // Path variables are not case sensitive! + aSubVarString = aSubString.toAsciiLowerCase(); + VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( aSubVarString ); + if ( pNTOIIter != m_aPreDefVarMap.end() ) + { + // Fixed/Predefined variable found + PreDefVariable nIndex = (PreDefVariable)pNTOIIter->second; + + // Determine variable value and length from array/table + if ( nIndex == PREDEFVAR_WORK && !bWorkRetrieved ) + { + // Transient value, retrieve it again + m_aPreDefVars.m_FixedVar[ (PreDefVariable)nIndex ] = GetWorkVariableValue(); + bWorkRetrieved = sal_True; + } + else if ( nIndex == PREDEFVAR_WORKDIRURL && !bWorkDirURLRetrieved ) + { + // Transient value, retrieve it again + m_aPreDefVars.m_FixedVar[ (PreDefVariable)nIndex ] = GetWorkPath(); + bWorkDirURLRetrieved = sal_True; + } + + aReplacement = m_aPreDefVars.m_FixedVar[ (PreDefVariable)nIndex ]; + nReplaceLength = nLength; + } + else + { + // Extract the variable name and try to find in the user defined variable set + rtl::OUString aVarName = aSubString.copy( 2, nLength-3 ); + SubstituteVariables::const_iterator pIter = m_aSubstVarMap.find( aVarName ); + if ( pIter != m_aSubstVarMap.end() ) + { + // found! + aReplacement = pIter->second.aSubstValue; + nReplaceLength = nLength; + } + } + + // Have we found something to replace? + if ( nReplaceLength > 0 ) + { + // Yes ... then do it. + aWorkText = aWorkText.replaceAt( nPosition, nReplaceLength, aReplacement ); + } + else + { + // Variable not known + bVarNotSubstituted = sal_False; + nPosition += nLength; + } + + // Step after replaced text! If no text was replaced (unknown variable!), + // length of aReplacement is 0 ... and we don't step then. + nPosition += aReplacement.getLength(); + + // We must control index in string before call something at OUString! + // The OUString-implementation don't do it for us :-( but the result is not defined otherwise. + if ( nPosition + 1 > aWorkText.getLength() ) + { + // Position is out of range. Break loop! + nPosition = STRPOS_NOTFOUND; + nLength = 0; + } + else + { + // Else; Position is valid. Search for next variable to replace. + nPosition = aWorkText.indexOf( m_aVarStart, nPosition ); + // Have we found any variable like "$(...)"? + if ( nPosition != STRPOS_NOTFOUND ) + { + // Yes; Get length of found variable. If no ")" was found - nLength must set to 0! + nLength = 0; + sal_Int32 nEndPosition = aWorkText.indexOf( m_aVarEnd, nPosition ); + if ( nEndPosition != STRPOS_NOTFOUND ) + nLength = nEndPosition - nPosition + 1; + } + } + } + + nPosition = aWorkText.indexOf( m_aVarStart ); + if ( nPosition == -1 ) + { + bSubstitutionCompleted = sal_True; + break; // All variables are substituted + } + else + { + // Check for recursion + const sal_uInt32 nCount = aEndlessRecursiveDetector.size(); + for ( sal_uInt32 i=0; i < nCount; i++ ) + { + if ( aEndlessRecursiveDetector[i] == aWorkText ) + { + if ( bVarNotSubstituted ) + break; // Not all variables could be substituted! + else + { + nDepth = nMaxRecursiveDepth; + break; // Recursion detected! + } + } + } + + aEndlessRecursiveDetector.push_back( aWorkText ); + + // Initialize values for next + sal_Int32 nEndPosition = aWorkText.indexOf( m_aVarEnd, nPosition ); + if ( nEndPosition != STRPOS_NOTFOUND ) + nLength = nEndPosition - nPosition + 1; + bVarNotSubstituted = sal_False; + ++nDepth; + } + } + + // Fill return value with result + if ( bSubstitutionCompleted ) + { + // Substitution successfull! + aResult = aWorkText; + } + else + { + // Substitution not successfull! + if ( nDepth == nMaxRecursiveDepth ) + { + // recursion depth reached! + if ( bSubstRequired ) + { + rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM( "Endless recursion detected. Cannot substitute variables!" )); + throw NoSuchElementException( aMsg, (cppu::OWeakObject *)this ); + } + else + aResult = rText; + } + else + { + // variable in text but unknwon! + if ( bSubstRequired ) + { + rtl::OUString aMsg( RTL_CONSTASCII_USTRINGPARAM( "Unknown variable found!" )); + throw NoSuchElementException( aMsg, (cppu::OWeakObject *)this ); + } + else + aResult = aWorkText; + } + } + + return aResult; +} + +rtl::OUString SubstitutePathVariables::impl_reSubstituteVariables( const ::rtl::OUString& rURL ) +throw ( RuntimeException ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::impl_reSubstituteVariables" ); + rtl::OUString aURL; + + INetURLObject aUrl( rURL ); + if ( !aUrl.HasError() ) + aURL = aUrl.GetMainURL( INetURLObject::NO_DECODE ); + else + { + // Convert a system path to a UCB compliant URL before resubstitution + rtl::OUString aTemp; + if ( osl::FileBase::getFileURLFromSystemPath( rURL, aTemp ) == osl::FileBase::E_None ) + { + aTemp = ConvertOSLtoUCBURL( aTemp ); + if ( aTemp.getLength() ) + aURL = INetURLObject( aTemp ).GetMainURL( INetURLObject::NO_DECODE ); + else + return rURL; + } + else + { + // rURL is not a valid URL nor a osl system path. Give up and return error! + return rURL; + } + } + + // Due to a recursive definition this code must exchange variables with variables! + sal_Bool bResubstitutionCompleted = sal_False; + sal_Bool bVariableFound = sal_False; + + // Get transient predefined path variable $(work) value before starting resubstitution + m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue(); + + while ( !bResubstitutionCompleted ) + { + ReSubstFixedVarOrderVector::const_iterator pIterFixed; + for ( pIterFixed = m_aReSubstFixedVarOrder.begin(); pIterFixed != m_aReSubstFixedVarOrder.end(); pIterFixed++ ) + { + rtl::OUString aValue = m_aPreDefVars.m_FixedVar[ (sal_Int32)pIterFixed->eVariable ]; + sal_Int32 nPos = aURL.indexOf( aValue ); + if ( nPos >= 0 ) + { + sal_Bool bMatch = sal_True; + if ( pIterFixed->eVariable == PREDEFVAR_LANG || + pIterFixed->eVariable == PREDEFVAR_LANGID || + pIterFixed->eVariable == PREDEFVAR_VLANG ) + { + // Special path variables as they can occur in the middle of a path. Only match if they + // describe a whole directory and not only a substring of a directory! + const sal_Unicode* pStr = aURL.getStr(); + + if ( nPos > 0 ) + bMatch = ( aURL[ nPos-1 ] == '/' ); + + if ( bMatch ) + { + if ( nPos + aValue.getLength() < aURL.getLength() ) + bMatch = ( pStr[ nPos + aValue.getLength() ] == '/' ); + } + } + + if ( bMatch ) + { + rtl::OUStringBuffer aStrBuffer( aURL.getLength() ); + aStrBuffer.append( aURL.copy( 0, nPos ) ); + aStrBuffer.append( m_aPreDefVars.m_FixedVarNames[ (sal_Int32)pIterFixed->eVariable ] ); // Get the variable name for struct var name array! + aStrBuffer.append( aURL.copy( nPos + aValue.getLength(), ( aURL.getLength() - ( nPos + aValue.getLength() )) )); + aURL = aStrBuffer.makeStringAndClear(); + bVariableFound = sal_True; // Resubstitution not finished yet! + break; + } + } + } + + // This part can be iteratered more than one time as variables can contain variables again! + ReSubstUserVarOrderVector::const_iterator pIterUser; + for ( pIterUser = m_aReSubstUserVarOrder.begin(); pIterUser != m_aReSubstUserVarOrder.end(); pIterUser++ ) + { + rtl::OUString aVarValue = pIterUser->aVarName; + sal_Int32 nPos = aURL.indexOf( aVarValue ); + if ( nPos >= 0 ) + { + rtl::OUStringBuffer aStrBuffer( aURL.getLength() ); + aStrBuffer.append( aURL.copy( 0, nPos ) ); + aStrBuffer.append( m_aVarStart ); + aStrBuffer.append( aVarValue ); + aStrBuffer.append( m_aVarEnd ); + aStrBuffer.append( aURL.copy( nPos + aVarValue.getLength(), ( aURL.getLength() - ( nPos + aVarValue.getLength() )) )); + aURL = aStrBuffer.makeStringAndClear(); + bVariableFound = sal_True; // Resubstitution not finished yet! + } + } + + if ( !bVariableFound ) + bResubstitutionCompleted = sal_True; + else + bVariableFound = sal_False; // Next resubstitution + } + + return aURL; +} + +// This method support both request schemes "$("<varname>")" or "<varname>". +::rtl::OUString SubstitutePathVariables::impl_getSubstituteVariableValue( const ::rtl::OUString& rVariable ) +throw ( NoSuchElementException, RuntimeException ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::impl_getSubstituteVariableValue" ); + rtl::OUString aVariable; + + sal_Int32 nPos = rVariable.indexOf( m_aVarStart ); + if ( nPos == -1 ) + { + // Prepare variable name before hash map access + rtl::OUStringBuffer aStrBuffer( rVariable.getLength() + m_aVarStart.getLength() + m_aVarEnd.getLength() ); + aStrBuffer.append( m_aVarStart ); + aStrBuffer.append( rVariable ); + aStrBuffer.append( m_aVarEnd ); + aVariable = aStrBuffer.makeStringAndClear(); + } + + VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( ( nPos == -1 ) ? aVariable : rVariable ); + + // Fixed/Predefined variable + if ( pNTOIIter != m_aPreDefVarMap.end() ) + { + PreDefVariable nIndex = (PreDefVariable)pNTOIIter->second; + return m_aPreDefVars.m_FixedVar[(sal_Int32)nIndex]; + } + else + { + // Prepare variable name before hash map access + if ( nPos >= 0 ) + { + if ( rVariable.getLength() > 3 ) + aVariable = rVariable.copy( 2, rVariable.getLength() - 3 ); + else + { + rtl::OUString aExceptionText( RTL_CONSTASCII_USTRINGPARAM( "Unknown variable!" )); + throw NoSuchElementException(); + } + } + else + aVariable = rVariable; + + // User defined variable + SubstituteVariables::const_iterator pIter = m_aSubstVarMap.find( aVariable ); + if ( pIter != m_aSubstVarMap.end() ) + { + // found! + return pIter->second.aSubstValue; + } + + rtl::OUString aExceptionText( RTL_CONSTASCII_USTRINGPARAM( "Unknown variable!" )); + throw NoSuchElementException( aExceptionText, (cppu::OWeakObject *)this ); + } +} + +void SubstitutePathVariables::SetPredefinedPathVariables( PredefinedPathVariables& aPreDefPathVariables ) +{ + RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "framework", "Ocke.Janssen@sun.com", "SubstitutePathVariables::SetPredefinedPathVariables" ); + Any aAny; + ::rtl::OUString aOfficePath; + ::rtl::OUString aUserPath; + ::rtl::OUString aTmp; + ::rtl::OUString aTmp2; + String aResult; + + // Get inspath and userpath from bootstrap mechanism in every case as file URL + ::utl::Bootstrap::PathStatus aState; + ::rtl::OUString sVal ; + + aState = utl::Bootstrap::locateBaseInstallation( sVal ); + if( aState==::utl::Bootstrap::PATH_EXISTS ) { + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ] = ConvertOSLtoUCBURL( sVal ); + } + else { + LOG_ERROR( "SubstitutePathVariables::SetPredefinedPathVariables", "Bootstrap code has no value for instpath!"); + } + + aState = utl::Bootstrap::locateUserData( sVal ); + if( aState == ::utl::Bootstrap::PATH_EXISTS ) { + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERPATH ] = ConvertOSLtoUCBURL( sVal ); + } + else { + LOG_ERROR( "SubstitutePathVariables::SetPredefinedPathVariables", "Bootstrap code has no value for userpath"); + } + + // Set $(inst), $(instpath), $(insturl) + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTURL ] = aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ]; + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INST ] = aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ]; + // --> PB 2004-10-27 #i32656# - new variable of hierachy service + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_BASEINSTURL ]= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ]; + // <-- + + // Set $(user), $(userpath), $(userurl) + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERURL ] = aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERPATH ]; + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USER ] = aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERPATH ]; + // --> PB 2004-11-11 #i32656# - new variable of hierachy service + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERDATAURL ]= aPreDefPathVariables.m_FixedVar[ PREDEFVAR_USERPATH ]; + // <-- + + // Detect the program directory + // Set $(prog), $(progpath), $(progurl) + INetURLObject aProgObj( + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_INSTPATH ] ); + if ( !aProgObj.HasError() && + aProgObj.insertName( + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("program")) ) ) + { + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROGPATH ] = aProgObj.GetMainURL(INetURLObject::NO_DECODE); + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROGURL ] = aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROGPATH ]; + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROG ] = aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PROGPATH ]; + } + + // Detect the language type of the current office + aPreDefPathVariables.m_eLanguageType = LANGUAGE_ENGLISH_US; + rtl::OUString aLocaleStr; + if ( utl::ConfigManager::GetConfigManager()->GetDirectConfigProperty( utl::ConfigManager::LOCALE ) >>= aLocaleStr ) + aPreDefPathVariables.m_eLanguageType = MsLangId::convertIsoStringToLanguage( aLocaleStr ); + else + { + LOG_ERROR( "SubstitutePathVariables::SetPredefinedPathVariables", "Wrong Any type for language!" ); + } + + // Set $(lang) + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_LANG ] = ConvertOSLtoUCBURL( + rtl::OUString::createFromAscii( ResMgr::GetLang( aPreDefPathVariables.m_eLanguageType, 0 ) )); + // Set $(vlang) + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_VLANG ] = aLocaleStr; + + // Set $(langid) + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_LANGID ] = rtl::OUString::valueOf( (sal_Int32)aPreDefPathVariables.m_eLanguageType ); + + // Set the other pre defined path variables + // Set $(work) + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue(); + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_HOME ] = GetHomeVariableValue(); + + // Set $(workdirurl) this is the value of the path PATH_WORK which doesn't make sense + // anymore because the path settings service has this value! It can deliver this value more + // quickly than the substitution service! + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_WORKDIRURL ] = GetWorkPath(); + + // Set $(path) variable + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_PATH ] = GetPathVariableValue(); + + // Set $(temp) + osl::FileBase::getTempDirURL( aTmp ); + aPreDefPathVariables.m_FixedVar[ PREDEFVAR_TEMP ] = ConvertOSLtoUCBURL( aTmp ); + + aPreDefPathVariables.m_FixedVar[PREDEFVAR_BRANDBASEURL] = rtl::OUString( + RTL_CONSTASCII_USTRINGPARAM("$BRAND_BASE_DIR")); + rtl::Bootstrap::expandMacros( + aPreDefPathVariables.m_FixedVar[PREDEFVAR_BRANDBASEURL]); +} + +} // namespace framework diff --git a/framework/source/services/tabwindowservice.cxx b/framework/source/services/tabwindowservice.cxx new file mode 100644 index 000000000000..9af8581652d3 --- /dev/null +++ b/framework/source/services/tabwindowservice.cxx @@ -0,0 +1,485 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_________________________________________________________________________________________________________________ +// my own includes +//_________________________________________________________________________________________________________________ + +#include <services/tabwindowservice.hxx> +#include <classes/fwktabwindow.hxx> +#include <threadhelp/resetableguard.hxx> +#include <services.h> +#include <properties.h> + +//_________________________________________________________________________________________________________________ +// interface includes +//_________________________________________________________________________________________________________________ + +#include <com/sun/star/awt/PosSize.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +//_________________________________________________________________________________________________________________ +// includes of other projects +//_________________________________________________________________________________________________________________ + +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/urlobj.hxx> +#include <rtl/ustrbuf.hxx> +#include <vcl/svapp.hxx> + +//_________________________________________________________________________________________________________________ +// namespace +//_________________________________________________________________________________________________________________ + +namespace framework{ + +//_________________________________________________________________________________________________________________ +// non exported definitions +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// declarations +//_________________________________________________________________________________________________________________ + +//***************************************************************************************************************** +// css::uno::XInterface, XTypeProvider, XServiceInfo +//***************************************************************************************************************** + +DEFINE_XINTERFACE_6 ( TabWindowService , + OWeakObject , + DIRECT_INTERFACE(css::lang::XTypeProvider ), + DIRECT_INTERFACE(css::lang::XServiceInfo ), + DIRECT_INTERFACE(css::lang::XComponent), + DIRECT_INTERFACE(css::awt::XSimpleTabController), + DIRECT_INTERFACE(css::beans::XPropertySet ), + DIRECT_INTERFACE(css::beans::XPropertySetInfo ) + ) + +DEFINE_XTYPEPROVIDER_6 ( TabWindowService , + css::lang::XTypeProvider , + css::lang::XServiceInfo , + css::lang::XComponent , + css::awt::XSimpleTabController , + css::beans::XPropertySet , + css::beans::XPropertySetInfo + ) + +DEFINE_XSERVICEINFO_MULTISERVICE ( TabWindowService , + OWeakObject , + SERVICENAME_TABWINDOWSERVICE , + IMPLEMENTATIONNAME_TABWINDOWSERVICE + ) + +DEFINE_INIT_SERVICE ( TabWindowService, + { + impl_initializePropInfo(); + m_aTransactionManager.setWorkingMode( E_WORK ); + } + ) + +//***************************************************************************************************************** +// constructor +//***************************************************************************************************************** +TabWindowService::TabWindowService( const css::uno::Reference< css::lang::XMultiServiceFactory >& xFactory ) + // Init baseclasses first + // Attention: + // Don't change order of initialization! + // ThreadHelpBase is a struct with a mutex as member. We can't use a mutex as member, while + // we must garant right initialization and a valid value of this! First initialize + // baseclasses and then members. And we need the mutex for other baseclasses !!! + : ThreadHelpBase ( &Application::GetSolarMutex() ) + , TransactionBase ( ) + , PropertySetHelper ( xFactory , + &m_aLock , + &m_aTransactionManager , + sal_False ) // FALSE => dont release shared mutex on calling us! + , OWeakObject ( ) + + // Init member + , m_xFactory ( xFactory ) + , m_xTabWin ( ) + , m_pTabWin ( NULL ) + , m_lTabPageInfos ( ) + , m_lListener ( m_aLock.getShareableOslMutex()) + , m_nPageIndexCounter ( 1 ) + , m_nCurrentPageIndex ( 0 ) +{ + // Safe impossible cases. + // Method not defined for all incoming parameter. + LOG_ASSERT( xFactory.is(), "TabWindowService::TabWindowService()\nInvalid parameter detected!\n" ) +} + +//***************************************************************************************************************** +// destructor +//***************************************************************************************************************** +TabWindowService::~TabWindowService() +{ + // SAFE-> + ResetableGuard aGuard(m_aLock); + + if (m_pTabWin) + m_pTabWin->RemoveEventListener( LINK( this, TabWindowService, EventListener ) ); +} + +//***************************************************************************************************************** +// XSimpleTabController +//***************************************************************************************************************** +::sal_Int32 SAL_CALL TabWindowService::insertTab() + throw ( css::uno::RuntimeException ) +{ + // SAFE -> + ResetableGuard aGuard( m_aLock ); + + ::sal_Int32 nID = m_nPageIndexCounter++; + TTabPageInfo aInfo(nID); + + m_lTabPageInfos[nID] = aInfo; + + return nID; +} + +//***************************************************************************************************************** +// XSimpleTabController +//***************************************************************************************************************** +void SAL_CALL TabWindowService::removeTab(::sal_Int32 nID) + throw (css::lang::IndexOutOfBoundsException, + css::uno::RuntimeException ) +{ + // SAFE -> + ResetableGuard aGuard(m_aLock); + + // throws suitable IndexOutOfBoundsException .-) + TTabPageInfoHash::iterator pIt = impl_getTabPageInfo (nID); + m_lTabPageInfos.erase(pIt); + + FwkTabWindow* pTabWin = mem_TabWin (); + if (pTabWin) + pTabWin->RemovePage(nID); +} + +//***************************************************************************************************************** +// XSimpleTabController +//***************************************************************************************************************** +void SAL_CALL TabWindowService::setTabProps( ::sal_Int32 nID , + const css::uno::Sequence< css::beans::NamedValue >& lProperties) + throw (css::lang::IndexOutOfBoundsException, + css::uno::RuntimeException ) +{ + // SAFE -> + ResetableGuard aGuard(m_aLock); + + // throws suitable IndexOutOfBoundsException .-) + TTabPageInfoHash::iterator pIt = impl_getTabPageInfo (nID); + TTabPageInfo& rInfo = pIt->second; + rInfo.m_lProperties = lProperties; + + if ( ! rInfo.m_bCreated) + { + FwkTabWindow* pTabWin = mem_TabWin (); + if (pTabWin) + { + pTabWin->AddTabPage(rInfo.m_nIndex, rInfo.m_lProperties); + rInfo.m_bCreated = sal_True; + } + } +} + +//***************************************************************************************************************** +// XSimpleTabController +//***************************************************************************************************************** +css::uno::Sequence< css::beans::NamedValue > SAL_CALL TabWindowService::getTabProps(::sal_Int32 nID) + throw (css::lang::IndexOutOfBoundsException, + css::uno::RuntimeException ) +{ + // SAFE -> + ResetableGuard aGuard(m_aLock); + + // throws suitable IndexOutOfBoundsException .-) + TTabPageInfoHash::const_iterator pIt = impl_getTabPageInfo (nID); + const TTabPageInfo& rInfo = pIt->second; + + return rInfo.m_lProperties; +} + +//***************************************************************************************************************** +// XSimpleTabController +//***************************************************************************************************************** +void SAL_CALL TabWindowService::activateTab(::sal_Int32 nID) + throw (css::lang::IndexOutOfBoundsException, + css::uno::RuntimeException ) +{ + // SAFE -> + ResetableGuard aGuard(m_aLock); + + // throws suitable IndexOutOfBoundsException .-) + impl_checkTabIndex (nID); + m_nCurrentPageIndex = nID; + + FwkTabWindow* pTabWin = mem_TabWin (); + if (pTabWin) + pTabWin->ActivatePage(nID); +} + +//***************************************************************************************************************** +// XSimpleTabController +//***************************************************************************************************************** +::sal_Int32 SAL_CALL TabWindowService::getActiveTabID() + throw (css::uno::RuntimeException) +{ + // SAFE-> + ResetableGuard aGuard( m_aLock ); + return m_nCurrentPageIndex; +} + +//***************************************************************************************************************** +// XSimpleTabController +//***************************************************************************************************************** +void SAL_CALL TabWindowService::addTabListener(const css::uno::Reference< css::awt::XTabListener >& xListener) + throw (css::uno::RuntimeException) +{ + m_lListener.addInterface(::getCppuType((const css::uno::Reference< css::awt::XTabListener >*)NULL), xListener); +} + +//***************************************************************************************************************** +// XSimpleTabController +//***************************************************************************************************************** +void SAL_CALL TabWindowService::removeTabListener(const css::uno::Reference< css::awt::XTabListener >& xListener) + throw (css::uno::RuntimeException) +{ + m_lListener.removeInterface(::getCppuType((const css::uno::Reference< css::awt::XTabListener >*)NULL), xListener); +} + +//***************************************************************************************************************** +// XComponent +//***************************************************************************************************************** +void SAL_CALL TabWindowService::dispose() + throw (css::uno::RuntimeException) +{ + // SAFE-> + ResetableGuard aGuard(m_aLock); + + css::uno::Reference< css::uno::XInterface > xThis(static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY); + css::lang::EventObject aEvent(xThis); + + m_lListener.disposeAndClear (aEvent); + + if (m_pTabWin) + m_pTabWin->RemoveEventListener( LINK( this, TabWindowService, EventListener ) ); + + m_pTabWin = NULL; + m_xTabWin.clear(); +} + +//***************************************************************************************************************** +// XComponent +//***************************************************************************************************************** +void SAL_CALL TabWindowService::addEventListener(const css::uno::Reference< css::lang::XEventListener >& xListener) + throw (css::uno::RuntimeException) +{ + m_lListener.addInterface(::getCppuType((const css::uno::Reference< css::lang::XEventListener >*)NULL), xListener); +} + +//***************************************************************************************************************** +// XComponent +//***************************************************************************************************************** +void SAL_CALL TabWindowService::removeEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener) + throw (css::uno::RuntimeException) +{ + m_lListener.removeInterface(::getCppuType((const css::uno::Reference< css::lang::XEventListener >*)NULL), xListener); +} + +//***************************************************************************************************************** +void TabWindowService::impl_initializePropInfo() +{ + impl_setPropertyChangeBroadcaster(static_cast< css::awt::XSimpleTabController* >(this)); + + impl_addPropertyInfo( + css::beans::Property( + TABWINDOWSERVICE_PROPNAME_WINDOW, + TABWINDOWSERVICE_PROPHANDLE_WINDOW, + ::getCppuType((const css::uno::Reference< css::awt::XWindow >*)NULL), + css::beans::PropertyAttribute::TRANSIENT)); +} + +//***************************************************************************************************************** +void SAL_CALL TabWindowService::impl_setPropertyValue(const ::rtl::OUString& /*sProperty*/, + sal_Int32 /*nHandle */, + const css::uno::Any& /*aValue */) + +{ +} + +//***************************************************************************************************************** +css::uno::Any SAL_CALL TabWindowService::impl_getPropertyValue(const ::rtl::OUString& /*sProperty*/, + sal_Int32 nHandle ) +{ + /* There is no need to lock any mutex here. Because we share the + solar mutex with our base class. And we said to our base class: "dont release it on calling us" .-) + see ctor of PropertySetHelper for further informations. + */ + css::uno::Any aValue; + + switch (nHandle) + { + case TABWINDOWSERVICE_PROPHANDLE_WINDOW: + { + mem_TabWin (); // force "creation on demand" of m_xTabWin :-) + aValue <<= m_xTabWin; + } + break; + } + + return aValue; +} + +//***************************************************************************************************************** +// TabWindowService +//***************************************************************************************************************** +IMPL_LINK( TabWindowService, EventListener, VclSimpleEvent*, pEvent ) +{ + + if ( !pEvent && !pEvent->ISA(VclWindowEvent)) + return 0; + + ULONG nEventId = pEvent->GetId(); + VclWindowEvent* pWinEvt = static_cast< VclWindowEvent* >(pEvent); + + css::uno::Reference< css::uno::XInterface > xThis ( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY ); + css::lang::EventObject aEvent( xThis ); + + if (nEventId == VCLEVENT_OBJECT_DYING) + { + m_lListener.disposeAndClear (aEvent); + + m_pTabWin->RemoveEventListener( LINK( this, TabWindowService, EventListener ) ); + m_pTabWin = NULL; + m_xTabWin.clear(); + + return 0; + } + + ::cppu::OInterfaceContainerHelper* pContainer = m_lListener.getContainer(::getCppuType((const css::uno::Reference< css::awt::XTabListener >*) NULL)); + if ( ! pContainer) + return 0; + + ::cppu::OInterfaceIteratorHelper pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + try + { + css::awt::XTabListener* pListener = (css::awt::XTabListener*)pIterator.next(); + + switch (nEventId) + { + case VCLEVENT_TABPAGE_ACTIVATE : + pListener->activated( (sal_Int32)(ULONG)pWinEvt->GetData() ); + break; + + case VCLEVENT_TABPAGE_DEACTIVATE : + pListener->deactivated( (sal_Int32)(ULONG)pWinEvt->GetData() ); + break; + + case VCLEVENT_TABPAGE_INSERTED : + pListener->inserted( (sal_Int32)(ULONG)pWinEvt->GetData() ); + break; + + case VCLEVENT_TABPAGE_REMOVED : + pListener->removed( (sal_Int32)(ULONG)pWinEvt->GetData() ); + break; + + case VCLEVENT_TABPAGE_PAGETEXTCHANGED : + case VCLEVENT_TABPAGE_REMOVEDALL : + break; + } + } + catch(const css::uno::RuntimeException&) + { + pIterator.remove(); + } + } + + return 0; +} + +//***************************************************************************************************************** +// TabWindowService +//***************************************************************************************************************** +void TabWindowService::impl_checkTabIndex (::sal_Int32 nID) + throw (css::lang::IndexOutOfBoundsException) +{ + if ( + (nID <= 0 ) || + (nID > m_nPageIndexCounter) + ) + { + throw css::lang::IndexOutOfBoundsException( + ::rtl::OUString::createFromAscii("Tab index out of bounds."), + css::uno::Reference< css::uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY )); + } +} + +//***************************************************************************************************************** +// TabWindowService +//***************************************************************************************************************** +TTabPageInfoHash::iterator TabWindowService::impl_getTabPageInfo(::sal_Int32 nID) + throw (css::lang::IndexOutOfBoundsException) +{ + TTabPageInfoHash::iterator pIt = m_lTabPageInfos.find(nID); + if (pIt == m_lTabPageInfos.end ()) + throw css::lang::IndexOutOfBoundsException( + ::rtl::OUString::createFromAscii("Tab index out of bounds."), + css::uno::Reference< css::uno::XInterface >( static_cast< ::cppu::OWeakObject* >(this), css::uno::UNO_QUERY )); + return pIt; +} + +//***************************************************************************************************************** +// TabWindowService +//***************************************************************************************************************** +FwkTabWindow* TabWindowService::mem_TabWin () +{ + FwkTabWindow* pWin = NULL; + + if ( ! m_xTabWin.is ()) + { + Window* pFakeParent = dynamic_cast< Window* >(Application::GetDefaultDevice ()); + + m_pTabWin = new FwkTabWindow (pFakeParent); + m_xTabWin = VCLUnoHelper::GetInterface (m_pTabWin); + + m_pTabWin->AddEventListener( LINK( this, TabWindowService, EventListener ) ); + } + + if (m_xTabWin.is ()) + pWin = m_pTabWin; + + return pWin; +} + +} // namespace framework diff --git a/framework/source/services/taskcreatorsrv.cxx b/framework/source/services/taskcreatorsrv.cxx new file mode 100644 index 000000000000..90bcdb93efbf --- /dev/null +++ b/framework/source/services/taskcreatorsrv.cxx @@ -0,0 +1,404 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" +#include "services/taskcreatorsrv.hxx" + +//_______________________________________________ +// own includes +#include <helper/persistentwindowstate.hxx> +#include <helper/tagwindowasmodified.hxx> +#include <helper/titlebarupdate.hxx> +#include <threadhelp/readguard.hxx> +#include <threadhelp/writeguard.hxx> +#include <loadenv/targethelper.hxx> +#include <services.h> + +//_______________________________________________ +// interface includes +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/frame/XController.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <com/sun/star/awt/XTopWindow.hpp> +#include <com/sun/star/awt/WindowDescriptor.hpp> +#include <com/sun/star/awt/WindowAttribute.hpp> +#include <com/sun/star/awt/VclWindowPeerAttribute.hpp> + +//_______________________________________________ +// other includes +#include <svtools/colorcfg.hxx> +#include <vcl/svapp.hxx> + +#ifndef _TOOLKIT_HELPER_VCLUNOHELPER_HXX_ +#include <toolkit/unohlp.hxx> +#endif +#include <vcl/window.hxx> + +//_______________________________________________ +// namespaces + +namespace framework +{ + +//----------------------------------------------- +const ::rtl::OUString TaskCreatorService::ARGUMENT_PARENTFRAME = ::rtl::OUString::createFromAscii("ParentFrame" ); // XFrame +const ::rtl::OUString TaskCreatorService::ARGUMENT_FRAMENAME = ::rtl::OUString::createFromAscii("FrameName" ); // OUString +const ::rtl::OUString TaskCreatorService::ARGUMENT_MAKEVISIBLE = ::rtl::OUString::createFromAscii("MakeVisible" ); // sal_Bool +const ::rtl::OUString TaskCreatorService::ARGUMENT_CREATETOPWINDOW = ::rtl::OUString::createFromAscii("CreateTopWindow" ); // sal_Bool +const ::rtl::OUString TaskCreatorService::ARGUMENT_POSSIZE = ::rtl::OUString::createFromAscii("PosSize" ); // Rectangle +const ::rtl::OUString TaskCreatorService::ARGUMENT_CONTAINERWINDOW = ::rtl::OUString::createFromAscii("ContainerWindow" ); // XWindow +const ::rtl::OUString TaskCreatorService::ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE = ::rtl::OUString::createFromAscii("SupportPersistentWindowState" ); // sal_Bool +const ::rtl::OUString TaskCreatorService::ARGUMENT_ENABLE_TITLEBARUPDATE = ::rtl::OUString::createFromAscii("EnableTitleBarUpdate" ); // sal_Bool + +//----------------------------------------------- +DEFINE_XINTERFACE_3(TaskCreatorService , + OWeakObject , + DIRECT_INTERFACE(css::lang::XTypeProvider ), + DIRECT_INTERFACE(css::lang::XServiceInfo ), + DIRECT_INTERFACE(css::lang::XSingleServiceFactory)) + +//----------------------------------------------- +DEFINE_XTYPEPROVIDER_3(TaskCreatorService , + css::lang::XTypeProvider , + css::lang::XServiceInfo , + css::lang::XSingleServiceFactory) + +//----------------------------------------------- +DEFINE_XSERVICEINFO_ONEINSTANCESERVICE(TaskCreatorService , + ::cppu::OWeakObject , + SERVICENAME_TASKCREATOR , + IMPLEMENTATIONNAME_FWK_TASKCREATOR) + +//----------------------------------------------- +DEFINE_INIT_SERVICE( + TaskCreatorService, + { + /*Attention + I think we don't need any mutex or lock here ... because we are called by our own static method impl_createInstance() + to create a new instance of this class by our own supported service factory. + see macro DEFINE_XSERVICEINFO_MULTISERVICE and "impl_initService()" for further informations! + */ + } + ) + +//----------------------------------------------- +TaskCreatorService::TaskCreatorService(const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR) + : ThreadHelpBase (&Application::GetSolarMutex()) + , ::cppu::OWeakObject( ) + , m_xSMGR (xSMGR ) +{ +} + +//----------------------------------------------- +TaskCreatorService::~TaskCreatorService() +{ +} + +//----------------------------------------------- +css::uno::Reference< css::uno::XInterface > SAL_CALL TaskCreatorService::createInstance() + throw(css::uno::Exception , + css::uno::RuntimeException) +{ + return createInstanceWithArguments(css::uno::Sequence< css::uno::Any >()); +} + +//----------------------------------------------- +css::uno::Reference< css::uno::XInterface > SAL_CALL TaskCreatorService::createInstanceWithArguments(const css::uno::Sequence< css::uno::Any >& lArguments) + throw(css::uno::Exception , + css::uno::RuntimeException) +{ + static ::rtl::OUString DEFAULTVAL_FRAMENAME = ::rtl::OUString(); + static sal_Bool DEFAULTVAL_MAKEVISIBLE = sal_False; + static sal_Bool DEFAULTVAL_CREATETOPWINDOW = sal_True; + static css::awt::Rectangle DEFAULTVAL_POSSIZE = css::awt::Rectangle(0, 0, 0, 0); // only possize=[0,0,0,0] triggers default handling of vcl ! + static sal_Bool DEFAULTVAL_SUPPORTPERSSISTENTWINDOWSTATE = sal_False; + static sal_Bool DEFAULTVAL_ENABLE_TITLEBARUPDATE = sal_True; + + ::comphelper::SequenceAsHashMap lArgs(lArguments); + + css::uno::Reference< css::frame::XFrame > xParentFrame = lArgs.getUnpackedValueOrDefault(TaskCreatorService::ARGUMENT_PARENTFRAME , css::uno::Reference< css::frame::XFrame >()); + ::rtl::OUString sFrameName = lArgs.getUnpackedValueOrDefault(TaskCreatorService::ARGUMENT_FRAMENAME , DEFAULTVAL_FRAMENAME ); + sal_Bool bVisible = lArgs.getUnpackedValueOrDefault(TaskCreatorService::ARGUMENT_MAKEVISIBLE , DEFAULTVAL_MAKEVISIBLE ); + sal_Bool bCreateTopWindow = lArgs.getUnpackedValueOrDefault(TaskCreatorService::ARGUMENT_CREATETOPWINDOW , DEFAULTVAL_CREATETOPWINDOW ); + css::awt::Rectangle aPosSize = lArgs.getUnpackedValueOrDefault(TaskCreatorService::ARGUMENT_POSSIZE , DEFAULTVAL_POSSIZE ); + css::uno::Reference< css::awt::XWindow > xContainerWindow = lArgs.getUnpackedValueOrDefault(TaskCreatorService::ARGUMENT_CONTAINERWINDOW , css::uno::Reference< css::awt::XWindow >() ); + sal_Bool bSupportPersistentWindowState = lArgs.getUnpackedValueOrDefault(TaskCreatorService::ARGUMENT_SUPPORTPERSISTENTWINDOWSTATE , DEFAULTVAL_SUPPORTPERSSISTENTWINDOWSTATE ); + sal_Bool bEnableTitleBarUpdate = lArgs.getUnpackedValueOrDefault(TaskCreatorService::ARGUMENT_ENABLE_TITLEBARUPDATE , DEFAULTVAL_ENABLE_TITLEBARUPDATE ); + + /* SAFE { */ + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + /* } SAFE */ + + // We use FrameName property to set it as API name of the new created frame later. + // But those frame names must be different from the set of special target names as e.g. _blank, _self etcpp ! + ::rtl::OUString sRightName = impl_filterNames(sFrameName); + + // if no external frame window was given ... create a new one. + if ( ! xContainerWindow.is()) + { + css::uno::Reference< css::awt::XWindow > xParentWindow; + if (xParentFrame.is()) + xParentWindow = xParentFrame->getContainerWindow(); + + // Parent has no own window ... + // So we have to create a top level window always ! + if ( ! xParentWindow.is()) + bCreateTopWindow = sal_True; + + xContainerWindow = implts_createContainerWindow(xParentWindow, aPosSize, bCreateTopWindow); + } + + //-------------------> + // HACK #125187# + #i53630# + // Mark all document windows as "special ones", so VCL can bind + // special features to it. Because VCL doesnt know anything about documents ... + // Note: Doing so it's no longer supported, that e.g. our wizards can use findFrame(_blank) + // to create it's previes frames. They must do it manually by using WindowDescriptor+Toolkit! + css::uno::Reference< css::frame::XDesktop > xDesktop(xParentFrame, css::uno::UNO_QUERY); + ::sal_Bool bTopLevelDocumentWindow = ( + (sRightName.getLength () < 1) && + ( + (! xParentFrame.is() ) || + ( xDesktop.is() ) + ) + ); + if (bTopLevelDocumentWindow) + implts_applyDocStyleToWindow(xContainerWindow); + //-------------------> + + // create the new frame + css::uno::Reference< css::frame::XFrame > xFrame = implts_createFrame(xParentFrame, xContainerWindow, sRightName); + + // special freature: + // A special listener will restore pos/size states in case + // a component was loaded into the frame first time. + if (bSupportPersistentWindowState) + implts_establishWindowStateListener(xFrame); + + // special feature: On Mac we need tagging the window in case + // the underlying model was modified. + // VCL will ignore our calls in case different platform then Mac + // is used ... + if (bTopLevelDocumentWindow) + implts_establishDocModifyListener (xFrame); + + // special freature: + // A special listener will update title bar (text and icon) + // if component of frame will be changed. + if (bEnableTitleBarUpdate) + implts_establishTitleBarUpdate(xFrame); + + // Make it visible directly here ... + // if its required from outside. + if (bVisible) + xContainerWindow->setVisible(bVisible); + + return css::uno::Reference< css::uno::XInterface >(xFrame, css::uno::UNO_QUERY_THROW); +} + +//----------------------------------------------- +void TaskCreatorService::implts_applyDocStyleToWindow(const css::uno::Reference< css::awt::XWindow >& xWindow) const +{ + // SYNCHRONIZED -> + ::vos::OClearableGuard aSolarGuard(Application::GetSolarMutex()); + Window* pVCLWindow = VCLUnoHelper::GetWindow(xWindow); + if (pVCLWindow) + pVCLWindow->SetExtendedStyle(WB_EXT_DOCUMENT); + aSolarGuard.clear(); + // <- SYNCHRONIZED +} + +//----------------------------------------------- +css::uno::Reference< css::awt::XWindow > TaskCreatorService::implts_createContainerWindow( const css::uno::Reference< css::awt::XWindow >& xParentWindow , + const css::awt::Rectangle& aPosSize , + sal_Bool bTopWindow ) +{ + // SAFE -> + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE + + // get toolkit to create task container window + css::uno::Reference< css::awt::XToolkit > xToolkit( xSMGR->createInstance( SERVICENAME_VCLTOOLKIT ), css::uno::UNO_QUERY_THROW); + + // Check if child frames can be created realy. We need at least a valid window at the parent frame ... + css::uno::Reference< css::awt::XWindowPeer > xParentWindowPeer; + if ( ! bTopWindow) + { + if ( ! xParentWindow.is()) + bTopWindow = sal_False; + else + xParentWindowPeer = css::uno::Reference< css::awt::XWindowPeer >(xParentWindow, css::uno::UNO_QUERY_THROW); + } + + // describe window properties. + css::awt::WindowDescriptor aDescriptor; + if (bTopWindow) + { + aDescriptor.Type = css::awt::WindowClass_TOP ; + aDescriptor.WindowServiceName = DECLARE_ASCII("window") ; + aDescriptor.ParentIndex = -1 ; + aDescriptor.Parent = css::uno::Reference< css::awt::XWindowPeer >() ; + aDescriptor.Bounds = aPosSize ; + aDescriptor.WindowAttributes = css::awt::WindowAttribute::BORDER | + css::awt::WindowAttribute::MOVEABLE | + css::awt::WindowAttribute::SIZEABLE | + css::awt::WindowAttribute::CLOSEABLE | + css::awt::VclWindowPeerAttribute::CLIPCHILDREN ; + } + else + { + aDescriptor.Type = css::awt::WindowClass_TOP ; + aDescriptor.WindowServiceName = DECLARE_ASCII("dockingwindow") ; + aDescriptor.ParentIndex = 1 ; + aDescriptor.Parent = xParentWindowPeer ; + aDescriptor.Bounds = aPosSize ; + aDescriptor.WindowAttributes = css::awt::VclWindowPeerAttribute::CLIPCHILDREN ; + } + + // create a new blank container window and get access to parent container to append new created task. + css::uno::Reference< css::awt::XWindowPeer > xPeer = xToolkit->createWindow( aDescriptor ); + css::uno::Reference< css::awt::XWindow > xWindow ( xPeer, css::uno::UNO_QUERY ); + if ( ! xWindow.is()) + throw css::uno::Exception(::rtl::OUString::createFromAscii("TaskCreator service was not able to create suitable frame window."), + static_cast< ::cppu::OWeakObject* >(this)); + if (bTopWindow) + xPeer->setBackground(::svtools::ColorConfig().GetColorValue(::svtools::APPBACKGROUND).nColor); + else + xPeer->setBackground(0xffffffff); + + return xWindow; +} + +//----------------------------------------------- +css::uno::Reference< css::frame::XFrame > TaskCreatorService::implts_createFrame( const css::uno::Reference< css::frame::XFrame >& xParentFrame , + const css::uno::Reference< css::awt::XWindow >& xContainerWindow, + const ::rtl::OUString& sName ) +{ + // SAFE -> + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE + + // create new frame. + css::uno::Reference< css::frame::XFrame > xNewFrame( xSMGR->createInstance( SERVICENAME_FRAME ), css::uno::UNO_QUERY_THROW ); + + // Set window on frame. + // Do it before calling any other interface methods ... + // The new created frame must be initialized before you can do anything else there. + xNewFrame->initialize( xContainerWindow ); + + // Put frame to the frame tree. + // Note: The property creator/parent will be set on the new putted frame automaticly ... by the parent container. + if (xParentFrame.is()) + { + css::uno::Reference< css::frame::XFramesSupplier > xSupplier (xParentFrame, css::uno::UNO_QUERY_THROW); + css::uno::Reference< css::frame::XFrames > xContainer = xSupplier->getFrames(); + xContainer->append( xNewFrame ); + } + + // Set it's API name (if there is one from outside) + if (sName.getLength()) + xNewFrame->setName( sName ); + + return xNewFrame; +} + +//----------------------------------------------- +void TaskCreatorService::implts_establishWindowStateListener( const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + // SAFE -> + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE + + // Special feature: It's allowed for frames using a top level window only! + // We must create a special listener service and couple it with the new created task frame. + // He will restore or save the window state of it ... + // See used classes for further informations too. + PersistentWindowState* pPersistentStateHandler = new PersistentWindowState(xSMGR); + css::uno::Reference< css::lang::XInitialization > xInit(static_cast< ::cppu::OWeakObject* >(pPersistentStateHandler), css::uno::UNO_QUERY_THROW); + + css::uno::Sequence< css::uno::Any > lInitData(1); + lInitData[0] <<= xFrame; + xInit->initialize(lInitData); +} + +//----------------------------------------------- +void TaskCreatorService::implts_establishDocModifyListener( const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + // SAFE -> + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE + + // Special feature: It's allowed for frames using a top level window only! + // We must create a special listener service and couple it with the new created task frame. + // It will tag the window as modified if the underlying model was modified ... + TagWindowAsModified* pTag = new TagWindowAsModified(xSMGR); + css::uno::Reference< css::lang::XInitialization > xInit(static_cast< ::cppu::OWeakObject* >(pTag), css::uno::UNO_QUERY_THROW); + + css::uno::Sequence< css::uno::Any > lInitData(1); + lInitData[0] <<= xFrame; + xInit->initialize(lInitData); +} + +//----------------------------------------------- +void TaskCreatorService::implts_establishTitleBarUpdate( const css::uno::Reference< css::frame::XFrame >& xFrame ) +{ + // SAFE -> + ReadGuard aReadLock( m_aLock ); + css::uno::Reference< css::lang::XMultiServiceFactory > xSMGR = m_xSMGR; + aReadLock.unlock(); + // <- SAFE + + TitleBarUpdate* pHelper = new TitleBarUpdate (xSMGR); + css::uno::Reference< css::lang::XInitialization > xInit(static_cast< ::cppu::OWeakObject* >(pHelper), css::uno::UNO_QUERY_THROW); + + css::uno::Sequence< css::uno::Any > lInitData(1); + lInitData[0] <<= xFrame; + xInit->initialize(lInitData); +} + +//----------------------------------------------- +::rtl::OUString TaskCreatorService::impl_filterNames( const ::rtl::OUString& sName ) +{ + ::rtl::OUString sFiltered; + if (TargetHelper::isValidNameForFrame(sName)) + sFiltered = sName; + return sFiltered; +} + +} // namespace framework diff --git a/framework/source/services/uriabbreviation.cxx b/framework/source/services/uriabbreviation.cxx new file mode 100644 index 000000000000..1c89870c429d --- /dev/null +++ b/framework/source/services/uriabbreviation.cxx @@ -0,0 +1,90 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +#include "services/uriabbreviation.hxx" +#include "services.h" + +#include "sal/config.h" +#include "cppuhelper/factory.hxx" +#include "cppuhelper/implementationentry.hxx" + +#include "tools/urlobj.hxx" + +// component helper namespace +namespace css = ::com::sun::star; + +// framework namespace +namespace framework +{ + +namespace css = ::com::sun::star; + +//_________________________________________________________________________________________________________________ +// declarations +//_________________________________________________________________________________________________________________ + +//***************************************************************************************************************** +// XInterface, XTypeProvider, XServiceInfo +//***************************************************************************************************************** +DEFINE_XSERVICEINFO_MULTISERVICE_2 ( UriAbbreviation , + ::cppu::OWeakObject , + SERVICENAME_STRINGABBREVIATION , + IMPLEMENTATIONNAME_URIABBREVIATION + ) + +DEFINE_INIT_SERVICE ( UriAbbreviation, + { + } + ) + +UriAbbreviation::UriAbbreviation(css::uno::Reference< css::uno::XComponentContext > const & context) : + m_xContext(context) +{ +} + +// ::com::sun::star::util::XStringAbbreviation: +::rtl::OUString SAL_CALL UriAbbreviation::abbreviateString(const css::uno::Reference< css::util::XStringWidth > & xStringWidth, ::sal_Int32 nWidth, const ::rtl::OUString & aString) throw (css::uno::RuntimeException) +{ + ::rtl::OUString aResult( aString ); + if ( xStringWidth.is() ) + { + // Use INetURLObject to abbreviate URLs + INetURLObject aURL( aString ); + aResult = aURL.getAbbreviated( xStringWidth, nWidth, INetURLObject::DECODE_UNAMBIGUOUS ); + } + + return aResult; +} + +} // namespace framework + + + + + diff --git a/framework/source/services/urltransformer.cxx b/framework/source/services/urltransformer.cxx new file mode 100644 index 000000000000..cac8e7e359ca --- /dev/null +++ b/framework/source/services/urltransformer.cxx @@ -0,0 +1,372 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_framework.hxx" + +//_________________________________________________________________________________________________________________ +// my own includes +//_________________________________________________________________________________________________________________ +#include <services/urltransformer.hxx> +#include <threadhelp/resetableguard.hxx> +#include <macros/debug.hxx> +#include <services.h> + +//_________________________________________________________________________________________________________________ +// interface includes +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// includes of other projects +//_________________________________________________________________________________________________________________ +#include <tools/urlobj.hxx> +#include <rtl/ustrbuf.hxx> +#include <vcl/svapp.hxx> + +//_________________________________________________________________________________________________________________ +// namespace +//_________________________________________________________________________________________________________________ + +namespace framework{ + +using namespace ::osl ; +using namespace ::cppu ; +using namespace ::com::sun::star::uno ; +using namespace ::com::sun::star::lang ; +using namespace ::com::sun::star::util ; + +//_________________________________________________________________________________________________________________ +// non exported const +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// non exported definitions +//_________________________________________________________________________________________________________________ + +//_________________________________________________________________________________________________________________ +// declarations +//_________________________________________________________________________________________________________________ + +//***************************************************************************************************************** +// constructor +//***************************************************************************************************************** +URLTransformer::URLTransformer( const Reference< XMultiServiceFactory >& /*xFactory*/ ) +{ + // Safe impossible cases. + // Method not defined for all incoming parameter. + //LOG_ASSERT( xFactory.is(), "URLTransformer::URLTransformer()\nInvalid parameter detected!\n" ) +} + +//***************************************************************************************************************** +// destructor +//***************************************************************************************************************** +URLTransformer::~URLTransformer() +{ +} + +//***************************************************************************************************************** +// XInterface, XTypeProvider, XServiceInfo +//***************************************************************************************************************** + +DEFINE_XSERVICEINFO_MULTISERVICE ( URLTransformer , + OWeakObject , + SERVICENAME_URLTRANSFORMER , + IMPLEMENTATIONNAME_URLTRANSFORMER + ) + +DEFINE_INIT_SERVICE ( URLTransformer, + { + } + ) + +namespace +{ + void lcl_ParserHelper(INetURLObject& _rParser,URL& _rURL,bool _bUseIntern) + { + // Get all information about this URL. + _rURL.Protocol = INetURLObject::GetScheme( _rParser.GetProtocol() ); + _rURL.User = _rParser.GetUser ( INetURLObject::DECODE_WITH_CHARSET ); + _rURL.Password = _rParser.GetPass ( INetURLObject::DECODE_WITH_CHARSET ); + _rURL.Server = _rParser.GetHost ( INetURLObject::DECODE_WITH_CHARSET ); + _rURL.Port = (sal_Int16)_rParser.GetPort(); + + sal_Int32 nCount = _rParser.getSegmentCount( false ); + if ( nCount > 0 ) + { + // Don't add last segment as it is the name! + --nCount; + + rtl::OUStringBuffer aPath; + for ( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + aPath.append( sal_Unicode( '/' )); + aPath.append( _rParser.getName( nIndex, false, INetURLObject::NO_DECODE )); + } + + if ( nCount > 0 ) + aPath.append( sal_Unicode( '/' )); // final slash! + + _rURL.Path = aPath.makeStringAndClear(); + _rURL.Name = _rParser.getName( INetURLObject::LAST_SEGMENT, false, INetURLObject::NO_DECODE ); + } + else + { + _rURL.Path = _rParser.GetURLPath( INetURLObject::NO_DECODE ); + _rURL.Name = _rParser.GetName ( ); + } + + _rURL.Arguments = _rParser.GetParam ( INetURLObject::NO_DECODE ); + _rURL.Mark = _rParser.GetMark ( INetURLObject::DECODE_WITH_CHARSET ); + + // INetURLObject supports only an intelligent method of parsing URL's. So write + // back Complete to have a valid encoded URL in all cases! + _rURL.Complete = _rParser.GetMainURL( INetURLObject::NO_DECODE ); + if ( _bUseIntern ) + _rURL.Complete = _rURL.Complete.intern(); + + _rParser.SetMark ( ::rtl::OUString() ); + _rParser.SetParam( ::rtl::OUString() ); + + _rURL.Main = _rParser.GetMainURL( INetURLObject::NO_DECODE ); + } +} +//***************************************************************************************************************** +// XURLTransformer +//***************************************************************************************************************** +sal_Bool SAL_CALL URLTransformer::parseStrict( URL& aURL ) throw( RuntimeException ) +{ + // Safe impossible cases. + if (( &aURL == NULL ) || + ( aURL.Complete.getLength() < 1 ) ) + { + return sal_False; + } + // Try to extract the protocol + sal_Int32 nURLIndex = aURL.Complete.indexOf( sal_Unicode( ':' )); + ::rtl::OUString aProtocol; + if ( nURLIndex > 1 ) + { + aProtocol = aURL.Complete.copy( 0, nURLIndex+1 ); + + // If INetURLObject knows this protocol let it parse + if ( INetURLObject::CompareProtocolScheme( aProtocol ) != INET_PROT_NOT_VALID ) + { + // Initialize parser with given URL. + INetURLObject aParser( aURL.Complete ); + + // Get all information about this URL. + INetProtocol eINetProt = aParser.GetProtocol(); + if ( eINetProt == INET_PROT_NOT_VALID ) + { + return sal_False; + } + else if ( !aParser.HasError() ) + { + lcl_ParserHelper(aParser,aURL,false); + // Return "URL is parsed". + return sal_True; + } + } + else + { + // Minmal support for unknown protocols. This is mandatory to support the "Protocol Handlers" implemented + // in framework! + aURL.Protocol = aProtocol; + aURL.Main = aURL.Complete; + aURL.Path = aURL.Complete.copy( nURLIndex+1 );; + + // Return "URL is parsed". + return sal_True; + } + } + + return sal_False; +} + +//***************************************************************************************************************** +// XURLTransformer +//***************************************************************************************************************** +sal_Bool SAL_CALL URLTransformer::parseSmart( URL& aURL , + const ::rtl::OUString& sSmartProtocol ) throw( RuntimeException ) +{ + // Safe impossible cases. + if (( &aURL == NULL ) || + ( aURL.Complete.getLength() < 1 ) ) + { + return sal_False; + } + + // Initialize parser with given URL. + INetURLObject aParser; + + aParser.SetSmartProtocol( INetURLObject::CompareProtocolScheme( sSmartProtocol )); + bool bOk = aParser.SetSmartURL( aURL.Complete ); + if ( bOk ) + { + lcl_ParserHelper(aParser,aURL,true); + // Return "URL is parsed". + return sal_True; + } + else + { + // Minmal support for unknown protocols. This is mandatory to support the "Protocol Handlers" implemented + // in framework! + if ( INetURLObject::CompareProtocolScheme( sSmartProtocol ) == INET_PROT_NOT_VALID ) + { + // Try to extract the protocol + sal_Int32 nIndex = aURL.Complete.indexOf( sal_Unicode( ':' )); + ::rtl::OUString aProtocol; + if ( nIndex > 1 ) + { + aProtocol = aURL.Complete.copy( 0, nIndex+1 ); + + // If INetURLObject knows this protocol something is wrong as detected before => + // give up and return false! + if ( INetURLObject::CompareProtocolScheme( aProtocol ) != INET_PROT_NOT_VALID ) + return sal_False; + else + aURL.Protocol = aProtocol; + } + else + return sal_False; + + aURL.Main = aURL.Complete; + aURL.Path = aURL.Complete.copy( nIndex+1 ); + return sal_True; + } + else + return sal_False; + } +} + +//***************************************************************************************************************** +// XURLTransformer +//***************************************************************************************************************** +sal_Bool SAL_CALL URLTransformer::assemble( URL& aURL ) throw( RuntimeException ) +{ + // Safe impossible cases. + if ( &aURL == NULL ) + return sal_False ; + + // Initialize parser. + INetURLObject aParser; + + if ( INetURLObject::CompareProtocolScheme( aURL.Protocol ) != INET_PROT_NOT_VALID ) + { + ::rtl::OUStringBuffer aCompletePath( aURL.Path ); + + // Concat the name if it is provided, just support a final slash + if ( aURL.Name.getLength() > 0 ) + { + sal_Int32 nIndex = aURL.Path.lastIndexOf( sal_Unicode('/') ); + if ( nIndex == ( aURL.Path.getLength() -1 )) + aCompletePath.append( aURL.Name ); + else + { + aCompletePath.append( sal_Unicode( '/' ) ); + aCompletePath.append( aURL.Name ); + } + } + + bool bResult = aParser.ConcatData( + INetURLObject::CompareProtocolScheme( aURL.Protocol ) , + aURL.User , + aURL.Password , + aURL.Server , + aURL.Port , + aCompletePath.makeStringAndClear() ); + + if ( !bResult ) + return sal_False; + + // First parse URL WITHOUT ... + aURL.Main = aParser.GetMainURL( INetURLObject::NO_DECODE ); + // ...and then WITH parameter and mark. + aParser.SetParam( aURL.Arguments); + aParser.SetMark ( aURL.Mark, INetURLObject::ENCODE_ALL ); + aURL.Complete = aParser.GetMainURL( INetURLObject::NO_DECODE ); + + // Return "URL is assembled". + return sal_True; + } + else if ( aURL.Protocol.getLength() > 0 ) + { + // Minimal support for unknown protocols + ::rtl::OUStringBuffer aBuffer( aURL.Protocol ); + aBuffer.append( aURL.Path ); + aURL.Complete = aBuffer.makeStringAndClear(); + aURL.Main = aURL.Complete; + return sal_True; + } + + return sal_False; +} + +//***************************************************************************************************************** +// XURLTransformer +//***************************************************************************************************************** +::rtl::OUString SAL_CALL URLTransformer::getPresentation( const URL& aURL , + sal_Bool bWithPassword ) throw( RuntimeException ) +{ + // Safe impossible cases. + if (( &aURL == NULL ) || + ( aURL.Complete.getLength() < 1 ) || + (( bWithPassword != sal_True ) && + ( bWithPassword != sal_False ) ) ) + { + return ::rtl::OUString(); + } + + // Check given URL + URL aTestURL = aURL; + sal_Bool bParseResult = parseSmart( aTestURL, aTestURL.Protocol ); + if ( bParseResult ) + { + if ( !bWithPassword && aTestURL.Password.getLength() > 0 ) + { + // Exchange password text with other placeholder string + aTestURL.Password = ::rtl::OUString::createFromAscii( "<******>" ); + assemble( aTestURL ); + } + + // Convert internal URLs to "praesentation"-URLs! + rtl::OUString sPraesentationURL; + INetURLObject::translateToExternal( aTestURL.Complete, sPraesentationURL, INetURLObject::DECODE_UNAMBIGUOUS ); + + return sPraesentationURL; + } + else + return ::rtl::OUString(); +} + +//_________________________________________________________________________________________________________________ +// debug methods +//_________________________________________________________________________________________________________________ + + +} // namespace framework + |