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