1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <sal/log.hxx>
23 #include <unotools/dynamicmenuoptions.hxx>
24 #include <unotools/configitem.hxx>
25 #include <tools/debug.hxx>
26 #include <com/sun/star/uno/Any.hxx>
27 #include <com/sun/star/uno/Sequence.hxx>
28 #include <com/sun/star/beans/PropertyValue.hpp>
32 #include "itemholder1.hxx"
36 using namespace ::std
;
37 using namespace ::utl
;
38 using namespace ::osl
;
39 using namespace ::com::sun::star::uno
;
40 using namespace ::com::sun::star::beans
;
42 #define ROOTNODE_MENUS "Office.Common/Menus/"
43 #define PATHDELIMITER "/"
45 #define SETNODE_NEWMENU "New"
46 #define SETNODE_WIZARDMENU "Wizard"
48 #define PROPERTYNAME_URL DYNAMICMENU_PROPERTYNAME_URL
49 #define PROPERTYNAME_TITLE DYNAMICMENU_PROPERTYNAME_TITLE
50 #define PROPERTYNAME_IMAGEIDENTIFIER DYNAMICMENU_PROPERTYNAME_IMAGEIDENTIFIER
51 #define PROPERTYNAME_TARGETNAME DYNAMICMENU_PROPERTYNAME_TARGETNAME
53 #define PROPERTYCOUNT 4
56 #define OFFSET_TITLE 1
57 #define OFFSET_IMAGEIDENTIFIER 2
58 #define OFFSET_TARGETNAME 3
60 #define PATHPREFIX_SETUP "m"
64 /*-****************************************************************************************************************
65 @descr struct to hold information about one menu entry.
66 ****************************************************************************************************************-*/
67 struct SvtDynMenuEntry
71 OUString sImageIdentifier
;
75 /*-****************************************************************************************************************
76 @descr support simple menu structures and operations on it
77 ****************************************************************************************************************-*/
81 // append setup written menu entry
82 // Don't touch name of entry. It was defined by setup and must be the same every time!
83 // Look for double menu entries here too... may be some separator items are superfluous...
84 void AppendSetupEntry( const SvtDynMenuEntry
& rEntry
)
87 ( lSetupEntries
.empty() ) ||
88 ( lSetupEntries
.rbegin()->sURL
!= rEntry
.sURL
)
91 lSetupEntries
.push_back( rEntry
);
95 // convert internal list to external format
96 // for using it on right menus really
97 // Notice: We build a property list with 4 entries and set it on result list then.
98 // Separator entries will be packed in another way then normal entries! We define
99 // special string "sSeparator" to perform too ...
100 Sequence
< Sequence
< PropertyValue
> > GetList() const
102 sal_Int32 nSetupCount
= static_cast<sal_Int32
>(lSetupEntries
.size());
103 sal_Int32 nUserCount
= static_cast<sal_Int32
>(lUserEntries
.size());
105 Sequence
< PropertyValue
> lProperties ( PROPERTYCOUNT
);
106 Sequence
< Sequence
< PropertyValue
> > lResult ( nSetupCount
+nUserCount
);
107 OUString
sSeparator ( "private:separator" );
109 lProperties
[OFFSET_URL
].Name
= PROPERTYNAME_URL
;
110 lProperties
[OFFSET_TITLE
].Name
= PROPERTYNAME_TITLE
;
111 lProperties
[OFFSET_IMAGEIDENTIFIER
].Name
= PROPERTYNAME_IMAGEIDENTIFIER
;
112 lProperties
[OFFSET_TARGETNAME
].Name
= PROPERTYNAME_TARGETNAME
;
114 for( const auto& pList
: {&lSetupEntries
, &lUserEntries
} )
116 for( const auto& rItem
: *pList
)
118 if( rItem
.sURL
== sSeparator
)
120 lProperties
[OFFSET_URL
].Value
<<= sSeparator
;
121 lProperties
[OFFSET_TITLE
].Value
<<= OUString();
122 lProperties
[OFFSET_IMAGEIDENTIFIER
].Value
<<= OUString();
123 lProperties
[OFFSET_TARGETNAME
].Value
<<= OUString();
127 lProperties
[OFFSET_URL
].Value
<<= rItem
.sURL
;
128 lProperties
[OFFSET_TITLE
].Value
<<= rItem
.sTitle
;
129 lProperties
[OFFSET_IMAGEIDENTIFIER
].Value
<<= rItem
.sImageIdentifier
;
130 lProperties
[OFFSET_TARGETNAME
].Value
<<= rItem
.sTargetName
;
132 lResult
[nStep
] = lProperties
;
140 vector
< SvtDynMenuEntry
> lSetupEntries
;
141 vector
< SvtDynMenuEntry
> lUserEntries
;
146 class SvtDynamicMenuOptions_Impl
: public ConfigItem
150 SvtDynamicMenuOptions_Impl();
151 virtual ~SvtDynamicMenuOptions_Impl() override
;
153 /*-****************************************************************************************************
154 @short called for notify of configmanager
155 @descr This method is called from the ConfigManager before the application ends or from the
156 PropertyChangeListener if the sub tree broadcasts changes. You must update your
159 @seealso baseclass ConfigItem
161 @param "lPropertyNames" is the list of properties which should be updated.
162 *//*-*****************************************************************************************************/
164 virtual void Notify( const Sequence
< OUString
>& lPropertyNames
) override
;
166 /*-****************************************************************************************************
167 @short base implementation of public interface for "SvtDynamicMenuOptions"!
168 @descr These class is used as static member of "SvtDynamicMenuOptions" ...
169 => The code exist only for one time and isn't duplicated for every instance!
170 *//*-*****************************************************************************************************/
172 Sequence
< Sequence
< PropertyValue
> > GetMenu ( EDynamicMenuType eMenu
) const;
176 virtual void ImplCommit() override
;
178 /*-****************************************************************************************************
179 @short return list of key names of our configuration management which represent our module tree
180 @descr This method returns the current list of key names! We need it to get needed values from our
181 configuration management and support dynamical menu item lists!
182 @param "nNewCount" , returns count of menu entries for "new"
183 @param "nWizardCount" , returns count of menu entries for "wizard"
184 @return A list of configuration key names is returned.
185 *//*-*****************************************************************************************************/
187 Sequence
< OUString
> impl_GetPropertyNames( sal_uInt32
& nNewCount
, sal_uInt32
& nWizardCount
);
189 /*-****************************************************************************************************
190 @short sort given source list and expand it for all well known properties to destination
191 @descr We must support sets of entries with count inside the name .. but some of them could be missing!
192 e.g. s1-s2-s3-s0-u1-s6-u5-u7
193 Then we must sort it by name and expand it to the follow one:
211 Rules: We start with all setup written entries names "sx" and x=[0..n].
212 Then we handle all "ux" items. Inside these blocks we sort it ascending by number.
214 @attention We add these expanded list to the end of given "lDestination" list!
215 So we must start on "lDestination.getLength()".
216 Reallocation of memory of destination list is done by us!
218 @seealso method impl_GetPropertyNames()
220 @param "lSource" , original list (e.g. [m1-m2-m3-m6-m0] )
221 @param "lDestination" , destination of operation
222 @param "sSetNode" , name of configuration set to build complete path
223 @return A list of configuration key names is returned.
224 *//*-*****************************************************************************************************/
226 static void impl_SortAndExpandPropertyNames( const Sequence
< OUString
>& lSource
,
227 Sequence
< OUString
>& lDestination
,
228 const OUString
& sSetNode
);
234 SvtDynMenu m_aNewMenu
;
235 SvtDynMenu m_aWizardMenu
;
240 SvtDynamicMenuOptions_Impl::SvtDynamicMenuOptions_Impl()
241 // Init baseclasses first
242 : ConfigItem( ROOTNODE_MENUS
)
243 // Init member then...
245 // Get names and values of all accessible menu entries and fill internal structures.
246 // See impl_GetPropertyNames() for further information.
247 sal_uInt32 nNewCount
= 0;
248 sal_uInt32 nWizardCount
= 0;
249 Sequence
< OUString
> lNames
= impl_GetPropertyNames ( nNewCount
,
251 Sequence
< Any
> lValues
= GetProperties ( lNames
);
253 // Safe impossible cases.
254 // We need values from ALL configuration keys.
255 // Follow assignment use order of values in relation to our list of key names!
256 DBG_ASSERT( !(lNames
.getLength()!=lValues
.getLength()), "SvtDynamicMenuOptions_Impl::SvtDynamicMenuOptions_Impl()\nI miss some values of configuration keys!\n" );
258 // Copy values from list in right order to our internal member.
259 // Attention: List for names and values have an internal construction pattern!
261 // first "New" menu ...
263 // /New/1/URL "private:factory/swriter"
264 // /New/1/Title "New Writer Document"
265 // /New/1/ImageIdentifier "icon_writer"
266 // /New/1/TargetName "_blank"
268 // /New/2/URL "private:factory/scalc"
269 // /New/2/Title "New Calc Document"
270 // /New/2/ImageIdentifier "icon_calc"
271 // /New/2/TargetName "_blank"
273 // second "Wizard" menu ...
274 // /Wizard/1/URL "file://b"
275 // /Wizard/1/Title "PaintSomething"
276 // /Wizard/1/ImageIdentifier "icon_?"
277 // /Wizard/1/TargetName "_self"
281 sal_uInt32 nItem
= 0;
282 sal_uInt32 nPosition
= 0;
284 // Get names/values for new menu.
285 // 4 subkeys for every item!
286 for( nItem
=0; nItem
<nNewCount
; ++nItem
)
288 SvtDynMenuEntry aItem
;
289 lValues
[nPosition
] >>= aItem
.sURL
;
291 lValues
[nPosition
] >>= aItem
.sTitle
;
293 lValues
[nPosition
] >>= aItem
.sImageIdentifier
;
295 lValues
[nPosition
] >>= aItem
.sTargetName
;
297 m_aNewMenu
.AppendSetupEntry( aItem
);
300 // Attention: Don't reset nPosition here!
302 // Get names/values for wizard menu.
303 // 4 subkeys for every item!
304 for( nItem
=0; nItem
<nWizardCount
; ++nItem
)
306 SvtDynMenuEntry aItem
;
307 lValues
[nPosition
] >>= aItem
.sURL
;
309 lValues
[nPosition
] >>= aItem
.sTitle
;
311 lValues
[nPosition
] >>= aItem
.sImageIdentifier
;
313 lValues
[nPosition
] >>= aItem
.sTargetName
;
315 m_aWizardMenu
.AppendSetupEntry( aItem
);
318 // Attention: Don't reset nPosition here!
320 /*TODO: Not used in the moment! see Notify() ...
321 // Enable notification mechanism of our baseclass.
322 // We need it to get information about changes outside these class on our used configuration keys!
323 EnableNotification( lNames );
329 SvtDynamicMenuOptions_Impl::~SvtDynamicMenuOptions_Impl()
331 assert(!IsModified()); // should have been committed
336 void SvtDynamicMenuOptions_Impl::Notify( const Sequence
< OUString
>& )
338 SAL_WARN( "unotools.config", "SvtDynamicMenuOptions_Impl::Notify() Not implemented yet! I don't know how I can handle a dynamical list of unknown properties ..." );
343 void SvtDynamicMenuOptions_Impl::ImplCommit()
345 SAL_WARN("unotools.config", "SvtDynamicMenuOptions_Impl::ImplCommit(): Not implemented yet!");
347 // Write all properties!
348 // Delete complete sets first.
349 ClearNodeSet( SETNODE_NEWMENU );
350 ClearNodeSet( SETNODE_WIZARDMENU );
354 Sequence< PropertyValue > lPropertyValues( PROPERTYCOUNT );
355 sal_uInt32 nItem = 0;
357 // Copy "new" menu entries to save-list!
358 sal_uInt32 nNewCount = m_aNewMenu.size();
359 for( nItem=0; nItem<nNewCount; ++nItem )
361 aItem = m_aNewMenu[nItem];
362 // Format: "New/1/URL"
365 sNode = SETNODE_NEWMENU + PATHDELIMITER + PATHPREFIX + OUString::valueOf( (sal_Int32)nItem ) + PATHDELIMITER;
367 lPropertyValues[OFFSET_URL ].Name = sNode + PROPERTYNAME_URL;
368 lPropertyValues[OFFSET_TITLE ].Name = sNode + PROPERTYNAME_TITLE;
369 lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Name = sNode + PROPERTYNAME_IMAGEIDENTIFIER;
370 lPropertyValues[OFFSET_TARGETNAME ].Name = sNode + PROPERTYNAME_TARGETNAME;
372 lPropertyValues[OFFSET_URL ].Value <<= aItem.sURL;
373 lPropertyValues[OFFSET_TITLE ].Value <<= aItem.sTitle;
374 lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Value <<= aItem.sImageIdentifier;
375 lPropertyValues[OFFSET_TARGETNAME ].Value <<= aItem.sTargetName;
377 SetSetProperties( SETNODE_NEWMENU, lPropertyValues );
380 // Copy "wizard" menu entries to save-list!
381 sal_uInt32 nWizardCount = m_aWizardMenu.size();
382 for( nItem=0; nItem<nWizardCount; ++nItem )
384 aItem = m_aWizardMenu[nItem];
385 // Format: "Wizard/1/URL"
388 sNode = SETNODE_WIZARDMENU + PATHDELIMITER + PATHPREFIX + OUString::valueOf( (sal_Int32)nItem ) + PATHDELIMITER;
390 lPropertyValues[OFFSET_URL ].Name = sNode + PROPERTYNAME_URL;
391 lPropertyValues[OFFSET_TITLE ].Name = sNode + PROPERTYNAME_TITLE;
392 lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Name = sNode + PROPERTYNAME_IMAGEIDENTIFIER;
393 lPropertyValues[OFFSET_TARGETNAME ].Name = sNode + PROPERTYNAME_TARGETNAME;
395 lPropertyValues[OFFSET_URL ].Value <<= aItem.sURL;
396 lPropertyValues[OFFSET_TITLE ].Value <<= aItem.sTitle;
397 lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Value <<= aItem.sImageIdentifier;
398 lPropertyValues[OFFSET_TARGETNAME ].Value <<= aItem.sTargetName;
400 SetSetProperties( SETNODE_WIZARDMENU, lPropertyValues );
408 Sequence
< Sequence
< PropertyValue
> > SvtDynamicMenuOptions_Impl::GetMenu( EDynamicMenuType eMenu
) const
410 Sequence
< Sequence
< PropertyValue
> > lReturn
;
413 case EDynamicMenuType::NewMenu
:
414 lReturn
= m_aNewMenu
.GetList();
417 case EDynamicMenuType::WizardMenu
:
418 lReturn
= m_aWizardMenu
.GetList();
426 Sequence
< OUString
> SvtDynamicMenuOptions_Impl::impl_GetPropertyNames( sal_uInt32
& nNewCount
, sal_uInt32
& nWizardCount
)
428 // First get ALL names of current existing list items in configuration!
429 Sequence
< OUString
> lNewItems
= GetNodeNames( SETNODE_NEWMENU
);
430 Sequence
< OUString
> lWizardItems
= GetNodeNames( SETNODE_WIZARDMENU
);
432 // Get information about list counts ...
433 nNewCount
= lNewItems
.getLength ();
434 nWizardCount
= lWizardItems
.getLength ();
436 // Sort and expand all three list to result list ...
437 Sequence
< OUString
> lProperties
;
438 impl_SortAndExpandPropertyNames( lNewItems
, lProperties
, SETNODE_NEWMENU
);
439 impl_SortAndExpandPropertyNames( lWizardItems
, lProperties
, SETNODE_WIZARDMENU
);
449 class CountWithPrefixSort
452 bool operator() ( const OUString
& s1
,
453 const OUString
& s2
) const
455 // Get order numbers from entry name without prefix.
458 sal_Int32 n1
= s1
.copy( 1 ).toInt32();
459 sal_Int32 n2
= s2
.copy( 1 ).toInt32();
460 // MUST be in [0,1] ... because it's a difference between
461 // insert-positions of given entries in sorted list!
469 bool operator() ( const OUString
& s
) const
471 // Prefer setup written entries by check first letter of given string. It must be a "s".
472 return s
.startsWith( PATHPREFIX_SETUP
);
480 void SvtDynamicMenuOptions_Impl::impl_SortAndExpandPropertyNames( const Sequence
< OUString
>& lSource
,
481 Sequence
< OUString
>& lDestination
,
482 const OUString
& sSetNode
)
484 vector
< OUString
> lTemp
;
485 sal_Int32 nSourceCount
= lSource
.getLength();
486 sal_Int32 nDestinationStep
= lDestination
.getLength(); // start on end of current list ...!
488 lDestination
.realloc( (nSourceCount
*PROPERTYCOUNT
)+nDestinationStep
); // get enough memory for copy operations after nDestination ...
490 // Copy all items to temp. vector to use fast sort operations :-)
491 lTemp
.insert( lTemp
.end(), lSource
.begin(), lSource
.end() );
493 // Sort all entries by number ...
494 stable_sort( lTemp
.begin(), lTemp
.end(), CountWithPrefixSort() );
495 // and split into setup & user written entries!
496 stable_partition( lTemp
.begin(), lTemp
.end(), SelectByPrefix() );
498 // Copy sorted entries to destination and expand every item with
499 // 4 supported sub properties.
500 for( const auto& rItem
: lTemp
)
502 OUString
sFixPath(sSetNode
+ PATHDELIMITER
+ rItem
+ PATHDELIMITER
);
503 lDestination
[nDestinationStep
++] = sFixPath
+ PROPERTYNAME_URL
;
504 lDestination
[nDestinationStep
++] = sFixPath
+ PROPERTYNAME_TITLE
;
505 lDestination
[nDestinationStep
++] = sFixPath
+ PROPERTYNAME_IMAGEIDENTIFIER
;
506 lDestination
[nDestinationStep
++] = sFixPath
+ PROPERTYNAME_TARGETNAME
;
512 std::weak_ptr
<SvtDynamicMenuOptions_Impl
> g_pDynamicMenuOptions
;
515 SvtDynamicMenuOptions::SvtDynamicMenuOptions()
517 // Global access, must be guarded (multithreading!).
518 MutexGuard
aGuard( GetOwnStaticMutex() );
520 m_pImpl
= g_pDynamicMenuOptions
.lock();
523 m_pImpl
= std::make_shared
<SvtDynamicMenuOptions_Impl
>();
524 g_pDynamicMenuOptions
= m_pImpl
;
525 ItemHolder1::holdConfigItem(EItem::DynamicMenuOptions
);
529 SvtDynamicMenuOptions::~SvtDynamicMenuOptions()
531 // Global access, must be guarded (multithreading!)
532 MutexGuard
aGuard( GetOwnStaticMutex() );
539 Sequence
< Sequence
< PropertyValue
> > SvtDynamicMenuOptions::GetMenu( EDynamicMenuType eMenu
) const
541 MutexGuard
aGuard( GetOwnStaticMutex() );
542 return m_pImpl
->GetMenu( eMenu
);
547 class theDynamicMenuOptionsMutex
: public rtl::Static
<osl::Mutex
, theDynamicMenuOptionsMutex
>{};
552 Mutex
& SvtDynamicMenuOptions::GetOwnStaticMutex()
554 return theDynamicMenuOptionsMutex::get();
557 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */