1 /*---------------------------------------------------------------------\
3 | __ __ ____ _____ ____ |
4 | \ \ / /_ _/ ___|_ _|___ \ |
5 | \ V / _` \___ \ | | __) | |
6 | | | (_| |___) || | / __/ |
7 | |_|\__,_|____/ |_| |_____| |
11 \----------------------------------------------------------------------/
15 Author: Stefan Hundhammer <sh@suse.de>
19 #include <rpc/types.h> // MAXHOSTNAMELEN
24 #include <qmessagebox.h>
25 #include <qsocketnotifier.h>
27 #include <qwidgetstack.h>
29 #include <ycp/YCPTerm.h>
30 #include <ycp/YCPCode.h>
32 #define y2log_component "qt-ui"
33 #include <ycp/y2log.h>
36 #include "YQApplication.h"
37 #include "YQWidgetFactory.h"
38 #include "YQOptionalWidgetFactory.h"
40 #include "YUISymbols.h"
45 #include "QY2Settings.h"
48 #define BUSY_CURSOR_TIMEOUT 200 // milliseconds
49 #define KYAST_EMBEDDING 0
55 static void qMessageHandler( QtMsgType type, const char * msg );
57 YQUI::YQUI( int argc, char **argv, bool with_threads, const char * macro_file )
62 , _do_exit_loop( false )
63 , _wm_close_blocked( false )
64 , _auto_activate_dialogs( true )
66 y2debug( "YQUI constructor start" );
72 _decorate_toplevel_window = true;
73 _usingVisionImpairedPalette = false;
74 _leftHandedMouse = false;
75 _askedForLeftHandedMouse = false;
76 screenShotNameTemplate = "";
78 qInstallMsgHandler( qMessageHandler );
80 y2debug( "Creating QApplication" );
81 new QApplication( argc, argv );
83 _normalPalette = qApp->palette();
85 // Qt keeps track to a global QApplication in qApp.
88 qApp->installEventFilter( this );
89 processCommandLineArgs( argc, argv );
93 // Create main window for `opt(`defaultsize) dialogs.
95 // We have to use something else than QWidgetStack since QWidgetStack
96 // doesn't accept a WFlags arg which we badly need here.
98 WFlags wflags = WType_TopLevel;
100 if ( ! _decorate_toplevel_window )
102 y2debug( "Suppressing WM decorations for toplevel window" );
103 wflags |= WStyle_Customize | WStyle_NoBorder;
106 // if we have a window already, delete it
110 _main_win = new QVBox( 0, 0, wflags ); // parent, name, wflags
113 // Create widget stack for `opt(`defaultsize) dialogs
115 _widget_stack = new QWidgetStack( _main_win );
116 _widget_stack->setFocusPolicy( QWidget::StrongFocus );
117 qApp->setMainWidget( _main_win );
118 _main_win->installEventFilter( this );
119 _main_win->resize( _default_size );
121 if ( _fullscreen || ! _have_wm )
122 _main_win->move( 0, 0 );
127 QString title( "YaST2" );
128 char hostname[ MAXHOSTNAMELEN+1 ];
130 if ( gethostname( hostname, sizeof( hostname )-1 ) == 0 )
132 hostname[ sizeof( hostname ) -1 ] = '\0'; // make sure it's terminated
134 if ( strlen( hostname ) > 0 )
136 if ( ( strcmp( hostname, "(none)" ) != 0 &&
137 strcmp( hostname, "linux" ) != 0 ) )
145 _main_win->setCaption( title );
148 // Hide the main window for now. The first call to UI::OpenDialog() on an
149 // `opt(`defaultSize) dialog will trigger a showDialog() call that shows
150 // the main window - there is nothing to display yet.
155 // Ugly hack as a workaround of bug #121872 (Segfault at program exit
156 // if no Qt style defined):
158 // Qt does not seem to be designed for use in plugin libs. It loads some
159 // add-on libs dynamically with dlopen() and unloads them at program exit
160 // (QGPluginManager). Unfortunately, since they all depend on the Qt master
161 // lib (libqt-mt) themselves, when they are unloading the last call to
162 // dlclose() for them causes the last reference to libqt-mt to vanish as
163 // well. Since libqt-mt is already in the process of destruction there is
164 // no more reference from the caller of libqt-mt, and the GLIBC decides
165 // that libqt-mt is not needed any more (zero references) and unmaps
166 // libqt-mt. When the static destructor of libqt-mt that triggered the
167 // cleanup in QGPluginManager returns, the code it is to return to is
168 // already unmapped, causing a segfault.
170 // Workaround: Keep one more reference to libqt-mt open - dlopen() it here
171 // and make sure there is no corresponding dlclose().
173 QString qt_lib_name = QString( QTLIBDIR "/libqt-mt.so.%1" ).arg( QT_VERSION >> 16 );;
174 void * qt_lib = dlopen( (const char *) qt_lib_name, RTLD_GLOBAL );
175 y2milestone( "Forcing %s open %s", (const char *) qt_lib_name,
176 qt_lib ? "successful" : "failed" );
181 qApp->setFont( yqApp()->currentFont() );
184 connect( & _user_input_timer, SIGNAL( timeout() ),
185 this, SLOT ( userInputTimeout() ) );
187 connect( & _busy_cursor_timer, SIGNAL( timeout() ),
188 this, SLOT ( busyCursor() ) );
191 playMacro( macro_file );
194 topmostConstructorHasFinished();
196 y2debug( "YQUI constructor end" );
203 return static_cast<YQApplication *>( app() );
207 void YQUI::processCommandLineArgs( int argc, char **argv )
211 for( int i=0; i < argc; i++ )
213 QString opt = argv[i];
215 y2milestone ("Qt argument: %s", argv[i]);
217 // Normalize command line option - accept "--xy" as well as "-xy"
219 if ( opt.startsWith( "--" ) )
222 if ( opt == QString( "-no-wm" ) ) _have_wm = false;
223 else if ( opt == QString( "-fullscreen" ) ) _fullscreen = true;
224 else if ( opt == QString( "-noborder" ) ) _decorate_toplevel_window = false;
225 else if ( opt == QString( "-auto-font" ) ) yqApp()->setAutoFonts( true );
226 else if ( opt == QString( "-auto-fonts" ) ) yqApp()->setAutoFonts( true );
227 // --macro is handled by YUI_component
228 else if ( opt == QString( "-help" ) )
231 "Command line options for the YaST2 Qt UI:\n"
233 "--nothreads run without additional UI threads\n"
234 "--no-wm assume no window manager is running\n"
235 "--fullscreen use full screen for `opt(`defaultsize) dialogs\n"
236 "--noborder no window manager border for `opt(`defaultsize) dialogs\n"
237 "--auto-fonts automatically pick fonts, disregard Qt standard settings\n"
238 "--help this help text\n"
240 "--macro <macro-file> play a macro right on startup\n"
242 "-no-wm, -noborder etc. are accepted as well as --no-wm, --noborder\n"
243 "to maintain backwards compatibility.\n"
252 // Qt handles command line option "-reverse" for Arabic / Hebrew
259 y2debug("Closing down Qt UI.");
263 // Intentionally NOT calling dlclose() to libqt-mt
264 // (see constructor for explanation)
270 YQUI::createWidgetFactory()
272 YQWidgetFactory * factory = new YQWidgetFactory();
273 YUI_CHECK_NEW( factory );
280 YOptionalWidgetFactory *
281 YQUI::createOptionalWidgetFactory()
283 YQOptionalWidgetFactory * factory = new YQOptionalWidgetFactory();
284 YUI_CHECK_NEW( factory );
291 YQUI::createApplication()
293 YQApplication * app = new YQApplication();
294 YUI_CHECK_NEW( app );
300 void YQUI::calcDefaultSize()
302 QSize primaryScreenSize = qApp->desktop()->screenGeometry( qApp->desktop()->primaryScreen() ).size();
303 QSize availableSize = qApp->desktop()->availableGeometry().size();
307 _default_size = availableSize;
309 y2milestone( "-fullscreen: using %dx%d for `opt(`defaultsize)",
310 _default_size.width(), _default_size.height() );
314 // Get _default_size via -geometry command line option (if set)
316 QWidget * dummy = new QWidget();
318 qApp->setMainWidget( dummy );
319 _default_size = dummy->size();
322 // Set min defaultsize or figure one out if -geometry was not used
324 if ( _default_size.width() < 800 ||
325 _default_size.height() < 600 )
327 if ( primaryScreenSize.width() >= 1024 && primaryScreenSize.height() >= 768 )
329 // Scale down to 70% of screen size
331 _default_size.setWidth ( max( (int) (availableSize.width() * 0.7), 800 ) );
332 _default_size.setHeight( max( (int) (availableSize.height() * 0.7), 600 ) );
336 _default_size = availableSize;
341 y2milestone( "Forced size (via -geometry): %dx%d",
342 _default_size.width(), _default_size.height() );
347 _default_size = primaryScreenSize;
351 y2milestone( "Default size: %dx%d", _default_size.width(), _default_size.height() );
356 void YQUI::internalError( const char * msg )
359 int button = QMessageBox::critical( 0, "YaST2 Internal Error", msg,
360 QMessageBox::Abort | QMessageBox::Default,
364 if ( button == QMessageBox::Abort )
369 // exit() leaves a process running (WFM?), so this really seems to be
370 // the only way to make sure we are really going down.
375 void YQUI::idleLoop( int fd_ycp )
377 _leave_idle_loop = false;
379 // process Qt events until fd_ycp is readable.
380 QSocketNotifier * notifier = new QSocketNotifier( fd_ycp, QSocketNotifier::Read );
381 QObject::connect( notifier, SIGNAL( activated ( int ) ),
382 this, SLOT ( leaveIdleLoop( int ) ) );
384 notifier->setEnabled( true );
386 while ( !_leave_idle_loop )
387 qApp->processOneEvent();
393 void YQUI::leaveIdleLoop( int )
395 _leave_idle_loop = true;
399 void YQUI::sendEvent( YEvent * event )
403 _event_handler.sendEvent( event );
411 YEvent * YQUI::userInput( unsigned long timeout_millisec )
414 YQDialog * dialog = dynamic_cast<YQDialog *> ( YDialog::currentDialog( false ) );
416 if ( _user_input_timer.isActive() )
417 _user_input_timer.stop();
421 if ( timeout_millisec > 0 )
422 _user_input_timer.start( timeout_millisec, true ); // single shot
424 dialog->activate( true );
426 if ( qApp->focusWidget() )
427 qApp->focusWidget()->setFocus();
430 _do_exit_loop = true; // should exit_loop() be called in sendEvent()?
432 while ( ! pendingEvent() )
437 _do_exit_loop = false;
438 event = _event_handler.consumePendingEvent();
439 dialog->activate( false );
441 // Display a busy cursor, but only if there is no other activity within
442 // BUSY_CURSOR_TIMEOUT milliseconds (avoid cursor flicker)
444 _busy_cursor_timer.start( BUSY_CURSOR_TIMEOUT, true ); // single shot
447 if ( _user_input_timer.isActive() )
448 _user_input_timer.stop();
454 YEvent * YQUI::pollInput()
458 if ( _user_input_timer.isActive() )
459 _user_input_timer.stop();
461 if ( ! pendingEvent() )
463 YQDialog * dialog = dynamic_cast<YQDialog *> ( YDialog::currentDialog( false ) );
467 dialog->activate( true );
468 qApp->processEvents();
469 event = _event_handler.consumePendingEvent();
470 dialog->activate( false );
474 if ( pendingEvent() )
475 event = _event_handler.consumePendingEvent();
481 void YQUI::userInputTimeout()
483 if ( ! pendingEvent() )
484 sendEvent( new YTimeoutEvent() );
488 YDialog * YQUI::createDialog( YWidgetOpt & opt )
490 bool has_defaultsize = opt.hasDefaultSize.value();
491 QWidget * qt_parent = _main_win;
493 // Popup dialogs get the topmost other popup dialog as their parent since
494 // some window managers (e.g., fvwm2 as used in the inst-sys) otherwise
495 // tend to confuse the stacking order of popup dialogs.
497 // This _popup_stack handling would be better placed in showDialog(), but we
498 // need the parent here for QWidget creation. libyui guarantees that each
499 // createDialog() will be followed by showDialog() for the same dialog
500 // without any chance for other dialogs to get in between.
502 if ( ! has_defaultsize && ! _popup_stack.empty() )
503 qt_parent = _popup_stack.back();
505 YQDialog * dialog = new YQDialog( opt, qt_parent, has_defaultsize );
508 if ( ! has_defaultsize )
509 _popup_stack.push_back( (QWidget *) dialog->widgetRep() );
515 void YQUI::showDialog( YDialog * dialog )
517 QWidget * qw = (QWidget *) dialog->widgetRep();
521 y2error( "No widgetRep() for dialog" );
525 if ( dialog->hasDefaultSize() )
527 _widget_stack->addWidget ( qw, ++_main_dialog_id );
528 _widget_stack->raiseWidget( qw ); // maybe this is not necessary (?)
530 if ( ! _main_win->isVisible() )
532 // y2milestone( "Showing main window" );
533 _main_win->resize( _default_size );
536 _main_win->move( 0, 0 );
542 else // non-defaultsize dialog
547 ( (YQDialog *) dialog)->ensureOnlyOneDefaultButton();
548 qApp->processEvents();
552 void YQUI::closeDialog( YDialog * dialog )
554 QWidget * qw = (QWidget *) dialog->widgetRep();
558 y2error( "No widgetRep() for dialog" );
562 if ( dialog->hasDefaultSize() )
564 _widget_stack->removeWidget( qw );
566 if ( --_main_dialog_id < 1 ) // nothing left on the stack
568 // y2milestone( "Hiding main window" );
570 _main_dialog_id = 0; // this should not be necessary - but better be safe than sorry
574 // FIXME: This is anti-social behaviour - do this only in the inst-sys
575 _widget_stack->raiseWidget( _main_dialog_id );
578 else // non-defaultsize dialog
582 // Clean up the popup stack. libyui guarantees that a dialog will be
583 // deleted after closeDialog() so it is safe to pop that dialog from
584 // the popup stack here.
586 if ( ! _popup_stack.empty() && _popup_stack.back() == qw )
587 _popup_stack.pop_back();
589 y2error( "Popup dialog stack corrupted!" );
594 void YQUI::easterEgg()
596 y2milestone( "Starting easter egg..." );
599 YQEasterBunny::layEgg();
600 y2milestone( "Done." );
603 // desktop()->repaint() has no effect - we need to do it the hard way.
604 system( "/usr/X11R6/bin/xrefresh" );
609 QString YQUI::productName() const
611 return fromUTF8( YUI::productName() );
616 YQUI::setTextdomain( const char * domain )
618 bindtextdomain( domain, LOCALEDIR );
619 bind_textdomain_codeset( domain, "utf8" );
620 textdomain( domain );
622 // Make change known.
624 extern int _nl_msg_cat_cntr;
632 qMessageHandler( QtMsgType type, const char * msg )
637 y2debug ("qt-debug: %s\n", msg);
640 y2warning ("qt-warning: %s\n", msg);
643 y2internal ("qt-fatal: %s\n", msg);
644 exit (1); // qt does the same