libyui  3.3.2
YDialogSpy.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: YDialogSpy.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23 /-*/
24 
25 #include <sstream>
26 
27 #define YUILogComponent "ui-dialog-spy"
28 #include "YUILog.h"
29 
30 #include <YDialogSpy.h>
31 #include <YWidgetFactory.h>
32 #include <YDialog.h>
33 #include <YEvent.h>
34 #include <YTable.h>
35 #include <YTree.h>
36 #include <YTreeItem.h>
37 #include <YLayoutBox.h>
38 #include <YAlignment.h>
39 #include <YButtonBox.h>
40 #include <YPushButton.h>
41 #include <YMenuButton.h>
42 #include <YComboBox.h>
43 #include <YInputField.h>
44 #include <YCheckBox.h>
45 #include <YRadioButton.h>
46 #include <YProgressBar.h>
47 #include <YRichText.h>
48 #include <YBusyIndicator.h>
49 #include <YSelectionBox.h>
50 #include <YMultiSelectionBox.h>
51 #include <YMultiLineEdit.h>
52 #include <YLabel.h>
53 #include <YLogView.h>
54 #include <YIntField.h>
55 #include <YImage.h>
56 #include <YSpacing.h>
57 #include <YFrame.h>
58 #include <YEmpty.h>
59 #include <YPackageSelector.h>
60 #include <YReplacePoint.h>
61 #include <YPropertyEditor.h>
62 #include <YPopupInternal.h>
63 #include <YAlignment.h>
64 #include <YCheckBoxFrame.h>
65 #include <YRadioButtonGroup.h>
66 #include <YUI.h>
67 
68 #define TREE_VWEIGHT 40
69 #define PROP_VWEIGHT 60
70 
71 #define DIA_HEIGHT 24
72 
73 #define TREE_HEIGHT 10
74 #define TREE_WIDTH 50
75 
76 #define PROP_HEIGHT 12
77 #define PROP_WIDTH 50
78 
79 /**
80  * Custom tree item class to map tree items to widgets
81  **/
83 {
84 public:
85  YWidgetTreeItem( YWidget * widget,
86  bool isOpen )
87  : YTreeItem( "", isOpen )
88  , _widget( widget )
89  {
90  setWidgetLabel();
91  }
92 
94  YWidget * widget,
95  bool isOpen )
96  : YTreeItem( parent, "", isOpen )
97  , _widget( widget )
98  {
99  setWidgetLabel();
100  }
101 
102  virtual ~YWidgetTreeItem() {}
103  YWidget * widget() const { return _widget; }
104 
105 
106 protected:
107 
108  void setWidgetLabel()
109  {
110  std::ostringstream str;
111  str << _widget;
112  setLabel( str.str() );
113  }
114 
115 private:
116  YWidget * _widget;
117 };
118 
119 
120 static void fillTree( YWidgetTreeItem * parent,
121  YWidgetListConstIterator begin,
122  YWidgetListConstIterator end,
123  int treeLevel );
124 
125 
126 
127 
129 {
130 public:
131 
133  : targetDialog( nullptr )
134  , spyDialog( nullptr )
135  , widgetTree( nullptr )
136  , propButton( nullptr )
137  , propReplacePoint( nullptr )
138  , propTable( nullptr )
139  {}
140 
142 
143  YDialog * targetDialog; // Dialog that is being inspected
144  YDialog * spyDialog; // Debug dialog that shows widget data
145  YTree * widgetTree; // Tree widget to show widget hierarchy
146  YPushButton * propButton;
147  YMenuButton * addButton;
148  YPushButton * deleteButton;
149  YPushButton * upButton;
150  YPushButton * downButton;
151  YReplacePoint * propReplacePoint;
152  YTable * propTable;
153  YMenuItem *exportMenu;
154 
155  YWidget * selectedWidget();
156  void selectedWidgetChanged();
157  void refreshProperties();
158  bool toggleProperties();
159  void highlightWidget(bool enable = true);
160 
161  void deleteWidget();
162  void addWidget(const std::string &type);
163  void editProperty();
164  void moveSelectedUp() { moveSelected(MOVE_UP); }
165  void moveSelectedDown() { moveSelected(MOVE_DOWN); }
166 
167 private:
168  enum Direction
169  {
170  MOVE_UP = 0,
171  MOVE_DOWN
172  };
173 
174  void moveSelected(Direction direction);
175  void showProperties();
176  void hideProperties();
177  bool propertiesShown() const;
178  void targetDialogUpdated();
179  void refreshButtonStates();
180  void editWidget(YWidget *widget, const std::string &property="Label");
181 };
182 
183 /** Destructor - switch off widget highlighting at the end
184 */
186 {
187  highlightWidget(false);
188 }
189 
190 /** Fill the widget tree content
191 * @param target the target dialog which will be examined
192 * @param widgetTree where to display the structure
193 */
194 void fillWidgetTree(YDialog *target, YTree *widgetTree)
195 {
196  YWidgetTreeItem * rootItem = new YWidgetTreeItem( target, true );
197  YUI_CHECK_NEW( rootItem );
198  fillTree( rootItem, target->childrenBegin(), target->childrenEnd(), 1 );
199  widgetTree->addItem( rootItem );
200  widgetTree->rebuildTree();
201 }
202 
203 /** Constructor - create the main spy dialog
204 */
206  : priv( new YDialogSpyPrivate() )
207 {
208  if ( ! targetDialog )
209  targetDialog = YDialog::topmostDialog();
210 
211  priv->targetDialog = targetDialog;
213 
214  priv->spyDialog = fac->createPopupDialog();
215  YAlignment * diaMin = fac->createMinHeight( priv->spyDialog, DIA_HEIGHT );
216  YLayoutBox * vbox = fac->createVBox( diaMin );
217 
218  auto alignment = fac->createLeft( vbox );
219  auto fileMenu = fac->createMenuButton( alignment, "&File" );
220 
221  YItemCollection items;
222  priv->exportMenu = new YMenuItem( "Export (TODO)" );
223  items.push_back( priv->exportMenu );
224  fileMenu->addItems( items );
225 
226  auto minSize = fac->createMinSize( vbox, TREE_WIDTH, TREE_HEIGHT );
227  minSize->setWeight( YD_VERT, TREE_VWEIGHT );
228  priv->widgetTree = fac->createTree( minSize, "Widget &Tree", false );
229  priv->widgetTree->setNotify( true );
230 
231  fillWidgetTree(priv->targetDialog, priv->widgetTree);
232 
233  auto hbox = fac->createHBox( vbox );
234  priv->propButton = fac->createPushButton( hbox, "&Properties >>>" );
235 
236  priv->addButton = fac->createMenuButton( hbox, "&Add" );
237  YItemCollection add_items;
238  YMenuItem *menu_info = new YMenuItem( "Info" );
239  YMenuItem *menu_buttons = new YMenuItem( "Buttons" );
240  YMenuItem *menu_input = new YMenuItem( "Input" );
241  YMenuItem *menu_align = new YMenuItem( "Alignment" );
242  YMenuItem *menu_size = new YMenuItem( "Size" );
243  YMenuItem *menu_containers = new YMenuItem( "Containers" );
244  YMenuItem *menu_special = new YMenuItem( "Special" );
245  add_items.push_back( menu_info );
246  add_items.push_back( menu_buttons );
247  add_items.push_back( menu_input );
248  add_items.push_back( menu_align );
249  add_items.push_back( menu_size );
250  add_items.push_back( menu_containers );
251  add_items.push_back( menu_special );
252 
253  new YMenuItem( menu_info, "Label" );
254  new YMenuItem( menu_info, "Heading" );
255  new YMenuItem( menu_info, "RichText" );
256  new YMenuItem( menu_info, "ProgressBar" );
257  new YMenuItem( menu_info, "BusyIndicator" );
258  new YMenuItem( menu_info, "Table" );
259 
260  new YMenuItem( menu_buttons, "PushButton" );
261  new YMenuItem( menu_buttons, "CheckBox" );
262  new YMenuItem( menu_buttons, "ComboBox" );
263  new YMenuItem( menu_buttons, "MenuButton" );
264  new YMenuItem( menu_buttons, "RadioButton" );
265 
266  new YMenuItem( menu_input, "InputField" );
267  new YMenuItem( menu_input, "IntField" );
268  new YMenuItem( menu_input, "MultiLineEdit" );
269  new YMenuItem( menu_input, "MultiSelectionBox" );
270  new YMenuItem( menu_input, "Password" );
271  new YMenuItem( menu_input, "SelectionBox" );
272 
273  new YMenuItem( menu_align, "Left" );
274  new YMenuItem( menu_align, "Right" );
275  new YMenuItem( menu_align, "Top" );
276  new YMenuItem( menu_align, "Bottom" );
277  new YMenuItem( menu_align, "HCenter" );
278  new YMenuItem( menu_align, "VCenter" );
279  new YMenuItem( menu_align, "HVCenter" );
280 
281  new YMenuItem( menu_size, "MinHeight" );
282  new YMenuItem( menu_size, "MinWidth" );
283  new YMenuItem( menu_size, "MinSize" );
284  new YMenuItem( menu_size, "HSquash" );
285  new YMenuItem( menu_size, "VSquash" );
286  new YMenuItem( menu_size, "HVSquash" );
287  new YMenuItem( menu_size, "HWeight" );
288  new YMenuItem( menu_size, "VWeight" );
289 
290  new YMenuItem( menu_containers, "MarginBox" );
291  new YMenuItem( menu_containers, "ButtonBox" );
292  new YMenuItem( menu_containers, "CheckBoxFrame" );
293  new YMenuItem( menu_containers, "Frame" );
294  new YMenuItem( menu_containers, "HBox" );
295  new YMenuItem( menu_containers, "HSpacing" );
296  new YMenuItem( menu_containers, "ReplacePoint" );
297  new YMenuItem( menu_containers, "VBox" );
298  new YMenuItem( menu_containers, "VSpacing" );
299 
300  // TODO: these are not available in ncurses UI
301  new YMenuItem( menu_special, "BarGraph" );
302  new YMenuItem( menu_special, "DateField" );
303  new YMenuItem( menu_special, "DumbTab" );
304  new YMenuItem( menu_special, "Graph" );
305  new YMenuItem( menu_special, "Slider" );
306  new YMenuItem( menu_input, "TimeField" );
307  new YMenuItem( menu_special, "TimezoneSelector" );
308 
309  priv->addButton->addItems( add_items );
310 
311  priv->deleteButton = fac->createPushButton( hbox, "&Delete" );
312  priv->upButton = fac->createPushButton( hbox, "⬆ Up" );
313  priv->downButton = fac->createPushButton( hbox, "⬇ Down" );
314 
315  priv->propReplacePoint = fac->createReplacePoint( vbox );
316  fac->createEmpty( priv->propReplacePoint );
317 
318  priv->selectedWidgetChanged();
319 }
320 
321 /**
322  * Destructor
323  */
325 {
326  if ( priv->spyDialog )
327  priv->spyDialog->destroy();
328 }
329 
330 /** Is the property dialog displayed?
331  * @return true if the dialog is displayed
332  */
333 bool YDialogSpyPrivate::propertiesShown() const
334 {
335  return propTable != nullptr;
336 }
337 
338 /**
339  * Highlight the currently selected widget in the spy dialog
340  */
342 {
343  if (targetDialog) targetDialog->highlight( enable ? selectedWidget() : nullptr);
344 }
345 
346 /**
347  * Display details about the currently selected widget
348  */
349 void YDialogSpyPrivate::showProperties()
350 {
351  if ( propertiesShown() ) return;
352 
353  propReplacePoint->deleteChildren();
354  propReplacePoint->setWeight( YD_VERT, PROP_VWEIGHT );
355 
356  auto fac = YUI::widgetFactory();
357  auto minSize = fac->createMinSize( propReplacePoint,
358  PROP_WIDTH, PROP_HEIGHT );
359  auto header = new YTableHeader();
360  YUI_CHECK_NEW( header );
361  header->addColumn( "Property" );
362  header->addColumn( "Value" );
363  header->addColumn( "Type" );
364 
365  propTable = fac->createTable( minSize, header );
366  propTable->setNotify( true );
367 
368  propButton->setLabel( "<<< &Properties" );
369  propReplacePoint->showChild();
370  spyDialog->recalcLayout();
371 }
372 
373 /**
374  * Hide property details
375  */
376 void YDialogSpyPrivate::hideProperties()
377 {
378  if ( !propertiesShown() ) return;
379 
380  propReplacePoint->deleteChildren();
381  propReplacePoint->setWeight( YD_VERT, 0 );
382  propTable = nullptr;
383  YUI::widgetFactory()->createEmpty( propReplacePoint );
384 
385  propButton->setLabel( "&Properties >>>" );
386  propReplacePoint->showChild();
387  spyDialog->recalcLayout();
388 }
389 
390 /**
391  * Hide or show the properties dialog
392  * @return true if the dialog is now displayed
393  */
395 {
396  bool ret = !propertiesShown();
397 
398  if (ret)
399  {
400  showProperties();
401  refreshProperties();
402  }
403  else
404  hideProperties();
405 
406  return ret;
407 }
408 /**
409  * Refresh the displayed properties
410  */
412 {
413  // properties shown?
414  if ( !propTable )
415  return;
416 
417  propTable->deleteAllItems();
418 
419  auto widget = selectedWidget();
420  if (!widget) return;
421 
422  auto propSet = widget->propertySet();
423  YItemCollection items;
424  items.reserve( propSet.size() );
425 
426  for ( YPropertySet::const_iterator it = propSet.propertiesBegin();
427  it != propSet.propertiesEnd();
428  ++it )
429  {
430  YProperty prop = *it;
431  YPropertyValue propVal = widget->getProperty( prop.name() );
432  std::string propValStr;
433 
434  switch ( prop.type() )
435  {
436  case YStringProperty:
437  propValStr = propVal.stringVal();
438  break;
439 
440  case YBoolProperty:
441  propValStr = propVal.boolVal() ? "true" : "false";
442  break;
443 
444  case YIntegerProperty:
445  propValStr = std::to_string(propVal.integerVal());
446  break;
447 
448  default:
449  propValStr = "???";
450  break;
451  }
452 
453  auto item = new YTableItem( prop.name(), propValStr, prop.typeAsStr() );
454  YUI_CHECK_NEW( item );
455  items.push_back( item );
456  }
457 
458  propTable->addItems( items );
459  propTable->deselectAllItems();
460 }
461 
462 /**
463  * Fill the widget tree dialog
464  * @param parent widget tree item
465  * @param begin iterator pointing to the first item
466  * @param end iterator pointing to the last item
467  * @param treeLevel current tree level (nesting)
468  */
469 void fillTree( YWidgetTreeItem * parent,
470  YWidgetListConstIterator begin,
471  YWidgetListConstIterator end,
472  int treeLevel )
473 {
474  for ( YWidgetListConstIterator it = begin; it != end; ++it )
475  {
476  YWidget * widget = *it;
477  auto item = new YWidgetTreeItem( parent, widget, treeLevel < 4 );
478 
479  if ( widget->hasChildren() )
480  fillTree( item, widget->childrenBegin(), widget->childrenEnd(), treeLevel+1 );
481  }
482 }
483 
484 /**
485  * The main loop of the spy dialog
486  */
488 {
489  YUI_CHECK_PTR( priv->spyDialog );
490 
491  while ( true )
492  {
493  auto event = priv->spyDialog->waitForEvent();
494  yuiMilestone() << "event: " << event;
495  if (!event) continue;
496 
497  // window manager "close window" button
498  if ( event->eventType() == YEvent::CancelEvent ) break;
499  else if ( event->eventType() == YEvent::MenuEvent)
500  {
501  YMenuItem * menu_item = dynamic_cast<YMenuItem *>(event->item());
502 
503  // TODO: handle the export menu item
504  if (menu_item == priv->exportMenu) continue;
505 
506  // handle all unhandled menu items as "Add" menu items, this is much
507  // simpler than comparing it with the huge amount of menu item pointers
508  if (menu_item)
509  {
510  auto menu_label = menu_item->label();
511  yuiMilestone() << "Activated menu item: " << menu_label << std::endl;
512  priv->addWidget(menu_label);
513  }
514 
515  continue;
516  }
517 
518  // just make sure we do not use NULL in some unexpected case
519  if (!event->widget()) continue;
520 
521  if ( event->widget() == priv->upButton ) priv->moveSelectedUp();
522  else if ( event->widget() == priv->downButton) priv->moveSelectedDown();
523  else if ( event->widget() == priv->propButton ) priv->toggleProperties();
524  else if ( event->widget() == priv->deleteButton) priv->deleteWidget();
525  else if ( event->widget() == priv->propTable ) priv->editProperty();
526  else if ( event->widget() == priv->widgetTree ) priv->selectedWidgetChanged();
527  }
528 }
529 
530 /**
531  * Run the spy dialog for selected UI dialog
532  * @param dialog UI dialog to examine
533  */
535 {
536  try
537  {
538  YDialogSpy dialogSpy( dialog );
539  dialogSpy.exec();
540  }
541  catch ( YUIException & exception )
542  {
543  // ignore all YUI exceptions which might happen when playing with the layout
544  YUI_CAUGHT( exception );
545  YPopupInternal::message("Error:\n" + exception.msg());
546  }
547 }
548 
549 /**
550  * The currently selected wiget
551  * @return The currently selected widget (or nullptr if nothing is selected)
552  */
554 {
555  auto item = dynamic_cast<YWidgetTreeItem *>(widgetTree->selectedItem());
556 
557  return item ? item->widget() : nullptr;
558 }
559 
560 /**
561  * The selected item has been changed, refresh the UI
562  */
564 {
565  highlightWidget();
566  refreshProperties();
567  refreshButtonStates();
568 }
569 
570 /**
571  * Run the property editor for the current widget
572  */
574 {
575  auto selected_item = dynamic_cast<YTableItem *>(propTable->selectedItem());
576  if (!selected_item) return;
577 
578  auto cell = selected_item->cell(0);
579  yuiMilestone() << "editing property: " << cell->label();
580 
581  YPropertyEditor editor(selectedWidget());
582  // update the property table when only the property has been changed
583  if (editor.edit(cell->label())) refreshProperties();
584 }
585 
586 /**
587  * Delete the currently selected widget
588  */
590 {
591  auto w = selectedWidget();
592  if (!w) return;
593 
594  auto parent = w->parent();
595  if (!parent) return;
596 
597  yuiMilestone() << "removing widget: " << w << std::endl;
598  parent->removeChild(w);
599 
600  if ( w->isValid() )
601  {
602  delete w;
603  }
604 
605  // any other child left after the removal?
606  if (!parent->hasChildren())
607  {
608  // add an Empty widget to have a valid widget tree
609  // e.g. empty VBoxes are not allowed
610  YUI::widgetFactory()->createEmpty(parent);
611  }
612 
613  targetDialogUpdated();
614 }
615 
616 /**
617  * Helper method - Is the widget a VBox or Hbox?
618  * @param widget the widget
619  * @return true if the widget is a VBox or HBox
620  */
621 bool isBox(const YWidget *widget)
622 {
623  return dynamic_cast<const YLayoutBox *>(widget);
624 }
625 
626 /**
627  * Helper method - Is the widget a VBox?
628  * @param widget the widget
629  * @return true if the widget is a VBox
630  */
631 bool isVBox(const YWidget *widget)
632 {
633  auto box = dynamic_cast<const YLayoutBox *>(widget);
634  return box && box->primary() == YD_VERT;
635 }
636 
637 /**
638  * Move the selected widget up/left or down/right. The visual direction
639  * actually depends on the widget, it just moves the widget to the begining
640  * or the end of the container.
641  * @param true = up move to the begining (up/left), false = to the end (down/right)
642  */
643 void YDialogSpyPrivate::moveSelected(Direction direction)
644 {
645  auto target_widget = selectedWidget();
646  if (!target_widget) return;
647 
648  auto parent = target_widget->parent();
649  if (!parent || !isBox(parent)) return;
650 
651  if (direction == MOVE_UP)
652  {
653  // the first child cannot be moved further
654  if (target_widget == parent->firstChild()) return;
655 
656  auto i = find( parent->childrenBegin(), parent->childrenEnd(), target_widget );
657  if (i != parent->childrenEnd())
658  {
659  // swap with the preceeding widget
660  // Note: use a temporary variable to not rely on the argument evaluation order!
661  auto other = i--;
662  std::swap(*other, *i);
663  }
664  }
665  else
666  // moving down
667  {
668  // the last child cannot be moved further to the end
669  if (target_widget == parent->lastChild()) return;
670 
671  auto i = find( parent->childrenBegin(), parent->childrenEnd(), target_widget );
672  if (i != parent->childrenEnd())
673  {
674  // swap with the succeeding widget
675  // Note: use a temporary variable to not rely on the argument evaluation order!
676  auto other = i++;
677  std::swap(*other, *i);
678  }
679  }
680 
681  targetDialogUpdated();
682 }
683 
684 /**
685  * Generic handler for adding widgets
686  * @param type Type of the widget to add
687  */
688 void YDialogSpyPrivate::addWidget(const std::string &type)
689 {
690  auto widget = selectedWidget();
691  if (!widget) return;
692 
693  try
694  {
695  auto f = YUI::widgetFactory();
696 
697  if (type == "Bottom")
698  editWidget(f->createBottom(widget));
699  else if (type == "BusyIndicator")
700  editWidget(f->createBusyIndicator(widget, "Busy Indicator", 10000));
701  else if (type == "ButtonBox")
702  editWidget(f->createButtonBox(widget));
703  else if (type == "ComboBox")
704  {
705  auto cb = f->createComboBox(widget, "Combo Box");
706  editWidget(cb);
707 
708  YPopupInternal::StringArray items(YPopupInternal::editNewStringArray("Menu Items"));
709 
710  YItemCollection add_items;
711  // access by reference
712  for(auto&& str: items) add_items.push_back( new YMenuItem( str ) );
713  cb->addItems( add_items );
714  }
715  else if (type == "Empty")
716  editWidget(f->createEmpty(widget));
717  else if (type == "Frame")
718  editWidget(f->createFrame(widget, "Frame"));
719  else if (type == "HBox")
720  editWidget(f->createHBox(widget));
721  else if (type == "Heading")
722  editWidget(f->createHeading(widget, "Heading"));
723  else if (type == "HSpacing")
724  editWidget(f->createHSpacing(widget));
725  else if (type == "HStretch")
726  editWidget(f->createHStretch(widget));
727  else if (type == "CheckBox")
728  editWidget(f->createCheckBox(widget, "Check Box"));
729  else if (type == "CheckBoxFrame")
730  // make it checked by default
731  editWidget(f->createCheckBoxFrame(widget, "Check Box Frame", true));
732  else if (type == "Image")
733  editWidget(f->createImage(widget, ""));
734  else if (type == "InputField")
735  editWidget(f->createInputField(widget, "Input"));
736  else if (type == "IntField")
737  editWidget(f->createIntField(widget, "Integer Field", 0, 100, 50));
738  else if (type == "Label")
739  editWidget(f->createLabel(widget, "Label"));
740  else if (type == "Left")
741  editWidget(f->createLeft(widget));
742  else if (type == "LogView")
743  editWidget(f->createLogView(widget, "Log View", 12));
744  else if (type == "MenuButton")
745  {
746  auto menu = f->createMenuButton( widget, "Menu" );
747  editWidget(menu);
748 
749  YPopupInternal::StringArray items(YPopupInternal::editNewStringArray("Menu Items"));
750 
751  YItemCollection add_items;
752  // access by reference
753  for(auto&& str: items) add_items.push_back( new YMenuItem( str ) );
754  menu->addItems( add_items );
755  }
756  else if (type == "MinHeight")
757  editWidget(f->createMinHeight(widget, 10));
758  else if (type == "MinWidth")
759  editWidget(f->createMinWidth(widget, 10));
760  else if (type == "MinSize")
761  editWidget(f->createMinSize(widget, 10, 10));
762  else if (type == "MultiLineEdit")
763  editWidget(f->createMultiLineEdit(widget, "MultiLineEdit"));
764  else if (type == "MultiSelectionBox")
765  {
766  auto msb = f->createMultiSelectionBox(widget, "MultiSelection Box");
767  editWidget(msb);
768 
769  // edit the item list and update the widget after pressing OK
770  YPopupInternal::StringArray items(YPopupInternal::editNewStringArray("Items"));
771  // access by reference
772  for(auto&& str: items) msb->addItem(str);
773  }
774  else if (type == "OutputField")
775  editWidget(f->createOutputField(widget, "Output Field"));
776  else if (type == "Password")
777  editWidget(f->createPasswordField(widget, "Password"));
778  else if (type == "ProgressBar")
779  editWidget(f->createProgressBar(widget, "Progress"));
780  else if (type == "PushButton")
781  editWidget(f->createPushButton(widget, "Button"));
782  else if (type == "RadioButton")
783  editWidget(f->createRadioButton(widget, "Radio Button"));
784  else if (type == "RadioButtonGroup")
785  editWidget(f->createRadioButtonGroup(widget));
786  else if (type == "ReplacePoint")
787  editWidget(f->createReplacePoint(widget));
788  else if (type == "Right")
789  editWidget(f->createRight(widget));
790  else if (type == "RichText")
791  editWidget(f->createRichText(widget, "This is a <b>RichText</b>."));
792  else if (type == "SelectionBox")
793  editWidget(f->createSelectionBox(widget, "Selection Box"));
794  else if (type == "Table")
795  {
796  YPopupInternal::StringArray items(YPopupInternal::editNewStringArray("Table Columns"));
797 
798  // abort adding if Cancel has been pressed
799  if (!items.empty())
800  {
801  auto header = new YTableHeader();
802 
803  // access by reference
804  for(auto&& str: items) header->addColumn(str);
805 
806  editWidget(f->createTable(widget, header));
807  }
808  }
809  else if (type == "Top")
810  editWidget(f->createTop(widget));
811  else if (type == "Tree")
812  editWidget(f->createTree(widget, "Tree"));
813  else if (type == "VBox")
814  editWidget(f->createVBox(widget));
815  else if (type == "VSpacing")
816  editWidget(f->createVSpacing(widget));
817  else if (type == "VStretch")
818  editWidget(f->createVStretch(widget));
819  else
820  {
822  "Adding \"" + type + "\" widget type is not supported.");
823  return;
824  }
825 
826  targetDialogUpdated();
827  }
828  catch( const YUIException & exception )
829  {
830  YPopupInternal::message("Could not add a new widget:\n"
831  + exception.msg());
832  }
833 }
834 
835 /**
836  * Refresh the target dialog after modifying it.
837  */
838 void YDialogSpyPrivate::targetDialogUpdated()
839 {
840  // redraw the target dialog
841  targetDialog->recalcLayout();
842 
843  // refresh the spy dialog
844  widgetTree->deleteAllItems();
845  fillWidgetTree(targetDialog, widgetTree);
846 }
847 
848 /**
849  * Refresh button states in the main spy dialog
850  */
851 void YDialogSpyPrivate::refreshButtonStates()
852 {
853  auto widget = selectedWidget();
854  auto parent = widget ? widget->parent() : nullptr;
855 
856  // Enable the moving buttons ony when the selected widget is inside
857  // a VBox/HBox container, set the labels according to stacking direction.
858  if (widget && parent && isBox(parent))
859  {
860  upButton->setEnabled(widget != parent->firstChild());
861  upButton->setLabel(isVBox(parent) ? "⬆ Up" : "⬅ Left");
862  downButton->setEnabled(widget != parent->lastChild());
863  downButton->setLabel(isVBox(parent) ? "⬇ Down" : "➡ Right");
864  }
865  else
866  {
867  upButton->setEnabled(false);
868  downButton->setEnabled(false);
869  }
870 
871  // TODO: Enable the [Add] menu button only when a widget can be added
872  // inside the current widget (i.e. it is a container). Check the widget's
873  // child manager wheter it is YSingleWidgetChildManager or a YWidgetChildrenRejector.
874 
875  // Disable the [Delete] button when for the top level widget (YDialog)
876  // TODO: disable it for the YQWizardButtons (Next, Back, ...), they cannot be
877  // removed from the dialog.
878  deleteButton->setEnabled(parent);
879 }
880 
881 /**
882  * Edit widget property
883  * @param widget selected widget
884  * @param property property name
885  */
886 void YDialogSpyPrivate::editWidget(YWidget *widget, const std::string &property)
887 {
888  // redraw the target dialog
889  targetDialog->recalcLayout();
890 
891  if (!widget->propertySet().contains(property)) return;
892 
893  YPropertyEditor editor(widget);
894  editor.edit(property);
895 }
virtual void setEnabled(bool enabled=true)
Enable or disable this widget, i.e.
Definition: YWidget.cc:495
void highlightWidget(bool enable=true)
Highlight the currently selected widget in the spy dialog.
Definition: YDialogSpy.cc:341
virtual bool hasChildren() const
Return &#39;true&#39; if this item has any child items.
Definition: YTreeItem.h:78
virtual YItemIterator childrenEnd()
Return an iterator that points after the last child item of this item.
Definition: YTreeItem.h:93
std::string label() const
Return this item&#39;s label.
Definition: YItem.h:82
static YWidgetFactory * widgetFactory()
Return the widget factory that provides all the createXY() methods for standard (mandatory, i.e.
Definition: YUI.cc:126
A vertical or horizontal stacking of widgets, implementing HBox and VBox.
Definition: YLayoutBox.h:37
bool hasChildren() const
Returns &#39;true&#39; if this widget has any children.
Definition: YWidget.h:192
A placeholder that can have its contents exchanged, using ReplaceWidget.
Definition: YReplacePoint.h:33
void deleteWidget()
Delete the currently selected widget.
Definition: YDialogSpy.cc:589
Transport class for the value of simple properties.
Definition: YProperty.h:104
std::vector< YItem * > YItemCollection
Collection of pointers to YItem.
Definition: YItem.h:38
void hideProperties()
Hide the "Properties" sub-window.
Helper class for YTable for table column properties:
Definition: YTableHeader.h:43
bool contains(const std::string &propertyName) const
Check if a property &#39;propertyName&#39; exists in this property set.
Definition: YProperty.cc:106
An internal helper class for displaying the widget property editor in the spy dialog.
YPropertyType type() const
Returns the type of this property.
Definition: YProperty.h:72
MenuButton: Similar to PushButton, but with several actions: Upon clicking on a MenuButton (or activa...
Definition: YMenuButton.h:48
const YTableCell * cell(int index) const
Return the cell at the specified index (counting from 0 on) or 0 if there is none.
Definition: YTableItem.cc:132
YWidget * parent() const
Return this widget&#39;s parent or 0 if it doesn&#39;t have a parent.
Definition: YWidget.cc:269
void editProperty()
Run the property editor for the current widget.
Definition: YDialogSpy.cc:573
bool isOpen() const
Return &#39;true&#39; if this tree item should be displayed open (with its children visible) by default...
Definition: YTreeItem.cc:99
std::string typeAsStr() const
Returns the type of this property as string.
Definition: YProperty.h:82
void showProperties()
Show the "Properties" sub-window.
std::string name() const
Returns the name of this property.
Definition: YProperty.h:67
bool edit(const std::string &property)
Display a popup for editing a widget property.
Table: Selection list with multiple columns.
Definition: YTable.h:55
virtual YTreeItem * parent() const
Returns this item&#39;s parent item or 0 if it is a toplevel item.
Definition: YTreeItem.h:129
An interactive dialog debugger: Show the structure and content of a dialog and its widgets...
Definition: YDialogSpy.h:43
virtual const YPropertySet & propertySet()
Return this class&#39;s property set.
Definition: YWidget.cc:393
A push button; may have an icon, and a F-key shortcut.
Definition: YPushButton.h:37
void addWidget(const std::string &type)
Generic handler for adding widgets.
Definition: YDialogSpy.cc:688
bool toggleProperties()
Hide or show the properties dialog.
Definition: YDialogSpy.cc:394
virtual void deleteAllItems()
Delete all items.
void setWeight(YUIDimension dim, int weight)
Set a weight in the specified dimension.
Definition: YWidget.cc:579
Implementation of all the alignment widgets:
Definition: YAlignment.h:41
virtual YItem * selectedItem()
Return the (first) selected item or 0 if none is selected.
YTreeItem(const std::string &label, bool isOpen=false)
Constructors for toplevel items.
Definition: YTreeItem.cc:28
virtual ~YDialogSpy()
Destructor.
Definition: YDialogSpy.cc:324
std::string stringVal() const
Methods to get the value of this property.
Definition: YProperty.h:180
virtual void addItems(const YItemCollection &itemCollection)
Add multiple items.
Definition: YMenuButton.cc:63
static StringArray editNewStringArray(const std::string &label)
Display a popup dialog with 3 initially empty input fields.
Custom tree item class to map tree items to widgets.
Definition: YDialogSpy.cc:82
YDialogSpy(YDialog *dialog=0)
Constructor: Create a YDialogSpy for the specified dialog.
Definition: YDialogSpy.cc:205
Class for widget properties.
Definition: YProperty.h:51
virtual void addItem(YItem *item_disown)
Add one item.
void setLabel(const std::string &newLabel)
Set this item&#39;s label.
Definition: YItem.h:87
static void showDialogSpy(YDialog *dialog=0)
Show a YDialogSpy for the specified dialog.
Definition: YDialogSpy.cc:534
void exec()
Execute the event loop.
Definition: YDialogSpy.cc:487
Item class for menu items.
Definition: YMenuItem.h:35
const std::string & msg() const
Return the message string provided to the constructor.
Definition: YUIException.h:334
YWidgetListIterator childrenBegin() const
Return an iterator that points to the first child or to childrenEnd() if there are no children...
Definition: YWidget.h:212
bool propertiesShown() const
Return &#39;true&#39; if the "Properties" sub-window is currently shown, &#39;false&#39; if not.
Tree: List box that displays a (scrollable) list of hierarchical items from which the user can select...
Definition: YTree.h:56
virtual void rebuildTree()=0
Rebuild the displayed tree from the internally stored YTreeItems.
void setNotify(bool notify=true)
Sets the Notify property.
Definition: YWidget.cc:517
void refreshProperties()
Refresh the displayed properties.
Definition: YDialogSpy.cc:411
A window in the desktop environment.
Definition: YDialog.h:47
Abstract widget factory for mandatory widgets.
virtual YItemIterator childrenBegin()
Return an iterator that points to the first child item of this item.
Definition: YTreeItem.h:85
Item class for YTable items.
Definition: YTableItem.h:58
void selectedWidgetChanged()
The selected item has been changed, refresh the UI.
Definition: YDialogSpy.cc:563
Abstract base class of all UI widgets.
Definition: YWidget.h:54
static YDialog * topmostDialog(bool doThrow=true)
Alias for currentDialog().
Definition: YDialog.h:200
Base class for UI Exceptions.
Definition: YUIException.h:297
~YDialogSpyPrivate()
Destructor - switch off widget highlighting at the end.
Definition: YDialogSpy.cc:185
YUIDimension primary() const
Return the primary dimension, i.e., the dimension this LayoutBox lays out its children in: YD_VERT fo...
Definition: YLayoutBox.cc:82
YWidget * selectedWidget()
The currently selected wiget.
Definition: YDialogSpy.cc:553
Item class for tree items.
Definition: YTreeItem.h:37
bool destroy(bool doThrow=true)
Close and delete this dialog (and all its children) if it is the topmost dialog.
Definition: YDialog.cc:252
YEvent * waitForEvent(int timeout_millisec=0)
Wait for a user event.
Definition: YDialog.cc:379
YWidgetListIterator childrenEnd() const
Return an interator that points after the last child.
Definition: YWidget.h:218
static void message(const std::string &label)
Display a simple popup dialog with OK button.