]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiApplication.cpp
458ab789a3e14ee06e1c1167a31ce16849a19109
[lyx.git] / src / frontends / qt4 / GuiApplication.cpp
1 /**
2  * \file GuiApplication.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author unknown
7  * \author John Levon
8  * \author Abdelrazak Younes
9  *
10  * Full author contact details are available in file CREDITS.
11  */
12
13 #include <config.h>
14
15 #include "GuiApplication.h"
16
17 #include "ColorCache.h"
18 #include "ColorSet.h"
19 #include "GuiClipboard.h"
20 #include "GuiImage.h"
21 #include "GuiKeySymbol.h"
22 #include "GuiSelection.h"
23 #include "GuiView.h"
24 #include "Menus.h"
25 #include "qt_helpers.h"
26 #include "Toolbars.h"
27
28 #include "frontends/alert.h"
29 #include "frontends/Application.h"
30 #include "frontends/FontLoader.h"
31 #include "frontends/FontMetrics.h"
32
33 #include "Buffer.h"
34 #include "BufferList.h"
35 #include "BufferView.h"
36 #include "Color.h"
37 #include "Font.h"
38 #include "FuncRequest.h"
39 #include "FuncStatus.h"
40 #include "Language.h"
41 #include "Lexer.h"
42 #include "LyX.h"
43 #include "LyXAction.h"
44 #include "LyXFunc.h"
45 #include "LyXRC.h"
46 #include "Session.h"
47 #include "version.h"
48
49 #include "support/lassert.h"
50 #include "support/debug.h"
51 #include "support/ExceptionMessage.h"
52 #include "support/FileName.h"
53 #include "support/filetools.h"
54 #include "support/foreach.h"
55 #include "support/ForkedCalls.h"
56 #include "support/gettext.h"
57 #include "support/lstrings.h"
58 #include "support/lyxalgo.h" // sorted
59 #include "support/Messages.h"
60 #include "support/os.h"
61 #include "support/Package.h"
62
63 #ifdef Q_WS_MACX
64 #include "support/linkback/LinkBackProxy.h"
65 #endif
66
67 #include <queue>
68
69 #include <QByteArray>
70 #include <QClipboard>
71 #include <QDateTime>
72 #include <QDir>
73 #include <QEventLoop>
74 #include <QFileOpenEvent>
75 #include <QFileInfo>
76 #include <QHash>
77 #include <QIcon>
78 #include <QImageReader>
79 #include <QLocale>
80 #include <QLibraryInfo>
81 #include <QList>
82 #include <QMacPasteboardMime>
83 #include <QMenuBar>
84 #include <QMimeData>
85 #include <QObject>
86 #include <QPixmap>
87 #include <QPixmapCache>
88 #include <QRegExp>
89 #include <QSessionManager>
90 #include <QSettings>
91 #include <QSocketNotifier>
92 #include <QSortFilterProxyModel>
93 #include <QStandardItemModel>
94 #include <QTextCodec>
95 #include <QTimer>
96 #include <QTranslator>
97 #include <QWidget>
98
99 #ifdef Q_WS_X11
100 #include <X11/Xatom.h>
101 #include <X11/Xlib.h>
102 #undef CursorShape
103 #undef None
104 #endif
105
106 #ifdef Q_WS_WIN
107 #include <QWindowsMime>
108 #ifdef Q_CC_GNU
109 #include <wtypes.h>
110 #endif
111 #include <objidl.h>
112 #endif // Q_WS_WIN
113
114 #include <boost/bind.hpp>
115 #include <boost/crc.hpp>
116
117 #include <exception>
118 #include <vector>
119
120 using namespace std;
121 using namespace lyx::support;
122
123
124 static void initializeResources()
125 {
126         static bool initialized = false;
127         if (!initialized) {
128                 Q_INIT_RESOURCE(Resources); 
129                 initialized = true;
130         }
131 }
132
133
134 namespace lyx {
135
136 frontend::Application * createApplication(int & argc, char * argv[])
137 {
138 #ifndef Q_WS_X11
139         // prune -geometry argument(s) by shifting
140         // the following ones 2 places down.
141         for (int i = 0 ; i < argc ; ++i) {
142                 if (strcmp(argv[i], "-geometry") == 0) {
143                         int const remove = (i+1) < argc ? 2 : 1;
144                         argc -= remove;
145                         for (int j = i; j < argc; ++j)
146                                 argv[j] = argv[j + remove];
147                         --i;
148                 }
149         }
150 #endif
151         return new frontend::GuiApplication(argc, argv);
152 }
153
154 namespace frontend {
155
156
157 /// Return the list of loadable formats.
158 vector<string> loadableImageFormats()
159 {
160         vector<string> fmts;
161
162         QList<QByteArray> qt_formats = QImageReader::supportedImageFormats();
163
164         LYXERR(Debug::GRAPHICS,
165                 "\nThe image loader can load the following directly:\n");
166
167         if (qt_formats.empty())
168                 LYXERR(Debug::GRAPHICS, "\nQt4 Problem: No Format available!");
169
170         for (QList<QByteArray>::const_iterator it = qt_formats.begin(); it != qt_formats.end(); ++it) {
171
172                 LYXERR(Debug::GRAPHICS, (const char *) *it << ", ");
173
174                 string ext = ascii_lowercase((const char *) *it);
175                 // special case
176                 if (ext == "jpeg")
177                         ext = "jpg";
178                 fmts.push_back(ext);
179         }
180
181         return fmts;
182 }
183
184
185 ////////////////////////////////////////////////////////////////////////
186 //
187 // Icon loading support code
188 //
189 ////////////////////////////////////////////////////////////////////////
190
191 namespace {
192
193 struct PngMap {
194         QString key;
195         QString value;
196 };
197
198
199 bool operator<(PngMap const & lhs, PngMap const & rhs)
200 {
201         return lhs.key < rhs.key;
202 }
203
204
205 class CompareKey {
206 public:
207         CompareKey(QString const & name) : name_(name) {}
208         bool operator()(PngMap const & other) const { return other.key == name_; }
209 private:
210         QString const name_;
211 };
212
213
214 // this must be sorted alphabetically
215 // Upper case comes before lower case
216 PngMap sorted_png_map[] = {
217         { "Bumpeq", "bumpeq2" },
218         { "Cap", "cap2" },
219         { "Cup", "cup2" },
220         { "Delta", "delta2" },
221         { "Downarrow", "downarrow2" },
222         { "Gamma", "gamma2" },
223         { "Lambda", "lambda2" },
224         { "Leftarrow", "leftarrow2" },
225         { "Leftrightarrow", "leftrightarrow2" },
226         { "Longleftarrow", "longleftarrow2" },
227         { "Longleftrightarrow", "longleftrightarrow2" },
228         { "Longrightarrow", "longrightarrow2" },
229         { "Omega", "omega2" },
230         { "Phi", "phi2" },
231         { "Pi", "pi2" },
232         { "Psi", "psi2" },
233         { "Rightarrow", "rightarrow2" },
234         { "Sigma", "sigma2" },
235         { "Subset", "subset2" },
236         { "Supset", "supset2" },
237         { "Theta", "theta2" },
238         { "Uparrow", "uparrow2" },
239         { "Updownarrow", "updownarrow2" },
240         { "Upsilon", "upsilon2" },
241         { "Vdash", "vdash3" },
242         { "Vert", "vert2" },
243         { "Xi", "xi2" },
244         { "nLeftarrow", "nleftarrow2" },
245         { "nLeftrightarrow", "nleftrightarrow2" },
246         { "nRightarrow", "nrightarrow2" },
247         { "nVDash", "nvdash3" },
248         { "nvDash", "nvdash2" },
249         { "textrm \\AA", "textrm_AA"},
250         { "textrm \\O", "textrm_O"},
251         { "vDash", "vdash2" }
252 };
253
254 size_t const nr_sorted_png_map = sizeof(sorted_png_map) / sizeof(PngMap);
255
256
257 QString findPng(QString const & name)
258 {
259         PngMap const * const begin = sorted_png_map;
260         PngMap const * const end = begin + nr_sorted_png_map;
261         LASSERT(sorted(begin, end), /**/);
262
263         PngMap const * const it = find_if(begin, end, CompareKey(name));
264
265         QString png_name;
266         if (it != end) {
267                 png_name = it->value;
268         } else {
269                 png_name = name;
270                 png_name.replace('_', "underscore");
271                 png_name.replace(' ', '_');
272
273                 // This way we can have "math-delim { }" on the toolbar.
274                 png_name.replace('(', "lparen");
275                 png_name.replace(')', "rparen");
276                 png_name.replace('[', "lbracket");
277                 png_name.replace(']', "rbracket");
278                 png_name.replace('{', "lbrace");
279                 png_name.replace('}', "rbrace");
280                 png_name.replace('|', "bars");
281                 png_name.replace(',', "thinspace");
282                 png_name.replace(':', "mediumspace");
283                 png_name.replace(';', "thickspace");
284                 png_name.replace('!', "negthinspace");
285         }
286
287         LYXERR(Debug::GUI, "findPng(" << name << ")\n"
288                 << "Looking for math PNG called \"" << png_name << '"');
289         return png_name;
290 }
291
292 } // namespace anon
293
294
295 QString iconName(FuncRequest const & f, bool unknown)
296 {
297         initializeResources();
298         QString name1;
299         QString name2;
300         QString path;
301         switch (f.action) {
302         case LFUN_MATH_INSERT:
303                 if (!f.argument().empty()) {
304                         path = "math/";
305                         name1 = findPng(toqstr(f.argument()).mid(1));
306                 }
307                 break;
308         case LFUN_MATH_DELIM:
309         case LFUN_MATH_BIGDELIM:
310                 path = "math/";
311                 name1 = findPng(toqstr(f.argument()));
312                 break;
313         case LFUN_CALL:
314                 path = "commands/";
315                 name1 = toqstr(f.argument());
316                 break;
317         case LFUN_COMMAND_ALTERNATIVES: {
318                 // use the first of the alternative commands
319                 docstring firstcom;
320                 docstring dummy = split(f.argument(), firstcom, ';');
321                 name1 = toqstr(firstcom);
322                 name1.replace(' ', '_');
323                 break;
324         }
325         default:
326                 name2 = toqstr(lyxaction.getActionName(f.action));
327                 name1 = name2;
328
329                 if (!f.argument().empty()) {
330                         name1 = name2 + ' ' + toqstr(f.argument());
331                         name1.replace(' ', '_');
332                         name1.replace('\\', "backslash");
333                 }
334         }
335
336         FileName fname = libFileSearch("images/" + path, name1, "png");
337         if (fname.exists())
338                 return toqstr(fname.absFilename());
339
340         fname = libFileSearch("images/" + path, name2, "png");
341         if (fname.exists())
342                 return toqstr(fname.absFilename());
343
344         path = ":/images/" + path;
345         QDir res(path);
346         if (!res.exists()) {
347                 LYXERR0("Directory " << path << " not found in resource!"); 
348                 return QString();
349         }
350         name1 += ".png";
351         if (res.exists(name1))
352                 return path + name1;
353
354         name2 += ".png";
355         if (res.exists(name2))
356                 return path + name2;
357
358         LYXERR(Debug::GUI, "Cannot find icon with filename "
359                            << "\"" << name1 << "\""
360                            << " or filename "
361                            << "\"" << name2 << "\"" 
362                            << " for command \""
363                            << lyxaction.getActionName(f.action)
364                            << '(' << to_utf8(f.argument()) << ")\"");
365
366         if (unknown) {
367                 fname = libFileSearch(QString("images/"), "unknown", "png");
368                 if (fname.exists())
369                         return toqstr(fname.absFilename());
370                 return QString(":/images/unknown.png");
371         }
372
373         return QString();
374 }
375
376 QPixmap getPixmap(QString const & path, QString const & name, QString const & ext)
377 {
378         QPixmap pixmap;
379         FileName fname = libFileSearch(path, name, ext);
380         QString path1 = toqstr(fname.absFilename());
381         QString path2 = ":/" + path + name + "." + ext;
382
383         if (pixmap.load(path1)) {
384                 return pixmap;
385         }
386         else if (pixmap.load(path2)) {
387                 return pixmap;
388         }
389
390         LYXERR0("Cannot load pixmap \""
391                 << path << name << '.' << ext
392                 << "\", please verify resource system!");
393
394         return QPixmap();
395 }
396
397 QIcon getIcon(FuncRequest const & f, bool unknown)
398 {
399         QString icon = iconName(f, unknown);
400         if (icon.isEmpty())
401                 return QIcon();
402
403         //LYXERR(Debug::GUI, "Found icon: " << icon);
404         QPixmap pm;
405         if (!pm.load(icon)) {
406                 LYXERR0("Cannot load icon " << icon << " please verify resource system!");
407                 return QIcon();
408         }
409
410         return QIcon(pm);
411 }
412
413
414 ////////////////////////////////////////////////////////////////////////
415 //
416 // LyX server support code.
417 //
418 ////////////////////////////////////////////////////////////////////////
419
420 class SocketNotifier : public QSocketNotifier
421 {
422 public:
423         /// connect a connection notification from the LyXServerSocket
424         SocketNotifier(QObject * parent, int fd, Application::SocketCallback func)
425                 : QSocketNotifier(fd, QSocketNotifier::Read, parent), func_(func)
426         {}
427
428 public:
429         /// The callback function
430         Application::SocketCallback func_;
431 };
432
433
434 ////////////////////////////////////////////////////////////////////////
435 //
436 // Mac specific stuff goes here...
437 //
438 ////////////////////////////////////////////////////////////////////////
439
440 class MenuTranslator : public QTranslator
441 {
442 public:
443         MenuTranslator(QObject * parent)
444                 : QTranslator(parent)
445         {}
446
447         QString translate(const char * /*context*/, 
448           const char * sourceText, 
449           const char * /*comment*/ = 0) 
450         {
451                 string const s = sourceText;
452                 if (s == N_("About %1") || s == N_("Preferences") 
453                                 || s == N_("Reconfigure") || s == N_("Quit %1"))
454                         return qt_(s);
455                 else 
456                         return QString();
457         }
458 };
459
460 class GlobalMenuBar : public QMenuBar
461 {
462 public:
463         ///
464         GlobalMenuBar() : QMenuBar(0) {}
465         
466         ///
467         bool event(QEvent * e)
468         {
469                 if (e->type() == QEvent::ShortcutOverride) {
470                         //          && activeWindow() == 0) {
471                         QKeyEvent * ke = static_cast<QKeyEvent*>(e);
472                         KeySymbol sym;
473                         setKeySymbol(&sym, ke);
474                         theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers()));
475                         e->accept();
476                         return true;
477                 }
478                 return false;
479         }
480 };
481
482 #ifdef Q_WS_MACX
483 // QMacPasteboardMimeGraphics can only be compiled on Mac.
484
485 class QMacPasteboardMimeGraphics : public QMacPasteboardMime
486 {
487 public:
488         QMacPasteboardMimeGraphics()
489                 : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL)
490         {}
491
492         QString convertorName() { return "Graphics"; }
493
494         QString flavorFor(QString const & mime)
495         {
496                 LYXERR(Debug::ACTION, "flavorFor " << mime);
497                 if (mime == pdfMimeType())
498                         return QLatin1String("com.adobe.pdf");
499                 return QString();
500         }
501
502         QString mimeFor(QString flav)
503         {
504                 LYXERR(Debug::ACTION, "mimeFor " << flav);
505                 if (flav == QLatin1String("com.adobe.pdf"))
506                         return pdfMimeType();
507                 return QString();
508         }
509
510         bool canConvert(QString const & mime, QString flav)
511         {
512                 return mimeFor(flav) == mime;
513         }
514
515         QVariant convertToMime(QString const & /*mime*/, QList<QByteArray> data,
516                 QString /*flav*/)
517         {
518                 if(data.count() > 1)
519                         qWarning("QMacPasteboardMimeGraphics: Cannot handle multiple member data");
520                 return data.first();
521         }
522
523         QList<QByteArray> convertFromMime(QString const & /*mime*/,
524                 QVariant data, QString /*flav*/)
525         {
526                 QList<QByteArray> ret;
527                 ret.append(data.toByteArray());
528                 return ret;
529         }
530 };
531 #endif
532
533 ///////////////////////////////////////////////////////////////
534 //
535 // You can find more platform specific stuff at the end of this file...
536 //
537 ///////////////////////////////////////////////////////////////
538
539 ////////////////////////////////////////////////////////////////////////
540 // Windows specific stuff goes here...
541
542 #ifdef Q_WS_WIN
543 // QWindowsMimeMetafile can only be compiled on Windows.
544
545 static FORMATETC cfFromMime(QString const & mimetype)
546 {
547         FORMATETC formatetc;
548         if (mimetype == emfMimeType()) {
549                 formatetc.cfFormat = CF_ENHMETAFILE;
550                 formatetc.tymed = TYMED_ENHMF;
551         } else if (mimetype == wmfMimeType()) {
552                 formatetc.cfFormat = CF_METAFILEPICT;
553                 formatetc.tymed = TYMED_MFPICT;
554         }
555         formatetc.ptd = 0;
556         formatetc.dwAspect = DVASPECT_CONTENT;
557         formatetc.lindex = -1;
558         return formatetc;
559 }
560
561
562 class QWindowsMimeMetafile : public QWindowsMime {
563 public:
564         QWindowsMimeMetafile() {}
565
566         bool canConvertFromMime(FORMATETC const & formatetc,
567                 QMimeData const * mimedata) const
568         {
569                 return false;
570         }
571
572         bool canConvertToMime(QString const & mimetype,
573                 IDataObject * pDataObj) const
574         {
575                 if (mimetype != emfMimeType() && mimetype != wmfMimeType())
576                         return false;
577                 FORMATETC formatetc = cfFromMime(mimetype);
578                 return pDataObj->QueryGetData(&formatetc) == S_OK;
579         }
580
581         bool convertFromMime(FORMATETC const & formatetc,
582                 const QMimeData * mimedata, STGMEDIUM * pmedium) const
583         {
584                 return false;
585         }
586
587         QVariant convertToMime(QString const & mimetype, IDataObject * pDataObj,
588                 QVariant::Type preferredType) const
589         {
590                 QByteArray data;
591                 if (!canConvertToMime(mimetype, pDataObj))
592                         return data;
593
594                 FORMATETC formatetc = cfFromMime(mimetype);
595                 STGMEDIUM s;
596                 if (pDataObj->GetData(&formatetc, &s) != S_OK)
597                         return data;
598
599                 int dataSize;
600                 if (s.tymed == TYMED_ENHMF) {
601                         dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, 0, 0);
602                         data.resize(dataSize);
603                         dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, dataSize,
604                                 (LPBYTE)data.data());
605                 } else if (s.tymed == TYMED_MFPICT) {
606                         dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, 0, 0);
607                         data.resize(dataSize);
608                         dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, dataSize,
609                                 (LPBYTE)data.data());
610                 }
611                 data.detach();
612                 ReleaseStgMedium(&s);
613
614                 return data;
615         }
616
617
618         QVector<FORMATETC> formatsForMime(QString const & mimetype,
619                 QMimeData const * mimedata) const
620         {
621                 QVector<FORMATETC> formats;
622                 if (mimetype == emfMimeType() || mimetype == wmfMimeType())
623                         formats += cfFromMime(mimetype);
624                 return formats;
625         }
626
627         QString mimeForFormat(FORMATETC const & formatetc) const
628         {
629                 switch (formatetc.cfFormat) {
630                 case CF_ENHMETAFILE:
631                         return emfMimeType(); 
632                 case CF_METAFILEPICT:
633                         return wmfMimeType();
634                 }
635                 return QString();
636         }
637 };
638
639 #endif // Q_WS_WIN
640
641 ////////////////////////////////////////////////////////////////////////
642 // GuiApplication::Private definition and implementation.
643 ////////////////////////////////////////////////////////////////////////
644
645 struct GuiApplication::Private
646 {
647         Private(): language_model_(0), global_menubar_(0) 
648         {
649         #ifdef Q_WS_WIN
650                 /// WMF Mime handler for Windows clipboard.
651                 wmf_mime_ = new QWindowsMimeMetafile();
652         #endif
653         }
654
655         ///
656         QSortFilterProxyModel * language_model_;
657         ///
658         GuiClipboard clipboard_;
659         ///
660         GuiSelection selection_;
661         ///
662         FontLoader font_loader_;
663         ///
664         ColorCache color_cache_;
665         ///
666         QTranslator qt_trans_;
667         ///
668         QHash<int, SocketNotifier *> socket_notifiers_;
669         ///
670         Menus menus_;
671         ///
672         /// The global instance
673         Toolbars toolbars_;
674
675         /// this timer is used for any regular events one wants to
676         /// perform. at present it is used to check if forked processes
677         /// are done.
678         QTimer general_timer_;
679
680         /// delayed FuncRequests
681         std::queue<FuncRequest> func_request_queue_;
682
683         /// Multiple views container.
684         /**
685         * Warning: This must not be a smart pointer as the destruction of the
686         * object is handled by Qt when the view is closed
687         * \sa Qt::WA_DeleteOnClose attribute.
688         */
689         QHash<int, GuiView *> views_;
690
691         /// Only used on mac.
692         GlobalMenuBar * global_menubar_;
693
694 #ifdef Q_WS_MACX
695         /// Linkback mime handler for MacOSX.
696         QMacPasteboardMimeGraphics mac_pasteboard_mime_;
697 #endif
698
699 #ifdef Q_WS_WIN
700         /// WMF Mime handler for Windows clipboard.
701         QWindowsMimeMetafile * wmf_mime_;
702 #endif
703 };
704
705
706 GuiApplication * guiApp;
707
708 GuiApplication::~GuiApplication()
709 {
710 #ifdef Q_WS_MACX
711         closeAllLinkBackLinks();
712 #endif
713         delete d;
714 }
715
716
717 GuiApplication::GuiApplication(int & argc, char ** argv)
718         : QApplication(argc, argv), current_view_(0),
719           d(new GuiApplication::Private)
720 {
721         QString app_name = "LyX";
722         QCoreApplication::setOrganizationName(app_name);
723         QCoreApplication::setOrganizationDomain("lyx.org");
724         QCoreApplication::setApplicationName(lyx_package);
725
726         // Install translator for GUI elements.
727         installTranslator(&d->qt_trans_);
728
729         // FIXME: quitOnLastWindowClosed is true by default. We should have a
730         // lyxrc setting for this in order to let the application stay resident.
731         // But then we need some kind of dock icon, at least on Windows.
732         /*
733         if (lyxrc.quit_on_last_window_closed)
734                 setQuitOnLastWindowClosed(false);
735         */
736 #ifdef Q_WS_MACX
737         // FIXME: Do we need a lyxrc setting for this on Mac? This behaviour
738         // seems to be the default case for applications like LyX.
739         setQuitOnLastWindowClosed(false);
740
741         // This allows to translate the strings that appear in the LyX menu.
742         /// A translator suitable for the entries in the LyX menu.
743         /// Only needed with Qt/Mac.
744         installTranslator(new MenuTranslator(this));
745 #endif
746         
747 #ifdef Q_WS_X11
748         // doubleClickInterval() is 400 ms on X11 which is just too long.
749         // On Windows and Mac OS X, the operating system's value is used.
750         // On Microsoft Windows, calling this function sets the double
751         // click interval for all applications. So we don't!
752         QApplication::setDoubleClickInterval(300);
753 #endif
754
755         connect(this, SIGNAL(lastWindowClosed()), this, SLOT(onLastWindowClosed()));
756
757         // needs to be done before reading lyxrc
758         QWidget w;
759         lyxrc.dpi = (w.logicalDpiX() + w.logicalDpiY()) / 2;
760
761         guiApp = this;
762
763         // Set the cache to 5120 kilobytes which corresponds to screen size of
764         // 1280 by 1024 pixels with a color depth of 32 bits.
765         QPixmapCache::setCacheLimit(5120);
766
767         // Initialize RC Fonts
768         if (lyxrc.roman_font_name.empty())
769                 lyxrc.roman_font_name = fromqstr(romanFontName());
770
771         if (lyxrc.sans_font_name.empty())
772                 lyxrc.sans_font_name = fromqstr(sansFontName());
773
774         if (lyxrc.typewriter_font_name.empty())
775                 lyxrc.typewriter_font_name = fromqstr(typewriterFontName());
776
777         d->general_timer_.setInterval(500);
778         connect(&d->general_timer_, SIGNAL(timeout()),
779                 this, SLOT(handleRegularEvents()));
780         d->general_timer_.start();
781 }
782
783
784 GuiApplication * theGuiApp()
785 {
786         return dynamic_cast<GuiApplication *>(theApp());
787 }
788
789
790 void GuiApplication::clearSession()
791 {
792         QSettings settings;
793         settings.clear();
794 }
795
796
797 docstring GuiApplication::iconName(FuncRequest const & f, bool unknown)
798 {
799         return qstring_to_ucs4(lyx::frontend::iconName(f, unknown));
800 }
801
802
803 LyXView * GuiApplication::currentWindow()
804 {
805 #ifdef Q_WS_MACX
806         /* In LyX/Mac, when a dialog is open, the menus of the
807            application can still be accessed without giving focus to
808            the main window. In this case, we want to disable the menu
809            entries that are buffer or view-related.
810         */
811         if (current_view_ && !current_view_->hasFocus())
812                 return 0;
813 #endif
814         return current_view_;
815 }
816
817
818 bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const
819 {
820         bool enable = true;
821
822         switch(cmd.action) {
823
824         case LFUN_WINDOW_CLOSE:
825                 enable = d->views_.size() > 0;
826                 break;
827
828         case LFUN_BUFFER_NEW:
829         case LFUN_BUFFER_NEW_TEMPLATE:
830         case LFUN_FILE_OPEN:
831         case LFUN_HELP_OPEN:
832         case LFUN_SCREEN_FONT_UPDATE:
833         case LFUN_SET_COLOR:
834         case LFUN_WINDOW_NEW:
835         case LFUN_LYX_QUIT:
836                 enable = true;
837                 break;
838
839         default:
840                 return false;
841         }
842
843         if (!enable)
844                 flag.setEnabled(false);
845
846         return true;
847 }
848
849         
850 bool GuiApplication::dispatch(FuncRequest const & cmd)
851 {
852         switch (cmd.action) {
853
854         case LFUN_WINDOW_NEW:
855                 createView(toqstr(cmd.argument()));
856                 break;
857
858         case LFUN_WINDOW_CLOSE:
859                 // update bookmark pit of the current buffer before window close
860                 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
861                         theLyXFunc().gotoBookmark(i+1, false, false);
862                 // clear the last opened list, because
863                 // maybe this will end the session
864                 theSession().lastOpened().clear();
865                 current_view_->close();
866                 break;
867
868         case LFUN_LYX_QUIT:
869                 // quitting is triggered by the gui code
870                 // (leaving the event loop).
871                 if (current_view_)
872                         current_view_->message(from_utf8(N_("Exiting.")));
873                 if (closeAllViews())
874                         quit();
875                 break;
876
877         case LFUN_SCREEN_FONT_UPDATE: {
878                 // handle the screen font changes.
879                 d->font_loader_.update();
880                 // Backup current_view_
881                 GuiView * view = current_view_;
882                 // Set current_view_ to zero to forbid GuiWorkArea::redraw()
883                 // to skip the refresh.
884                 current_view_ = 0;
885                 BufferList::iterator it = theBufferList().begin();
886                 BufferList::iterator const end = theBufferList().end();
887                 for (; it != end; ++it)
888                         (*it)->changed();
889                 // Restore current_view_
890                 current_view_ = view;
891                 break;
892         }
893
894         case LFUN_BUFFER_NEW:
895                 if (d->views_.empty()
896                     || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
897                         createView(QString(), false); // keep hidden
898                         current_view_->newDocument(to_utf8(cmd.argument()), false);
899                         current_view_->show();
900                         setActiveWindow(current_view_);
901                 } else {
902                         current_view_->newDocument(to_utf8(cmd.argument()), false);
903                 }
904                 break;
905
906         case LFUN_BUFFER_NEW_TEMPLATE:
907                 if (d->views_.empty()
908                     || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
909                         createView();
910                         current_view_->newDocument(to_utf8(cmd.argument()), true);
911                         if (!current_view_->documentBufferView())
912                                 current_view_->close();
913                 } else {
914                         current_view_->newDocument(to_utf8(cmd.argument()), true);
915                 }
916                 break;
917
918         case LFUN_FILE_OPEN:
919                 // FIXME: create a new method shared with LFUN_HELP_OPEN.
920                 if (d->views_.empty()
921                     || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
922                         string const fname = to_utf8(cmd.argument());
923                         // We want the ui session to be saved per document and not per
924                         // window number. The filename crc is a good enough identifier.
925                         boost::crc_32_type crc;
926                         crc = for_each(fname.begin(), fname.end(), crc);
927                         createView(crc.checksum());
928                         current_view_->openDocument(fname);
929                         if (current_view_ && !current_view_->documentBufferView())
930                                 current_view_->close();
931                 } else
932                         current_view_->openDocument(to_utf8(cmd.argument()));
933                 break;
934
935         case LFUN_HELP_OPEN: {
936                 // FIXME: create a new method shared with LFUN_FILE_OPEN.
937                 if (current_view_ == 0)
938                         createView();
939                 string const arg = to_utf8(cmd.argument());
940                 if (arg.empty()) {
941                         current_view_->message(_("Missing argument"));
942                         break;
943                 }
944                 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
945                 if (fname.empty()) 
946                         fname = i18nLibFileSearch("examples", arg, "lyx");
947
948                 if (fname.empty()) {
949                         lyxerr << "LyX: unable to find documentation file `"
950                                 << arg << "'. Bad installation?" << endl;
951                         break;
952                 }
953                 current_view_->message(bformat(_("Opening help file %1$s..."),
954                         makeDisplayPath(fname.absFilename())));
955                 Buffer * buf = current_view_->loadDocument(fname, false);
956                 if (buf) {
957                         current_view_->setBuffer(buf);
958                         buf->updateLabels();
959                         buf->errors("Parse");
960                 }
961                 break;
962         }
963
964         case LFUN_SET_COLOR: {
965                 string lyx_name;
966                 string const x11_name = split(to_utf8(cmd.argument()), lyx_name, ' ');
967                 if (lyx_name.empty() || x11_name.empty()) {
968                         current_view_->message(
969                                 _("Syntax: set-color <lyx_name> <x11_name>"));
970                         break;
971                 }
972
973                 string const graphicsbg = lcolor.getLyXName(Color_graphicsbg);
974                 bool const graphicsbg_changed = lyx_name == graphicsbg
975                         && x11_name != graphicsbg;
976                 if (graphicsbg_changed) {
977                         // FIXME: The graphics cache no longer has a changeDisplay method.
978 #if 0
979                         graphics::GCache::get().changeDisplay(true);
980 #endif
981                 }
982
983                 if (!lcolor.setColor(lyx_name, x11_name)) {
984                         current_view_->message(
985                                         bformat(_("Set-color \"%1$s\" failed "
986                                                                "- color is undefined or "
987                                                                "may not be redefined"),
988                                                                    from_utf8(lyx_name)));
989                         break;
990                 }
991                 // Make sure we don't keep old colors in cache.
992                 d->color_cache_.clear();
993                 break;
994         }
995
996         default:
997                 // Notify the caller that the action has not been dispatched.
998                 return false;
999         }
1000
1001         // The action has been dispatched.
1002         return true;
1003 }
1004
1005
1006 void GuiApplication::dispatchDelayed(FuncRequest const & func)
1007 {
1008         d->func_request_queue_.push(func);
1009         QTimer::singleShot(0, this, SLOT(processFuncRequestQueue()));
1010 }
1011
1012
1013 void GuiApplication::resetGui()
1014 {
1015         // Set the language defined by the user.
1016         setGuiLanguage();
1017
1018         // Read menus
1019         if (!readUIFile(toqstr(lyxrc.ui_file)))
1020                 // Gives some error box here.
1021                 return;
1022
1023         if (d->global_menubar_)
1024                 d->menus_.fillMenuBar(d->global_menubar_, 0, false);
1025
1026         QHash<int, GuiView *>::iterator it;
1027         for (it = d->views_.begin(); it != d->views_.end(); ++it) {
1028                 GuiView * gv = *it;
1029                 gv->setLayoutDirection(layoutDirection());
1030                 gv->resetDialogs();
1031         }
1032
1033         dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
1034 }
1035
1036
1037 void GuiApplication::createView(int view_id)
1038 {
1039         createView(QString(), true, view_id);
1040 }
1041
1042
1043 void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
1044         int view_id)
1045 {
1046         // release the keyboard which might have been grabed by the global
1047         // menubar on Mac to catch shortcuts even without any GuiView.
1048         if (d->global_menubar_)
1049                 d->global_menubar_->releaseKeyboard();
1050
1051         // create new view
1052         int id = view_id;
1053         while (d->views_.find(id) != d->views_.end())
1054                 id++;
1055
1056         LYXERR(Debug::GUI, "About to create new window with ID " << id);
1057         GuiView * view = new GuiView(id);
1058         // register view
1059         d->views_[id] = view;
1060
1061         if (autoShow) {
1062                 view->show();
1063                 setActiveWindow(view);
1064         }
1065
1066         if (!geometry_arg.isEmpty()) {
1067 #ifdef Q_WS_WIN
1068                 int x, y;
1069                 int w, h;
1070                 QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)([+-][0-9]*)){0,1}" );
1071                 re.indexIn(geometry_arg);
1072                 w = re.cap(1).toInt();
1073                 h = re.cap(2).toInt();
1074                 x = re.cap(3).toInt();
1075                 y = re.cap(4).toInt();
1076                 view->setGeometry(x, y, w, h);
1077 #endif
1078         }
1079         view->setFocus();
1080 }
1081
1082
1083 Clipboard & GuiApplication::clipboard()
1084 {
1085         return d->clipboard_;
1086 }
1087
1088
1089 Selection & GuiApplication::selection()
1090 {
1091         return d->selection_;
1092 }
1093
1094
1095 FontLoader & GuiApplication::fontLoader() 
1096 {
1097         return d->font_loader_;
1098 }
1099
1100
1101 Toolbars const & GuiApplication::toolbars() const 
1102 {
1103         return d->toolbars_;
1104 }
1105
1106
1107 Toolbars & GuiApplication::toolbars()
1108 {
1109         return d->toolbars_; 
1110 }
1111
1112
1113 Menus const & GuiApplication::menus() const 
1114 {
1115         return d->menus_;
1116 }
1117
1118
1119 Menus & GuiApplication::menus()
1120 {
1121         return d->menus_; 
1122 }
1123
1124
1125 QList<int> GuiApplication::viewIds() const
1126 {
1127         return d->views_.keys();
1128 }
1129
1130
1131 ColorCache & GuiApplication::colorCache()
1132 {
1133         return d->color_cache_;
1134 }
1135
1136
1137 int GuiApplication::exec()
1138 {
1139         // asynchronously handle batch commands. This event will be in
1140         // the event queue in front of other asynchronous events. Hence,
1141         // we can assume in the latter that the gui is setup already.
1142         QTimer::singleShot(0, this, SLOT(execBatchCommands()));
1143
1144         return QApplication::exec();
1145 }
1146
1147
1148 void GuiApplication::exit(int status)
1149 {
1150         QApplication::exit(status);
1151 }
1152
1153
1154 void GuiApplication::setGuiLanguage()
1155 {
1156         // Set the language defined by the user.
1157         setRcGuiLanguage();
1158
1159         QString const default_language = toqstr(Messages::defaultLanguage());
1160         LYXERR(Debug::LOCALE, "Tring to set default locale to: " << default_language);
1161         QLocale const default_locale(default_language);
1162         QLocale::setDefault(default_locale);
1163
1164         // install translation file for Qt built-in dialogs
1165         QString const language_name = QString("qt_") + default_locale.name();
1166
1167         // language_name can be short (e.g. qt_zh) or long (e.g. qt_zh_CN). 
1168         // Short-named translator can be loaded from a long name, but not the
1169         // opposite. Therefore, long name should be used without truncation.
1170         // c.f. http://doc.trolltech.com/4.1/qtranslator.html#load
1171         if (!d->qt_trans_.load(language_name,
1172                         QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
1173                 LYXERR(Debug::LOCALE, "Could not find  Qt translations for locale "
1174                         << language_name);
1175         } else {
1176                 LYXERR(Debug::LOCALE, "Successfully installed Qt translations for locale "
1177                         << language_name);
1178         }
1179
1180         switch (default_locale.language()) {
1181         case QLocale::Arabic :
1182         case QLocale::Hebrew :
1183         case QLocale::Persian :
1184         case QLocale::Urdu :
1185         setLayoutDirection(Qt::RightToLeft);
1186                 break;
1187         default:
1188         setLayoutDirection(Qt::LeftToRight);
1189         }
1190 }
1191
1192
1193 void GuiApplication::processFuncRequestQueue()
1194 {
1195         while (!d->func_request_queue_.empty()) {
1196                 lyx::dispatch(d->func_request_queue_.back());
1197                 d->func_request_queue_.pop();
1198         }
1199 }
1200
1201
1202 void GuiApplication::execBatchCommands()
1203 {
1204         setGuiLanguage();
1205
1206         // Read menus
1207         if (!readUIFile(toqstr(lyxrc.ui_file)))
1208                 // Gives some error box here.
1209                 return;
1210
1211 #ifdef Q_WS_MACX
1212         // Create the global default menubar which is shown for the dialogs
1213         // and if no GuiView is visible.
1214         // This must be done after the session was recovered to know the "last files".
1215         d->global_menubar_ = new GlobalMenuBar();
1216         d->menus_.fillMenuBar(d->global_menubar_, 0, true);
1217 #endif
1218
1219         lyx::execBatchCommands();
1220 }
1221
1222
1223 QAbstractItemModel * GuiApplication::languageModel()
1224 {
1225         if (d->language_model_)
1226                 return d->language_model_;
1227
1228         QStandardItemModel * lang_model = new QStandardItemModel(this);
1229         lang_model->insertColumns(0, 1);
1230         int current_row;
1231         Languages::const_iterator it = languages.begin();
1232         Languages::const_iterator end = languages.end();
1233         for (; it != end; ++it) {
1234                 current_row = lang_model->rowCount();
1235                 lang_model->insertRows(current_row, 1);
1236                 QModelIndex item = lang_model->index(current_row, 0);
1237                 lang_model->setData(item, qt_(it->second.display()), Qt::DisplayRole);
1238                 lang_model->setData(item, toqstr(it->second.lang()), Qt::UserRole);
1239         }
1240         d->language_model_ = new QSortFilterProxyModel(this);
1241         d->language_model_->setSourceModel(lang_model);
1242 #if QT_VERSION >= 0x040300
1243         d->language_model_->setSortLocaleAware(true);
1244 #endif
1245         return d->language_model_;
1246 }
1247
1248
1249 void GuiApplication::restoreGuiSession()
1250 {
1251         if (!lyxrc.load_session)
1252                 return;
1253
1254         Session & session = theSession();
1255         LastOpenedSection::LastOpened const & lastopened = 
1256                 session.lastOpened().getfiles();
1257
1258         FileName active_file;
1259         // do not add to the lastfile list since these files are restored from
1260         // last session, and should be already there (regular files), or should
1261         // not be added at all (help files).
1262         for (size_t i = 0; i < lastopened.size(); ++i) {
1263                 FileName const & file_name = lastopened[i].file_name;
1264                 if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs
1265                           && current_view_->documentBufferView() != 0)) {
1266                         boost::crc_32_type crc;
1267                         string const & fname = file_name.absFilename();
1268                         crc = for_each(fname.begin(), fname.end(), crc);
1269                         createView(crc.checksum());
1270                 }
1271                 current_view_->loadDocument(file_name, false);
1272
1273                 if (lastopened[i].active)
1274                         active_file = file_name;
1275         }
1276
1277         // Restore last active buffer
1278         Buffer * buffer = theBufferList().getBuffer(active_file);
1279         if (buffer)
1280                 current_view_->setBuffer(buffer);
1281
1282         // clear this list to save a few bytes of RAM
1283         session.lastOpened().clear();
1284 }
1285
1286
1287 QString const GuiApplication::romanFontName()
1288 {
1289         QFont font;
1290         font.setKerning(false);
1291         font.setStyleHint(QFont::Serif);
1292         font.setFamily("serif");
1293
1294         return QFontInfo(font).family();
1295 }
1296
1297
1298 QString const GuiApplication::sansFontName()
1299 {
1300         QFont font;
1301         font.setKerning(false);
1302         font.setStyleHint(QFont::SansSerif);
1303         font.setFamily("sans");
1304
1305         return QFontInfo(font).family();
1306 }
1307
1308
1309 QString const GuiApplication::typewriterFontName()
1310 {
1311         QFont font;
1312         font.setKerning(false);
1313         font.setStyleHint(QFont::TypeWriter);
1314         font.setFamily("monospace");
1315
1316         return QFontInfo(font).family();
1317 }
1318
1319
1320 void GuiApplication::handleRegularEvents()
1321 {
1322         ForkedCallsController::handleCompletedProcesses();
1323 }
1324
1325
1326 bool GuiApplication::event(QEvent * e)
1327 {
1328         switch(e->type()) {
1329         case QEvent::FileOpen: {
1330                 // Open a file; this happens only on Mac OS X for now.
1331                 //
1332                 // We do this asynchronously because on startup the batch
1333                 // commands are not executed here yet and the gui is not ready
1334                 // therefore.
1335                 QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
1336                 dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file())));
1337                 e->accept();
1338                 return true;
1339         }
1340         default:
1341                 return QApplication::event(e);
1342         }
1343 }
1344
1345
1346 bool GuiApplication::notify(QObject * receiver, QEvent * event)
1347 {
1348         try {
1349                 return QApplication::notify(receiver, event);
1350         }
1351         catch (ExceptionMessage const & e) {
1352                 switch(e.type_) { 
1353                 case ErrorException:
1354                         emergencyCleanup();
1355                         setQuitOnLastWindowClosed(false);
1356                         closeAllViews();
1357                         Alert::error(e.title_, e.details_);
1358 #ifndef NDEBUG
1359                         // Properly crash in debug mode in order to get a useful backtrace.
1360                         abort();
1361 #endif
1362                         // In release mode, try to exit gracefully.
1363                         this->exit(1);
1364
1365                 case BufferException: {
1366                         if (!current_view_->documentBufferView())
1367                                 return false;
1368                         Buffer * buf = &current_view_->documentBufferView()->buffer();
1369                         docstring details = e.details_ + '\n';
1370                         details += buf->emergencyWrite();
1371                         theBufferList().release(buf);
1372                         details += "\n" + _("The current document was closed.");
1373                         Alert::error(e.title_, details);
1374                         return false;
1375                 }
1376                 case WarningException:
1377                         Alert::warning(e.title_, e.details_);
1378                         return false;
1379                 }
1380         }
1381         catch (exception const & e) {
1382                 docstring s = _("LyX has caught an exception, it will now "
1383                         "attempt to save all unsaved documents and exit."
1384                         "\n\nException: ");
1385                 s += from_ascii(e.what());
1386                 Alert::error(_("Software exception Detected"), s);
1387                 lyx_exit(1);
1388         }
1389         catch (...) {
1390                 docstring s = _("LyX has caught some really weird exception, it will "
1391                         "now attempt to save all unsaved documents and exit.");
1392                 Alert::error(_("Software exception Detected"), s);
1393                 lyx_exit(1);
1394         }
1395
1396         return false;
1397 }
1398
1399
1400 bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
1401 {
1402         QColor const & qcol = d->color_cache_.get(col);
1403         if (!qcol.isValid()) {
1404                 rgbcol.r = 0;
1405                 rgbcol.g = 0;
1406                 rgbcol.b = 0;
1407                 return false;
1408         }
1409         rgbcol.r = qcol.red();
1410         rgbcol.g = qcol.green();
1411         rgbcol.b = qcol.blue();
1412         return true;
1413 }
1414
1415
1416 string const GuiApplication::hexName(ColorCode col)
1417 {
1418         return ltrim(fromqstr(d->color_cache_.get(col).name()), "#");
1419 }
1420
1421
1422 void GuiApplication::registerSocketCallback(int fd, SocketCallback func)
1423 {
1424         SocketNotifier * sn = new SocketNotifier(this, fd, func);
1425         d->socket_notifiers_[fd] = sn;
1426         connect(sn, SIGNAL(activated(int)), this, SLOT(socketDataReceived(int)));
1427 }
1428
1429
1430 void GuiApplication::socketDataReceived(int fd)
1431 {
1432         d->socket_notifiers_[fd]->func_();
1433 }
1434
1435
1436 void GuiApplication::unregisterSocketCallback(int fd)
1437 {
1438         d->socket_notifiers_.take(fd)->setEnabled(false);
1439 }
1440
1441
1442 void GuiApplication::commitData(QSessionManager & sm)
1443 {
1444         /// The implementation is required to avoid an application exit
1445         /// when session state save is triggered by session manager.
1446         /// The default implementation sends a close event to all
1447         /// visible top level widgets when session managment allows
1448         /// interaction.
1449         /// We are changing that to close all wiew one by one.
1450         /// FIXME: verify if the default implementation is enough now.
1451         if (sm.allowsInteraction() && !closeAllViews())
1452                 sm.cancel();
1453 }
1454
1455
1456 void GuiApplication::unregisterView(GuiView * gv)
1457 {
1458         LASSERT(d->views_[gv->id()] == gv, /**/);
1459         d->views_.remove(gv->id());
1460         if (current_view_ == gv)
1461                 current_view_ = 0;
1462 }
1463
1464
1465 bool GuiApplication::closeAllViews()
1466 {
1467         if (d->views_.empty())
1468                 return true;
1469
1470         // When a view/window was closed before without quitting LyX, there
1471         // are already entries in the lastOpened list.
1472         theSession().lastOpened().clear();
1473
1474         QList<GuiView *> views = d->views_.values();
1475         foreach (GuiView * view, views) {
1476                 if (!view->close())
1477                         return false;
1478         }
1479
1480         d->views_.clear();
1481         return true;
1482 }
1483
1484
1485 GuiView & GuiApplication::view(int id) const
1486 {
1487         LASSERT(d->views_.contains(id), /**/);
1488         return *d->views_.value(id);
1489 }
1490
1491
1492 void GuiApplication::hideDialogs(string const & name, Inset * inset) const
1493 {
1494         QList<GuiView *> views = d->views_.values();
1495         foreach (GuiView * view, views)
1496                 view->hideDialog(name, inset);
1497 }
1498
1499
1500 Buffer const * GuiApplication::updateInset(Inset const * inset) const
1501 {
1502         Buffer const * buffer_ = 0;
1503         QHash<int, GuiView *>::iterator end = d->views_.end();
1504         for (QHash<int, GuiView *>::iterator it = d->views_.begin(); it != end; ++it) {
1505                 if (Buffer const * ptr = (*it)->updateInset(inset))
1506                         buffer_ = ptr;
1507         }
1508         return buffer_;
1509 }
1510
1511
1512 bool GuiApplication::searchMenu(FuncRequest const & func,
1513         docstring_list & names) const
1514 {
1515         return d->menus_.searchMenu(func, names);
1516 }
1517
1518
1519 bool GuiApplication::readUIFile(QString const & name, bool include)
1520 {
1521         LYXERR(Debug::INIT, "About to read " << name << "...");
1522
1523         FileName ui_path;
1524         if (include) {
1525                 ui_path = libFileSearch("ui", name, "inc");
1526                 if (ui_path.empty())
1527                         ui_path = libFileSearch("ui", changeExtension(name, "inc"));
1528         } else {
1529                 ui_path = libFileSearch("ui", name, "ui");
1530         }
1531
1532         if (ui_path.empty()) {
1533                 static const QString defaultUIFile = "default";
1534                 LYXERR(Debug::INIT, "Could not find " << name);
1535                 if (include) {
1536                         Alert::warning(_("Could not find UI definition file"),
1537                                 bformat(_("Error while reading the included file\n%1$s\n"
1538                                         "Please check your installation."), qstring_to_ucs4(name)));
1539                         return false;
1540                 }
1541                 if (name == defaultUIFile) {
1542                         LYXERR(Debug::INIT, "Could not find default UI file!!");
1543                         Alert::warning(_("Could not find default UI file"),
1544                                 _("LyX could not find the default UI file!\n"
1545                                   "Please check your installation."));
1546                         return false;
1547                 }
1548                 Alert::warning(_("Could not find UI definition file"),
1549                 bformat(_("Error while reading the configuration file\n%1$s\n"
1550                         "Falling back to default.\n"
1551                         "Please look under Tools>Preferences>User Interface and\n"
1552                         "check which User Interface file you are using."), qstring_to_ucs4(name)));
1553                 return readUIFile(defaultUIFile, false);
1554         }
1555
1556         // Ensure that a file is read only once (prevents include loops)
1557         static QStringList uifiles;
1558         QString const uifile = toqstr(ui_path.absFilename());
1559         if (uifiles.contains(uifile)) {
1560                 if (!include) {
1561                         // We are reading again the top uifile so reset the safeguard:
1562                         uifiles.clear();
1563                         d->menus_.reset();
1564                         d->toolbars_.reset();
1565                 } else {
1566                         LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1567                                 << "Is this an include loop?");
1568                         return false;
1569                 }
1570         }
1571         uifiles.push_back(uifile);
1572
1573         LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1574
1575         enum {
1576                 ui_menuset = 1,
1577                 ui_toolbars,
1578                 ui_toolbarset,
1579                 ui_include,
1580                 ui_last
1581         };
1582
1583         LexerKeyword uitags[] = {
1584                 { "include", ui_include },
1585                 { "menuset", ui_menuset },
1586                 { "toolbars", ui_toolbars },
1587                 { "toolbarset", ui_toolbarset }
1588         };
1589
1590         Lexer lex(uitags);
1591         lex.setFile(ui_path);
1592         if (!lex.isOK()) {
1593                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1594                        << endl;
1595         }
1596
1597         if (lyxerr.debugging(Debug::PARSER))
1598                 lex.printTable(lyxerr);
1599
1600         // store which ui files define Toolbars
1601         static QStringList toolbar_uifiles;
1602
1603         while (lex.isOK()) {
1604                 switch (lex.lex()) {
1605                 case ui_include: {
1606                         lex.next(true);
1607                         QString const file = toqstr(lex.getString());
1608                         if (!readUIFile(file, true))
1609                                 return false;
1610                         break;
1611                 }
1612                 case ui_menuset:
1613                         d->menus_.read(lex);
1614                         break;
1615
1616                 case ui_toolbarset:
1617                         d->toolbars_.readToolbars(lex);
1618                         break;
1619
1620                 case ui_toolbars:
1621                         d->toolbars_.readToolbarSettings(lex);
1622                         toolbar_uifiles.push_back(uifile);
1623                         break;
1624
1625                 default:
1626                         if (!rtrim(lex.getString()).empty())
1627                                 lex.printError("LyX::ReadUIFile: "
1628                                                "Unknown menu tag: `$$Token'");
1629                         break;
1630                 }
1631         }
1632
1633         if (include)
1634                 return true;
1635
1636         QSettings settings;
1637         settings.beginGroup("ui_files");
1638         bool touched = false;
1639         for (int i = 0; i != uifiles.size(); ++i) {
1640                 QFileInfo fi(uifiles[i]);
1641                 QDateTime const date_value = fi.lastModified();
1642                 QString const name_key = QString::number(i);
1643                 // if an ui file which defines Toolbars has changed,
1644                 // we have to reset the settings
1645                 if (toolbar_uifiles.contains(uifiles[i])
1646                  && (!settings.contains(name_key)
1647                  || settings.value(name_key).toString() != uifiles[i]
1648                  || settings.value(name_key + "/date").toDateTime() != date_value)) {
1649                         touched = true;
1650                         settings.setValue(name_key, uifiles[i]);
1651                         settings.setValue(name_key + "/date", date_value);
1652                 }
1653         }
1654         settings.endGroup();
1655         if (touched)
1656                 settings.remove("views");
1657
1658         return true;
1659 }
1660
1661
1662 void GuiApplication::onLastWindowClosed()
1663 {
1664         if (d->global_menubar_)
1665                 d->global_menubar_->grabKeyboard();
1666 }
1667
1668
1669 ////////////////////////////////////////////////////////////////////////
1670 //
1671 // X11 specific stuff goes here...
1672
1673 #ifdef Q_WS_X11
1674 bool GuiApplication::x11EventFilter(XEvent * xev)
1675 {
1676         if (!current_view_)
1677                 return false;
1678
1679         switch (xev->type) {
1680         case SelectionRequest: {
1681                 if (xev->xselectionrequest.selection != XA_PRIMARY)
1682                         break;
1683                 LYXERR(Debug::SELECTION, "X requested selection.");
1684                 BufferView * bv = current_view_->currentBufferView();
1685                 if (bv) {
1686                         docstring const sel = bv->requestSelection();
1687                         if (!sel.empty())
1688                                 d->selection_.put(sel);
1689                 }
1690                 break;
1691         }
1692         case SelectionClear: {
1693                 if (xev->xselectionclear.selection != XA_PRIMARY)
1694                         break;
1695                 LYXERR(Debug::SELECTION, "Lost selection.");
1696                 BufferView * bv = current_view_->currentBufferView();
1697                 if (bv)
1698                         bv->clearSelection();
1699                 break;
1700         }
1701         }
1702         return false;
1703 }
1704 #endif
1705
1706 } // namespace frontend
1707
1708
1709 void hideDialogs(std::string const & name, Inset * inset)
1710 {
1711         if (theApp())
1712                 theApp()->hideDialogs(name, inset);
1713 }
1714
1715
1716 ////////////////////////////////////////////////////////////////////
1717 //
1718 // Font stuff
1719 //
1720 ////////////////////////////////////////////////////////////////////
1721
1722 frontend::FontLoader & theFontLoader()
1723 {
1724         LASSERT(frontend::guiApp, /**/);
1725         return frontend::guiApp->fontLoader();
1726 }
1727
1728
1729 frontend::FontMetrics const & theFontMetrics(Font const & f)
1730 {
1731         return theFontMetrics(f.fontInfo());
1732 }
1733
1734
1735 frontend::FontMetrics const & theFontMetrics(FontInfo const & f)
1736 {
1737         LASSERT(frontend::guiApp, /**/);
1738         return frontend::guiApp->fontLoader().metrics(f);
1739 }
1740
1741
1742 ////////////////////////////////////////////////////////////////////
1743 //
1744 // Misc stuff
1745 //
1746 ////////////////////////////////////////////////////////////////////
1747
1748 frontend::Clipboard & theClipboard()
1749 {
1750         LASSERT(frontend::guiApp, /**/);
1751         return frontend::guiApp->clipboard();
1752 }
1753
1754
1755 frontend::Selection & theSelection()
1756 {
1757         LASSERT(frontend::guiApp, /**/);
1758         return frontend::guiApp->selection();
1759 }
1760
1761
1762 } // namespace lyx
1763
1764 #include "moc_GuiApplication.cpp"