libyui  3.10.0
YSelectionWidget.cc
1 /*
2  Copyright (C) 2000-2012 Novell, Inc
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: YSelectionWidget.cc
20 
21  Author: Stefan Hundhammer <shundhammer@suse.de>
22 
23 /-*/
24 
25 
26 #define YUILogComponent "ui"
27 #include "YUILog.h"
28 
29 #include <algorithm>
30 #include "YSelectionWidget.h"
31 #include "YUIException.h"
32 #include "YApplication.h"
33 
34 using std::string;
35 
36 
38 {
39  YSelectionWidgetPrivate( const string & label,
40  bool enforceSingleSelection,
41  bool recursiveSelection )
42  : label( label )
43  , enforceSingleSelection( enforceSingleSelection )
44  , enforceInitialSelection( true )
45  , recursiveSelection ( recursiveSelection )
46  {}
47 
48  string label;
49  bool enforceSingleSelection;
50  bool enforceInitialSelection;
51  bool recursiveSelection;
52  string iconBasePath;
53  YItemCollection itemCollection;
54 };
55 
56 
57 
58 
60  const string & label,
61  bool enforceSingleSelection ,
62  bool recursiveSelection )
63  : YWidget( parent )
64  , priv( new YSelectionWidgetPrivate( label, enforceSingleSelection, recursiveSelection ) )
65 {
66  YUI_CHECK_NEW( priv );
67 
69  YUI_THROW( YUIException( "recursiveSelection is only available for multiSelection Widgets."));
70 
71 }
72 
73 
75 {
77 }
78 
79 
81 {
83 
84  while ( it != itemsEnd() )
85  {
86  YItem * item = *it;
87  ++it;
88  delete item;
89 
90  // No need to check for item->hasChildren() and iterate recursively
91  // over the children: The item will take care of its children in its
92  // destructor.
93  }
94 
95  priv->itemCollection.clear();
96 }
97 
98 
100 {
101  return priv->label;
102 }
103 
104 
105 void YSelectionWidget::setLabel( const string & newLabel )
106 {
107  priv->label = newLabel;
108 }
109 
110 
112 {
113  return priv->enforceSingleSelection;
114 }
115 
116 
118 {
119  if ( priv->enforceSingleSelection )
120  priv->enforceInitialSelection = newVal;
121 }
122 
123 
125 {
126  return priv->enforceInitialSelection && priv->enforceSingleSelection;
127 }
128 
129 
131 {
132  return priv->recursiveSelection;
133 }
134 
135 
136 
137 void YSelectionWidget::setEnforceSingleSelection( bool enforceSingleSelection )
138 {
139  priv->enforceSingleSelection = enforceSingleSelection;
140 }
141 
142 
143 void YSelectionWidget::setIconBasePath( const string & basePath )
144 {
145  priv->iconBasePath = basePath;
146 }
147 
148 
150 {
151  return priv->iconBasePath;
152 }
153 
154 
155 string YSelectionWidget::iconFullPath( const string & iconName ) const
156 {
157  string fullPath;
158 
159  if ( ! iconName.empty() )
160  {
161  if ( iconName[0] == '/' )
162  return iconName;
163 
164  if ( priv->iconBasePath.empty() ||
165  priv->iconBasePath[0] != '/' )
166  {
167  return YUI::yApp()->iconLoader()->findIcon( iconName );
168  }
169 
170  fullPath += priv->iconBasePath + "/" + iconName;
171  }
172 
173  return fullPath;
174 }
175 
176 
177 string YSelectionWidget::iconFullPath( YItem * item ) const
178 {
179  if ( item )
180  return iconFullPath( item->iconName() );
181  else
182  return "";
183 }
184 
185 
187 {
188  YUI_CHECK_PTR( item );
189 
190  if ( item->parent() )
191  {
192  YUI_THROW( YUIException( "Item already owned by parent item -"
193  " call addItem() only for toplevel items!" ) );
194  }
195 
196  // Add the new item to the item list
197 
198  priv->itemCollection.push_back( item );
199  item->setIndex( priv->itemCollection.size() - 1 );
200 
201  // yuiDebug() << "Adding item \"" << item->label() << "\"" << endl;
202 
203  //
204  // Enforce single selection (if applicable)
205  //
206 
207  if ( priv->enforceSingleSelection )
208  {
209  YItem * newItemSelected = 0;
210 
211  if ( item->selected() )
212  {
213  newItemSelected = item;
214  }
215  else
216  {
217  newItemSelected = findSelectedItem( item->childrenBegin(),
218  item->childrenEnd() );
219  }
220 
221  if ( newItemSelected )
222  {
223  // This looks expensive, but it is not: Even though deselectAllItems()
224  // searches the complete item list and deselects them all.
225  //
226  // This prevents that the calling application does this systematically wrong
227  // and sets the "selected" flag for more items or children.
228 
230  newItemSelected->setSelected( true );
231  }
232 
233 
234  if ( priv->enforceInitialSelection )
235  {
236  // Make sure there is one item selected initially.
237  //
238  // If any other subsequently added items are to be selected, they will
239  // override this initial selection.
240 
241  if ( priv->itemCollection.size() == 1 )
242  item->setSelected( true );
243  }
244  }
245 }
246 
247 
248 void YSelectionWidget::addItem( const string & itemLabel,
249  const string & iconName,
250  bool selected )
251 {
252  YItem * item = new YItem( itemLabel, iconName, selected );
253  YUI_CHECK_NEW( item );
254  addItem( item );
255 }
256 
257 
258 void YSelectionWidget::addItem( const string & itemLabel, bool selected )
259 {
260  addItem( itemLabel, "", selected );
261 }
262 
263 
264 void YSelectionWidget::addItems( const YItemCollection & itemCollection )
265 {
266  OptimizeChanges below( *this ); // Delay screen updates until this block is left
267  priv->itemCollection.reserve( priv->itemCollection.size() + itemCollection.size() );
268 
269  for ( YItemConstIterator it = itemCollection.begin();
270  it != itemCollection.end();
271  ++it )
272  {
273  addItem( *it );
274 
275  // No need to check for (*it)->hasChildren() and iterate recursively
276  // over the children: Any children of this item simply remain in this
277  // item's YItemCollection.
278  }
279 }
280 
281 
284 {
285  return priv->itemCollection.begin();
286 }
287 
290 {
291  return priv->itemCollection.begin();
292 }
293 
294 
297 {
298  return priv->itemCollection.end();
299 }
300 
301 
304 {
305  return priv->itemCollection.end();
306 }
307 
308 
310 {
311  return ! priv->itemCollection.empty();
312 }
313 
314 
316 {
317  return priv->itemCollection.size();
318 }
319 
320 
321 YItem *
323 {
324  if ( priv->itemCollection.empty() )
325  return 0;
326  else
327  return priv->itemCollection.front();
328 }
329 
330 
331 YItem *
332 YSelectionWidget::itemAt( int index ) const
333 {
334  if ( index < 0 || index >= (int) priv->itemCollection.size() )
335  return 0;
336 
337  return priv->itemCollection[ index ];
338 }
339 
340 
341 YItem *
343 {
344  return findSelectedItem( itemsBegin(), itemsEnd() );
345 }
346 
347 
348 YItem *
350  YItemConstIterator end )
351 {
352  for ( YItemConstIterator it = begin; it != end; ++it )
353  {
354  const YItem * item = *it;
355 
356  if ( item->selected() )
357  {
358  return *it;
359  }
360  if ( item->hasChildren() )
361  {
363  item->childrenEnd() );
364  if ( selectedItem )
365  {
366  // yuiDebug() << "Selected item: \"" << selectedItem->label() << "\"" << endl;
367  return selectedItem;
368  }
369  }
370  }
371 
372  return 0;
373 }
374 
375 
378 {
381 
382  return selectedItems;
383 }
384 
385 
386 void
388  YItemConstIterator begin,
389  YItemConstIterator end )
390 {
391  for ( YItemConstIterator it = begin; it != end; ++it )
392  {
393  YItem * item = *it;
394 
395  if ( item->selected() )
396  selectedItems.push_back( item );
397 
398  if ( item->hasChildren() )
399  {
401  item->childrenBegin(),
402  item->childrenEnd() );
403  }
404  }
405 }
406 
407 
409 {
410  return selectedItem() != 0;
411 }
412 
413 
414 void YSelectionWidget::selectItem( YItem * item, bool selected )
415 {
416  YUI_CHECK_PTR( item );
417 
418  if ( ! itemsContain( item ) )
419  YUI_THROW( YUIException( "Item does not belong to this widget" ) );
420 
421  if ( priv->enforceSingleSelection && selected )
422  {
423  YItem * oldSelectedItem = selectedItem();
424 
425  if ( oldSelectedItem )
426  oldSelectedItem->setSelected( false );
427  }
428 
429 
430  if ( recursiveSelection() && item->hasChildren() )
431  {
432  for ( YItemIterator it = item->childrenBegin(); it != item->childrenEnd(); ++it )
433  {
434  YItem * item = *it;
435  selectItem(item, selected );
436  item->setSelected( selected );
437  }
438  }
439 
440  item->setSelected( selected );
441 }
442 
443 
444 void YSelectionWidget::setItemStatus( YItem * item, int status )
445 {
446  selectItem( item, status != 0 );
447 }
448 
449 
450 bool YSelectionWidget::itemsContain( YItem * wantedItem ) const
451 {
452  return itemsContain( wantedItem, itemsBegin(), itemsEnd() );
453 }
454 
455 
456 
457 bool
459  YItemConstIterator begin,
460  YItemConstIterator end ) const
461 {
462  for ( YItemConstIterator it = begin; it != end; ++it )
463  {
464  const YItem * item = *it;
465 
466  if ( item == wantedItem )
467  return true;
468 
469  if ( item->hasChildren() )
470  {
471  if ( itemsContain( wantedItem,
472  item->childrenBegin(),
473  item->childrenEnd() ) )
474  {
475  return true;
476  }
477  }
478  }
479 
480  return false;
481 }
482 
483 
485 {
487 }
488 
489 
491  YItemIterator end )
492 {
493  for ( YItemConstIterator it = begin; it != end; ++it )
494  {
495  YItem * item = *it;
496 
497  item->setSelected( false );
498 
499  if ( item->hasChildren() )
500  deselectAllItems( item->childrenBegin(), item->childrenEnd() );
501  }
502 }
503 
504 
505 YItem *
506 YSelectionWidget::findItem( const string & wantedItemLabel ) const
507 {
508  return findItem( wantedItemLabel, itemsBegin(), itemsEnd() );
509 }
510 
511 
512 YItem *
513 YSelectionWidget::findItem( const string & wantedItemLabel,
514  YItemConstIterator begin,
515  YItemConstIterator end ) const
516 {
517  for ( YItemConstIterator it = begin; it != end; ++it )
518  {
519  YItem * item = *it;
520 
521  if ( item->label() == wantedItemLabel )
522  return item;
523 
524  if ( item->hasChildren() )
525  {
526  YItem * wantedItem = findItem( wantedItemLabel,
527  item->childrenBegin(),
528  item->childrenEnd() );
529  if ( wantedItem )
530  return wantedItem;
531  }
532  }
533 
534  return 0;
535 }
536 
537 
539 {
540  yuiMilestone() << "Items:" << endl;
541 
542  for ( YItemConstIterator it = itemsBegin(); it != itemsEnd(); ++it )
543  {
544  string status;
545 
546  switch ( (*it)->status() )
547  {
548  case 0: status = "[ ]"; break;
549  case 1: status = "[x]"; break;
550  default:
551  {
552  char buffer[80];
553  sprintf( buffer, "[%d]", (*it)->status() );
554  status = buffer;
555  }
556  }
557 
558  yuiMilestone() << " " << status << " "
559  << (*it)->label()
560  << endl;
561  }
562 
563  yuiMilestone() << "---" << endl;
564 }
YItemCollection::const_iterator YItemConstIterator
Const iterator over YItemCollection.
Definition: YItem.h:42
YItemCollection::iterator YItemIterator
Mutable iterator over YItemCollection.
Definition: YItem.h:40
std::vector< YItem * > YItemCollection
Collection of pointers to YItem.
Definition: YItem.h:38
Helper class that calls startMultipleChanges() in its constructor and cares about the necessary call ...
Simple item class for SelectionBox, ComboBox, MultiSelectionBox etc.
Definition: YItem.h:50
std::string label() const
Return this item's label.
Definition: YItem.h:82
bool selected() const
Return 'true' if this item is currently selected.
Definition: YItem.h:107
virtual YItemIterator childrenBegin()
Return an iterator that points to the first child item of this item.
Definition: YItem.h:186
virtual YItem * parent() const
Returns this item's parent item or 0 if it is a toplevel item.
Definition: YItem.h:203
virtual bool hasChildren() const
Return 'true' if this item has any child items.
Definition: YItem.h:167
void setSelected(bool sel=true)
Select or unselect this item.
Definition: YItem.h:114
void setIndex(int index)
Set this item's index.
Definition: YItem.h:133
virtual YItemIterator childrenEnd()
Return an iterator that points after the last child item of this item.
Definition: YItem.h:195
std::string iconName() const
Return this item's icon name.
Definition: YItem.h:92
bool enforceSingleSelection() const
Return 'true' if this base class should enforce single selection.
std::string iconFullPath(const std::string &iconName) const
Return the full path + file name for the specified icon name.
YItem * findSelectedItem(YItemConstIterator begin, YItemConstIterator end)
Recursively try to find the first selected item between iterators 'begin' and 'end'.
virtual void deleteAllItems()
Delete all items.
virtual void deselectAllItems()
Deselect all items.
void findSelectedItems(YItemCollection &selectedItems, YItemConstIterator begin, YItemConstIterator end)
Recursively find all selected items between iterators 'begin' and 'end' and add each of them to the '...
virtual void addItems(const YItemCollection &itemCollection)
Add multiple items.
YItemIterator itemsEnd()
Return an iterator that points behind the last item.
bool recursiveSelection() const
Return 'true' if this base class should select children recursively.
std::string iconBasePath() const
Return this widget's base path where to look up icons as set with setIconBasePath().
YItem * firstItem() const
Return the first item or 0 if there is none.
YItemIterator itemsBegin()
Return an iterator that points to the first item.
YItem * findItem(const std::string &itemLabel) const
Find the (first) item with the specified label.
bool hasItems() const
Return 'true' if this widget has any items.
bool enforceInitialSelection() const
Return 'true' if this class enforces an initial selection.
virtual void addItem(YItem *item_disown)
Add one item.
void setEnforceSingleSelection(bool on)
Set single selection mode on or off.
YSelectionWidget(YWidget *parent, const std::string &label, bool enforceSingleSelection, bool recursiveSelection=false)
Constructor.
void dumpItems() const
Dump all items and their selection state to the log.
virtual void setItemStatus(YItem *item, int status)
Set the status of an item.
std::string label() const
Return this widget's label (the caption above the item list).
virtual ~YSelectionWidget()
Destructor.
virtual YItem * selectedItem()
Return the (first) selected item or 0 if none is selected.
virtual void setLabel(const std::string &newLabel)
Change this widget's label (the caption above the item list).
void setEnforceInitialSelection(bool on)
In single selection mode, enforce selecting an initial item ('true' by default).
virtual void selectItem(YItem *item, bool selected=true)
Select or deselect an item.
virtual YItemCollection selectedItems()
Return all selected items.
bool hasSelectedItem()
Return 'true' if any item is selected.
int itemsCount() const
Return the number of items.
YItem * itemAt(int index) const
Return the item at index 'index' (from 0) or 0 if there is no such item.
void setIconBasePath(const std::string &basePath)
Set this widget's base path where to look up icons.
bool itemsContain(YItem *item) const
Return 'true' if this widget's items contain the specified item.
Base class for UI Exceptions.
Definition: YUIException.h:298
Abstract base class of all UI widgets.
Definition: YWidget.h:55
YWidgetListIterator end()
A helper for the range-based "for" loop.
Definition: YWidget.h:245
YWidgetListIterator begin()
A helper for the range-based "for" loop.
Definition: YWidget.h:238