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