]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiApplication.cpp
Fix crash noticed by Bennett:
[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_ && activeWindow() != current_view_)
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                 setCurrentView(gv);
1030                 gv->setLayoutDirection(layoutDirection());
1031                 gv->resetDialogs();
1032         }
1033
1034         dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
1035 }
1036
1037
1038 void GuiApplication::createView(int view_id)
1039 {
1040         createView(QString(), true, view_id);
1041 }
1042
1043
1044 void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
1045         int view_id)
1046 {
1047         // release the keyboard which might have been grabed by the global
1048         // menubar on Mac to catch shortcuts even without any GuiView.
1049         if (d->global_menubar_)
1050                 d->global_menubar_->releaseKeyboard();
1051
1052         // create new view
1053         int id = view_id;
1054         while (d->views_.find(id) != d->views_.end())
1055                 id++;
1056
1057         LYXERR(Debug::GUI, "About to create new window with ID " << id);
1058         GuiView * view = new GuiView(id);
1059         // register view
1060         d->views_[id] = view;
1061
1062         if (autoShow) {
1063                 view->show();
1064                 setActiveWindow(view);
1065         }
1066
1067         if (!geometry_arg.isEmpty()) {
1068 #ifdef Q_WS_WIN
1069                 int x, y;
1070                 int w, h;
1071                 QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)([+-][0-9]*)){0,1}" );
1072                 re.indexIn(geometry_arg);
1073                 w = re.cap(1).toInt();
1074                 h = re.cap(2).toInt();
1075                 x = re.cap(3).toInt();
1076                 y = re.cap(4).toInt();
1077                 view->setGeometry(x, y, w, h);
1078 #endif
1079         }
1080         view->setFocus();
1081 }
1082
1083
1084 Clipboard & GuiApplication::clipboard()
1085 {
1086         return d->clipboard_;
1087 }
1088
1089
1090 Selection & GuiApplication::selection()
1091 {
1092         return d->selection_;
1093 }
1094
1095
1096 FontLoader & GuiApplication::fontLoader() 
1097 {
1098         return d->font_loader_;
1099 }
1100
1101
1102 Toolbars const & GuiApplication::toolbars() const 
1103 {
1104         return d->toolbars_;
1105 }
1106
1107
1108 Toolbars & GuiApplication::toolbars()
1109 {
1110         return d->toolbars_; 
1111 }
1112
1113
1114 Menus const & GuiApplication::menus() const 
1115 {
1116         return d->menus_;
1117 }
1118
1119
1120 Menus & GuiApplication::menus()
1121 {
1122         return d->menus_; 
1123 }
1124
1125
1126 QList<int> GuiApplication::viewIds() const
1127 {
1128         return d->views_.keys();
1129 }
1130
1131
1132 ColorCache & GuiApplication::colorCache()
1133 {
1134         return d->color_cache_;
1135 }
1136
1137
1138 int GuiApplication::exec()
1139 {
1140         // asynchronously handle batch commands. This event will be in
1141         // the event queue in front of other asynchronous events. Hence,
1142         // we can assume in the latter that the gui is setup already.
1143         QTimer::singleShot(0, this, SLOT(execBatchCommands()));
1144
1145         return QApplication::exec();
1146 }
1147
1148
1149 void GuiApplication::exit(int status)
1150 {
1151         QApplication::exit(status);
1152 }
1153
1154
1155 void GuiApplication::setGuiLanguage()
1156 {
1157         // Set the language defined by the user.
1158         setRcGuiLanguage();
1159
1160         QString const default_language = toqstr(Messages::defaultLanguage());
1161         LYXERR(Debug::LOCALE, "Tring to set default locale to: " << default_language);
1162         QLocale const default_locale(default_language);
1163         QLocale::setDefault(default_locale);
1164
1165         // install translation file for Qt built-in dialogs
1166         QString const language_name = QString("qt_") + default_locale.name();
1167
1168         // language_name can be short (e.g. qt_zh) or long (e.g. qt_zh_CN). 
1169         // Short-named translator can be loaded from a long name, but not the
1170         // opposite. Therefore, long name should be used without truncation.
1171         // c.f. http://doc.trolltech.com/4.1/qtranslator.html#load
1172         if (!d->qt_trans_.load(language_name,
1173                         QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
1174                 LYXERR(Debug::LOCALE, "Could not find  Qt translations for locale "
1175                         << language_name);
1176         } else {
1177                 LYXERR(Debug::LOCALE, "Successfully installed Qt translations for locale "
1178                         << language_name);
1179         }
1180
1181         switch (default_locale.language()) {
1182         case QLocale::Arabic :
1183         case QLocale::Hebrew :
1184         case QLocale::Persian :
1185         case QLocale::Urdu :
1186         setLayoutDirection(Qt::RightToLeft);
1187                 break;
1188         default:
1189         setLayoutDirection(Qt::LeftToRight);
1190         }
1191 }
1192
1193
1194 void GuiApplication::processFuncRequestQueue()
1195 {
1196         while (!d->func_request_queue_.empty()) {
1197                 lyx::dispatch(d->func_request_queue_.back());
1198                 d->func_request_queue_.pop();
1199         }
1200 }
1201
1202
1203 void GuiApplication::execBatchCommands()
1204 {
1205         setGuiLanguage();
1206
1207         // Read menus
1208         if (!readUIFile(toqstr(lyxrc.ui_file)))
1209                 // Gives some error box here.
1210                 return;
1211
1212 #ifdef Q_WS_MACX
1213         // Create the global default menubar which is shown for the dialogs
1214         // and if no GuiView is visible.
1215         // This must be done after the session was recovered to know the "last files".
1216         d->global_menubar_ = new GlobalMenuBar();
1217         d->menus_.fillMenuBar(d->global_menubar_, 0, true);
1218 #endif
1219
1220         lyx::execBatchCommands();
1221 }
1222
1223
1224 QAbstractItemModel * GuiApplication::languageModel()
1225 {
1226         if (d->language_model_)
1227                 return d->language_model_;
1228
1229         QStandardItemModel * lang_model = new QStandardItemModel(this);
1230         lang_model->insertColumns(0, 1);
1231         int current_row;
1232         Languages::const_iterator it = languages.begin();
1233         Languages::const_iterator end = languages.end();
1234         for (; it != end; ++it) {
1235                 current_row = lang_model->rowCount();
1236                 lang_model->insertRows(current_row, 1);
1237                 QModelIndex item = lang_model->index(current_row, 0);
1238                 lang_model->setData(item, qt_(it->second.display()), Qt::DisplayRole);
1239                 lang_model->setData(item, toqstr(it->second.lang()), Qt::UserRole);
1240         }
1241         d->language_model_ = new QSortFilterProxyModel(this);
1242         d->language_model_->setSourceModel(lang_model);
1243 #if QT_VERSION >= 0x040300
1244         d->language_model_->setSortLocaleAware(true);
1245 #endif
1246         return d->language_model_;
1247 }
1248
1249
1250 void GuiApplication::restoreGuiSession()
1251 {
1252         if (!lyxrc.load_session)
1253                 return;
1254
1255         Session & session = theSession();
1256         LastOpenedSection::LastOpened const & lastopened = 
1257                 session.lastOpened().getfiles();
1258
1259         FileName active_file;
1260         // do not add to the lastfile list since these files are restored from
1261         // last session, and should be already there (regular files), or should
1262         // not be added at all (help files).
1263         for (size_t i = 0; i < lastopened.size(); ++i) {
1264                 FileName const & file_name = lastopened[i].file_name;
1265                 if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs
1266                           && current_view_->documentBufferView() != 0)) {
1267                         boost::crc_32_type crc;
1268                         string const & fname = file_name.absFilename();
1269                         crc = for_each(fname.begin(), fname.end(), crc);
1270                         createView(crc.checksum());
1271                 }
1272                 current_view_->loadDocument(file_name, false);
1273
1274                 if (lastopened[i].active)
1275                         active_file = file_name;
1276         }
1277
1278         // Restore last active buffer
1279         Buffer * buffer = theBufferList().getBuffer(active_file);
1280         if (buffer)
1281                 current_view_->setBuffer(buffer);
1282
1283         // clear this list to save a few bytes of RAM
1284         session.lastOpened().clear();
1285 }
1286
1287
1288 QString const GuiApplication::romanFontName()
1289 {
1290         QFont font;
1291         font.setKerning(false);
1292         font.setStyleHint(QFont::Serif);
1293         font.setFamily("serif");
1294
1295         return QFontInfo(font).family();
1296 }
1297
1298
1299 QString const GuiApplication::sansFontName()
1300 {
1301         QFont font;
1302         font.setKerning(false);
1303         font.setStyleHint(QFont::SansSerif);
1304         font.setFamily("sans");
1305
1306         return QFontInfo(font).family();
1307 }
1308
1309
1310 QString const GuiApplication::typewriterFontName()
1311 {
1312         QFont font;
1313         font.setKerning(false);
1314         font.setStyleHint(QFont::TypeWriter);
1315         font.setFamily("monospace");
1316
1317         return QFontInfo(font).family();
1318 }
1319
1320
1321 void GuiApplication::handleRegularEvents()
1322 {
1323         ForkedCallsController::handleCompletedProcesses();
1324 }
1325
1326
1327 bool GuiApplication::event(QEvent * e)
1328 {
1329         switch(e->type()) {
1330         case QEvent::FileOpen: {
1331                 // Open a file; this happens only on Mac OS X for now.
1332                 //
1333                 // We do this asynchronously because on startup the batch
1334                 // commands are not executed here yet and the gui is not ready
1335                 // therefore.
1336                 QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
1337                 dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file())));
1338                 e->accept();
1339                 return true;
1340         }
1341         default:
1342                 return QApplication::event(e);
1343         }
1344 }
1345
1346
1347 bool GuiApplication::notify(QObject * receiver, QEvent * event)
1348 {
1349         try {
1350                 return QApplication::notify(receiver, event);
1351         }
1352         catch (ExceptionMessage const & e) {
1353                 switch(e.type_) { 
1354                 case ErrorException:
1355                         emergencyCleanup();
1356                         setQuitOnLastWindowClosed(false);
1357                         closeAllViews();
1358                         Alert::error(e.title_, e.details_);
1359 #ifndef NDEBUG
1360                         // Properly crash in debug mode in order to get a useful backtrace.
1361                         abort();
1362 #endif
1363                         // In release mode, try to exit gracefully.
1364                         this->exit(1);
1365
1366                 case BufferException: {
1367                         if (!current_view_->documentBufferView())
1368                                 return false;
1369                         Buffer * buf = &current_view_->documentBufferView()->buffer();
1370                         docstring details = e.details_ + '\n';
1371                         details += buf->emergencyWrite();
1372                         theBufferList().release(buf);
1373                         details += "\n" + _("The current document was closed.");
1374                         Alert::error(e.title_, details);
1375                         return false;
1376                 }
1377                 case WarningException:
1378                         Alert::warning(e.title_, e.details_);
1379                         return false;
1380                 }
1381         }
1382         catch (exception const & e) {
1383                 docstring s = _("LyX has caught an exception, it will now "
1384                         "attempt to save all unsaved documents and exit."
1385                         "\n\nException: ");
1386                 s += from_ascii(e.what());
1387                 Alert::error(_("Software exception Detected"), s);
1388                 lyx_exit(1);
1389         }
1390         catch (...) {
1391                 docstring s = _("LyX has caught some really weird exception, it will "
1392                         "now attempt to save all unsaved documents and exit.");
1393                 Alert::error(_("Software exception Detected"), s);
1394                 lyx_exit(1);
1395         }
1396
1397         return false;
1398 }
1399
1400
1401 bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
1402 {
1403         QColor const & qcol = d->color_cache_.get(col);
1404         if (!qcol.isValid()) {
1405                 rgbcol.r = 0;
1406                 rgbcol.g = 0;
1407                 rgbcol.b = 0;
1408                 return false;
1409         }
1410         rgbcol.r = qcol.red();
1411         rgbcol.g = qcol.green();
1412         rgbcol.b = qcol.blue();
1413         return true;
1414 }
1415
1416
1417 string const GuiApplication::hexName(ColorCode col)
1418 {
1419         return ltrim(fromqstr(d->color_cache_.get(col).name()), "#");
1420 }
1421
1422
1423 void GuiApplication::registerSocketCallback(int fd, SocketCallback func)
1424 {
1425         SocketNotifier * sn = new SocketNotifier(this, fd, func);
1426         d->socket_notifiers_[fd] = sn;
1427         connect(sn, SIGNAL(activated(int)), this, SLOT(socketDataReceived(int)));
1428 }
1429
1430
1431 void GuiApplication::socketDataReceived(int fd)
1432 {
1433         d->socket_notifiers_[fd]->func_();
1434 }
1435
1436
1437 void GuiApplication::unregisterSocketCallback(int fd)
1438 {
1439         d->socket_notifiers_.take(fd)->setEnabled(false);
1440 }
1441
1442
1443 void GuiApplication::commitData(QSessionManager & sm)
1444 {
1445         /// The implementation is required to avoid an application exit
1446         /// when session state save is triggered by session manager.
1447         /// The default implementation sends a close event to all
1448         /// visible top level widgets when session managment allows
1449         /// interaction.
1450         /// We are changing that to close all wiew one by one.
1451         /// FIXME: verify if the default implementation is enough now.
1452         if (sm.allowsInteraction() && !closeAllViews())
1453                 sm.cancel();
1454 }
1455
1456
1457 void GuiApplication::unregisterView(GuiView * gv)
1458 {
1459         LASSERT(d->views_[gv->id()] == gv, /**/);
1460         d->views_.remove(gv->id());
1461         if (current_view_ == gv)
1462                 current_view_ = 0;
1463 }
1464
1465
1466 bool GuiApplication::closeAllViews()
1467 {
1468         if (d->views_.empty())
1469                 return true;
1470
1471         // When a view/window was closed before without quitting LyX, there
1472         // are already entries in the lastOpened list.
1473         theSession().lastOpened().clear();
1474
1475         QList<GuiView *> views = d->views_.values();
1476         foreach (GuiView * view, views) {
1477                 if (!view->close())
1478                         return false;
1479         }
1480
1481         d->views_.clear();
1482         return true;
1483 }
1484
1485
1486 GuiView & GuiApplication::view(int id) const
1487 {
1488         LASSERT(d->views_.contains(id), /**/);
1489         return *d->views_.value(id);
1490 }
1491
1492
1493 void GuiApplication::hideDialogs(string const & name, Inset * inset) const
1494 {
1495         QList<GuiView *> views = d->views_.values();
1496         foreach (GuiView * view, views)
1497                 view->hideDialog(name, inset);
1498 }
1499
1500
1501 Buffer const * GuiApplication::updateInset(Inset const * inset) const
1502 {
1503         Buffer const * buffer_ = 0;
1504         QHash<int, GuiView *>::iterator end = d->views_.end();
1505         for (QHash<int, GuiView *>::iterator it = d->views_.begin(); it != end; ++it) {
1506                 if (Buffer const * ptr = (*it)->updateInset(inset))
1507                         buffer_ = ptr;
1508         }
1509         return buffer_;
1510 }
1511
1512
1513 bool GuiApplication::searchMenu(FuncRequest const & func,
1514         docstring_list & names) const
1515 {
1516         return d->menus_.searchMenu(func, names);
1517 }
1518
1519
1520 bool GuiApplication::readUIFile(QString const & name, bool include)
1521 {
1522         LYXERR(Debug::INIT, "About to read " << name << "...");
1523
1524         FileName ui_path;
1525         if (include) {
1526                 ui_path = libFileSearch("ui", name, "inc");
1527                 if (ui_path.empty())
1528                         ui_path = libFileSearch("ui", changeExtension(name, "inc"));
1529         } else {
1530                 ui_path = libFileSearch("ui", name, "ui");
1531         }
1532
1533         if (ui_path.empty()) {
1534                 static const QString defaultUIFile = "default";
1535                 LYXERR(Debug::INIT, "Could not find " << name);
1536                 if (include) {
1537                         Alert::warning(_("Could not find UI definition file"),
1538                                 bformat(_("Error while reading the included file\n%1$s\n"
1539                                         "Please check your installation."), qstring_to_ucs4(name)));
1540                         return false;
1541                 }
1542                 if (name == defaultUIFile) {
1543                         LYXERR(Debug::INIT, "Could not find default UI file!!");
1544                         Alert::warning(_("Could not find default UI file"),
1545                                 _("LyX could not find the default UI file!\n"
1546                                   "Please check your installation."));
1547                         return false;
1548                 }
1549                 Alert::warning(_("Could not find UI definition file"),
1550                 bformat(_("Error while reading the configuration file\n%1$s\n"
1551                         "Falling back to default.\n"
1552                         "Please look under Tools>Preferences>User Interface and\n"
1553                         "check which User Interface file you are using."), qstring_to_ucs4(name)));
1554                 return readUIFile(defaultUIFile, false);
1555         }
1556
1557         // Ensure that a file is read only once (prevents include loops)
1558         static QStringList uifiles;
1559         QString const uifile = toqstr(ui_path.absFilename());
1560         if (uifiles.contains(uifile)) {
1561                 if (!include) {
1562                         // We are reading again the top uifile so reset the safeguard:
1563                         uifiles.clear();
1564                         d->menus_.reset();
1565                         d->toolbars_.reset();
1566                 } else {
1567                         LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1568                                 << "Is this an include loop?");
1569                         return false;
1570                 }
1571         }
1572         uifiles.push_back(uifile);
1573
1574         LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1575
1576         enum {
1577                 ui_menuset = 1,
1578                 ui_toolbars,
1579                 ui_toolbarset,
1580                 ui_include,
1581                 ui_last
1582         };
1583
1584         LexerKeyword uitags[] = {
1585                 { "include", ui_include },
1586                 { "menuset", ui_menuset },
1587                 { "toolbars", ui_toolbars },
1588                 { "toolbarset", ui_toolbarset }
1589         };
1590
1591         Lexer lex(uitags);
1592         lex.setFile(ui_path);
1593         if (!lex.isOK()) {
1594                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1595                        << endl;
1596         }
1597
1598         if (lyxerr.debugging(Debug::PARSER))
1599                 lex.printTable(lyxerr);
1600
1601         // store which ui files define Toolbars
1602         static QStringList toolbar_uifiles;
1603
1604         while (lex.isOK()) {
1605                 switch (lex.lex()) {
1606                 case ui_include: {
1607                         lex.next(true);
1608                         QString const file = toqstr(lex.getString());
1609                         if (!readUIFile(file, true))
1610                                 return false;
1611                         break;
1612                 }
1613                 case ui_menuset:
1614                         d->menus_.read(lex);
1615                         break;
1616
1617                 case ui_toolbarset:
1618                         d->toolbars_.readToolbars(lex);
1619                         break;
1620
1621                 case ui_toolbars:
1622                         d->toolbars_.readToolbarSettings(lex);
1623                         toolbar_uifiles.push_back(uifile);
1624                         break;
1625
1626                 default:
1627                         if (!rtrim(lex.getString()).empty())
1628                                 lex.printError("LyX::ReadUIFile: "
1629                                                "Unknown menu tag: `$$Token'");
1630                         break;
1631                 }
1632         }
1633
1634         if (include)
1635                 return true;
1636
1637         QSettings settings;
1638         settings.beginGroup("ui_files");
1639         bool touched = false;
1640         for (int i = 0; i != uifiles.size(); ++i) {
1641                 QFileInfo fi(uifiles[i]);
1642                 QDateTime const date_value = fi.lastModified();
1643                 QString const name_key = QString::number(i);
1644                 // if an ui file which defines Toolbars has changed,
1645                 // we have to reset the settings
1646                 if (toolbar_uifiles.contains(uifiles[i])
1647                  && (!settings.contains(name_key)
1648                  || settings.value(name_key).toString() != uifiles[i]
1649                  || settings.value(name_key + "/date").toDateTime() != date_value)) {
1650                         touched = true;
1651                         settings.setValue(name_key, uifiles[i]);
1652                         settings.setValue(name_key + "/date", date_value);
1653                 }
1654         }
1655         settings.endGroup();
1656         if (touched)
1657                 settings.remove("views");
1658
1659         return true;
1660 }
1661
1662
1663 void GuiApplication::onLastWindowClosed()
1664 {
1665         if (d->global_menubar_)
1666                 d->global_menubar_->grabKeyboard();
1667 }
1668
1669
1670 ////////////////////////////////////////////////////////////////////////
1671 //
1672 // X11 specific stuff goes here...
1673
1674 #ifdef Q_WS_X11
1675 bool GuiApplication::x11EventFilter(XEvent * xev)
1676 {
1677         if (!current_view_)
1678                 return false;
1679
1680         switch (xev->type) {
1681         case SelectionRequest: {
1682                 if (xev->xselectionrequest.selection != XA_PRIMARY)
1683                         break;
1684                 LYXERR(Debug::SELECTION, "X requested selection.");
1685                 BufferView * bv = current_view_->currentBufferView();
1686                 if (bv) {
1687                         docstring const sel = bv->requestSelection();
1688                         if (!sel.empty())
1689                                 d->selection_.put(sel);
1690                 }
1691                 break;
1692         }
1693         case SelectionClear: {
1694                 if (xev->xselectionclear.selection != XA_PRIMARY)
1695                         break;
1696                 LYXERR(Debug::SELECTION, "Lost selection.");
1697                 BufferView * bv = current_view_->currentBufferView();
1698                 if (bv)
1699                         bv->clearSelection();
1700                 break;
1701         }
1702         }
1703         return false;
1704 }
1705 #endif
1706
1707 } // namespace frontend
1708
1709
1710 void hideDialogs(std::string const & name, Inset * inset)
1711 {
1712         if (theApp())
1713                 theApp()->hideDialogs(name, inset);
1714 }
1715
1716
1717 ////////////////////////////////////////////////////////////////////
1718 //
1719 // Font stuff
1720 //
1721 ////////////////////////////////////////////////////////////////////
1722
1723 frontend::FontLoader & theFontLoader()
1724 {
1725         LASSERT(frontend::guiApp, /**/);
1726         return frontend::guiApp->fontLoader();
1727 }
1728
1729
1730 frontend::FontMetrics const & theFontMetrics(Font const & f)
1731 {
1732         return theFontMetrics(f.fontInfo());
1733 }
1734
1735
1736 frontend::FontMetrics const & theFontMetrics(FontInfo const & f)
1737 {
1738         LASSERT(frontend::guiApp, /**/);
1739         return frontend::guiApp->fontLoader().metrics(f);
1740 }
1741
1742
1743 ////////////////////////////////////////////////////////////////////
1744 //
1745 // Misc stuff
1746 //
1747 ////////////////////////////////////////////////////////////////////
1748
1749 frontend::Clipboard & theClipboard()
1750 {
1751         LASSERT(frontend::guiApp, /**/);
1752         return frontend::guiApp->clipboard();
1753 }
1754
1755
1756 frontend::Selection & theSelection()
1757 {
1758         LASSERT(frontend::guiApp, /**/);
1759         return frontend::guiApp->selection();
1760 }
1761
1762
1763 } // namespace lyx
1764
1765 #include "moc_GuiApplication.cpp"