libyui-qt  2.42.4
 All Classes Functions Variables
YQApplication.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: YQApplication.cc
20 
21  Author: Stefan Hundhammer <sh@suse.de>
22 
23  Textdomain "qt"
24 /-*/
25 
26 #include <unistd.h> // access()
27 
28 #include <QApplication>
29 #include <QLocale>
30 #include <QRegExp>
31 #include <QFileDialog>
32 #include <QDesktopWidget>
33 #include <QMessageBox>
34 #include <QSettings>
35 #include <QFontDatabase>
36 #include <QMenu>
37 
38 #include <fontconfig/fontconfig.h>
39 
40 #define YUILogComponent "qt-ui"
41 #include <yui/YUILog.h>
42 #include <yui/YUISymbols.h>
43 #include <yui/Libyui_config.h>
44 
45 #include "YQUI.h"
46 
47 #include "utf8.h"
48 #include "YQi18n.h"
49 
50 #include "YQApplication.h"
51 #include "YQPackageSelectorPluginStub.h"
52 #include "YQGraphPluginStub.h"
53 #include "YQContextMenu.h"
54 
55 
57  : YApplication()
58  , _currentFont( 0 )
59  , _headingFont( 0 )
60  , _boldFont( 0 )
61  , _langFonts( 0 )
62  , _qtTranslations( 0 )
63  , _autoFonts( false )
64  , _autoNormalFontSize( -1 )
65  , _autoHeadingFontSize( -1 )
66  , _leftHandedMouse( false )
67  , _askedForLeftHandedMouse( false )
68  , _contextMenuPos ( QPoint (0, 0) )
69  , _contextMenu ( 0 )
70 {
71  yuiDebug() << "YQApplication constructor start" << std::endl;
72 
73  //setIconBasePath( ICONDIR "/icons/22x22/apps/" );
74  // the above works too, but let's try it the icon-loader way - FaTE #306356
75  iconLoader()->addIconSearchPath( ICONDIR "/icons/" );
77 
78  yuiDebug() << "YQApplication constructor end" << std::endl;
79 }
80 
81 
83 {
84  delete _langFonts;
85  delete _qtTranslations;
86 
87  deleteFonts();
88 }
89 
90 static std::string glob_language = "";
91 
92 void
93 YQApplication::setLanguage( const std::string & language,
94  const std::string & encoding )
95 {
96  glob_language = language;
97  YApplication::setLanguage( language, encoding );
99 
100  bool oldReverseLayout = YApplication::reverseLayout();
101  setLayoutDirection( language );
102  setLangFonts( language, encoding );
103 
104  if ( oldReverseLayout != YApplication::reverseLayout() )
105  {
106  YDialog * dialog = YDialog::topmostDialog( false ); // don't throw
107 
108  if ( dialog )
109  dialog->recalcLayout();
110  }
111 }
112 
113 
114 void
116 {
117  QString path = QT_LOCALEDIR;
118  QString language;
119 
120  if (glob_language == "")
121  language = QLocale::system().name();
122  else
123  language = glob_language.c_str();
124 
125  QString transFile = QString( "qt_%1.qm").arg( language );
126 
127  yuiMilestone() << "Selected language: " << language << std::endl;
128 
129  if ( path.isEmpty() )
130  {
131  yuiWarning() << "Qt locale directory not set - "
132  << "no translations for predefined Qt dialogs"
133  << std::endl;
134  return;
135  }
136 
137  if ( ! _qtTranslations )
138  _qtTranslations = new QTranslator();
139 
140  _qtTranslations->load( transFile, path );
141 
142  if ( _qtTranslations->isEmpty() )
143  {
144  // try fallback
145  transFile = QString( "qt_%1.qm").arg( language.toLower().left(2) );
146  _qtTranslations->load( transFile, path );
147  }
148 
149  if ( _qtTranslations->isEmpty() )
150  {
151  yuiWarning() << "Can't load translations for predefined Qt dialogs from "
152  << path << "/" << transFile << std::endl;
153  }
154  else
155  {
156  yuiMilestone() << "Loaded translations for predefined Qt dialogs from "
157  << path << "/" << transFile << std::endl;
158 
159  qApp->installTranslator( _qtTranslations );
160 
161  if ( qApp->layoutDirection() == Qt::RightToLeft )
162  YApplication::setReverseLayout( true );
163  }
164 }
165 
166 
167 void
168 YQApplication::setLayoutDirection( const std::string & language )
169 {
170  QString lang( language.c_str() );
171 
172  // Force reverse layout for Arabic and Hebrew
173 
174  if ( lang.startsWith( "ar" ) || // Arabic
175  lang.startsWith( "he" ) ) // Hebrew
176  {
177  yuiMilestone() << "Using reverse layout for " << language << std::endl;
178 
179  qApp->setLayoutDirection( Qt::RightToLeft );
180  YApplication::setReverseLayout( true );
181  }
182  else
183  {
184  qApp->setLayoutDirection( Qt::LeftToRight );
185  YApplication::setReverseLayout( false );
186  }
187 
188  // Qt tries to figure that out by having translators translate a message
189  // "QT_LAYOUT_DIRECTION" to "RTL" for right-to-left languages (i.e.,
190  // Arabic, Hebrew) with QQapplication::tr(). This of course only works if
191  // there are translations for those languages for QTranslator in the first
192  // place, i.e. it only works if translations for the predefined Qt dialogs
193  // (file selection dialog etc.) are available - and being loaded.
194  //
195  // libqt4-x11 contains Arabic translations for those Qt standard dialogs in
196  // /usr/share/qt4/translations/qt_ar.qm, but (as of Sept. 2008) no Hebrew
197  // translations.
198  //
199  // Anyway, that Qt standard way is not very reliable. And they only do it
200  // at program startup anyway. Any later loading of those translations will
201  // not help.
202 }
203 
204 
205 void
206 YQApplication::setLangFonts( const std::string & language, const std::string & encoding )
207 {
208  if ( _fontFamily.isEmpty() )
209  _fontFamily = qApp->font().family();
210 
211  QString oldFontFamily = _fontFamily;
212 
213  if ( ! _langFonts )
214  {
215  _langFonts = new QSettings( LANG_FONTS_FILE, QSettings::IniFormat );
216  Q_CHECK_PTR( _langFonts );
217 
218  if ( _langFonts->status() != QSettings::NoError )
219  yuiError() << "Error reading " << _langFonts->fileName() << std::endl;
220  else
221  yuiMilestone() << _langFonts->fileName() << " read OK"
222  << qPrintable( _langFonts->allKeys().join( "-" ) )
223  << std::endl;
224  }
225 
226  QString lang = language.c_str();
227 
228  if ( ! encoding.empty() )
229  lang += QString( "." ) + encoding.c_str();
230 
231  QString key;
232 
233  if ( ! _langFonts->contains( fontKey( lang ) ) ) // Try with encoding ("zh_CN.UTF8" etc.)
234  {
235  lang = language.c_str(); // Try without encoding ("zh_CN")
236 
237  if ( ! _langFonts->contains( fontKey( lang ) ) )
238  lang.replace( QRegExp( "_.*$" ), "" ); // Cut off trailing country ("_CN")
239  }
240 
241  if ( _langFonts->contains( fontKey( lang ) ) )
242  {
243  _fontFamily = _langFonts->value( fontKey( lang ), _fontFamily ).toString();
244  yuiMilestone() << fontKey( lang ) << " = \"" << _fontFamily << "\"" << std::endl;
245  }
246  else
247  {
248  _fontFamily = _langFonts->value( fontKey( "" ), _fontFamily ).toString();
249  yuiMilestone() << "Using fallback for " << lang
250  << ": font = \"" << _fontFamily << "\""
251  << std::endl;
252  }
253 
254  if ( _fontFamily.isEmpty() ) {
255  _fontFamily = "Sans Serif";
256  }
257 
258  if ( _fontFamily != oldFontFamily )
259  {
260  yuiMilestone() << "New font family: " << _fontFamily << std::endl;
261  deleteFonts();
262  // setting the language loads fonts and we need to tell fontconfig
263  FcInitReinitialize();
264 
265  foreach ( QWidget *widget, QApplication::allWidgets() )
266  {
267  if ( widget->font().family() != oldFontFamily )
268  continue;
269 
270  QFont wfont( widget->font() );
271  wfont.setFamily( _fontFamily );
272  widget->setFont( wfont );
273  }
274  QFont font( qApp->font() );
275  font.setFamily( _fontFamily );
276  qApp->setFont(font); // font, informWidgets
277 
278  yuiMilestone() << "Reloading fonts - now using \"" << font.toString() << "\"" << std::endl;
279  }
280  else
281  {
282  yuiDebug() << "No font change" << std::endl;
283  }
284 
285 }
286 
287 
288 QString
289 YQApplication::fontKey( const QString & lang )
290 {
291  if ( lang.isEmpty() )
292  return "font";
293  else
294  return QString( "font[%1]").arg( lang );
295 }
296 
297 
298 const QFont &
300 {
301  /**
302  * Brute force approach to make sure we'll really get a complete Unicode font:
303  * Explicitly load the one font that we made sure to contain all required
304  * characters, including Latin1, Latin2, Japanese, Korean, and the
305  * characters used for glyphs.
306  *
307  * There are many fonts that claim to be Unicode, but most of them contain
308  * just a sorry excuse for a complete Unicode character set. Qt can't know
309  * how complete a font is, so it chooses one that might be better in otherf
310  * aspects, but lacks necessary characters.
311  **/
312 
313  if ( ! _currentFont )
314  {
315  if ( autoFonts() )
316  {
317  pickAutoFonts();
318 
319  _currentFont = new QFont( _fontFamily );
320  _currentFont->setPixelSize( _autoNormalFontSize );
321  _currentFont->setWeight( QFont::Normal );
322 
323  yuiMilestone() << "Loaded " << _autoNormalFontSize
324  << " pixel font: " << _currentFont->toString()
325  << std::endl;
326 
327  qApp->setFont( * _currentFont); // font, informWidgets
328  }
329  else
330  {
331  // yuiDebug() << "Copying QApplication::font()" << std::endl;
332  _currentFont = new QFont( qApp->font() );
333  }
334  }
335 
336  return * _currentFont;
337 }
338 
339 
340 const QFont &
342 {
343  if ( ! _boldFont )
344  {
345  _boldFont = new QFont( currentFont() );
346  _boldFont->setBold( true );
347  }
348 
349  return * _boldFont;
350 }
351 
352 
353 const QFont &
355 {
356  /**
357  * Brute force load the heading font - see currentFont() above for more.
358  **/
359 
360  if ( ! _headingFont )
361  {
362  if ( autoFonts() )
363  {
364  pickAutoFonts();
365 
366  _headingFont = new QFont( _fontFamily );
367  _headingFont->setPixelSize( _autoHeadingFontSize );
368  _headingFont->setWeight( QFont::Bold );
369 
370  yuiMilestone() << "Loaded " << _autoHeadingFontSize
371  << " pixel bold font: " << _headingFont->toString()
372  << std::endl;
373  }
374  else
375  {
376  _headingFont = new QFont( _fontFamily, 14, QFont::Bold );
377  }
378  }
379 
380  return * _headingFont;
381 }
382 
383 
384 void
386 {
387  delete _currentFont;
388  delete _headingFont;
389  delete _boldFont;
390 
391  _currentFont = 0;
392  _headingFont = 0;
393  _boldFont = 0;
394 }
395 
396 
397 void
398 YQApplication::setAutoFonts( bool useAutoFonts )
399 {
400  _autoFonts = useAutoFonts;
401 }
402 
403 
404 void
406 {
407  if ( _autoNormalFontSize >= 0 ) // Use cached values
408  return;
409 
410  int x = defaultWidth();
411  int y = defaultHeight();
412 
413  int normal = 10;
414  int heading = 12;
415 
416  if ( x >= 800 && y >= 600 )
417  {
418  normal = 10;
419  heading = 12;
420  }
421 
422  if ( x >= 1024 && y >= 768 )
423  {
424  normal = 12;
425  heading = 14;
426  }
427 
428  if ( x >= 1280 && y >= 1024 )
429  {
430  normal = 14;
431  heading = 18;
432  }
433 
434  if ( x >= 1400 )
435  {
436  normal = 16;
437  heading = 20;
438  }
439 
440  if ( x >= 1600 )
441  {
442  normal = 18;
443  heading = 24;
444  }
445 
446  if ( x >= 2048 ) // Sounds futuristic? Just wait one or two years...
447  {
448  normal = 20;
449  heading = 28;
450  }
451 
452  _autoNormalFontSize = normal;
453  _autoHeadingFontSize = heading;
454 
455  yuiMilestone() << "Selecting auto fonts - normal: " << _autoNormalFontSize
456  << ", heading: " << _autoHeadingFontSize << " (bold)"
457  << std::endl;
458 }
459 
460 
461 string
462 YQApplication::glyph( const std::string & sym )
463 {
464  QChar unicodeChar;
465 
466  // Hint: Use the 'xfd' program to view characters available in the Unicode font.
467 
468  if ( sym == YUIGlyph_ArrowLeft ) unicodeChar = QChar( reverseLayout() ? 0x2192 : 0x2190 );
469  else if ( sym == YUIGlyph_ArrowRight ) unicodeChar = QChar( reverseLayout() ? 0x2190 : 0x2192 );
470  else if ( sym == YUIGlyph_ArrowUp ) unicodeChar = QChar( 0x2191 );
471  else if ( sym == YUIGlyph_ArrowDown ) unicodeChar = QChar( 0x2193 );
472  else if ( sym == YUIGlyph_CheckMark ) unicodeChar = QChar( 0x2714 );
473  else if ( sym == YUIGlyph_BulletArrowRight ) unicodeChar = QChar( 0x279c );
474  else if ( sym == YUIGlyph_BulletCircle ) unicodeChar = QChar( 0x274d );
475  else if ( sym == YUIGlyph_BulletSquare ) unicodeChar = QChar( 0x274f );
476  else return "";
477 
478  return toUTF8( QString( unicodeChar ) );
479 }
480 
481 
482 string
483 YQApplication::askForExistingDirectory( const std::string & startDir,
484  const std::string & headline )
485 {
486  normalCursor();
487 
488  QString dirName =
489  QFileDialog::getExistingDirectory( 0, // parent
490  fromUTF8( headline ) , // caption
491  fromUTF8( startDir ), QFileDialog::DontUseNativeDialog); // dir
492 
493  busyCursor();
494 
495  return toUTF8( dirName );
496 }
497 
498 
499 string
500 YQApplication::askForExistingFile( const std::string & startWith,
501  const std::string & filter,
502  const std::string & headline )
503 {
504  normalCursor();
505 
506  QFileDialog* dialog = new QFileDialog( 0, // parent
507  fromUTF8( headline ), // caption
508  fromUTF8( startWith ), // dir
509  fromUTF8( filter )); // filter
510  dialog->setFileMode( QFileDialog::ExistingFile );
511  dialog->setFilter( QDir::System | dialog->filter() );
512  dialog->setOptions( QFileDialog::DontUseNativeDialog );
513 
514  QString fileName;
515  if( dialog->exec() == QDialog::Accepted )
516  fileName = dialog->selectedFiles().value( 0 );
517  delete dialog;
518 
519  busyCursor();
520 
521  return toUTF8( fileName );
522 }
523 
524 
525 string
526 YQApplication::askForSaveFileName( const std::string & startWith,
527  const std::string & filter,
528  const std::string & headline )
529 {
530  normalCursor();
531 
532  QString fileName = askForSaveFileName( fromUTF8( startWith ),
533  fromUTF8( filter ),
534  fromUTF8( headline ) );
535  busyCursor();
536 
537  return toUTF8( fileName );
538 }
539 
540 
541 bool
542 YQApplication::openContextMenu( const YItemCollection & itemCollection )
543 {
544  YQContextMenu* menu = new YQContextMenu( _contextMenuPos );
545  menu->addItems(itemCollection);
546 
547  return true;
548 }
549 
550 
551 QString
552 YQApplication::askForSaveFileName( const QString & startWith,
553  const QString & filter,
554  const QString & headline )
555 {
556  QString fileName;
557 
558  QWidget* parent = 0;
559  YDialog * currentDialog = YDialog::currentDialog( false );
560  if (currentDialog)
561  parent = (QWidget *) currentDialog->widgetRep();
562 
563 
564  // Leave the mouse cursor alone - this function might be called from
565  // some other widget, not only from UI::AskForSaveFileName().
566 
567  fileName = QFileDialog::getSaveFileName( parent, // parent
568  headline, // caption
569  startWith, // dir
570  filter, 0, QFileDialog::DontUseNativeDialog ); // filter
571 
572  if ( fileName.isEmpty() ) // this includes fileName.isNull()
573  return QString::null;
574 
575  return fileName;
576 }
577 
578 
579 int
580 YQApplication::displayWidth()
581 {
582  return qApp->desktop()->width();
583 }
584 
585 
586 int
587 YQApplication::displayHeight()
588 {
589  return qApp->desktop()->height();
590 }
591 
592 
593 int
594 YQApplication::displayDepth()
595 {
596  return qApp->desktop()->depth();
597 }
598 
599 
600 long
601 YQApplication::displayColors()
602 {
603  return 1L << qApp->desktop()->depth();
604 }
605 
606 
607 int
608 YQApplication::defaultWidth()
609 {
610  return YQUI::ui()->defaultSize( YD_HORIZ );
611 }
612 
613 
614 int
615 YQApplication::defaultHeight()
616 {
617  return YQUI::ui()->defaultSize( YD_VERT );
618 }
619 
620 
621 bool
622 YQApplication::leftHandedMouse()
623 {
624  return _leftHandedMouse;
625 }
626 
627 
628 void
630 {
631  if ( _askedForLeftHandedMouse )
632  return;
633 
634  QString message =
635  _( "You clicked the right mouse button "
636  "where a left-click was expected."
637  "\n"
638  "Switch left and right mouse buttons?"
639  );
640 
641  QWidget* parent = 0;
642  YDialog * currentDialog = YDialog::currentDialog( false );
643  if (currentDialog)
644  parent = (QWidget *) currentDialog->widgetRep();
645 
646  int button = QMessageBox::question( parent,
647  // Popup dialog caption
648  _( "Unexpected Click" ),
649  message,
650  QMessageBox::Yes | QMessageBox::Default,
651  QMessageBox::No,
652  QMessageBox::Cancel | QMessageBox::Escape );
653 
654  if ( button == QMessageBox::Yes )
655  {
656  int result;
657  const char * command =
658  _leftHandedMouse ?
659  "xmodmap -e \"pointer = 1 2 3\"": // switch back to right-handed mouse
660  "xmodmap -e \"pointer = 3 2 1\""; // switch to left-handed mouse
661 
662  _leftHandedMouse = ! _leftHandedMouse; // might be set repeatedly!
663  _askedForLeftHandedMouse = false; // give the user a chance to switch back
664  yuiMilestone() << "Switching mouse buttons: " << command << std::endl;
665 
666  result = system( command );
667  if (result < 0)
668  yuiError() << "Calling '" << command << "' failed" << std::endl;
669  else if (result > 0)
670  yuiError() << "Running '" << command << "' exited with " << result << std::endl;
671  }
672  else if ( button == 1 ) // No
673  {
674  _askedForLeftHandedMouse = true;
675  }
676 }
677 
678 
679 int YQApplication::deviceUnits( YUIDimension dim, float layoutUnits )
680 {
681  if ( dim==YD_HORIZ ) layoutUnits *= ( 640.0/80 );
682  else layoutUnits *= ( 480.0/25 );
683 
684  return (int) ( layoutUnits + 0.5 );
685 }
686 
687 
688 float YQApplication::layoutUnits( YUIDimension dim, int deviceUnits )
689 {
690  float size = (float) deviceUnits;
691 
692  if ( dim==YD_HORIZ ) size *= ( 80/640.0 );
693  else size *= ( 25/480.0 );
694 
695  return size;
696 }
697 
698 
700 {
701  qApp->beep();
702 }
703 
704 
706 {
707  YQUI::ui()->busyCursor();
708 }
709 
710 
712 {
713  YQUI::ui()->normalCursor();
714 }
715 
716 
717 void YQApplication::makeScreenShot( const std::string & fileName )
718 {
719  YQUI::ui()->makeScreenShot( fileName );
720 }
721 
722 
725 {
726  static YQPackageSelectorPluginStub * plugin = 0;
727 
728  if ( ! plugin )
729  {
730  plugin = new YQPackageSelectorPluginStub();
731 
732  // This is a deliberate memory leak: If an application requires a
733  // PackageSelector, it is a package selection application by
734  // definition. In this case, the ncurses_pkg plugin is intentionally
735  // kept open to avoid repeated start-up cost of the plugin and libzypp.
736  }
737 
738  return plugin;
739 }
740 
741 
744 {
745  static YQGraphPluginStub * plugin = 0;
746 
747  if ( ! plugin )
748  {
749  plugin = new YQGraphPluginStub();
750 
751  // This is a deliberate memory leak: Plugin is intentionally
752  // kept open to avoid repeated start-up cost of the plugin.
753  }
754 
755  return plugin;
756 }
757 
758 void
759 YQApplication::setContextMenuPos( QPoint contextMenuPos )
760 {
761  _contextMenuPos = contextMenuPos;
762 }
763 
764 void YQApplication::setApplicationTitle ( const string& title )
765 {
766  QString qtTitle = fromUTF8( title );
767  YApplication::setApplicationTitle ( title );
768  YQUI::ui()->setApplicationTitle(qtTitle);
769  qApp->setApplicationName(qtTitle);
770 }
771 
772 void YQApplication::setApplicationIcon ( const string& icon )
773 {
774  QString qtIcon = fromUTF8( icon );
775  YApplication::setApplicationIcon ( icon );
776  QPixmap pixmap (qtIcon);
777  if ( !pixmap.isNull() )
778  qApp->setWindowIcon ( QIcon ( pixmap ) );
779 }
780 
781 #include "YQApplication.moc"