libyui  3.12.1
YMenuWidget.cc
1 /*
2  Copyright (c) [2020] SUSE LLC
3  This library is free software; you can redistribute it and/or modify
4  it under the terms of the GNU Lesser General Public License as
5  published by the Free Software Foundation; either version 2.1 of the
6  License, or (at your option) version 3.0 of the License. This library
7  is distributed in the hope that it will be useful, but WITHOUT ANY
8  WARRANTY; without even the implied warranty of MERCHANTABILITY or
9  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
10  License for more details. You should have received a copy of the GNU
11  Lesser General Public License along with this library; if not, write
12  to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
13  Floor, Boston, MA 02110-1301 USA
14 */
15 
16 
17 /*-/
18 
19  File: YMenuWidget.cc
20 
21  Author: Stefan Hundhammer <shundhammer@suse.de>
22 
23 /-*/
24 
25 
26 #define YUILogComponent "ui"
27 #include "YUILog.h"
28 
29 #include "YUISymbols.h"
30 #include "YShortcut.h"
31 #include "YMenuWidget.h"
32 
33 #define VERBOSE_SHORTCUTS 0
34 
35 
36 using std::string;
37 
38 
40 {
42  : nextSerialNo( 0 )
43  {}
44 
45  int nextSerialNo;
46 };
47 
48 
49 
50 
51 YMenuWidget::YMenuWidget( YWidget * parent, const string & label )
52  : YSelectionWidget( parent,
53  label,
54  false ) // enforceSingleSelection
55  , priv( new YMenuWidgetPrivate() )
56 {
57  YUI_CHECK_NEW( priv );
58 }
59 
60 
62 {
63  // NOP
64 }
65 
66 
67 void
68 YMenuWidget::addItems( const YItemCollection & itemCollection )
69 {
70  YSelectionWidget::addItems( itemCollection );
73 }
74 
75 
76 void
78 {
80  item->setIndex( ++(priv->nextSerialNo) );
81 
82  if ( item->hasChildren() )
83  assignUniqueIndex( item->childrenBegin(), item->childrenEnd() );
84 }
85 
86 
87 void
89 {
90  for ( YItemIterator it = begin; it != end; ++it )
91  {
92  YItem * item = *it;
93 
94  item->setIndex( ++(priv->nextSerialNo) );
95 
96  if ( item->hasChildren() )
97  assignUniqueIndex( item->childrenBegin(), item->childrenEnd() );
98  }
99 }
100 
101 
102 void
104 {
106  priv->nextSerialNo = 0;
107 }
108 
109 
110 void
111 YMenuWidget::setItemEnabled( YMenuItem * item, bool enabled )
112 {
113  if ( item )
114  item->setEnabled( enabled );
115 }
116 
117 
118 void
119 YMenuWidget::setItemVisible( YMenuItem * item, bool visible )
120 {
121  if ( item )
122  item->setVisible( visible );
123 }
124 
125 
126 YMenuItem *
128 {
129  return findMenuItem( index, itemsBegin(), itemsEnd() );
130 }
131 
132 
133 YMenuItem *
134 YMenuWidget::findMenuItem( int wantedIndex,
135  YItemConstIterator begin,
136  YItemConstIterator end )
137 {
138  for ( YItemConstIterator it = begin; it != end; ++it )
139  {
140  YMenuItem * item = dynamic_cast<YMenuItem *> (*it);
141 
142  if ( item )
143  {
144  if ( item->index() == wantedIndex )
145  return item;
146 
147  if ( item->hasChildren() )
148  {
149  YMenuItem * result = findMenuItem( wantedIndex,
150  item->childrenBegin(),
151  item->childrenEnd() );
152  if ( result )
153  return result;
154  }
155  }
156  }
157 
158  return 0;
159 }
160 
161 
162 // FIXME: This is ugly code; candidate for refactoring.
163 void
165  YItemConstIterator end )
166 {
167 #define USED_SIZE ((int) sizeof( char ) << 8)
168 
169  bool used[ USED_SIZE ];
170 
171  for ( int i = 0; i < USED_SIZE; i++ )
172  used[i] = false;
173 
174  std::vector<YMenuItem*> conflicts;
175 
176  for ( YItemConstIterator it = begin; it != end; ++it )
177  {
178  YMenuItem * item = dynamic_cast<YMenuItem *> (*it);
179 
180  if ( item->isSeparator() )
181  continue;
182 
183  if ( item )
184  {
185  if ( item->hasChildren() )
186  {
188  }
189 
190  char shortcut = YShortcut::normalized( YShortcut::findShortcut( item->label() ) ) ;
191 
192  if ( shortcut == 0 )
193  {
194  conflicts.push_back(item);
195 
196 #if VERBOSE_SHORTCUTS
197  yuiMilestone() << "No or invalid shortcut found: \""
198  << item->label() << "\""
199  << endl;
200 #endif
201  }
202  else if ( used[ (unsigned) shortcut ] )
203  {
204  conflicts.push_back(item);
205 #if VERBOSE_SHORTCUTS
206  yuiWarning() << "Conflicting shortcut found: \""
207  << item->label() << "\""
208  << endl;
209 #endif
210  }
211  else
212  {
213  used[ (unsigned) shortcut ] = true;
214  }
215  }
216  else
217  {
218  yuiWarning() << "Non-menu item used in call: \""
219  << (*it)->label() << "\""
220  << endl;
221  }
222  }
223 
224  // Cannot use YShortcut directly as an YItem is not a YWidget
225 
226  for ( YMenuItem *i: conflicts )
227  {
228  string clean = YShortcut::cleanShortcutString(i->label());
229  char new_c = 0;
230 
231  size_t index = 0;
232  for (; index < clean.size(); ++index)
233  {
234  char ch = YShortcut::normalized( clean[ index ] );
235 
236  // ch is set to 0 by normalized() if not valid
237  if ( ch != 0 && ! used[ (unsigned) ch ] )
238  {
239  new_c = ch;
240  used[ (unsigned) ch ] = true;
241  break;
242  }
243  }
244 
245  if ( new_c != 0 )
246  {
247  clean.insert( index, 1, YShortcut::shortcutMarker() );
248 
249 #if VERBOSE_SHORTCUTS
250  yuiDebug() << "New label used: " << clean << endl;
251 #endif
252  }
253 
254  i->setLabel( clean );
255  }
256 }
257 
258 // FIXME End
259 
260 
261 void
263 {
265 }
266 
267 
268 YMenuItem *
269 YMenuWidget::findItem( std::vector<std::string> & path ) const
270 {
271  return findItem( path.begin(), path.end(),
272  itemsBegin(), itemsEnd() );
273 }
274 
275 
276 YMenuItem *
277 YMenuWidget::findItem( std::vector<std::string>::iterator path_begin,
278  std::vector<std::string>::iterator path_end,
279  YItemConstIterator begin,
280  YItemConstIterator end ) const
281 {
282  for ( YItemConstIterator it = begin; it != end; ++it )
283  {
284  YMenuItem * item = dynamic_cast<YMenuItem *>(*it);
285  // Test that dynamic_cast didn't fail
286 
287  if ( !item )
288  return 0;
289 
290  if ( item->label() == *path_begin )
291  {
292  if ( std::next( path_begin ) == path_end )
293  {
294  // Only return items which can trigger an action.
295  // Intermediate items only open a submenu, so continue looking.
296  if ( item->hasChildren() )
297  continue;
298 
299  return item;
300  }
301 
302  // Look in child nodes
303  YMenuItem * result = findItem( ++path_begin, path_end,
304  item->childrenBegin(), item->childrenEnd() );
305  if ( result )
306  return result;
307  }
308  }
309  return 0;
310 }
311 
YMenuItem::setEnabled
void setEnabled(bool enabled=true)
Enable or disable this item.
Definition: YMenuItem.h:134
YItem::label
std::string label() const
Return this item's label.
Definition: YItem.h:97
YWidget
Abstract base class of all UI widgets.
Definition: YWidget.h:55
YShortcut::shortcutMarker
static char shortcutMarker()
Static function: Returns the character used for marking keyboard shortcuts.
Definition: YShortcut.h:158
YSelectionWidget
Base class for various kinds of multi-value widgets.
Definition: YSelectionWidget.h:46
YWidget::end
YWidgetListIterator end()
A helper for the range-based "for" loop.
Definition: YWidget.h:245
YItem::setIndex
void setIndex(int index)
Set this item's index.
Definition: YItem.h:148
YItemIterator
YItemCollection::iterator YItemIterator
Mutable iterator over YItemCollection.
Definition: YItem.h:42
YItemCollection
std::vector< YItem * > YItemCollection
Collection of pointers to YItem.
Definition: YItem.h:39
YMenuWidget::deleteAllItems
virtual void deleteAllItems()
Delete all items.
Definition: YMenuWidget.cc:103
YMenuWidget::assignUniqueIndex
void assignUniqueIndex(YItemIterator begin, YItemIterator end)
Assign a unique index to all items from iterator 'begin' to iterator 'end'.
Definition: YMenuWidget.cc:88
YMenuWidget::setItemEnabled
virtual void setItemEnabled(YMenuItem *item, bool enabled)
Enable or disable an item.
Definition: YMenuWidget.cc:111
YItem::index
int index() const
Return the index of this item (as set with setIndex() ).
Definition: YItem.h:153
YItem::childrenBegin
virtual YItemIterator childrenBegin()
Return an iterator that points to the first child item of this item.
Definition: YItem.h:201
YShortcut::findShortcut
static char findShortcut(const std::string &str, std::string::size_type start_pos=0)
Static function: Find the next shortcut marker in a string, beginning at starting position start_pos.
Definition: YShortcut.cc:282
YMenuWidget::setItemVisible
virtual void setItemVisible(YMenuItem *item, bool visible)
Show or hide an item.
Definition: YMenuWidget.cc:119
YMenuWidget::addItem
virtual void addItem(YItem *item_disown)
Add one item.
Definition: YMenuWidget.cc:77
YSelectionWidget::itemsEnd
YItemIterator itemsEnd()
Return an iterator that points behind the last item.
Definition: YSelectionWidget.cc:303
YWidget::begin
YWidgetListIterator begin()
A helper for the range-based "for" loop.
Definition: YWidget.h:238
YTreeItem::hasChildren
virtual bool hasChildren() const
Return 'true' if this item has any child items.
Definition: YTreeItem.h:82
YTreeItem::childrenBegin
virtual YItemIterator childrenBegin()
Return an iterator that points to the first child item of this item.
Definition: YTreeItem.h:89
YTreeItem::childrenEnd
virtual YItemIterator childrenEnd()
Return an iterator that points after the last child item of this item.
Definition: YTreeItem.h:97
YItem::hasChildren
virtual bool hasChildren() const
Return 'true' if this item has any child items.
Definition: YItem.h:182
YMenuItem::setVisible
void setVisible(bool visible=true)
Show or hide this item.
Definition: YMenuItem.h:148
YShortcut::normalized
static char normalized(char c)
Return the normalized version of shortcut character 'c', i.e.
Definition: YShortcut.cc:301
YMenuWidget::rebuildMenuTree
virtual void rebuildMenuTree()=0
Rebuild the displayed menu tree from the internally stored YMenuItems.
YMenuWidgetPrivate
Definition: YMenuWidget.cc:40
YMenuWidget::addItems
virtual void addItems(const YItemCollection &itemCollection)
Add multiple items.
Definition: YMenuWidget.cc:68
YMenuWidget::resolveShortcutConflicts
void resolveShortcutConflicts()
Resolve keyboard shortcut conflicts: Change shortcuts of menu items if there are duplicates in the re...
Definition: YMenuWidget.cc:262
YSelectionWidget::itemsBegin
YItemIterator itemsBegin()
Return an iterator that points to the first item.
Definition: YSelectionWidget.cc:290
YMenuWidget::YMenuWidget
YMenuWidget(YWidget *parent, const std::string &label="")
Constructor.
Definition: YMenuWidget.cc:51
YSelectionWidget::deleteAllItems
virtual void deleteAllItems()
Delete all items.
Definition: YSelectionWidget.cc:80
YMenuItem
Item class for menu items.
Definition: YMenuItem.h:44
YMenuWidget::~YMenuWidget
virtual ~YMenuWidget()
Destructor.
Definition: YMenuWidget.cc:61
YItem::childrenEnd
virtual YItemIterator childrenEnd()
Return an iterator that points after the last child item of this item.
Definition: YItem.h:210
YSelectionWidget::addItems
virtual void addItems(const YItemCollection &itemCollection)
Add multiple items.
Definition: YSelectionWidget.cc:271
YMenuItem::isSeparator
bool isSeparator() const
Return 'true' if this is a menu separator, i.e.
Definition: YMenuItem.h:120
YSelectionWidget::addItem
virtual void addItem(YItem *item_disown)
Add one item.
Definition: YSelectionWidget.cc:193
YMenuWidget::findMenuItem
YMenuItem * findMenuItem(int index)
Recursively find the first menu item with the specified index.
Definition: YMenuWidget.cc:127
YMenuWidget::findItem
YMenuItem * findItem(std::vector< std::string > &path) const
Support for the Rest API for UI testing:
Definition: YMenuWidget.cc:269
YShortcut::cleanShortcutString
std::string cleanShortcutString()
Returns the shortcut string ( from the widget's shortcut property ) without any "&" markers.
Definition: YShortcut.cc:93
YItemConstIterator
YItemCollection::const_iterator YItemConstIterator
Const iterator over YItemCollection.
Definition: YItem.h:45
YItem
Simple item class for SelectionBox, ComboBox, MultiSelectionBox etc.
Definition: YItem.h:56