libyui-qt  2.46.1
 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  QWidget* parent = 0;
545  YDialog * currentDialog = YDialog::currentDialog( false );
546  if (currentDialog)
547  parent = (QWidget *) currentDialog->widgetRep();
548 
549  YQContextMenu* menu = new YQContextMenu(parent, _contextMenuPos );
550  menu->addItems(itemCollection);
551 
552  return true;
553 }
554 
555 
556 QString
557 YQApplication::askForSaveFileName( const QString & startWith,
558  const QString & filter,
559  const QString & headline )
560 {
561  QString fileName;
562 
563  QWidget* parent = 0;
564  YDialog * currentDialog = YDialog::currentDialog( false );
565  if (currentDialog)
566  parent = (QWidget *) currentDialog->widgetRep();
567 
568 
569  // Leave the mouse cursor alone - this function might be called from
570  // some other widget, not only from UI::AskForSaveFileName().
571 
572  fileName = QFileDialog::getSaveFileName( parent, // parent
573  headline, // caption
574  startWith, // dir
575  filter, 0, QFileDialog::DontUseNativeDialog ); // filter
576 
577  if ( fileName.isEmpty() ) // this includes fileName.isNull()
578  return QString::null;
579 
580  return fileName;
581 }
582 
583 
584 int
585 YQApplication::displayWidth()
586 {
587  return qApp->desktop()->width();
588 }
589 
590 
591 int
592 YQApplication::displayHeight()
593 {
594  return qApp->desktop()->height();
595 }
596 
597 
598 int
599 YQApplication::displayDepth()
600 {
601  return qApp->desktop()->depth();
602 }
603 
604 
605 long
606 YQApplication::displayColors()
607 {
608  return 1L << qApp->desktop()->depth();
609 }
610 
611 
612 int
613 YQApplication::defaultWidth()
614 {
615  return YQUI::ui()->defaultSize( YD_HORIZ );
616 }
617 
618 
619 int
620 YQApplication::defaultHeight()
621 {
622  return YQUI::ui()->defaultSize( YD_VERT );
623 }
624 
625 
626 bool
627 YQApplication::leftHandedMouse()
628 {
629  return _leftHandedMouse;
630 }
631 
632 
633 void
635 {
636  if ( _askedForLeftHandedMouse )
637  return;
638 
639  QString message =
640  _( "You clicked the right mouse button "
641  "where a left-click was expected."
642  "\n"
643  "Switch left and right mouse buttons?"
644  );
645 
646  QWidget* parent = 0;
647  YDialog * currentDialog = YDialog::currentDialog( false );
648  if (currentDialog)
649  parent = (QWidget *) currentDialog->widgetRep();
650 
651  int button = QMessageBox::question( parent,
652  // Popup dialog caption
653  _( "Unexpected Click" ),
654  message,
655  QMessageBox::Yes | QMessageBox::Default,
656  QMessageBox::No,
657  QMessageBox::Cancel | QMessageBox::Escape );
658 
659  if ( button == QMessageBox::Yes )
660  {
661  int result;
662  const char * command =
663  _leftHandedMouse ?
664  "xmodmap -e \"pointer = 1 2 3\"": // switch back to right-handed mouse
665  "xmodmap -e \"pointer = 3 2 1\""; // switch to left-handed mouse
666 
667  _leftHandedMouse = ! _leftHandedMouse; // might be set repeatedly!
668  _askedForLeftHandedMouse = false; // give the user a chance to switch back
669  yuiMilestone() << "Switching mouse buttons: " << command << std::endl;
670 
671  result = system( command );
672  if (result < 0)
673  yuiError() << "Calling '" << command << "' failed" << std::endl;
674  else if (result > 0)
675  yuiError() << "Running '" << command << "' exited with " << result << std::endl;
676  }
677  else if ( button == 1 ) // No
678  {
679  _askedForLeftHandedMouse = true;
680  }
681 }
682 
683 
684 int YQApplication::deviceUnits( YUIDimension dim, float layoutUnits )
685 {
686  if ( dim==YD_HORIZ ) layoutUnits *= ( 640.0/80 );
687  else layoutUnits *= ( 480.0/25 );
688 
689  return (int) ( layoutUnits + 0.5 );
690 }
691 
692 
693 float YQApplication::layoutUnits( YUIDimension dim, int deviceUnits )
694 {
695  float size = (float) deviceUnits;
696 
697  if ( dim==YD_HORIZ ) size *= ( 80/640.0 );
698  else size *= ( 25/480.0 );
699 
700  return size;
701 }
702 
703 
705 {
706  qApp->beep();
707 }
708 
709 
711 {
712  YQUI::ui()->busyCursor();
713 }
714 
715 
717 {
718  YQUI::ui()->normalCursor();
719 }
720 
721 
722 void YQApplication::makeScreenShot( const std::string & fileName )
723 {
724  YQUI::ui()->makeScreenShot( fileName );
725 }
726 
727 
730 {
731  static YQPackageSelectorPluginStub * plugin = 0;
732 
733  if ( ! plugin )
734  {
735  plugin = new YQPackageSelectorPluginStub();
736 
737  // This is a deliberate memory leak: If an application requires a
738  // PackageSelector, it is a package selection application by
739  // definition. In this case, the ncurses_pkg plugin is intentionally
740  // kept open to avoid repeated start-up cost of the plugin and libzypp.
741  }
742 
743  return plugin;
744 }
745 
746 
749 {
750  static YQGraphPluginStub * plugin = 0;
751 
752  if ( ! plugin )
753  {
754  plugin = new YQGraphPluginStub();
755 
756  // This is a deliberate memory leak: Plugin is intentionally
757  // kept open to avoid repeated start-up cost of the plugin.
758  }
759 
760  return plugin;
761 }
762 
763 void
764 YQApplication::setContextMenuPos( QPoint contextMenuPos )
765 {
766  _contextMenuPos = contextMenuPos;
767 }
768 
769 void YQApplication::setApplicationTitle ( const string& title )
770 {
771  QString qtTitle = fromUTF8( title );
772  YApplication::setApplicationTitle ( title );
773  YQUI::ui()->setApplicationTitle(qtTitle);
774  qApp->setApplicationName(qtTitle);
775 }
776 
777 void YQApplication::setApplicationIcon ( const string& icon )
778 {
779  QString qtIcon = fromUTF8( icon );
780  YApplication::setApplicationIcon ( icon );
781  QPixmap pixmap (qtIcon);
782  if ( !pixmap.isNull() )
783  qApp->setWindowIcon ( QIcon ( pixmap ) );
784 }
785 
786 #include "YQApplication.moc"
virtual void normalCursor()
virtual int deviceUnits(YUIDimension dim, float layoutUnits)
virtual std::string glyph(const std::string &glyphSymbolName)
void setLayoutDirection(const std::string &language)
void maybeLeftHandedUser()
virtual QPoint contextMenuPos()
virtual ~YQApplication()
void setApplicationTitle(const QString &title)
Definition: YQUI.h:303
virtual void setApplicationTitle(const std::string &title)
virtual void busyCursor()
QSettings * _langFonts
int defaultSize(YUIDimension dim) const
Definition: YQUI.cc:584
virtual void beep()
void makeScreenShot(std::string filename)
void setAutoFonts(bool useAutoFonts)
void setLangFonts(const std::string &language, const std::string &encoding=std::string())
QTranslator * _qtTranslations
virtual bool openContextMenu(const YItemCollection &itemCollection)
QString fontKey(const QString &lang)
static YQGraphPluginStub * graphPlugin()
virtual void setContextMenuPos(QPoint contextMenuPos)
virtual void setApplicationIcon(const std::string &icon)
virtual void makeScreenShot(const std::string &fileName)
const QFont & headingFont()
QString _fontFamily
virtual std::string askForExistingDirectory(const std::string &startDir, const std::string &headline)
virtual std::string askForSaveFileName(const std::string &startWith, const std::string &filter, const std::string &headline)
void busyCursor()
Definition: YQUI.cc:559
bool autoFonts() const
virtual void setLanguage(const std::string &language, const std::string &encoding=std::string())
virtual float layoutUnits(YUIDimension dim, int deviceUnits)
const QFont & boldFont()
void loadPredefinedQtTranslations()
void normalCursor()
Definition: YQUI.cc:565
void pickAutoFonts()
static YQPackageSelectorPluginStub * packageSelectorPlugin()
static YQUI * ui()
Definition: YQUI.h:81
const QFont & currentFont()
virtual std::string askForExistingFile(const std::string &startWith, const std::string &filter, const std::string &headline)