/* -*- 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 #include #include #include #include #include #include #include #include #include "itemholder1.hxx" #include 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" /*-**************************************************************************************************************** @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(lSetupEntries.size()); sal_Int32 nUserCount = static_cast(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>= 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>= 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 > 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 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& 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); for (sal_Int32 nSourceStep = 0; nSourceStep < nSourceCount; ++nSourceStep) lTemp.push_back( lSource[nSourceStep] ); // 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 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(); 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{}; } // private method Mutex& SvtDynamicMenuOptions::GetOwnStaticMutex() { return theDynamicMenuOptionsMutex::get(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */