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