diff options
Diffstat (limited to '')
-rw-r--r-- | unotools/source/config/dynamicmenuoptions.cxx | 558 |
1 files changed, 558 insertions, 0 deletions
diff --git a/unotools/source/config/dynamicmenuoptions.cxx b/unotools/source/config/dynamicmenuoptions.cxx new file mode 100644 index 000000000..019ca1b86 --- /dev/null +++ b/unotools/source/config/dynamicmenuoptions.cxx @@ -0,0 +1,558 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <sal/log.hxx> +#include <unotools/dynamicmenuoptions.hxx> +#include <unotools/configitem.hxx> +#include <tools/debug.hxx> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <vector> + +#include "itemholder1.hxx" + +#include <algorithm> + +using namespace ::std; +using namespace ::utl; +using namespace ::osl; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + +#define ROOTNODE_MENUS "Office.Common/Menus/" +#define PATHDELIMITER "/" + +#define SETNODE_NEWMENU "New" +#define SETNODE_WIZARDMENU "Wizard" + +#define PROPERTYNAME_URL DYNAMICMENU_PROPERTYNAME_URL +#define PROPERTYNAME_TITLE DYNAMICMENU_PROPERTYNAME_TITLE +#define PROPERTYNAME_IMAGEIDENTIFIER DYNAMICMENU_PROPERTYNAME_IMAGEIDENTIFIER +#define PROPERTYNAME_TARGETNAME DYNAMICMENU_PROPERTYNAME_TARGETNAME + +#define PROPERTYCOUNT 4 + +#define OFFSET_URL 0 +#define OFFSET_TITLE 1 +#define OFFSET_IMAGEIDENTIFIER 2 +#define OFFSET_TARGETNAME 3 + +#define PATHPREFIX_SETUP "m" + +namespace { + +/*-**************************************************************************************************************** + @descr struct to hold information about one menu entry. +****************************************************************************************************************-*/ +struct SvtDynMenuEntry +{ + OUString sURL; + OUString sTitle; + OUString sImageIdentifier; + OUString sTargetName; +}; + +/*-**************************************************************************************************************** + @descr support simple menu structures and operations on it +****************************************************************************************************************-*/ +class SvtDynMenu +{ + public: + // append setup written menu entry + // Don't touch name of entry. It was defined by setup and must be the same every time! + // Look for double menu entries here too... may be some separator items are superfluous... + void AppendSetupEntry( const SvtDynMenuEntry& rEntry ) + { + if( + ( lSetupEntries.empty() ) || + ( lSetupEntries.rbegin()->sURL != rEntry.sURL ) + ) + { + lSetupEntries.push_back( rEntry ); + } + } + + // convert internal list to external format + // for using it on right menus really + // Notice: We build a property list with 4 entries and set it on result list then. + // Separator entries will be packed in another way then normal entries! We define + // special string "sSeparator" to perform too ... + Sequence< Sequence< PropertyValue > > GetList() const + { + sal_Int32 nSetupCount = static_cast<sal_Int32>(lSetupEntries.size()); + sal_Int32 nUserCount = static_cast<sal_Int32>(lUserEntries.size()); + sal_Int32 nStep = 0; + Sequence< PropertyValue > lProperties ( PROPERTYCOUNT ); + Sequence< Sequence< PropertyValue > > lResult ( nSetupCount+nUserCount ); + OUString sSeparator ( "private:separator" ); + + lProperties[OFFSET_URL ].Name = PROPERTYNAME_URL; + lProperties[OFFSET_TITLE ].Name = PROPERTYNAME_TITLE; + lProperties[OFFSET_IMAGEIDENTIFIER].Name = PROPERTYNAME_IMAGEIDENTIFIER; + lProperties[OFFSET_TARGETNAME ].Name = PROPERTYNAME_TARGETNAME; + + for( const auto& pList : {&lSetupEntries, &lUserEntries} ) + { + for( const auto& rItem : *pList ) + { + if( rItem.sURL == sSeparator ) + { + lProperties[OFFSET_URL ].Value <<= sSeparator; + lProperties[OFFSET_TITLE ].Value <<= OUString(); + lProperties[OFFSET_IMAGEIDENTIFIER ].Value <<= OUString(); + lProperties[OFFSET_TARGETNAME ].Value <<= OUString(); + } + else + { + lProperties[OFFSET_URL ].Value <<= rItem.sURL; + lProperties[OFFSET_TITLE ].Value <<= rItem.sTitle; + lProperties[OFFSET_IMAGEIDENTIFIER ].Value <<= rItem.sImageIdentifier; + lProperties[OFFSET_TARGETNAME ].Value <<= rItem.sTargetName; + } + lResult[nStep] = lProperties; + ++nStep; + } + } + return lResult; + } + + private: + vector< SvtDynMenuEntry > lSetupEntries; + vector< SvtDynMenuEntry > lUserEntries; +}; + +} + +class SvtDynamicMenuOptions_Impl : public ConfigItem +{ + public: + + SvtDynamicMenuOptions_Impl(); + virtual ~SvtDynamicMenuOptions_Impl() override; + + /*-**************************************************************************************************** + @short called for notify of configmanager + @descr This method is called from the ConfigManager before the application ends or from the + PropertyChangeListener if the sub tree broadcasts changes. You must update your + internal values. + + @seealso baseclass ConfigItem + + @param "lPropertyNames" is the list of properties which should be updated. + *//*-*****************************************************************************************************/ + + virtual void Notify( const Sequence< OUString >& lPropertyNames ) override; + + /*-**************************************************************************************************** + @short base implementation of public interface for "SvtDynamicMenuOptions"! + @descr These class is used as static member of "SvtDynamicMenuOptions" ... + => The code exist only for one time and isn't duplicated for every instance! + *//*-*****************************************************************************************************/ + + Sequence< Sequence< PropertyValue > > GetMenu ( EDynamicMenuType eMenu ) const; + + private: + + virtual void ImplCommit() override; + + /*-**************************************************************************************************** + @short return list of key names of our configuration management which represent our module tree + @descr This method returns the current list of key names! We need it to get needed values from our + configuration management and support dynamical menu item lists! + @param "nNewCount" , returns count of menu entries for "new" + @param "nWizardCount" , returns count of menu entries for "wizard" + @return A list of configuration key names is returned. + *//*-*****************************************************************************************************/ + + Sequence< OUString > impl_GetPropertyNames( sal_uInt32& nNewCount, sal_uInt32& nWizardCount ); + + /*-**************************************************************************************************** + @short sort given source list and expand it for all well known properties to destination + @descr We must support sets of entries with count inside the name .. but some of them could be missing! + e.g. s1-s2-s3-s0-u1-s6-u5-u7 + Then we must sort it by name and expand it to the follow one: + sSetNode/s0/URL + sSetNode/s0/Title + sSetNode/s0/... + sSetNode/s1/URL + sSetNode/s1/Title + sSetNode/s1/... + ... + sSetNode/s6/URL + sSetNode/s6/Title + sSetNode/s6/... + sSetNode/u1/URL + sSetNode/u1/Title + sSetNode/u1/... + ... + sSetNode/u7/URL + sSetNode/u7/Title + sSetNode/u7/... + Rules: We start with all setup written entries names "sx" and x=[0..n]. + Then we handle all "ux" items. Inside these blocks we sort it ascending by number. + + @attention We add these expanded list to the end of given "lDestination" list! + So we must start on "lDestination.getLength()". + Reallocation of memory of destination list is done by us! + + @seealso method impl_GetPropertyNames() + + @param "lSource" , original list (e.g. [m1-m2-m3-m6-m0] ) + @param "lDestination" , destination of operation + @param "sSetNode" , name of configuration set to build complete path + @return A list of configuration key names is returned. + *//*-*****************************************************************************************************/ + + static void impl_SortAndExpandPropertyNames( const Sequence< OUString >& lSource , + Sequence< OUString >& lDestination , + const OUString& sSetNode ); + + // private member + + private: + + SvtDynMenu m_aNewMenu; + SvtDynMenu m_aWizardMenu; +}; + +// constructor + +SvtDynamicMenuOptions_Impl::SvtDynamicMenuOptions_Impl() + // Init baseclasses first + : ConfigItem( ROOTNODE_MENUS ) + // Init member then... +{ + // Get names and values of all accessible menu entries and fill internal structures. + // See impl_GetPropertyNames() for further information. + sal_uInt32 nNewCount = 0; + sal_uInt32 nWizardCount = 0; + Sequence< OUString > lNames = impl_GetPropertyNames ( nNewCount , + nWizardCount ); + Sequence< Any > lValues = GetProperties ( lNames ); + + // Safe impossible cases. + // We need values from ALL configuration keys. + // Follow assignment use order of values in relation to our list of key names! + DBG_ASSERT( !(lNames.getLength()!=lValues.getLength()), "SvtDynamicMenuOptions_Impl::SvtDynamicMenuOptions_Impl()\nI miss some values of configuration keys!\n" ); + + // Copy values from list in right order to our internal member. + // Attention: List for names and values have an internal construction pattern! + + // first "New" menu ... + // Name Value + // /New/1/URL "private:factory/swriter" + // /New/1/Title "New Writer Document" + // /New/1/ImageIdentifier "icon_writer" + // /New/1/TargetName "_blank" + + // /New/2/URL "private:factory/scalc" + // /New/2/Title "New Calc Document" + // /New/2/ImageIdentifier "icon_calc" + // /New/2/TargetName "_blank" + + // second "Wizard" menu ... + // /Wizard/1/URL "file://b" + // /Wizard/1/Title "PaintSomething" + // /Wizard/1/ImageIdentifier "icon_?" + // /Wizard/1/TargetName "_self" + + // ... and so on ... + + sal_uInt32 nItem = 0; + sal_uInt32 nPosition = 0; + + // Get names/values for new menu. + // 4 subkeys for every item! + for( nItem=0; nItem<nNewCount; ++nItem ) + { + SvtDynMenuEntry aItem; + lValues[nPosition] >>= aItem.sURL; + ++nPosition; + lValues[nPosition] >>= aItem.sTitle; + ++nPosition; + lValues[nPosition] >>= aItem.sImageIdentifier; + ++nPosition; + lValues[nPosition] >>= aItem.sTargetName; + ++nPosition; + m_aNewMenu.AppendSetupEntry( aItem ); + } + + // Attention: Don't reset nPosition here! + + // Get names/values for wizard menu. + // 4 subkeys for every item! + for( nItem=0; nItem<nWizardCount; ++nItem ) + { + SvtDynMenuEntry aItem; + lValues[nPosition] >>= aItem.sURL; + ++nPosition; + lValues[nPosition] >>= aItem.sTitle; + ++nPosition; + lValues[nPosition] >>= aItem.sImageIdentifier; + ++nPosition; + lValues[nPosition] >>= aItem.sTargetName; + ++nPosition; + m_aWizardMenu.AppendSetupEntry( aItem ); + } + + // Attention: Don't reset nPosition here! + +/*TODO: Not used in the moment! see Notify() ... + // Enable notification mechanism of our baseclass. + // We need it to get information about changes outside these class on our used configuration keys! + EnableNotification( lNames ); +*/ +} + +// destructor + +SvtDynamicMenuOptions_Impl::~SvtDynamicMenuOptions_Impl() +{ + assert(!IsModified()); // should have been committed +} + +// public method + +void SvtDynamicMenuOptions_Impl::Notify( const Sequence< OUString >& ) +{ + SAL_WARN( "unotools.config", "SvtDynamicMenuOptions_Impl::Notify() Not implemented yet! I don't know how I can handle a dynamical list of unknown properties ..." ); +} + +// public method + +void SvtDynamicMenuOptions_Impl::ImplCommit() +{ + SAL_WARN("unotools.config", "SvtDynamicMenuOptions_Impl::ImplCommit(): Not implemented yet!"); + /* + // Write all properties! + // Delete complete sets first. + ClearNodeSet( SETNODE_NEWMENU ); + ClearNodeSet( SETNODE_WIZARDMENU ); + + MenuEntry aItem; + OUString sNode; + Sequence< PropertyValue > lPropertyValues( PROPERTYCOUNT ); + sal_uInt32 nItem = 0; + + // Copy "new" menu entries to save-list! + sal_uInt32 nNewCount = m_aNewMenu.size(); + for( nItem=0; nItem<nNewCount; ++nItem ) + { + aItem = m_aNewMenu[nItem]; + // Format: "New/1/URL" + // "New/1/Title" + // ... + sNode = SETNODE_NEWMENU + PATHDELIMITER + PATHPREFIX + OUString::valueOf( (sal_Int32)nItem ) + PATHDELIMITER; + + lPropertyValues[OFFSET_URL ].Name = sNode + PROPERTYNAME_URL; + lPropertyValues[OFFSET_TITLE ].Name = sNode + PROPERTYNAME_TITLE; + lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Name = sNode + PROPERTYNAME_IMAGEIDENTIFIER; + lPropertyValues[OFFSET_TARGETNAME ].Name = sNode + PROPERTYNAME_TARGETNAME; + + lPropertyValues[OFFSET_URL ].Value <<= aItem.sURL; + lPropertyValues[OFFSET_TITLE ].Value <<= aItem.sTitle; + lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Value <<= aItem.sImageIdentifier; + lPropertyValues[OFFSET_TARGETNAME ].Value <<= aItem.sTargetName; + + SetSetProperties( SETNODE_NEWMENU, lPropertyValues ); + } + + // Copy "wizard" menu entries to save-list! + sal_uInt32 nWizardCount = m_aWizardMenu.size(); + for( nItem=0; nItem<nWizardCount; ++nItem ) + { + aItem = m_aWizardMenu[nItem]; + // Format: "Wizard/1/URL" + // "Wizard/1/Title" + // ... + sNode = SETNODE_WIZARDMENU + PATHDELIMITER + PATHPREFIX + OUString::valueOf( (sal_Int32)nItem ) + PATHDELIMITER; + + lPropertyValues[OFFSET_URL ].Name = sNode + PROPERTYNAME_URL; + lPropertyValues[OFFSET_TITLE ].Name = sNode + PROPERTYNAME_TITLE; + lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Name = sNode + PROPERTYNAME_IMAGEIDENTIFIER; + lPropertyValues[OFFSET_TARGETNAME ].Name = sNode + PROPERTYNAME_TARGETNAME; + + lPropertyValues[OFFSET_URL ].Value <<= aItem.sURL; + lPropertyValues[OFFSET_TITLE ].Value <<= aItem.sTitle; + lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Value <<= aItem.sImageIdentifier; + lPropertyValues[OFFSET_TARGETNAME ].Value <<= aItem.sTargetName; + + SetSetProperties( SETNODE_WIZARDMENU, lPropertyValues ); + } + + */ +} + +// public method + +Sequence< Sequence< PropertyValue > > SvtDynamicMenuOptions_Impl::GetMenu( EDynamicMenuType eMenu ) const +{ + Sequence< Sequence< PropertyValue > > lReturn; + switch( eMenu ) + { + case EDynamicMenuType::NewMenu : + lReturn = m_aNewMenu.GetList(); + break; + + case EDynamicMenuType::WizardMenu : + lReturn = m_aWizardMenu.GetList(); + break; + } + return lReturn; +} + +// private method + +Sequence< OUString > SvtDynamicMenuOptions_Impl::impl_GetPropertyNames( sal_uInt32& nNewCount, sal_uInt32& nWizardCount ) +{ + // First get ALL names of current existing list items in configuration! + Sequence< OUString > lNewItems = GetNodeNames( SETNODE_NEWMENU ); + Sequence< OUString > lWizardItems = GetNodeNames( SETNODE_WIZARDMENU ); + + // Get information about list counts ... + nNewCount = lNewItems.getLength (); + nWizardCount = lWizardItems.getLength (); + + // Sort and expand all three list to result list ... + Sequence< OUString > lProperties; + impl_SortAndExpandPropertyNames( lNewItems , lProperties, SETNODE_NEWMENU ); + impl_SortAndExpandPropertyNames( lWizardItems , lProperties, SETNODE_WIZARDMENU ); + + // Return result. + return lProperties; +} + +// private helper + +namespace { + +class CountWithPrefixSort +{ + public: + bool operator() ( const OUString& s1 , + const OUString& s2 ) const + { + // Get order numbers from entry name without prefix. + // e.g. "m10" => 10 + // "m5" => 5 + sal_Int32 n1 = s1.copy( 1 ).toInt32(); + sal_Int32 n2 = s2.copy( 1 ).toInt32(); + // MUST be in [0,1] ... because it's a difference between + // insert-positions of given entries in sorted list! + return( n1<n2 ); + } +}; + +class SelectByPrefix +{ + public: + bool operator() ( const OUString& s ) const + { + // Prefer setup written entries by check first letter of given string. It must be a "s". + return s.startsWith( PATHPREFIX_SETUP ); + } +}; + +} + +// private method + +void SvtDynamicMenuOptions_Impl::impl_SortAndExpandPropertyNames( const Sequence< OUString >& lSource , + Sequence< OUString >& lDestination , + const OUString& sSetNode ) +{ + vector< OUString > lTemp; + sal_Int32 nSourceCount = lSource.getLength(); + sal_Int32 nDestinationStep = lDestination.getLength(); // start on end of current list ...! + + lDestination.realloc( (nSourceCount*PROPERTYCOUNT)+nDestinationStep ); // get enough memory for copy operations after nDestination ... + + // Copy all items to temp. vector to use fast sort operations :-) + lTemp.reserve(nSourceCount); + std::copy(lSource.begin(), lSource.end(), std::back_inserter(lTemp)); + + // Sort all entries by number ... + stable_sort( lTemp.begin(), lTemp.end(), CountWithPrefixSort() ); + // and split into setup & user written entries! + stable_partition( lTemp.begin(), lTemp.end(), SelectByPrefix() ); + + // Copy sorted entries to destination and expand every item with + // 4 supported sub properties. + for( const auto& rItem : lTemp ) + { + OUString sFixPath(sSetNode + PATHDELIMITER + rItem + PATHDELIMITER); + lDestination[nDestinationStep++] = sFixPath + PROPERTYNAME_URL; + lDestination[nDestinationStep++] = sFixPath + PROPERTYNAME_TITLE; + lDestination[nDestinationStep++] = sFixPath + PROPERTYNAME_IMAGEIDENTIFIER; + lDestination[nDestinationStep++] = sFixPath + PROPERTYNAME_TARGETNAME; + } +} + +namespace { + // global + std::weak_ptr<SvtDynamicMenuOptions_Impl> g_pDynamicMenuOptions; +} + +SvtDynamicMenuOptions::SvtDynamicMenuOptions() +{ + // Global access, must be guarded (multithreading!). + MutexGuard aGuard( GetOwnStaticMutex() ); + + m_pImpl = g_pDynamicMenuOptions.lock(); + if( !m_pImpl ) + { + m_pImpl = std::make_shared<SvtDynamicMenuOptions_Impl>(); + g_pDynamicMenuOptions = m_pImpl; + ItemHolder1::holdConfigItem(EItem::DynamicMenuOptions); + } +} + +SvtDynamicMenuOptions::~SvtDynamicMenuOptions() +{ + // Global access, must be guarded (multithreading!) + MutexGuard aGuard( GetOwnStaticMutex() ); + + m_pImpl.reset(); +} + +// public method + +Sequence< Sequence< PropertyValue > > SvtDynamicMenuOptions::GetMenu( EDynamicMenuType eMenu ) const +{ + MutexGuard aGuard( GetOwnStaticMutex() ); + return m_pImpl->GetMenu( eMenu ); +} + +namespace +{ + class theDynamicMenuOptionsMutex : public rtl::Static<osl::Mutex, theDynamicMenuOptionsMutex>{}; +} + +// private method + +Mutex& SvtDynamicMenuOptions::GetOwnStaticMutex() +{ + return theDynamicMenuOptionsMutex::get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |