libyui  3.10.0
YDialog.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: YDialog.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23 /-*/
24 
25 
26 #define YUILogComponent "ui"
27 #include "YUILog.h"
28 
29 #include "YDialog.h"
30 #include "YEvent.h"
31 #include "YShortcutManager.h"
32 #include "YPushButton.h"
33 #include "YUI.h"
34 #include "YEventFilter.h"
35 
36 #define VERBOSE_DIALOGS 0
37 #define VERBOSE_DISCARDED_EVENTS 0
38 #define VERBOSE_EVENTS 0
39 
40 typedef std::list<YEventFilter *> YEventFilterList;
41 
42 using std::string;
43 
44 
46 {
47  YDialogPrivate( YDialogType dialogType, YDialogColorMode colorMode )
48  : dialogType( dialogType )
49  , colorMode( colorMode )
50  , shortcutCheckPostponed( false )
51  , defaultButton( 0 )
52  , isOpen( false )
53  , multiPassLayout( false )
54  , layoutPass( 0 )
55  , lastEvent( 0 )
56  {}
57 
58  YDialogType dialogType;
59  YDialogColorMode colorMode;
60  bool shortcutCheckPostponed;
61  YPushButton * defaultButton;
62  bool isOpen;
63  bool multiPassLayout;
64  int layoutPass;
65  YEvent * lastEvent;
66  YEventFilterList eventFilterList;
67 };
68 
69 
70 
71 /**
72  * Helper class: Event filter that handles "Help" buttons.
73  **/
75 {
76 public:
78  : YEventFilter( dialog )
79  {}
80 
81  virtual ~YHelpButtonHandler() {}
82 
83  YEvent * filter( YEvent * event )
84  {
85  if ( event && event->widget() )
86  {
87  YPushButton * button = dynamic_cast<YPushButton *> ( event->widget() );
88 
89  if ( button && button->isHelpButton() )
90  {
91  if ( YDialog::showHelpText( button ) )
92  {
93  event = 0; // consume event
94  }
95  }
96  }
97 
98  return event;
99  }
100 };
101 
102 /**
103  * Helper class: Event filter that handles "ReleaseNotes" buttons.
104  **/
106 {
107 public:
109  : YEventFilter( dialog )
110  {}
111 
112  virtual ~YRelNotesButtonHandler() {}
113 
114  YEvent * filter( YEvent * event )
115  {
116  if ( event && event->widget() )
117  {
118  YPushButton * button = dynamic_cast<YPushButton *> ( event->widget() );
119 
120  if ( button && button->isRelNotesButton() )
121  {
123  {
124  event = 0; // consume event
125  }
126  }
127  }
128 
129  return event;
130  }
131 };
132 
133 
134 
135 
136 YDialog::YDialog( YDialogType dialogType, YDialogColorMode colorMode )
138  , priv( new YDialogPrivate( dialogType, colorMode ) )
139 {
140  YUI_CHECK_NEW( priv );
141 
142  _dialogStack.push( this );
143 
144 #if VERBOSE_DIALOGS
145  yuiDebug() << "New " << this << endl;
146 #endif
147 
148  new YHelpButtonHandler( this );
149  new YRelNotesButtonHandler( this );
150 }
151 
152 
154 {
155 #if VERBOSE_DIALOGS
156  yuiDebug() << "Destroying " << this << endl;
157 #endif
158 
159  // Inform attached classes that this dialog is in the process of being
160  // destroyed. This also happens in the base class destructor, but that
161  // might be too late.
163 
164  if ( priv->lastEvent )
165  deleteEvent( priv->lastEvent );
166 
167  // The base class also deletes all children, but this should be done before
168  // the event filters are deleted to prevent duplicate event filter deletion
169  // from (a) child widget destructors and (b) here.
170  deleteChildren();
171 
172  // Delete the remaining event filters: Those installed by this dialog and
173  // those installed by some child widget that are not deleted yet.
175 
176  if ( ! _dialogStack.empty() && _dialogStack.top() == this )
177  {
178  _dialogStack.pop();
179 
180  if ( ! _dialogStack.empty() )
181  _dialogStack.top()->activate();
182  }
183  else
184  yuiError() << "Not top of dialog stack: " << this << endl;
185 }
186 
187 
188 void
190 {
191  if ( priv->isOpen )
192  return;
193 
194  checkShortcuts();
195  setInitialSize();
196  openInternal(); // Make sure this is only called once!
197 
198  priv->isOpen = true;
199 }
200 
201 
202 bool
204 {
205  return priv->isOpen;
206 }
207 
208 
209 bool
211 {
212  if ( _dialogStack.empty() )
213  {
214  yuiError() << "Dialog stack empty, but dialog existing: " << this << endl;
215  return false;
216  }
217 
218  return _dialogStack.top() == this;
219 }
220 
221 
222 void
224 {
225  while ( ! priv->eventFilterList.empty() )
226  {
227  YEventFilter * filter = priv->eventFilterList.back();
228 
229 #if VERBOSE_DIALOGS
230  yuiDebug() << "Deleting event filter " << hex << filter << dec << endl;
231 #endif
232  delete filter;
233  }
234 }
235 
236 
237 bool
238 YDialog::destroy( bool doThrow )
239 {
240  YUI_CHECK_WIDGET( this );
241 
242  if ( isTopmostDialog() )
243  {
244  delete this;
245 
246  return true;
247  }
248  else
249  {
250  if ( doThrow )
251  YUI_THROW( YUIDialogStackingOrderException() );
252 
253  return false;
254  }
255 }
256 
257 
260 {
261  return priv->dialogType;
262 }
263 
264 
265 bool
267 {
268  switch ( priv->dialogType )
269  {
270  case YMainDialog: return true;
271  case YWizardDialog: return true;
272  case YPopupDialog: return false;
273 
274  // Intentionally omitting the 'default' case so the compiler can
275  // catch unhandled enum values
276  }
277 
278  /*NOTREACHED*/
279  return false;
280 }
281 
282 
283 YDialogColorMode
285 {
286  return priv->colorMode;
287 }
288 
289 
290 void
292 {
293  priv->shortcutCheckPostponed = true;
294 }
295 
296 
297 bool
299 {
300  return priv->shortcutCheckPostponed;
301 }
302 
303 
304 void
306 {
307  if ( priv->shortcutCheckPostponed && ! force )
308  {
309  yuiDebug() << "Shortcut check postponed" << endl;
310  }
311  else
312  {
313 
314  YShortcutManager shortcutManager( this );
315  shortcutManager.checkShortcuts();
316 
317  priv->shortcutCheckPostponed = false;
318  }
319 }
320 
321 
322 YPushButton *
324 {
325  return priv->defaultButton;
326 }
327 
328 
329 void
331 {
332  if ( newDefaultButton && priv->defaultButton ) // already have one?
333  {
334  yuiError() << "Too many `opt(`default) PushButtons: ["
335  << newDefaultButton->label()
336  << "]" << endl;
337  }
338 
339  priv->defaultButton = newDefaultButton;
340 }
341 
342 
343 void
345 {
346 #if VERBOSE_DIALOGS
347  yuiDebug() << "Setting initial size for " << this << endl;
348 #endif
349 
350  // Trigger geometry management
351  doLayout();
352 }
353 
354 
355 void
357 {
358  yuiDebug() << "Recalculating layout for " << this << endl;
359  doLayout();
360 }
361 
362 
363 void
365 {
366  priv->layoutPass = 1;
368 
369  if ( priv->multiPassLayout )
370  {
371  priv->layoutPass = 2;
373  }
374 
375  priv->layoutPass = 0;
376 }
377 
378 
379 int
381 {
382  return priv->layoutPass;
383 }
384 
385 
386 YEvent *
387 YDialog::waitForEvent( int timeout_millisec )
388 {
389  if ( ! isTopmostDialog() )
390  YUI_THROW( YUIDialogStackingOrderException() );
391 
392  if ( timeout_millisec < 0 )
393  timeout_millisec = 0;
394 
395  if ( ! isOpen() )
396  open();
397 
398  if ( shortcutCheckPostponed() )
399  {
400  yuiError() << "Performing missing keyboard shortcut check now in "
401  << this << endl;
402 
403  checkShortcuts( true );
404  }
405 
406  deleteEvent( priv->lastEvent );
407  YEvent * event = 0;
408 
409  do
410  {
411  event = filterInvalidEvents( waitForEventInternal( timeout_millisec ) );
412  event = callEventFilters( event );
413 
414  // If there was no event, if filterInvalidEvents() discarded an invalid
415  // event, or if one of the event filters consumed an event, go back and
416  // get the next event.
417 
418  } while ( ! event );
419 
420  priv->lastEvent = event;
421 
422  return event;
423 }
424 
425 
426 YEvent *
428 {
429  if ( ! isTopmostDialog() )
430  YUI_THROW( YUIDialogStackingOrderException() );
431 
432  if ( ! isOpen() )
433  open();
434 
436 
437  if ( event ) // Optimization (calling with 0 wouldn't hurt)
438  event = callEventFilters( event );
439 
440  priv->lastEvent = event;
441 
442  // Nevermind if filterInvalidEvents() discarded an invalid event.
443  // pollInput() is normally called very often (in a loop), and most of the
444  // times it returns 0 anyway, so there is no need to care for just another
445  // 0 that is returned in this exotic case.
446 
447  return event;
448 }
449 
450 
451 YEvent *
453 {
454  if ( ! event )
455  return 0;
456 
457  YWidgetEvent * widgetEvent = dynamic_cast<YWidgetEvent *> (event);
458 
459  if ( widgetEvent && widgetEvent->widget() )
460  {
461  if ( ! widgetEvent->widget()->isValid() )
462  {
463  /**
464  * Silently discard events from widgets that have become invalid.
465  *
466  * This may legitimately happen if some widget triggered an event yet
467  * nobody cared for that event (i.e. called UserInput() or PollInput() )
468  * and the widget has been destroyed meanwhile.
469  **/
470 
471  // yuiDebug() << "Discarding event for widget that has become invalid" << endl;
472 
473  deleteEvent( widgetEvent );
474  return 0;
475  }
476 
477  if ( widgetEvent->widget()->findDialog() != this )
478  {
479  /**
480  * Silently discard events from all but the current (topmost) dialog.
481  *
482  * This may happen even here even though the specific UI should have
483  * taken care about that: Events may still be in the queue. They might
484  * have been valid (i.e. belonged to the topmost dialog) when they
485  * arrived, but maybe simply nobody has evaluated them.
486  **/
487 
488  // Yes, really yuiDebug() - this may legitimately happen.
489  yuiDebug() << "Discarding event from widget from foreign dialog" << endl;
490 
491 #if VERBOSE_DISCARDED_EVENTS
492  yuiDebug() << "Expected: " << this
493  << ", received: " << widgetEvent->widget()->findDialog()
494  << endl;
495 
496  yuiDebug() << "Event widget: " << widgetEvent->widget() << endl;
497  yuiDebug() << "From:" << endl;
498  widgetEvent->widget()->findDialog()->dumpWidgetTree();
499  yuiDebug() << "Current dialog:" << endl;
500  dumpWidgetTree();
501 #endif
502 
503  activate(); // try to force this dialog to the foreground
504 
505  deleteEvent( widgetEvent );
506  return 0;
507  }
508 
509  }
510 
511  return event;
512 }
513 
514 
515 void
517 {
518  if ( event == priv->lastEvent )
519  priv->lastEvent = 0;
520 
521  if ( event )
522  {
523  if ( event->isValid() )
524  {
525 #if VERBOSE_EVENTS
526  yuiDebug() << "Deleting " << event << endl;
527 #endif
528  delete event;
529  }
530  else
531  {
532  yuiError() << "Attempt to delete invalid event " << event << endl;
533  }
534  }
535 }
536 
537 
538 YDialog *
539 YDialog::currentDialog( bool doThrow )
540 {
541  if ( _dialogStack.empty() )
542  {
543  if ( doThrow )
544  YUI_THROW( YUINoDialogException() );
545  return 0;
546  }
547  else
548  return _dialogStack.top();
549 }
550 
551 
552 bool
554 {
555  if ( _dialogStack.empty() )
556  {
557  if ( doThrow )
558  YUI_THROW( YUINoDialogException() );
559  }
560  else
561  {
562  delete _dialogStack.top();
563  }
564 
565  return ! _dialogStack.empty();
566 }
567 
568 
569 void
571 {
572  while ( ! _dialogStack.empty() )
573  {
574  delete _dialogStack.top();
575  }
576 }
577 
578 
579 void
580 YDialog::deleteTo( YDialog * targetDialog )
581 {
582  YUI_CHECK_WIDGET( targetDialog );
583 
584  while ( ! _dialogStack.empty() )
585  {
586  YDialog * dialog = _dialogStack.top();
587 
588  delete dialog;
589 
590  if ( dialog == targetDialog )
591  return;
592  }
593 
594  // If we ever get here, targetDialog was nowhere in the dialog stack.
595 
596  YUI_THROW( YUIDialogStackingOrderException() );
597 }
598 
599 
600 int
602 {
603  return _dialogStack.size();
604 }
605 
606 
607 void
609 {
610  YUI_CHECK_PTR( eventFilter );
611 
612  if ( find( priv->eventFilterList.begin(), priv->eventFilterList.end(),
613  eventFilter ) != priv->eventFilterList.end() )
614  {
615  yuiError() << "event filter " << std::hex << eventFilter << std::dec
616  << " already added to " << this
617  << endl;
618  }
619  else
620  {
621 #if VERBOSE_DIALOGS
622  yuiDebug() << "Adding event filter " << hex << eventFilter << dec << endl;
623 #endif
624  priv->eventFilterList.push_back( eventFilter );
625  }
626 }
627 
628 
629 void
631 {
632  YUI_CHECK_PTR( eventFilter );
633 
634 #if VERBOSE_DIALOGS
635  yuiDebug() << "Removing event filter " << hex << eventFilter << dec << endl;
636 #endif
637  priv->eventFilterList.remove( eventFilter );
638 }
639 
640 
641 YEvent *
643 {
644  YEventFilterList::const_iterator it = priv->eventFilterList.begin();
645 
646  while ( it != priv->eventFilterList.end() && event )
647  {
648  YEvent * oldEvent = event;
649  event = (*it)->filter( event );
650 
651  if ( oldEvent != event ) // event filter consumed or changed the old event?
652  deleteEvent( oldEvent ); // get rid of the old one
653 
654  ++it;
655  }
656 
657  return event;
658 }
659 
660 
661 void
663 {
664  if ( ! priv->multiPassLayout )
665  yuiDebug() << "Multiple layout passes requested" << endl;
666 
667  priv->multiPassLayout = true;
668 }
YDialogType
Type of dialog: Main / Popup / Wizard.
Definition: YTypes.h:67
A window in the desktop environment.
Definition: YDialog.h:48
void open()
Open a newly created dialog: Finalize it and make it visible on the screen.
Definition: YDialog.cc:189
virtual void setDefaultButton(YPushButton *defaultButton)
Set this dialog's default button (the button that is activated when the user hits [Return] anywhere i...
Definition: YDialog.cc:330
void checkShortcuts(bool force=false)
Checks the keyboard shortcuts of widgets in this dialog unless shortcut checks are postponed or 'forc...
Definition: YDialog.cc:305
static bool showRelNotesText()
Show the release notes.
virtual YEvent * pollEventInternal()=0
Check if a user event is pending.
virtual void activate()=0
Activate this dialog: Make sure that it is shown as the topmost dialog of this application and that i...
static YDialog * currentDialog(bool doThrow=true)
Return the current (topmost) dialog.
Definition: YDialog.cc:539
static std::stack< YDialog * > _dialogStack
Stack holding all currently existing dialogs.
Definition: YDialog.h:442
void deleteEventFilters()
Delete all (remaining) event filters.
Definition: YDialog.cc:223
static int openDialogsCount()
Returns the number of currently open dialogs (from 1 on), i.e., the depth of the dialog stack.
Definition: YDialog.cc:601
bool destroy(bool doThrow=true)
Close and delete this dialog (and all its children) if it is the topmost dialog.
Definition: YDialog.cc:238
YEvent * filterInvalidEvents(YEvent *event)
Filter out invalid events: Return 0 if the event does not belong to this dialog or the unchanged even...
Definition: YDialog.cc:452
YDialogType dialogType() const
Return this dialog's type (YMainDialog / YPopupDialog /YWizardDialog).
Definition: YDialog.cc:259
void deleteEvent(YEvent *event)
Delete an event.
Definition: YDialog.cc:516
YEvent * waitForEvent(int timeout_millisec=0)
Wait for a user event.
Definition: YDialog.cc:387
bool isOpen() const
Return 'true' if open() has already been called for this dialog.
Definition: YDialog.cc:203
int layoutPass() const
Return the number of the current layout pass: 0: No layout going on right now 1: First pass 2: Second...
Definition: YDialog.cc:380
static void deleteAllDialogs()
Delete all open dialogs.
Definition: YDialog.cc:570
static bool showHelpText(YWidget *widget)
Show the help text for the specified widget.
YEvent * pollEvent()
Check if a user event is pending.
Definition: YDialog.cc:427
virtual ~YDialog()
Destructor.
Definition: YDialog.cc:153
void postponeShortcutCheck()
From now on, postpone keyboard shortcut checks - i.e.
Definition: YDialog.cc:291
void setInitialSize()
Set the initial dialog size, depending on dialogType: YMainDialog dialogs get the UI's "default main ...
Definition: YDialog.cc:344
static bool deleteTopmostDialog(bool doThrow=true)
Delete the topmost dialog.
Definition: YDialog.cc:553
static void deleteTo(YDialog *dialog)
Delete all dialogs from the topmost to the one specified.
Definition: YDialog.cc:580
void addEventFilter(YEventFilter *eventFilter)
Add an event filter.
Definition: YDialog.cc:608
bool shortcutCheckPostponed() const
Return whether or not shortcut checking is currently postponed.
Definition: YDialog.cc:298
YEvent * callEventFilters(YEvent *event)
Call the installed event filters.
Definition: YDialog.cc:642
bool isTopmostDialog() const
Return 'true' if this dialog is the topmost dialog.
Definition: YDialog.cc:210
void doLayout()
Calculate the layout and set the size of the dialog and all widgets.
Definition: YDialog.cc:364
void removeEventFilter(YEventFilter *eventFilter)
Remove an event filter.
Definition: YDialog.cc:630
virtual void openInternal()=0
Internal open() method.
bool isMainDialog()
Return 'true' if this dialog is a dialog of main dialog size: YMainDialog or YWizardDialog.
Definition: YDialog.cc:266
YPushButton * defaultButton() const
Return this dialog's default button: The button that is activated when the user hits [Return] anywher...
Definition: YDialog.cc:323
void recalcLayout()
Recalculate the layout of the dialog and of all its children after children have been added or remove...
Definition: YDialog.cc:356
void requestMultiPassLayout()
Request multiple passes of the layout engine.
Definition: YDialog.cc:662
YDialogColorMode colorMode() const
Return this dialog's color mode.
Definition: YDialog.cc:284
YDialog(YDialogType dialogType, YDialogColorMode colorMode=YDialogNormalColor)
Constructor.
Definition: YDialog.cc:136
virtual YEvent * waitForEventInternal(int timeout_millisec)=0
Wait for a user event.
Abstract base class to filter events.
Definition: YEventFilter.h:63
YEventFilter(YDialog *dialog=0)
Constructor.
Definition: YEventFilter.cc:44
YDialog * dialog() const
Return the dialog this event filter belongs to.
Definition: YEventFilter.cc:63
Abstract base class for events to be returned upon UI::UserInput() and related functions.
Definition: YEvent.h:44
virtual YWidget * widget() const
Returns the widget that caused this event or 0 if there is none.
Definition: YEvent.h:93
bool isValid() const
Check if this event is valid.
Definition: YEvent.cc:55
Helper class: Event filter that handles "Help" buttons.
Definition: YDialog.cc:75
YEvent * filter(YEvent *event)
The heart of the matter: The event filter function.
Definition: YDialog.cc:83
A push button; may have an icon, and a F-key shortcut.
Definition: YPushButton.h:38
bool isHelpButton() const
Returns 'true' if this is a "Help" button.
Definition: YPushButton.cc:127
std::string label() const
Get the label (the text on the button).
Definition: YPushButton.cc:86
bool isRelNotesButton() const
Returns 'true' if this is a "Release Notes" button.
Definition: YPushButton.cc:139
Helper class: Event filter that handles "ReleaseNotes" buttons.
Definition: YDialog.cc:106
YEvent * filter(YEvent *event)
The heart of the matter: The event filter function.
Definition: YDialog.cc:114
Helper class to manage keyboard shortcuts within one dialog and resolve keyboard shortcut conflicts.
void checkShortcuts(bool autoResolve=true)
Check the keyboard shortcuts of all children of this dialog (not for sub-dialogs!).
Container widget class that manages one child.
virtual int preferredHeight()
Preferred height of the widget.
virtual int preferredWidth()
Preferred width of the widget.
virtual void setSize(int newWidth, int newHeight)
Set the new size of the widget.
virtual YWidget * widget() const
Returns the widget that caused this event.
Definition: YEvent.h:180
void dumpWidgetTree(int indentationLevel=0)
Debugging function: Dump the widget tree from here on to the log file.
Definition: YWidget.cc:674
YDialog * findDialog()
Traverse up the widget hierarchy and find the dialog this widget belongs to.
Definition: YWidget.cc:376
bool isValid() const
Checks whether or not this object is valid.
Definition: YWidget.cc:244
void deleteChildren()
Delete all children and remove them from the children manager's list.
Definition: YWidget.cc:202
void setBeingDestroyed()
Set the "being destroyed" flag, i.e.
Definition: YWidget.cc:264