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