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"
62 /*-****************************************************************************************************************
63 @descr struct to hold information about one menu entry.
64 ****************************************************************************************************************-*/
65 struct SvtDynMenuEntry
69 OUString sImageIdentifier
;
73 /*-****************************************************************************************************************
74 @descr support simple menu structures and operations on it
75 ****************************************************************************************************************-*/
79 // append setup written menu entry
80 // Don't touch name of entry. It was defined by setup and must be the same every time!
81 // Look for double menu entries here too... may be some separator items are superfluous...
82 void AppendSetupEntry( const SvtDynMenuEntry
& rEntry
)
85 ( lSetupEntries
.empty() ) ||
86 ( lSetupEntries
.rbegin()->sURL
!= rEntry
.sURL
)
89 lSetupEntries
.push_back( rEntry
);
93 // convert internal list to external format
94 // for using it on right menus really
95 // Notice: We build a property list with 4 entries and set it on result list then.
96 // Separator entries will be packed in another way then normal entries! We define
97 // special string "sSeparator" to perform too ...
98 Sequence
< Sequence
< PropertyValue
> > GetList() const
100 sal_Int32 nSetupCount
= static_cast<sal_Int32
>(lSetupEntries
.size());
101 sal_Int32 nUserCount
= static_cast<sal_Int32
>(lUserEntries
.size());
103 Sequence
< PropertyValue
> lProperties ( PROPERTYCOUNT
);
104 Sequence
< Sequence
< PropertyValue
> > lResult ( nSetupCount
+nUserCount
);
105 OUString
sSeparator ( "private:separator" );
107 lProperties
[OFFSET_URL
].Name
= PROPERTYNAME_URL
;
108 lProperties
[OFFSET_TITLE
].Name
= PROPERTYNAME_TITLE
;
109 lProperties
[OFFSET_IMAGEIDENTIFIER
].Name
= PROPERTYNAME_IMAGEIDENTIFIER
;
110 lProperties
[OFFSET_TARGETNAME
].Name
= PROPERTYNAME_TARGETNAME
;
112 for( const auto& pList
: {&lSetupEntries
, &lUserEntries
} )
114 for( const auto& rItem
: *pList
)
116 if( rItem
.sURL
== sSeparator
)
118 lProperties
[OFFSET_URL
].Value
<<= sSeparator
;
119 lProperties
[OFFSET_TITLE
].Value
<<= OUString();
120 lProperties
[OFFSET_IMAGEIDENTIFIER
].Value
<<= OUString();
121 lProperties
[OFFSET_TARGETNAME
].Value
<<= OUString();
125 lProperties
[OFFSET_URL
].Value
<<= rItem
.sURL
;
126 lProperties
[OFFSET_TITLE
].Value
<<= rItem
.sTitle
;
127 lProperties
[OFFSET_IMAGEIDENTIFIER
].Value
<<= rItem
.sImageIdentifier
;
128 lProperties
[OFFSET_TARGETNAME
].Value
<<= rItem
.sTargetName
;
130 lResult
[nStep
] = lProperties
;
138 vector
< SvtDynMenuEntry
> lSetupEntries
;
139 vector
< SvtDynMenuEntry
> lUserEntries
;
142 class SvtDynamicMenuOptions_Impl
: public ConfigItem
146 SvtDynamicMenuOptions_Impl();
147 virtual ~SvtDynamicMenuOptions_Impl() override
;
149 /*-****************************************************************************************************
150 @short called for notify of configmanager
151 @descr This method is called from the ConfigManager before the application ends or from the
152 PropertyChangeListener if the sub tree broadcasts changes. You must update your
155 @seealso baseclass ConfigItem
157 @param "lPropertyNames" is the list of properties which should be updated.
158 *//*-*****************************************************************************************************/
160 virtual void Notify( const Sequence
< OUString
>& lPropertyNames
) override
;
162 /*-****************************************************************************************************
163 @short base implementation of public interface for "SvtDynamicMenuOptions"!
164 @descr These class is used as static member of "SvtDynamicMenuOptions" ...
165 => The code exist only for one time and isn't duplicated for every instance!
166 *//*-*****************************************************************************************************/
168 Sequence
< Sequence
< PropertyValue
> > GetMenu ( EDynamicMenuType eMenu
) const;
172 virtual void ImplCommit() override
;
174 /*-****************************************************************************************************
175 @short return list of key names of our configuration management which represent our module tree
176 @descr This method returns the current list of key names! We need it to get needed values from our
177 configuration management and support dynamical menu item lists!
178 @param "nNewCount" , returns count of menu entries for "new"
179 @param "nWizardCount" , returns count of menu entries for "wizard"
180 @return A list of configuration key names is returned.
181 *//*-*****************************************************************************************************/
183 Sequence
< OUString
> impl_GetPropertyNames( sal_uInt32
& nNewCount
, sal_uInt32
& nWizardCount
);
185 /*-****************************************************************************************************
186 @short sort given source list and expand it for all well known properties to destination
187 @descr We must support sets of entries with count inside the name .. but some of them could be missing!
188 e.g. s1-s2-s3-s0-u1-s6-u5-u7
189 Then we must sort it by name and expand it to the follow one:
207 Rules: We start with all setup written entries names "sx" and x=[0..n].
208 Then we handle all "ux" items. Inside these blocks we sort it ascending by number.
210 @attention We add these expanded list to the end of given "lDestination" list!
211 So we must start on "lDestination.getLength()".
212 Reallocation of memory of destination list is done by us!
214 @seealso method impl_GetPropertyNames()
216 @param "lSource" , original list (e.g. [m1-m2-m3-m6-m0] )
217 @param "lDestination" , destination of operation
218 @param "sSetNode" , name of configuration set to build complete path
219 @return A list of configuration key names is returned.
220 *//*-*****************************************************************************************************/
222 static void impl_SortAndExpandPropertyNames( const Sequence
< OUString
>& lSource
,
223 Sequence
< OUString
>& lDestination
,
224 const OUString
& sSetNode
);
230 SvtDynMenu m_aNewMenu
;
231 SvtDynMenu m_aWizardMenu
;
236 SvtDynamicMenuOptions_Impl::SvtDynamicMenuOptions_Impl()
237 // Init baseclasses first
238 : ConfigItem( ROOTNODE_MENUS
)
239 // Init member then...
241 // Get names and values of all accessible menu entries and fill internal structures.
242 // See impl_GetPropertyNames() for further information.
243 sal_uInt32 nNewCount
= 0;
244 sal_uInt32 nWizardCount
= 0;
245 Sequence
< OUString
> lNames
= impl_GetPropertyNames ( nNewCount
,
247 Sequence
< Any
> lValues
= GetProperties ( lNames
);
249 // Safe impossible cases.
250 // We need values from ALL configuration keys.
251 // Follow assignment use order of values in relation to our list of key names!
252 DBG_ASSERT( !(lNames
.getLength()!=lValues
.getLength()), "SvtDynamicMenuOptions_Impl::SvtDynamicMenuOptions_Impl()\nI miss some values of configuration keys!\n" );
254 // Copy values from list in right order to our internal member.
255 // Attention: List for names and values have an internal construction pattern!
257 // first "New" menu ...
259 // /New/1/URL "private:factory/swriter"
260 // /New/1/Title "New Writer Document"
261 // /New/1/ImageIdentifier "icon_writer"
262 // /New/1/TargetName "_blank"
264 // /New/2/URL "private:factory/scalc"
265 // /New/2/Title "New Calc Document"
266 // /New/2/ImageIdentifier "icon_calc"
267 // /New/2/TargetName "_blank"
269 // second "Wizard" menu ...
270 // /Wizard/1/URL "file://b"
271 // /Wizard/1/Title "PaintSomething"
272 // /Wizard/1/ImageIdentifier "icon_?"
273 // /Wizard/1/TargetName "_self"
277 sal_uInt32 nItem
= 0;
278 sal_uInt32 nPosition
= 0;
280 // Get names/values for new menu.
281 // 4 subkeys for every item!
282 for( nItem
=0; nItem
<nNewCount
; ++nItem
)
284 SvtDynMenuEntry aItem
;
285 lValues
[nPosition
] >>= aItem
.sURL
;
287 lValues
[nPosition
] >>= aItem
.sTitle
;
289 lValues
[nPosition
] >>= aItem
.sImageIdentifier
;
291 lValues
[nPosition
] >>= aItem
.sTargetName
;
293 m_aNewMenu
.AppendSetupEntry( aItem
);
296 // Attention: Don't reset nPosition here!
298 // Get names/values for wizard menu.
299 // 4 subkeys for every item!
300 for( nItem
=0; nItem
<nWizardCount
; ++nItem
)
302 SvtDynMenuEntry aItem
;
303 lValues
[nPosition
] >>= aItem
.sURL
;
305 lValues
[nPosition
] >>= aItem
.sTitle
;
307 lValues
[nPosition
] >>= aItem
.sImageIdentifier
;
309 lValues
[nPosition
] >>= aItem
.sTargetName
;
311 m_aWizardMenu
.AppendSetupEntry( aItem
);
314 // Attention: Don't reset nPosition here!
316 /*TODO: Not used in the moment! see Notify() ...
317 // Enable notification mechanism of our baseclass.
318 // We need it to get information about changes outside these class on our used configuration keys!
319 EnableNotification( lNames );
325 SvtDynamicMenuOptions_Impl::~SvtDynamicMenuOptions_Impl()
327 assert(!IsModified()); // should have been committed
332 void SvtDynamicMenuOptions_Impl::Notify( const Sequence
< OUString
>& )
334 SAL_WARN( "unotools.config", "SvtDynamicMenuOptions_Impl::Notify() Not implemented yet! I don't know how I can handle a dynamical list of unknown properties ..." );
339 void SvtDynamicMenuOptions_Impl::ImplCommit()
341 SAL_WARN("unotools.config", "SvtDynamicMenuOptions_Impl::ImplCommit(): Not implemented yet!");
343 // Write all properties!
344 // Delete complete sets first.
345 ClearNodeSet( SETNODE_NEWMENU );
346 ClearNodeSet( SETNODE_WIZARDMENU );
350 Sequence< PropertyValue > lPropertyValues( PROPERTYCOUNT );
351 sal_uInt32 nItem = 0;
353 // Copy "new" menu entries to save-list!
354 sal_uInt32 nNewCount = m_aNewMenu.size();
355 for( nItem=0; nItem<nNewCount; ++nItem )
357 aItem = m_aNewMenu[nItem];
358 // Format: "New/1/URL"
361 sNode = SETNODE_NEWMENU + PATHDELIMITER + PATHPREFIX + OUString::valueOf( (sal_Int32)nItem ) + PATHDELIMITER;
363 lPropertyValues[OFFSET_URL ].Name = sNode + PROPERTYNAME_URL;
364 lPropertyValues[OFFSET_TITLE ].Name = sNode + PROPERTYNAME_TITLE;
365 lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Name = sNode + PROPERTYNAME_IMAGEIDENTIFIER;
366 lPropertyValues[OFFSET_TARGETNAME ].Name = sNode + PROPERTYNAME_TARGETNAME;
368 lPropertyValues[OFFSET_URL ].Value <<= aItem.sURL;
369 lPropertyValues[OFFSET_TITLE ].Value <<= aItem.sTitle;
370 lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Value <<= aItem.sImageIdentifier;
371 lPropertyValues[OFFSET_TARGETNAME ].Value <<= aItem.sTargetName;
373 SetSetProperties( SETNODE_NEWMENU, lPropertyValues );
376 // Copy "wizard" menu entries to save-list!
377 sal_uInt32 nWizardCount = m_aWizardMenu.size();
378 for( nItem=0; nItem<nWizardCount; ++nItem )
380 aItem = m_aWizardMenu[nItem];
381 // Format: "Wizard/1/URL"
384 sNode = SETNODE_WIZARDMENU + PATHDELIMITER + PATHPREFIX + OUString::valueOf( (sal_Int32)nItem ) + PATHDELIMITER;
386 lPropertyValues[OFFSET_URL ].Name = sNode + PROPERTYNAME_URL;
387 lPropertyValues[OFFSET_TITLE ].Name = sNode + PROPERTYNAME_TITLE;
388 lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Name = sNode + PROPERTYNAME_IMAGEIDENTIFIER;
389 lPropertyValues[OFFSET_TARGETNAME ].Name = sNode + PROPERTYNAME_TARGETNAME;
391 lPropertyValues[OFFSET_URL ].Value <<= aItem.sURL;
392 lPropertyValues[OFFSET_TITLE ].Value <<= aItem.sTitle;
393 lPropertyValues[OFFSET_IMAGEIDENTIFIER ].Value <<= aItem.sImageIdentifier;
394 lPropertyValues[OFFSET_TARGETNAME ].Value <<= aItem.sTargetName;
396 SetSetProperties( SETNODE_WIZARDMENU, lPropertyValues );
404 Sequence
< Sequence
< PropertyValue
> > SvtDynamicMenuOptions_Impl::GetMenu( EDynamicMenuType eMenu
) const
406 Sequence
< Sequence
< PropertyValue
> > lReturn
;
409 case EDynamicMenuType::NewMenu
:
410 lReturn
= m_aNewMenu
.GetList();
413 case EDynamicMenuType::WizardMenu
:
414 lReturn
= m_aWizardMenu
.GetList();
422 Sequence
< OUString
> SvtDynamicMenuOptions_Impl::impl_GetPropertyNames( sal_uInt32
& nNewCount
, sal_uInt32
& nWizardCount
)
424 // First get ALL names of current existing list items in configuration!
425 Sequence
< OUString
> lNewItems
= GetNodeNames( SETNODE_NEWMENU
);
426 Sequence
< OUString
> lWizardItems
= GetNodeNames( SETNODE_WIZARDMENU
);
428 // Get information about list counts ...
429 nNewCount
= lNewItems
.getLength ();
430 nWizardCount
= lWizardItems
.getLength ();
432 // Sort and expand all three list to result list ...
433 Sequence
< OUString
> lProperties
;
434 impl_SortAndExpandPropertyNames( lNewItems
, lProperties
, SETNODE_NEWMENU
);
435 impl_SortAndExpandPropertyNames( lWizardItems
, lProperties
, SETNODE_WIZARDMENU
);
443 class CountWithPrefixSort
446 bool operator() ( const OUString
& s1
,
447 const OUString
& s2
) const
449 // Get order numbers from entry name without prefix.
452 sal_Int32 n1
= s1
.copy( 1 ).toInt32();
453 sal_Int32 n2
= s2
.copy( 1 ).toInt32();
454 // MUST be in [0,1] ... because it's a difference between
455 // insert-positions of given entries in sorted list!
463 bool operator() ( const OUString
& s
) const
465 // Prefer setup written entries by check first letter of given string. It must be a "s".
466 return s
.startsWith( PATHPREFIX_SETUP
);
472 void SvtDynamicMenuOptions_Impl::impl_SortAndExpandPropertyNames( const Sequence
< OUString
>& lSource
,
473 Sequence
< OUString
>& lDestination
,
474 const OUString
& sSetNode
)
476 vector
< OUString
> lTemp
;
477 sal_Int32 nSourceCount
= lSource
.getLength();
478 sal_Int32 nDestinationStep
= lDestination
.getLength(); // start on end of current list ...!
480 lDestination
.realloc( (nSourceCount
*PROPERTYCOUNT
)+nDestinationStep
); // get enough memory for copy operations after nDestination ...
482 // Copy all items to temp. vector to use fast sort operations :-)
483 lTemp
.reserve(nSourceCount
);
484 std::copy(lSource
.begin(), lSource
.end(), std::back_inserter(lTemp
));
486 // Sort all entries by number ...
487 stable_sort( lTemp
.begin(), lTemp
.end(), CountWithPrefixSort() );
488 // and split into setup & user written entries!
489 stable_partition( lTemp
.begin(), lTemp
.end(), SelectByPrefix() );
491 // Copy sorted entries to destination and expand every item with
492 // 4 supported sub properties.
493 for( const auto& rItem
: lTemp
)
495 OUString
sFixPath(sSetNode
+ PATHDELIMITER
+ rItem
+ PATHDELIMITER
);
496 lDestination
[nDestinationStep
++] = sFixPath
+ PROPERTYNAME_URL
;
497 lDestination
[nDestinationStep
++] = sFixPath
+ PROPERTYNAME_TITLE
;
498 lDestination
[nDestinationStep
++] = sFixPath
+ PROPERTYNAME_IMAGEIDENTIFIER
;
499 lDestination
[nDestinationStep
++] = sFixPath
+ PROPERTYNAME_TARGETNAME
;
505 std::weak_ptr
<SvtDynamicMenuOptions_Impl
> g_pDynamicMenuOptions
;
508 SvtDynamicMenuOptions::SvtDynamicMenuOptions()
510 // Global access, must be guarded (multithreading!).
511 MutexGuard
aGuard( GetOwnStaticMutex() );
513 m_pImpl
= g_pDynamicMenuOptions
.lock();
516 m_pImpl
= std::make_shared
<SvtDynamicMenuOptions_Impl
>();
517 g_pDynamicMenuOptions
= m_pImpl
;
518 ItemHolder1::holdConfigItem(EItem::DynamicMenuOptions
);
522 SvtDynamicMenuOptions::~SvtDynamicMenuOptions()
524 // Global access, must be guarded (multithreading!)
525 MutexGuard
aGuard( GetOwnStaticMutex() );
532 Sequence
< Sequence
< PropertyValue
> > SvtDynamicMenuOptions::GetMenu( EDynamicMenuType eMenu
) const
534 MutexGuard
aGuard( GetOwnStaticMutex() );
535 return m_pImpl
->GetMenu( eMenu
);
540 class theDynamicMenuOptionsMutex
: public rtl::Static
<osl::Mutex
, theDynamicMenuOptionsMutex
>{};
545 Mutex
& SvtDynamicMenuOptions::GetOwnStaticMutex()
547 return theDynamicMenuOptionsMutex::get();
550 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */