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