libyui-gtk  2.49.0
YGUI.cc
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 
5 /*
6  Textdomain "gtk"
7  */
8 
9 #include <string.h>
10 #include <stdio.h>
11 #include <sys/stat.h>
12 #include <sys/types.h>
13 #include <YEvent.h>
14 #include <YMacro.h>
15 #include <YCommandLine.h>
16 #include "YGUI.h"
17 #include "YGi18n.h"
18 #include "YGUtils.h"
19 #include "YGDialog.h"
20 #include <glib.h>
21 
22 static std::string askForFileOrDirectory (GtkFileChooserAction action,
23  const std::string &path, const std::string &filter, const std::string &title);
24 
25 static void errorMsg (const char *msg)
26 {
27  GtkWidget* dialog = gtk_message_dialog_new (NULL,
28  GtkDialogFlags (0), GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", _("Error"));
29  gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", msg);
30  gtk_dialog_run (GTK_DIALOG (dialog));
31  gtk_widget_destroy (dialog);
32 }
33 
34 #define DEFAULT_MACRO_FILE_NAME "macro.ycp"
35 #define BUSY_CURSOR_TIMEOUT 250
36 
37 YUI *createUI( bool withThreads )
38 {
39  static YGUI *ui = 0;
40  if (!ui)
41  ui = new YGUI (withThreads);
42  return ui;
43 }
44 
45 YGUI::YGUI (bool with_threads)
46  : YUI (with_threads), m_done_init (false), busy_timeout (0)
47 {
48  yuiMilestone() << "This is libyui-gtk " << VERSION << std::endl;
49 
50  m_no_border = m_fullscreen = m_swsingle = false;
51 
52  YGUI::setTextdomain( TEXTDOMAIN );
53 
54  // If we're running without threads, initialize Gtk stuff
55  // This enables standalone libyui use Gtk interface
56  if (!with_threads)
57  checkInit();
58 
59  // without this none of the (default) threading action works ...
60  topmostConstructorHasFinished();
61 }
62 
63 void YGUI::setTextdomain( const char * domain )
64 {
65  bindtextdomain( domain, YSettings::localeDir().c_str() );
66  bind_textdomain_codeset( domain, "utf8" );
67  textdomain( domain );
68 
69  // Make change known.
70  {
71  extern int _nl_msg_cat_cntr;
72  ++_nl_msg_cat_cntr;
73  }
74 }
75 
76 static void print_log (const gchar *domain, GLogLevelFlags level, const gchar *message, void *pData)
77 {
78  YUILogLevel_t ylevel = YUI_LOG_MILESTONE;
79  switch (level) {
80  case G_LOG_LEVEL_ERROR:
81  case G_LOG_LEVEL_CRITICAL:
82  ylevel = YUI_LOG_ERROR;
83  break;
84  case G_LOG_LEVEL_WARNING:
85  ylevel = YUI_LOG_WARNING;
86  break;
87  case G_LOG_LEVEL_DEBUG:
88  ylevel = YUI_LOG_DEBUG;
89  break;
90  case G_LOG_LEVEL_MESSAGE:
91  case G_LOG_LEVEL_INFO:
92  default:
93  break;
94  }
95  // YUILog.cc assumes 'logComponent' (etc.) are static strings, that it
96  // can just keep lying around for ever, and use later, so we have to
97  // intern the domain - that can be allocated (or belong to a transient
98  // plugin's address space).
99  const char *component = domain ? g_intern_string (domain) : "libyui-gtk";
100  YUILog::instance()->log (ylevel, component, "libyui-gtk", 0, "") << message << std::endl;
101 #if 0 // uncomment to put a stop to gdb
102  static int bugStop = 0;
103  if (bugStop-- <= 0)
104  abort();
105 #endif
106 }
107 
108 void YGUI::checkInit()
109 {
110  if (m_done_init)
111  return;
112  m_done_init = true;
113 
114  // retrieve command line args from /proc/<pid>/cmdline
115  YCommandLine cmdLine;
116  int argc = cmdLine.argc();
117  char **argv = cmdLine.argv();
118  for (int i = 1; i < argc; i++) {
119  const char *argp = argv[i];
120  if (argp[0] != '-') {
121  if (!strcmp (argp, "sw_single") || !strcmp (argp, "online_update"))
122  m_swsingle = true;
123  continue;
124  }
125  argp++;
126  if (argp[0] == '-') argp++;
127 
128  if (!strcmp (argp, "fullscreen"))
129  m_fullscreen = true;
130  else if (!strcmp (argp, "noborder"))
131  m_no_border = true;
132  else if (!strcmp (argp, "help")) {
133  printf ("%s",
134  _("Command line options for the YaST2 UI (GTK plugin):\n\n"
135  "--noborder no window manager border for main dialogs\n"
136  "--fullscreen use full screen for main dialogs\n"
137  "--nothreads run without additional UI threads\n"
138  "--help prints this help text\n"
139  "\n"
140  ));
141  exit (0);
142  }
143  }
144 
145  gtk_init (&argc, &argv);
146 
147  g_log_set_default_handler (print_log, NULL); // send gtk logs to libyui system
148 #if 0 // to crash right away in order to get a stack trace
149  g_log_set_always_fatal (GLogLevelFlags (G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|
150  G_LOG_LEVEL_WARNING| G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG));
151 #endif
152  std::string themeSubDir = YSettings::themeDir();
153 
154  char* st = getenv("Y2STYLE");
155  std::string style = st ? st : "";
156 
157  if (style.size())
158  {
159  style = themeSubDir + style;
160  }
161  else
162  {
163  style = themeSubDir + "style.css";
164  }
165 
166  yuiMilestone() << "Style \"" << style << "\"\n";
167 
168  GtkCssProvider *provider = gtk_css_provider_new();
169  GFile * file = g_file_new_for_path(style.c_str());
170  if (g_file_query_exists(file, NULL))
171  {
172  GError *error = NULL;
173  if (!gtk_css_provider_load_from_file (provider, file, &error))
174  {
175  g_printerr ("%s\n", error->message);
176  }
177  else
178  {
179  GdkDisplay *display = gdk_display_get_default ();
180  GdkScreen *screen = gdk_display_get_default_screen (display);
181 
182  gtk_style_context_add_provider_for_screen (screen,
183  GTK_STYLE_PROVIDER (provider),
184  GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
185  }
186  }
187  else
188  yuiMilestone() << "Style \"" << style << "\" not found. Ignoring style\n";
189 
190  g_object_unref (provider);
191 
192  GdkPixbuf *pixbuf = YGUtils::loadPixbuf (THEMEDIR "/icons/32x32/apps/yast.png");
193  if (pixbuf) { // default window icon
194  gtk_window_set_default_icon (pixbuf);
195  g_object_unref (G_OBJECT (pixbuf));
196  }
197 }
198 
199 static gboolean ycp_wakeup_fn (GIOChannel *source, GIOCondition condition,
200  gpointer data)
201 {
202  *(int *)data = TRUE;
203  return TRUE;
204 }
205 
206 void YGUI::idleLoop (int fd_ycp)
207 {
208  // The rational for this is that we need somewhere to run
209  // the magic 'main' thread, that can process thread unsafe
210  // incoming CORBA messages for us
211  checkInit();
212 
213  GIOChannel *wakeup;
214  wakeup = g_io_channel_unix_new (fd_ycp);
215  g_io_channel_set_encoding (wakeup, NULL, NULL);
216  g_io_channel_set_buffered (wakeup, FALSE);
217 
218  int woken = FALSE;
219  guint watch_tag = g_io_add_watch (wakeup, (GIOCondition)(G_IO_IN | G_IO_PRI),
220  ycp_wakeup_fn, &woken);
221  while (!woken)
222  g_main_context_iteration (NULL, TRUE);
223 
224  g_source_remove (watch_tag);
225  g_io_channel_unref (wakeup);
226 }
227 
228 static gboolean user_input_timeout_cb (YGUI *pThis)
229 {
230  if (!pThis->pendingEvent())
231  pThis->sendEvent (new YTimeoutEvent());
232  return FALSE;
233 }
234 
235 // utility that implements both userInput() and pollInput()
236 YEvent *YGUI::waitInput (unsigned long timeout_ms, bool block)
237 {
238  checkInit();
239  if (!YDialog::currentDialog (false))
240  return NULL;
241 
242  if (block)
243  normalCursor(); // waiting for input, so no more busy
244 
245  guint timeout = 0;
246 
247  if (timeout_ms > 0)
248  // timeout is automatically removed if callback returns FALSE
249  timeout = g_timeout_add (timeout_ms,
250  (GSourceFunc) user_input_timeout_cb, this);
251 
252  if (block) {
253  while (!pendingEvent())
254  g_main_context_iteration (NULL, TRUE);
255  }
256  else
257  while (g_main_context_iteration (NULL, FALSE)) ;
258 
259  YEvent *event = NULL;
260  if (pendingEvent())
261  event = m_event_handler.consumePendingEvent();
262 
263  if (block) { // if YCP keeps working for more than X time, set busy cursor
264  if (busy_timeout)
265  g_source_remove (busy_timeout);
266  busy_timeout = g_timeout_add (BUSY_CURSOR_TIMEOUT, busy_timeout_cb, this);
267  }
268  return event;
269 }
270 
271 void YGUI::sendEvent (YEvent *event)
272 {
273  m_event_handler.sendEvent (event);
274  g_main_context_wakeup (NULL);
275 }
276 
277 gboolean YGUI::busy_timeout_cb (gpointer data)
278 {
279  YGUI *pThis = (YGUI *) data;
280  pThis->busyCursor();
281  pThis->busy_timeout = 0;
282  return FALSE;
283 }
284 
285 void YGUI::busyCursor()
286 {
287  YGDialog *dialog = YGDialog::currentDialog();
288  if (dialog)
289  dialog->busyCursor();
290 }
291 
292 void YGUI::normalCursor()
293 {
294  if (busy_timeout) {
295  g_source_remove (busy_timeout);
296  busy_timeout = 0;
297  }
298 
299  YGDialog *dialog = YGDialog::currentDialog();
300  if (dialog)
301  dialog->normalCursor();
302 }
303 
304 void YGUI::makeScreenShot()
305 { ((YGApplication *) app())->makeScreenShot (""); }
306 
307 YEvent *YGUI::runPkgSelection (YWidget *packageSelector)
308 {
309  yuiMilestone() << "Running package selection...\n";
310  YEvent *event = 0;
311 
312  try {
313  event = packageSelector->findDialog()->waitForEvent();
314  } catch (const std::exception &e) {
315  yuiError() << "UI::RunPkgSelection() error: " << e.what() << std::endl;
316  yuiError() << "This is a libzypp problem. Do not file a bug against the UI!\n";
317  } catch (...) {
318  yuiError() << "UI::RunPkgSelection() error (unspecified)\n";
319  yuiError() << "This is a libzypp problem. Do not file a bug against the UI!\n";
320  }
321  return event;
322 }
323 
324 void YGUI::askPlayMacro()
325 {
326  std::string filename = askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_OPEN,
327  DEFAULT_MACRO_FILE_NAME, "*.ycp", _("Open Macro file"));
328  if (!filename.empty()) {
329  busyCursor();
330  YMacro::play (filename);
331  sendEvent (new YEvent()); // flush
332  }
333 }
334 
335 void YGUI::toggleRecordMacro()
336 {
337  if (YMacro::recording()) {
338  YMacro::endRecording();
339  normalCursor();
340 
341  GtkWidget* dialog = gtk_message_dialog_new (NULL,
342  GtkDialogFlags (0), GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "%s",
343  _("Macro recording done."));
344  gtk_dialog_run (GTK_DIALOG (dialog));
345  gtk_widget_destroy (dialog);
346  }
347  else {
348  std::string filename = askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_SAVE,
349  DEFAULT_MACRO_FILE_NAME, "*.ycp", _("Save Macro"));
350  if (!filename.empty())
351  YMacro::record (filename);
352  }
353 }
354 
355 void YGUI::askSaveLogs()
356 {
357  std::string filename = askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_SAVE,
358  "/tmp/y2logs.tgz", "*.tgz *.tar.gz", _("Save y2logs"));
359  if (!filename.empty()) {
360  std::string command = "/usr/sbin/save_y2logs";
361  command += " '" + filename + "'";
362  yuiMilestone() << "Saving y2logs: " << command << std::endl;
363  int ret = system (command.c_str());
364  if (ret == 0)
365  yuiMilestone() << "y2logs saved to " << filename << std::endl;
366  else {
367  char *error = g_strdup_printf (
368  _("Could not run: '%s' (exit value: %d)"),
369  command.c_str(), ret);
370  yuiError() << error << std::endl;
371  errorMsg (error);
372  g_free (error);
373  }
374  }
375 }
376 
377 //** YGApplication
378 
379 #define ICONDIR THEMEDIR "/icons/22x22/apps/"
380 
381 YGApplication::YGApplication()
382 {
383  setIconBasePath (ICONDIR);
384 }
385 
386 void YGApplication::makeScreenShot (const std::string &_filename)
387 {
388  std::string filename (_filename);
389  bool interactive = filename.empty();
390 
391  GtkWidget *widget = GTK_WIDGET (YGDialog::currentWindow());
392  if (!widget) {
393  if (interactive)
394  errorMsg (_("No dialog to take screenshot of."));
395  return;
396  }
397 
398  GtkAllocation alloc;
399  gtk_widget_get_allocation(widget, &alloc);
400 
401  GError *error = 0;
402  GdkPixbuf *shot =
403  gdk_pixbuf_get_from_window (gtk_widget_get_window(widget),
404  0, 0, alloc.width,
405  alloc.height);
406 
407  if (!shot) {
408  if (interactive)
409  errorMsg (_("Could not take screenshot."));
410  return;
411  }
412 
413  if (interactive) {
414  //** ask user for filename
415  // calculate a default directory...
416  if (screenShotNameTemplate.empty()) {
417  std::string dir;
418  const char *homedir = getenv("HOME");
419  const char *ssdir = getenv("Y2SCREENSHOTS");
420  if (!homedir || !strcmp (homedir, "/")) {
421  // no homedir defined (installer)
422  dir = "/tmp/" + (ssdir ? (std::string(ssdir)) : (std::string("")));
423  if (mkdir (dir.c_str(), 0700) == -1)
424  dir = "";
425  }
426  else {
427  dir = homedir + (ssdir ? ("/" + std::string(ssdir)) : (std::string("")));
428  mkdir (dir.c_str(), 0750); // create a dir for what to put the pics
429  }
430 
431  screenShotNameTemplate = dir + "/%s-%03d.png";
432  }
433 
434  // calculate a default filename...
435  const char *baseName = "yast2-";
436 
437  int nb;
438  std::map <std::string, int>::iterator it = screenShotNb.find (baseName);
439  if (it == screenShotNb.end())
440  nb = 0;
441 
442  {
443  char *tmp_name = g_strdup_printf (screenShotNameTemplate.c_str(), baseName, nb);
444  filename = tmp_name;
445  g_free (tmp_name);
446  }
447  yuiDebug() << "screenshot: " << filename << std::endl;
448 
449  filename = askForFileOrDirectory (
450  GTK_FILE_CHOOSER_ACTION_SAVE, "", "*.png", _("Save screenshot"));
451  if (filename.empty()) { // user dismissed the dialog
452  yuiDebug() << "Save screen shot canceled by user\n";
453  goto makeScreenShot_ret;
454  }
455 
456  screenShotNb.erase (baseName);
457  screenShotNb[baseName] = nb + 1;
458  }
459 
460  yuiDebug() << "Saving screen shot to " << filename << std::endl;
461  if (!gdk_pixbuf_save (shot, filename.c_str(), "png", &error, NULL)) {
462  std::string msg = _("Could not save to:");
463  msg += " "; msg += filename;
464  if (error) {
465  msg += "\n"; msg += "\n";
466  msg += error->message;
467  }
468  yuiError() << msg << std::endl;
469  if (interactive)
470  errorMsg (msg.c_str());
471  goto makeScreenShot_ret;
472  }
473 
474  makeScreenShot_ret:
475  g_object_unref (G_OBJECT (shot));
476 }
477 
478 void YGApplication::beep()
479 {
480  GtkWindow *window = YGDialog::currentWindow();
481  if (window) {
482  gtk_window_present (window);
483  gtk_widget_error_bell (GTK_WIDGET (window));
484  }
485  else
486  gdk_beep();
487 }
488 
489 // File/directory dialogs
490 #include <sstream>
491 
492 std::string askForFileOrDirectory (GtkFileChooserAction action,
493  const std::string &path, const std::string &filter, const std::string &title)
494 {
495  GtkWindow *parent = YGDialog::currentWindow();
496  const char *button;
497  switch (action) {
498  case GTK_FILE_CHOOSER_ACTION_SAVE:
499  button = _("_Save"); break;
500  case GTK_FILE_CHOOSER_ACTION_OPEN:
501  button = _("_Open"); break;
502  case GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER:
503  case GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER:
504  default:
505  button = _("Select"); break;
506  }
507  GtkWidget *dialog;
508  dialog = gtk_file_chooser_dialog_new (title.c_str(),
509  parent, action, _("_Cancel"), GTK_RESPONSE_CANCEL,
510  button, GTK_RESPONSE_ACCEPT, NULL);
511  GtkFileChooser *fileChooser = GTK_FILE_CHOOSER (dialog);
512  gtk_file_chooser_set_local_only (fileChooser, TRUE);
513  gtk_file_chooser_set_do_overwrite_confirmation (fileChooser, TRUE);
514 
515  // filepath can be a dir or a file path, split that up
516  std::string dirname, filename;
517  if (!path.empty()) {
518  if (path[0] != '/')
519  yuiWarning() << "FileDialog: Relative paths are not supported: '" << path << "'\n";
520  else if (!g_file_test (path.c_str(), G_FILE_TEST_EXISTS))
521  yuiWarning() << "FileDialog: Path doesn't exist: '" << path << "'\n";
522  else if (g_file_test (path.c_str(), G_FILE_TEST_IS_DIR))
523  dirname = path;
524  else { // its a file
525  std::string::size_type i = path.find_last_of ("/");
526  if (i != std::string::npos) {
527  dirname = path.substr (0, i+1);
528  filename = path.substr (i+1);
529  }
530  }
531  }
532 
533  if (!dirname.empty())
534  gtk_file_chooser_set_current_folder (fileChooser, dirname.c_str());
535  if (!filename.empty())
536  gtk_file_chooser_set_current_name (fileChooser, filename.c_str());
537 
538  if (!filter.empty() && filter != "*") {
539  GtkFileFilter *gtk_filter = gtk_file_filter_new();
540  gtk_file_filter_set_name (gtk_filter, filter.c_str());
541  // cut filter_pattern into chuncks like GTK likes
542  std::istringstream stream (filter);
543  while (!stream.eof()) {
544  std::string str;
545  stream >> str;
546  if (!str.empty() && str [str.size()-1] == ',')
547  str.erase (str.size()-1);
548  gtk_file_filter_add_pattern (gtk_filter, str.c_str());
549  }
550  gtk_file_chooser_add_filter (fileChooser, gtk_filter);
551  }
552 
553  // bug 335492: because "/" gets hidden as a an arrow at the top, make sure
554  // there is a root shortcut at the side pane (will not add if already exists)
555  gtk_file_chooser_add_shortcut_folder (fileChooser, "/", NULL);
556 
557  std::string ret;
558  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) {
559  gchar *filename = gtk_file_chooser_get_filename (fileChooser);
560  if (filename) {
561  ret = filename;
562  g_free (filename);
563  }
564  }
565  gtk_widget_destroy (dialog);
566  return ret;
567 }
568 
569 std::string YGApplication::askForExistingDirectory (
570  const std::string &path, const std::string &title)
571 { return askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, path, "", title); }
572 
573 std::string YGApplication::askForExistingFile (
574  const std::string &path, const std::string &filter, const std::string &title)
575 { return askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_OPEN, path, filter, title); }
576 
577 std::string YGApplication::askForSaveFileName (
578  const std::string &path, const std::string &filter, const std::string &title)
579 { return askForFileOrDirectory (GTK_FILE_CHOOSER_ACTION_SAVE, path, filter, title); }
580 
581 std::string YGApplication::glyph (const std::string &sym)
582 {
583  bool reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL;
584  if (sym == YUIGlyph_ArrowLeft)
585  return reverse ? "\u25b6" : "\u25c0";
586  if (sym == YUIGlyph_ArrowRight)
587  return reverse ? "\u25c0" : "\u25b6";
588  if (sym == YUIGlyph_ArrowUp)
589  return "\u25b2";
590  if (sym == YUIGlyph_ArrowDown)
591  return "\u25bc";
592  if (sym == YUIGlyph_CheckMark)
593  return "\u2714";
594  if (sym == YUIGlyph_BulletArrowRight)
595  return reverse ? "\u21e6" : "\u279c";
596  if (sym == YUIGlyph_BulletCircle)
597  return "\u26ab";
598  if (sym == YUIGlyph_BulletSquare)
599  return "\u25fe";
600  return "";
601 }
602 
603 // YWidget layout units -> pixels conversion. Same as yast-qt's.
604 int YGApplication::deviceUnits (YUIDimension dim, float size)
605 {
606  if (dim == YD_HORIZ)
607  size *= 640.0 / 80;
608  else
609  size *= 480.0 / 25;
610  return size + 0.5;
611 }
612 
613 float YGApplication::layoutUnits (YUIDimension dim, int units)
614 {
615  float size = (float) units;
616  if (dim == YD_HORIZ) return size * (80/640.0);
617  else return size * (25/480.0);
618 }
619 
620 static inline GdkScreen *getScreen ()
621 { return gdk_display_get_default_screen (gdk_display_get_default()); }
622 // GTK doesn't seem to have some desktopWidth/Height like Qt, so we to report
623 // a reduced display size to compensate for the panel, or the window frame
624 int YGApplication::displayWidth()
625 {
626 # if GTK_CHECK_VERSION (3, 22, 0)
627  GdkRectangle monitor;
628  gdk_monitor_get_geometry (
629  gdk_display_get_primary_monitor (
630  gdk_display_get_default()),
631  &monitor);
632  return monitor.x - 80;
633 # else
634  return gdk_screen_get_width (getScreen()) - 80;
635 # endif
636 }
637 
638 int YGApplication::displayHeight()
639 {
640 # if GTK_CHECK_VERSION (3, 22, 0)
641  GdkRectangle monitor;
642  gdk_monitor_get_geometry (
643  gdk_display_get_primary_monitor (
644  gdk_display_get_default()),
645  &monitor);
646  return monitor.y - 80;
647 # else
648  return gdk_screen_get_height (getScreen()) - 80;
649 # endif
650 }
651 
652 int YGApplication::displayDepth()
653 {
654 # if GTK_CHECK_VERSION (3, 22, 0)
655  return gdk_visual_get_depth (gdk_screen_get_system_visual (
656  gdk_screen_get_default ()));
657 # else
658  return gdk_visual_get_best_depth();
659 #endif
660 }
661 
662 long YGApplication::displayColors()
663 { return 1L << displayDepth(); /*from yast-qt*/ }
664 
665 // YCP uses defaultWidth/Height() to use their smaller YWizard impl; we
666 // want to use a smaller default size than qt though, so assume a bigger size
667 int YGApplication::defaultWidth() { return MIN (displayWidth(), 1024); }
668 int YGApplication::defaultHeight() { return MIN (displayHeight(), 768); }
669 
670 YWidgetFactory *YGUI::createWidgetFactory()
671 { return new YGWidgetFactory; }
672 YOptionalWidgetFactory *YGUI::createOptionalWidgetFactory()
673 { return new YGOptionalWidgetFactory; }
674 YApplication *YGUI::createApplication()
675 { return new YGApplication(); }
676 
YGOptionalWidgetFactory
Definition: YGUI.h:122
YGUI
Definition: YGUI.h:20
YGWidgetFactory
Definition: YGUI.h:78
YGDialog
Definition: YGDialog.h:14
YGApplication
Definition: YGUI.h:175