1 /*---------------------------------------------------------------------\
3 | __ __ ____ _____ ____ |
4 | \ \ / /_ _/ ___|_ _|___ \ |
5 | \ V / _` \___ \ | | __) | |
6 | | | (_| |___) || | / __/ |
7 | |_|\__,_|____/ |_| |_____| |
11 \----------------------------------------------------------------------/
15 Author: Stefan Hundhammer <sh@suse.de>
19 #include <rpc/types.h> // MAXHOSTNAMELEN
24 #include <QMessageBox>
25 #include <QSocketNotifier>
26 #include <QStackedWidget>
27 #include <QDesktopWidget>
29 #include <QVBoxLayout>
31 #define y2log_component "qt-ui"
32 #include <ycp/y2log.h>
35 #include "QY2Styler.h"
36 #include "YQApplication.h"
37 #include "YQWidgetFactory.h"
38 #include "YQOptionalWidgetFactory.h"
40 #include "YUISymbols.h"
44 #include "QY2Settings.h"
46 #define BUSY_CURSOR_TIMEOUT 200 // milliseconds
50 static void qMessageHandler( QtMsgType type, const char * msg );
52 YQUI::YQUI( int argc, char **argv, bool with_threads, const char * macro_file )
55 , _do_exit_loop( false )
58 y2milestone( "YQUI constructor start" );
63 _usingVisionImpairedPalette = false;
64 _leftHandedMouse = false;
65 _askedForLeftHandedMouse = false;
67 screenShotNameTemplate = "";
70 qInstallMsgHandler( qMessageHandler );
72 // Copy command line arguments for QApplication
74 _ui_argv = (char **) malloc( (argc+1) * sizeof( const char * ) );
77 for ( int i=0; i < argc; i++ )
78 _ui_argv[i+1] = strdup( argv[i] );
80 _ui_argv[0] = strdup( "YaST2" );
83 topmostConstructorHasFinished();
98 new QApplication( _ui_argc, _ui_argv);
100 _qobject = new YQUI_Ui();
101 _busy_cursor_timer = new QTimer( _qobject );
102 _busy_cursor_timer->setSingleShot( true );
104 _user_input_timer = new QTimer( _qobject );
105 _user_input_timer->setSingleShot( true );
107 _normalPalette = qApp->palette();
109 // Qt keeps track to a global QApplication in qApp.
112 processCommandLineArgs( _ui_argc, _ui_argv );
115 _styler = new QY2Styler( qApp );
116 QString style = getenv("Y2STYLE");
117 if ( !style.isEmpty() )
118 _styler->setStyleSheet( style );
120 _styler->setStyleSheet( "style.qss" );
122 // Event loop object. Required since a YaST2 UI needs to react to commands
123 // from the YCP command stream as well as to X11 / Qt events.
124 _eventLoop = new QEventLoop( qApp );
125 _do_exit_loop = false;
127 // Create main window for `opt(`defaultsize) dialogs.
129 // We have to use something else than QWidgetStack since QWidgetStack
130 // doesn't accept a WFlags arg which we badly need here.
132 _main_win = new QWidget( 0, Qt::Window ); // parent, wflags
133 _main_win->setFocusPolicy( Qt::StrongFocus );
134 _main_win->setObjectName( "main_window" );
136 _main_win->resize( _default_size );
139 _main_win->move( 0, 0 );
144 QString title( "YaST2" );
145 char hostname[ MAXHOSTNAMELEN+1 ];
147 if ( gethostname( hostname, sizeof( hostname )-1 ) == 0 )
149 hostname[ sizeof( hostname ) -1 ] = '\0'; // make sure it's terminated
151 if ( strlen( hostname ) > 0 )
153 if ( ( strcmp( hostname, "(none)" ) != 0 &&
154 strcmp( hostname, "linux" ) != 0 ) )
162 _main_win->setWindowTitle( title );
165 // Hide the main window for now. The first call to UI::OpenDialog() on an
166 // `opt(`defaultSize) dialog will trigger a showDialog() call that shows
167 // the main window - there is nothing to display yet.
172 // Ugly hack as a workaround of bug #121872 (Segfault at program exit
173 // if no Qt style defined):
175 // Qt does not seem to be designed for use in plugin libs. It loads some
176 // add-on libs dynamically with dlopen() and unloads them at program exit
177 // (QGPluginManager). Unfortunately, since they all depend on the Qt master
178 // lib (libqt-mt) themselves, when they are unloading the last call to
179 // dlclose() for them causes the last reference to libqt-mt to vanish as
180 // well. Since libqt-mt is already in the process of destruction there is
181 // no more reference from the caller of libqt-mt, and the GLIBC decides
182 // that libqt-mt is not needed any more (zero references) and unmaps
183 // libqt-mt. When the static destructor of libqt-mt that triggered the
184 // cleanup in QGPluginManager returns, the code it is to return to is
185 // already unmapped, causing a segfault.
187 // Workaround: Keep one more reference to libqt-mt open - dlopen() it here
188 // and make sure there is no corresponding dlclose().
190 QString qt_lib_name = QString( QTLIBDIR "/libQtGui.so.%1" ).arg( QT_VERSION >> 16 );;
191 void * qt_lib = dlopen( qt_lib_name.toUtf8().constData(), RTLD_GLOBAL );
192 y2milestone( "Forcing %s open %s", qt_lib_name.toUtf8().constData(),
193 qt_lib ? "successful" : "failed" );
197 qApp->setFont( yqApp()->currentFont() );
200 QObject::connect( _user_input_timer, SIGNAL( timeout() ),
201 _qobject, SLOT ( slotUserInputTimeout() ) );
203 QObject::connect( _busy_cursor_timer, SIGNAL( timeout() ),
204 _qobject, SLOT ( slotBusyCursor() ) );
208 // playMacro( macro_file );
210 y2milestone( "YQUI constructor end %ld", QThread::currentThreadId () );
211 qApp->processEvents();
218 return static_cast<YQApplication *>( app() );
222 void YQUI::processCommandLineArgs( int argc, char **argv )
226 for( int i=0; i < argc; i++ )
228 QString opt = argv[i];
230 y2milestone ("Qt argument: %s", argv[i]);
232 // Normalize command line option - accept "--xy" as well as "-xy"
234 if ( opt.startsWith( "--" ) )
237 if ( opt == QString( "-fullscreen" ) ) _fullscreen = true;
238 else if ( opt == QString( "-noborder" ) ) _noborder = true;
239 else if ( opt == QString( "-auto-font" ) ) yqApp()->setAutoFonts( true );
240 else if ( opt == QString( "-auto-fonts" ) ) yqApp()->setAutoFonts( true );
241 // --macro is handled by YUI_component
242 else if ( opt == QString( "-help" ) )
245 "Command line options for the YaST2 Qt UI:\n"
247 "--nothreads run without additional UI threads\n"
248 "--fullscreen use full screen for `opt(`defaultsize) dialogs\n"
249 "--noborder no window manager border for `opt(`defaultsize) dialogs\n"
250 "--auto-fonts automatically pick fonts, disregard Qt standard settings\n"
251 "--help this help text\n"
253 "--macro <macro-file> play a macro right on startup\n"
255 "-no-wm, -noborder etc. are accepted as well as --no-wm, --noborder\n"
256 "to maintain backwards compatibility.\n"
265 // Qt handles command line option "-reverse" for Arabic / Hebrew
272 y2debug("Closing down Qt UI.");
276 // Intentionally NOT calling dlclose() to libqt-mt
277 // (see constructor for explanation)
283 YQUI::createWidgetFactory()
285 YQWidgetFactory * factory = new YQWidgetFactory();
286 YUI_CHECK_NEW( factory );
293 YOptionalWidgetFactory *
294 YQUI::createOptionalWidgetFactory()
296 YQOptionalWidgetFactory * factory = new YQOptionalWidgetFactory();
297 YUI_CHECK_NEW( factory );
304 YQUI::createApplication()
306 YQApplication * app = new YQApplication();
307 YUI_CHECK_NEW( app );
313 void YQUI::calcDefaultSize()
315 QSize primaryScreenSize = qApp->desktop()->screenGeometry( qApp->desktop()->primaryScreen() ).size();
316 QSize availableSize = qApp->desktop()->availableGeometry().size();
320 _default_size = availableSize;
322 y2milestone( "-fullscreen: using %dx%d for `opt(`defaultsize)",
323 _default_size.width(), _default_size.height() );
327 // Get _default_size via -geometry command line option (if set)
329 // NOTE not needed in qt4
330 // QWidget * dummy = new QWidget();
332 // qApp->setMainWidget( dummy );
333 // _default_size = dummy->size();
335 // Set min defaultsize or figure one out if -geometry was not used
337 if ( _default_size.width() < 800 ||
338 _default_size.height() < 600 )
340 if ( primaryScreenSize.width() >= 1024 && primaryScreenSize.height() >= 768 )
342 // Scale down to 70% of screen size
344 _default_size.setWidth ( max( (int) (availableSize.width() * 0.7), 800 ) );
345 _default_size.setHeight( max( (int) (availableSize.height() * 0.7), 600 ) );
349 _default_size = availableSize;
354 y2milestone( "Forced size (via -geometry): %dx%d",
355 _default_size.width(), _default_size.height() );
359 y2milestone( "Default size: %dx%d", _default_size.width(), _default_size.height() );
364 void YQUI::internalError( const char * msg )
367 int button = QMessageBox::critical( 0, "YaST2 Internal Error", msg,
368 QMessageBox::Abort | QMessageBox::Default,
372 if ( button == QMessageBox::Abort )
377 // exit() leaves a process running (WFM?), so this really seems to be
378 // the only way to make sure we are really going down.
382 void YQUI::idleLoop( int fd_ycp )
386 _leave_idle_loop = false;
388 // process Qt events until fd_ycp is readable.
389 QSocketNotifier * notifier = new QSocketNotifier( fd_ycp, QSocketNotifier::Read );
390 QObject::connect( notifier, SIGNAL( activated ( int ) ),
391 _qobject, SLOT( slotLeaveIdleLoop() ) );
393 notifier->setEnabled( true );
395 while ( !_leave_idle_loop )
396 _eventLoop->processEvents( QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents );
401 void YQUI::leaveIdleLoop()
403 _leave_idle_loop = true;
406 void YQUI_Ui::slotLeaveIdleLoop()
408 YQUI::ui()->leaveIdleLoop();
411 void YQUI::sendEvent( YEvent * event )
415 _event_handler.sendEvent( event );
418 _eventLoop->exit( 1 );
423 YEvent * YQUI::userInput( unsigned long timeout_millisec )
427 _event_handler.blockEvents( false );
428 _eventLoop->wakeUp();
431 //y2milestone( "userInput %ld %ld", timeout_millisec, QThread::currentThreadId() );
434 YQDialog * dialog = dynamic_cast<YQDialog *> ( YDialog::currentDialog( false ) );
436 _user_input_timer->stop();
440 if ( timeout_millisec > 0 )
441 _user_input_timer->start( timeout_millisec ); // single shot
443 if ( qApp->focusWidget() )
444 qApp->focusWidget()->setFocus();
447 _do_exit_loop = true; // should exit_loop() be called in sendEvent()?
449 _do_exit_loop = false;
451 event = _event_handler.consumePendingEvent();
453 // Display a busy cursor, but only if there is no other activity within
454 // BUSY_CURSOR_TIMEOUT milliseconds (avoid cursor flicker)
456 _busy_cursor_timer->start( BUSY_CURSOR_TIMEOUT ); // single shot
459 _user_input_timer->stop();
465 YEvent * YQUI::pollInput()
469 _user_input_timer->stop();
471 if ( ! pendingEvent() )
473 YQDialog * dialog = dynamic_cast<YQDialog *> ( YDialog::currentDialog( false ) );
477 _eventLoop->processEvents( QEventLoop::AllEvents, 10 );
478 event = _event_handler.consumePendingEvent();
482 if ( pendingEvent() )
483 event = _event_handler.consumePendingEvent();
489 void YQUI_Ui::slotUserInputTimeout()
491 YQUI::ui()->userInputTimeout();
494 void YQUI::userInputTimeout()
496 if ( ! pendingEvent() )
497 sendEvent( new YTimeoutEvent() );
501 #warning FIXME Move this to Y(Q)Dialog (and rename it to ::finalize()?)
502 void YQUI::showDialog( YDialog * dialog )
504 QWidget * qw = (QWidget *) dialog->widgetRep();
513 ( (YQDialog *) dialog)->ensureOnlyOneDefaultButton();
515 //qApp->processEvents();
519 void YQUI::closeDialog( YDialog * dialog )
524 QString YQUI::productName() const
526 return fromUTF8( YUI::productName() );
531 YQUI::setTextdomain( const char * domain )
533 bindtextdomain( domain, LOCALEDIR );
534 bind_textdomain_codeset( domain, "utf8" );
535 textdomain( domain );
537 // Make change known.
539 extern int _nl_msg_cat_cntr;
545 void YQUI::blockEvents( bool block )
551 if ( ++blocked_level == 1 )
553 _event_handler.blockEvents( true );
559 if ( --blocked_level == 0 )
561 _event_handler.blockEvents( false );
562 _eventLoop->wakeUp();
568 bool YQUI::eventsBlocked() const
570 return _event_handler.eventsBlocked();
574 qMessageHandler( QtMsgType type, const char * msg )
579 y2milestone ("qt-debug: %s\n", msg);
582 y2warning ("qt-warning: %s\n", msg);
588 y2warning ("qt-critical: %s\n", msg);
594 y2internal ("qt-fatal: %s\n", msg);
596 exit (1); // qt does the same