libyui-qt  2.49.2
YQUI.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: YQUI.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23 /-*/
24 
25 #include <sys/param.h> // MAXHOSTNAMELEN
26 #include <dlfcn.h>
27 #include <libintl.h>
28 #include <algorithm>
29 #include <stdio.h>
30 
31 #include <QWidget>
32 #include <QThread>
33 #include <QSocketNotifier>
34 #include <QDesktopWidget>
35 #include <QEvent>
36 #include <QCursor>
37 #include <QLocale>
38 #include <QMessageLogContext>
39 #include <QMessageBox>
40 #include <QInputDialog>
41 
42 
43 #define YUILogComponent "qt-ui"
44 #include <yui/YUILog.h>
45 #include <yui/Libyui_config.h>
46 
47 #include "YQUI.h"
48 
49 #include <yui/YEvent.h>
50 #include <yui/YCommandLine.h>
51 #include <yui/YButtonBox.h>
52 #include <yui/YUISymbols.h>
53 
54 #include "QY2Styler.h"
55 #include "YQApplication.h"
56 #include "YQDialog.h"
57 #include "YQWidgetFactory.h"
58 #include "YQOptionalWidgetFactory.h"
59 
60 #include "YQWizardButton.h"
61 
62 #include "YQi18n.h"
63 #include "utf8.h"
64 
65 // Include low-level X headers AFTER Qt headers:
66 // X.h pollutes the global namespace (!!!) with pretty useless #defines
67 // like "Above", "Below" etc. that clash with some Qt headers.
68 #include <X11/Xlib.h>
69 
70 
71 using std::max;
72 
73 #define BUSY_CURSOR_TIMEOUT 200 // milliseconds
74 #define VERBOSE_EVENT_LOOP 0
75 
76 
77 
78 static void qMessageHandler( QtMsgType type, const QMessageLogContext &, const QString & msg );
79 YQUI * YQUI::_ui = 0;
80 
81 
82 YUI * createUI( bool withThreads )
83 {
84  if ( ! YQUI::ui() )
85  {
86  YQUI * ui = new YQUI( withThreads );
87 
88  if ( ui && ! withThreads )
89  ui->initUI();
90  }
91 
92  return YQUI::ui();
93 }
94 
95 
96 YQUI::YQUI( bool withThreads )
97  : YUI( withThreads )
98 #if 0
99  , _main_win( NULL )
100 #endif
101  , _do_exit_loop( false )
102 {
103  yuiDebug() << "YQUI constructor start" << std::endl;
104  yuiMilestone() << "This is libyui-qt " << VERSION << std::endl;
105 
106  _ui = this;
107  _uiInitialized = false;
108  _fatalError = false;
109  _fullscreen = false;
110  _noborder = false;
111  screenShotNameTemplate = "";
112  _blockedLevel = 0;
113 
114  qInstallMessageHandler( qMessageHandler );
115 
116  yuiDebug() << "YQUI constructor finished" << std::endl;
117 
118  topmostConstructorHasFinished();
119 }
120 
121 
123 {
124  if ( _uiInitialized )
125  return;
126 
127  _uiInitialized = true;
128  yuiDebug() << "Initializing Qt part" << std::endl;
129 
130  YCommandLine cmdLine; // Retrieve command line args from /proc/<pid>/cmdline
131  std::string progName;
132 
133  if ( cmdLine.argc() > 0 )
134  {
135  progName = cmdLine[0];
136  std::size_t lastSlashPos = progName.find_last_of( '/' );
137 
138  if ( lastSlashPos != std::string::npos )
139  progName = progName.substr( lastSlashPos+1 );
140 
141  // Qt will display argv[0] as the window manager title.
142  // For YaST2, display "YaST2" instead of "y2base".
143  // For other applications, leave argv[0] alone.
144 
145  if ( progName == "y2base" )
146  cmdLine.replace( 0, "YaST2" );
147  }
148 
149  _ui_argc = cmdLine.argc();
150  char ** argv = cmdLine.argv();
151 
152  yuiDebug() << "Creating QApplication" << std::endl;
153  new QApplication( _ui_argc, argv );
154  Q_CHECK_PTR( qApp );
155  // Qt keeps track to a global QApplication in qApp.
156 
157  _signalReceiver = new YQUISignalReceiver();
158  _busyCursorTimer = new QTimer( _signalReceiver );
159  _busyCursorTimer->setSingleShot( true );
160 
161  (void) QY2Styler::styler(); // Make sure QY2Styler singleton is created
162 
163  setButtonOrderFromEnvironment();
164  processCommandLineArgs( _ui_argc, argv );
165  calcDefaultSize();
166 
167  _do_exit_loop = false;
168 
169 #if 0
170  // Create main window for `opt(`defaultsize) dialogs.
171  //
172  // We have to use something else than QWidgetStack since QWidgetStack
173  // doesn't accept a WFlags arg which we badly need here.
174 
175  _main_win = new QWidget( 0, Qt::Window ); // parent, wflags
176  _main_win->setFocusPolicy( Qt::StrongFocus );
177  _main_win->setObjectName( "main_window" );
178 
179  _main_win->resize( _defaultSize );
180 
181  if ( _fullscreen )
182  _main_win->move( 0, 0 );
183 #endif
184 
185 
186  //
187  // Set application title (used by YQDialog and YQWizard)
188  //
189 
190  // for YaST2, display "YaST2" instead of "y2base"
191  if ( progName == "y2base" )
192  _applicationTitle = QString( "YaST2" );
193  else
194  _applicationTitle = fromUTF8( progName );
195 
196  // read x11 display from commandline or environment variable
197  int displayArgPos = cmdLine.find( "-display" );
198  QString displayName;
199 
200  if ( displayArgPos > 0 && displayArgPos+1 < cmdLine.argc() )
201  displayName = cmdLine[ displayArgPos+1 ].c_str();
202  else
203  displayName = getenv( "DISPLAY" );
204 
205  // identify hostname
206  char hostname[ MAXHOSTNAMELEN+1 ];
207  if ( gethostname( hostname, sizeof( hostname )-1 ) == 0 )
208  hostname[ sizeof( hostname ) -1 ] = '\0'; // make sure it's terminated
209  else
210  hostname[0] = '\0';
211 
212  // add hostname to the window title if it's not a local display
213  if ( !displayName.startsWith( ":" ) && strlen( hostname ) > 0 )
214  {
215  _applicationTitle += QString( "@" );
216  _applicationTitle += fromUTF8( hostname );
217  }
218 
219 
220 #if 0
221  // Hide the main window for now. The first call to UI::OpenDialog() on an
222  // `opt(`defaultSize) dialog will trigger a dialog->open() call that shows
223  // the main window - there is nothing to display yet.
224 
225  _main_win->hide();
226 #endif
227 
228  YButtonBoxMargins buttonBoxMargins;
229  buttonBoxMargins.left = 8;
230  buttonBoxMargins.right = 8;
231  buttonBoxMargins.top = 6;
232  buttonBoxMargins.bottom = 6;
233 
234  buttonBoxMargins.spacing = 4;
235  buttonBoxMargins.helpButtonExtraSpacing = 16;
236  YButtonBox::setDefaultMargins( buttonBoxMargins );
237 
238  // Init other stuff
239 
240  qApp->setFont( yqApp()->currentFont() );
241  busyCursor();
242 
243 
244  QObject::connect( _busyCursorTimer, &pclass(_busyCursorTimer)::timeout,
245  _signalReceiver, &pclass(_signalReceiver)::slotBusyCursor );
246 
247  yuiMilestone() << "YQUI initialized. Thread ID: 0x"
248  << hex << QThread::currentThreadId () << dec
249  << std::endl;
250 
251  qApp->processEvents();
252 }
253 
254 
257 {
258  return static_cast<YQApplication *>( app() );
259 }
260 
261 
262 void YQUI::processCommandLineArgs( int argc, char **argv )
263 {
264  if ( argv )
265  {
266  for( int i=0; i < argc; i++ )
267  {
268  QString opt = argv[i];
269 
270  yuiMilestone() << "Qt argument: " << argv[i] << std::endl;
271 
272  // Normalize command line option - accept "--xy" as well as "-xy"
273 
274  if ( opt.startsWith( "--" ) )
275  opt.remove(0, 1);
276 
277  if ( opt == QString( "-fullscreen" ) ) _fullscreen = true;
278  else if ( opt == QString( "-noborder" ) ) _noborder = true;
279  else if ( opt == QString( "-auto-font" ) ) yqApp()->setAutoFonts( true );
280  else if ( opt == QString( "-auto-fonts" ) ) yqApp()->setAutoFonts( true );
281  else if ( opt == QString( "-gnome-button-order" ) ) YButtonBox::setLayoutPolicy( YButtonBox::gnomeLayoutPolicy() );
282  else if ( opt == QString( "-kde-button-order" ) ) YButtonBox::setLayoutPolicy( YButtonBox::kdeLayoutPolicy() );
283  // --macro is handled by YUI_component
284  else if ( opt == QString( "-help" ) )
285  {
286  fprintf( stderr,
287  "Command line options for the YaST2 Qt UI:\n"
288  "\n"
289  "--nothreads run without additional UI threads\n"
290  "--fullscreen use full screen for `opt(`defaultsize) dialogs\n"
291  "--noborder no window manager border for `opt(`defaultsize) dialogs\n"
292  "--auto-fonts automatically pick fonts, disregard Qt standard settings\n"
293  "--help this help text\n"
294  "\n"
295  "--macro <macro-file> play a macro right on startup\n"
296  "\n"
297  "-no-wm, -noborder etc. are accepted as well as --no-wm, --noborder\n"
298  "to maintain backwards compatibility.\n"
299  "\n"
300  );
301 
302  raiseFatalError();
303  }
304  }
305  }
306 
307  // Qt handles command line option "-reverse" for Arabic / Hebrew
308 }
309 
310 
311 
313 {
314  yuiMilestone() <<"Closing down Qt UI." << std::endl;
315 
316  // Intentionally NOT calling dlclose() to libqt-mt
317  // (see constructor for explanation)
318 
319  if ( qApp ) // might already be reset to 0 internally from Qt
320  {
321  qApp->exit();
322  qApp->deleteLater();
323  }
324 
325  delete _signalReceiver;
326 }
327 
328 void
330 {
331  yuiMilestone() <<"Destroying UI thread" << std::endl;
332 
333  if ( qApp ) // might already be reset to 0 internally from Qt
334  {
335  if ( YDialog::openDialogsCount() > 0 )
336  {
337  yuiError() << YDialog::openDialogsCount() << " open dialogs left over" << endl;
338  yuiError() << "Topmost dialog:" << endl;
339  YDialog::topmostDialog()->dumpWidgetTree();
340  }
341 
342  YDialog::deleteAllDialogs();
343  qApp->exit();
344  qApp->deleteLater();
345  }
346 }
347 
348 
349 YWidgetFactory *
351 {
352  YQWidgetFactory * factory = new YQWidgetFactory();
353  YUI_CHECK_NEW( factory );
354 
355  return factory;
356 }
357 
358 
359 
360 YOptionalWidgetFactory *
362 {
364  YUI_CHECK_NEW( factory );
365 
366  return factory;
367 }
368 
369 
370 YApplication *
371 YQUI::createApplication()
372 {
373  YQApplication * app = new YQApplication();
374  YUI_CHECK_NEW( app );
375 
376  return app;
377 }
378 
379 
381 {
382  QSize primaryScreenSize = qApp->desktop()->screenGeometry( qApp->desktop()->primaryScreen() ).size();
383  QSize availableSize = qApp->desktop()->availableGeometry( qApp->desktop()->primaryScreen() ).size();
384 
385  if ( _fullscreen )
386  {
387  _defaultSize = availableSize;
388 
389  yuiMilestone() << "-fullscreen: using "
390  << _defaultSize.width() << " x " << _defaultSize.height()
391  << "for `opt(`defaultsize)"
392  << std::endl;
393  }
394  else
395  {
396  // Get _defaultSize via -geometry command line option (if set)
397 
398  // Set min defaultsize or figure one out if -geometry was not used
399 
400  if ( _defaultSize.width() < 800 ||
401  _defaultSize.height() < 600 )
402  {
403  if ( primaryScreenSize.width() >= 1024 && primaryScreenSize.height() >= 768 )
404  {
405  // Scale down to 70% of screen size
406 
407  _defaultSize.setWidth ( max( (int) (availableSize.width() * 0.7), 800 ) );
408  _defaultSize.setHeight( max( (int) (availableSize.height() * 0.7), 600 ) );
409  }
410  else
411  {
412  _defaultSize = availableSize;
413  }
414  }
415  else
416  {
417  yuiMilestone() << "Forced size (via -geometry): "
418  << _defaultSize.width() << " x " << _defaultSize.height()
419  << std::endl;
420  }
421  }
422 
423  yuiMilestone() << "Default size: "
424  << _defaultSize.width() << " x " << _defaultSize.height()
425  << std::endl;
426 }
427 
428 
429 void YQUI::idleLoop( int fd_ycp )
430 {
431  initUI();
432 
433  _received_ycp_command = false;
434  QSocketNotifier * notifier = new QSocketNotifier( fd_ycp, QSocketNotifier::Read );
435  QObject::connect( notifier, &pclass(notifier)::activated,
436  _signalReceiver, &pclass(_signalReceiver)::slotReceivedYCPCommand );
437 
438  notifier->setEnabled( true );
439 
440 
441  //
442  // Process Qt events until fd_ycp is readable
443  //
444 
445 #if VERBOSE_EVENT_LOOP
446  yuiDebug() << "Entering idle loop" << std::endl;
447 #endif
448 
449  QEventLoop eventLoop( qApp );
450 
451  while ( !_received_ycp_command )
452  eventLoop.processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents );
453 
454 #if VERBOSE_EVENT_LOOP
455  yuiDebug() << "Leaving idle loop" << std::endl;
456 #endif
457 
458  delete notifier;
459 }
460 
461 
463 {
464  _received_ycp_command = true;
465 }
466 
467 
468 void YQUI::sendEvent( YEvent * event )
469 {
470  if ( event )
471  {
472  _eventHandler.sendEvent( event );
473  YQDialog * dialog = (YQDialog *) YDialog::currentDialog( false ); // don't throw
474 
475  if ( dialog )
476  {
477  if ( dialog->eventLoop()->isRunning() )
478  dialog->eventLoop()->exit( 0 );
479  }
480  else
481  {
482  yuiError() << "No dialog" << std::endl;
483  }
484  }
485 }
486 
487 
488 void YQUI::setTextdomain( const char * domain )
489 {
490  bindtextdomain( domain, YSettings::localeDir().c_str() );
491  bind_textdomain_codeset( domain, "utf8" );
492  textdomain( domain );
493 
494  // Make change known.
495  {
496  extern int _nl_msg_cat_cntr;
497  ++_nl_msg_cat_cntr;
498  }
499 }
500 
501 
502 void YQUI::blockEvents( bool block )
503 {
504  initUI();
505 
506  if ( block )
507  {
508  if ( ++_blockedLevel == 1 )
509  {
510  _eventHandler.blockEvents( true );
511 
512  YQDialog * dialog = (YQDialog *) YDialog::currentDialog( false ); // don't throw
513 
514  if ( dialog && dialog->eventLoop()->isRunning() )
515  {
516  yuiWarning() << "blocking events in active event loop of " << dialog << std::endl;
517  dialog->eventLoop()->exit();
518  }
519  }
520  }
521  else
522  {
523  if ( --_blockedLevel == 0 )
524  {
525  _eventHandler.blockEvents( false );
526 
527  YQDialog * dialog = (YQDialog *) YDialog::currentDialog( false ); // don't throw
528 
529  if ( dialog )
530  dialog->eventLoop()->wakeUp();
531  }
532  }
533 }
534 
535 
537 {
538  initUI();
539  _blockedLevel = 0;
540  _eventHandler.blockEvents( false );
541 }
542 
543 
545 {
546  return _eventHandler.eventsBlocked();
547 }
548 
549 
551 {
552  qApp->setOverrideCursor( Qt::BusyCursor );
553 }
554 
555 
557 {
558  if ( _busyCursorTimer->isActive() )
559  _busyCursorTimer->stop();
560 
561  while ( qApp->overrideCursor() )
562  qApp->restoreOverrideCursor();
563 }
564 
565 
567 {
568  // Display a busy cursor, but only if there is no other activity within
569  // BUSY_CURSOR_TIMEOUT milliseconds: Avoid cursor flicker.
570 
571  _busyCursorTimer->start( BUSY_CURSOR_TIMEOUT ); // single shot
572 }
573 
574 
575 int YQUI::defaultSize(YUIDimension dim) const
576 {
577  return dim == YD_HORIZ ? _defaultSize.width() : _defaultSize.height();
578 }
579 
580 
581 void YQUI::probeX11Display( const YCommandLine & cmdLine )
582 {
583  // obsolete, see https://bugzilla.suse.com/show_bug.cgi?id=1072411
584 }
585 
586 
587 void YQUI::deleteNotify( YWidget * widget )
588 {
589  _eventHandler.deletePendingEventsFor( widget );
590 }
591 
592 // FIXME: Does this still do anything now that YQUI is no longer a QObject?
594 {
595  yuiMilestone() << "Closing application" << std::endl;
596  sendEvent( new YCancelEvent() );
597  return true;
598 }
599 
600 
602 {
603  QWidget * parent = 0;
604  YDialog * dialog = YDialog::currentDialog( false ); // doThrow
605 
606  if ( dialog )
607  parent = (QWidget *) dialog->widgetRep();
608 
609  QString id = QInputDialog::getText( parent,
610  _( "Widget ID" ), // dialog title
611  _( "Enter Widget ID:" ) // label
612  );
613  if ( ! id.isEmpty() )
614  {
615  try
616  {
617  YWidget * widget = sendWidgetID( toUTF8( id ) );
618  YQGenericButton * yqButton = dynamic_cast<YQGenericButton *>( widget );
619 
620  if ( yqButton )
621  {
622  yuiMilestone() << "Activating " << widget << endl;
623  yqButton->activate();
624  }
625  }
626  catch ( YUIWidgetNotFoundException & ex )
627  {
628  YUI_CAUGHT( ex );
629  QMessageBox::warning( parent,
630  _( "Error" ), // title
631  _( "No widget with ID \"%1\"" ).arg( id ) );
632  }
633  }
634 }
635 
636 
637 
638 
639 
640 YQUISignalReceiver::YQUISignalReceiver()
641  : QObject()
642 {
643 }
644 
645 
646 void YQUISignalReceiver::slotBusyCursor()
647 {
648  YQUI::ui()->busyCursor();
649 }
650 
651 
652 void YQUISignalReceiver::slotReceivedYCPCommand()
653 {
655 }
656 
657 
658 
659 static void
660 qMessageHandler( QtMsgType type, const QMessageLogContext &, const QString & msg )
661 {
662  switch (type)
663  {
664  case QtDebugMsg:
665  yuiMilestone() << "<libqt-debug> " << msg << std::endl;
666  break;
667 
668 #if QT_VERSION >= 0x050500
669  case QtInfoMsg:
670  yuiMilestone() << "<libqt-info> " << msg << std::endl;
671  break;
672 #endif
673 
674  case QtWarningMsg:
675  yuiWarning() << "<libqt-warning> " << msg << std::endl;
676  break;
677 
678  case QtCriticalMsg:
679  yuiError() << "<libqt-critical>" << msg << std::endl;
680  break;
681 
682  case QtFatalMsg:
683  yuiError() << "<libqt-fatal> " << msg << std::endl;
684  abort();
685  exit(1); // Qt does the same
686  }
687 
688  if ( QString( msg ).contains( "Fatal IO error", Qt::CaseInsensitive ) &&
689  QString( msg ).contains( "client killed", Qt::CaseInsensitive ) )
690  yuiError() << "Client killed. Possibly caused by X server shutdown or crash." << std::endl;
691 }
692 
693 
694 
695 #include "YQUI.moc"
int defaultSize(YUIDimension dim) const
Returns size for opt(defaultsize) dialogs (in one dimension).
Definition: YQUI.cc:575
void receivedYCPCommand()
Notification that a YCP command has been received on fd_ycp to leave idleLoop()
Definition: YQUI.cc:462
void activate()
Activate (animated) this button.
static YQApplication * yqApp()
Return the global YApplication object as YQApplication.
Definition: YQUI.cc:256
void forceUnblockEvents()
Force unblocking all events, no matter how many times blockEvents() has This returns 0 if there is no...
Definition: YQUI.cc:536
void setAutoFonts(bool useAutoFonts)
Set whether or not fonts should automatically be picked.
virtual YOptionalWidgetFactory * createOptionalWidgetFactory()
Create the widget factory that provides all the createXY() methods for optional ("special") widgets a...
Definition: YQUI.cc:361
void askSendWidgetID()
Open a pop-up dialog to ask the user for a widget ID and then send it with sendWidgetID().
Definition: YQUI.cc:601
void calcDefaultSize()
Calculate size of opt(defaultsize) dialogs.
Definition: YQUI.cc:380
QEventLoop * eventLoop()
Access to this dialog&#39;s event loop.
Definition: YQDialog.h:201
Helper class that acts as a Qt signal receiver for YQUI.
Definition: YQUI.h:374
Abstract base class for push button and similar widgets - all that can become a YQDialog&#39;s "default b...
void sendEvent(YEvent *event)
Widget event handlers (slots) call this when an event occured that should be the answer to a UserInpu...
Definition: YQUI.cc:468
YQUI(bool withThreads)
Constructor.
Definition: YQUI.cc:96
virtual void idleLoop(int fd_ycp)
Idle around until fd_ycp is readable and handle repaints.
Definition: YQUI.cc:429
void probeX11Display(const YCommandLine &cmdLine)
Probe the X11 display.
Definition: YQUI.cc:581
virtual void deleteNotify(YWidget *widget)
Notification that a widget is being deleted.
Definition: YQUI.cc:587
void busyCursor()
Show mouse cursor indicating busy state.
Definition: YQUI.cc:550
Definition: YQUI.h:61
void processCommandLineArgs(int argc, char **argv)
Handle command line args.
Definition: YQUI.cc:262
Concrete widget factory for mandatory widgets.
virtual void uiThreadDestructor()
Destroy whatever needs to be destroyed within the UI thread.
Definition: YQUI.cc:329
virtual void blockEvents(bool block=true)
Block (or unblock) events.
Definition: YQUI.cc:502
void timeoutBusyCursor()
Show mouse cursor indicating busy state if the UI is unable to respond to user input for more than a ...
Definition: YQUI.cc:566
bool close()
Application shutdown.
Definition: YQUI.cc:593
void normalCursor()
Show normal mouse cursor not indicating busy status.
Definition: YQUI.cc:556
void initUI()
Post-constructor initialization.
Definition: YQUI.cc:122
virtual ~YQUI()
Destructor.
Definition: YQUI.cc:312
void raiseFatalError()
Raise a fatal UI error.
Definition: YQUI.h:184
static void setTextdomain(const char *domain)
Initialize and set a textdomain for gettext()
Definition: YQUI.cc:488
static YQUI * ui()
Access the global Qt-UI.
Definition: YQUI.h:80
Widget factory for optional ("special") widgets.
virtual bool eventsBlocked() const
Returns &#39;true&#39; if events are currently blocked.
Definition: YQUI.cc:544
virtual YWidgetFactory * createWidgetFactory()
Create the widget factory that provides all the createXY() methods for standard (mandatory, i.e.
Definition: YQUI.cc:350