]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiApplication.cpp
Code cleanup
[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         { "Downarrow", "downarrow2" },
230         { "Gamma", "gamma2" },
231         { "Lambda", "lambda2" },
232         { "Leftarrow", "leftarrow2" },
233         { "Leftrightarrow", "leftrightarrow2" },
234         { "Longleftarrow", "longleftarrow2" },
235         { "Longleftrightarrow", "longleftrightarrow2" },
236         { "Longrightarrow", "longrightarrow2" },
237         { "Omega", "omega2" },
238         { "Phi", "phi2" },
239         { "Pi", "pi2" },
240         { "Psi", "psi2" },
241         { "Rightarrow", "rightarrow2" },
242         { "Sigma", "sigma2" },
243         { "Subset", "subset2" },
244         { "Supset", "supset2" },
245         { "Theta", "theta2" },
246         { "Uparrow", "uparrow2" },
247         { "Updownarrow", "updownarrow2" },
248         { "Upsilon", "upsilon2" },
249         { "Vdash", "vdash3" },
250         { "Vert", "vert2" },
251         { "Xi", "xi2" },
252         { "nLeftarrow", "nleftarrow2" },
253         { "nLeftrightarrow", "nleftrightarrow2" },
254         { "nRightarrow", "nrightarrow2" },
255         { "nVDash", "nvdash3" },
256         { "nvDash", "nvdash2" },
257         { "textrm \\AA", "textrm_AA"},
258         { "textrm \\O", "textrm_O"},
259         { "vDash", "vdash2" }
260 };
261
262 size_t const nr_sorted_png_map = sizeof(sorted_png_map) / sizeof(PngMap);
263
264
265 QString findPng(QString const & name)
266 {
267         PngMap const * const begin = sorted_png_map;
268         PngMap const * const end = begin + nr_sorted_png_map;
269         LASSERT(sorted(begin, end), /**/);
270
271         PngMap const * const it = find_if(begin, end, CompareKey(name));
272
273         QString png_name;
274         if (it != end) {
275                 png_name = it->value;
276         } else {
277                 png_name = name;
278                 png_name.replace('_', "underscore");
279                 png_name.replace(' ', '_');
280
281                 // This way we can have "math-delim { }" on the toolbar.
282                 png_name.replace('(', "lparen");
283                 png_name.replace(')', "rparen");
284                 png_name.replace('[', "lbracket");
285                 png_name.replace(']', "rbracket");
286                 png_name.replace('{', "lbrace");
287                 png_name.replace('}', "rbrace");
288                 png_name.replace('|', "bars");
289                 png_name.replace(',', "thinspace");
290                 png_name.replace(':', "mediumspace");
291                 png_name.replace(';', "thickspace");
292                 png_name.replace('!', "negthinspace");
293         }
294
295         LYXERR(Debug::GUI, "findPng(" << name << ")\n"
296                 << "Looking for math PNG called \"" << png_name << '"');
297         return png_name;
298 }
299
300 } // namespace anon
301
302
303 QString iconName(FuncRequest const & f, bool unknown)
304 {
305         initializeResources();
306         QString name1;
307         QString name2;
308         QString path;
309         switch (f.action) {
310         case LFUN_MATH_INSERT:
311                 if (!f.argument().empty()) {
312                         path = "math/";
313                         name1 = findPng(toqstr(f.argument()).mid(1));
314                 }
315                 break;
316         case LFUN_MATH_DELIM:
317         case LFUN_MATH_BIGDELIM:
318                 path = "math/";
319                 name1 = findPng(toqstr(f.argument()));
320                 break;
321         case LFUN_CALL:
322                 path = "commands/";
323                 name1 = toqstr(f.argument());
324                 break;
325         case LFUN_COMMAND_ALTERNATIVES: {
326                 // use the first of the alternative commands
327                 docstring firstcom;
328                 docstring dummy = split(f.argument(), firstcom, ';');
329                 name1 = toqstr(firstcom);
330                 name1.replace(' ', '_');
331                 break;
332         }
333         default:
334                 name2 = toqstr(lyxaction.getActionName(f.action));
335                 name1 = name2;
336
337                 if (!f.argument().empty()) {
338                         name1 = name2 + ' ' + toqstr(f.argument());
339                         name1.replace(' ', '_');
340                         name1.replace('\\', "backslash");
341                 }
342         }
343
344         FileName fname = libFileSearch("images/" + path, name1, "png");
345         if (fname.exists())
346                 return toqstr(fname.absFilename());
347
348         fname = libFileSearch("images/" + path, name2, "png");
349         if (fname.exists())
350                 return toqstr(fname.absFilename());
351
352         path = ":/images/" + path;
353         QDir res(path);
354         if (!res.exists()) {
355                 LYXERR0("Directory " << path << " not found in resource!"); 
356                 return QString();
357         }
358         name1 += ".png";
359         if (res.exists(name1))
360                 return path + name1;
361
362         name2 += ".png";
363         if (res.exists(name2))
364                 return path + name2;
365
366         LYXERR(Debug::GUI, "Cannot find icon with filename "
367                            << "\"" << name1 << "\""
368                            << " or filename "
369                            << "\"" << name2 << "\"" 
370                            << " for command \""
371                            << lyxaction.getActionName(f.action)
372                            << '(' << to_utf8(f.argument()) << ")\"");
373
374         if (unknown) {
375                 fname = libFileSearch(QString("images/"), "unknown", "png");
376                 if (fname.exists())
377                         return toqstr(fname.absFilename());
378                 return QString(":/images/unknown.png");
379         }
380
381         return QString();
382 }
383
384 QPixmap getPixmap(QString const & path, QString const & name, QString const & ext)
385 {
386         QPixmap pixmap;
387         FileName fname = libFileSearch(path, name, ext);
388         QString path1 = toqstr(fname.absFilename());
389         QString path2 = ":/" + path + name + "." + ext;
390
391         if (pixmap.load(path1)) {
392                 return pixmap;
393         }
394         else if (pixmap.load(path2)) {
395                 return pixmap;
396         }
397
398         LYXERR0("Cannot load pixmap \""
399                 << path << name << '.' << ext
400                 << "\", please verify resource system!");
401
402         return QPixmap();
403 }
404
405 QIcon getIcon(FuncRequest const & f, bool unknown)
406 {
407         QString icon = iconName(f, unknown);
408         if (icon.isEmpty())
409                 return QIcon();
410
411         //LYXERR(Debug::GUI, "Found icon: " << icon);
412         QPixmap pm;
413         if (!pm.load(icon)) {
414                 LYXERR0("Cannot load icon " << icon << " please verify resource system!");
415                 return QIcon();
416         }
417
418         return QIcon(pm);
419 }
420
421
422 ////////////////////////////////////////////////////////////////////////
423 //
424 // LyX server support code.
425 //
426 ////////////////////////////////////////////////////////////////////////
427
428 class SocketNotifier : public QSocketNotifier
429 {
430 public:
431         /// connect a connection notification from the LyXServerSocket
432         SocketNotifier(QObject * parent, int fd, Application::SocketCallback func)
433                 : QSocketNotifier(fd, QSocketNotifier::Read, parent), func_(func)
434         {}
435
436 public:
437         /// The callback function
438         Application::SocketCallback func_;
439 };
440
441
442 ////////////////////////////////////////////////////////////////////////
443 //
444 // Mac specific stuff goes here...
445 //
446 ////////////////////////////////////////////////////////////////////////
447
448 class MenuTranslator : public QTranslator
449 {
450 public:
451         MenuTranslator(QObject * parent)
452                 : QTranslator(parent)
453         {}
454
455         QString translate(const char * /*context*/, 
456           const char * sourceText, 
457           const char * /*comment*/ = 0) 
458         {
459                 string const s = sourceText;
460                 if (s == N_("About %1") || s == N_("Preferences") 
461                                 || s == N_("Reconfigure") || s == N_("Quit %1"))
462                         return qt_(s);
463                 else 
464                         return QString();
465         }
466 };
467
468 class GlobalMenuBar : public QMenuBar
469 {
470 public:
471         ///
472         GlobalMenuBar() : QMenuBar(0) {}
473         
474         ///
475         bool event(QEvent * e)
476         {
477                 if (e->type() == QEvent::ShortcutOverride) {
478                         //          && activeWindow() == 0) {
479                         QKeyEvent * ke = static_cast<QKeyEvent*>(e);
480                         KeySymbol sym;
481                         setKeySymbol(&sym, ke);
482                         guiApp->processKeySym(sym, q_key_state(ke->modifiers()));
483                         e->accept();
484                         return true;
485                 }
486                 return false;
487         }
488 };
489
490 #ifdef Q_WS_MACX
491 // QMacPasteboardMimeGraphics can only be compiled on Mac.
492
493 class QMacPasteboardMimeGraphics : public QMacPasteboardMime
494 {
495 public:
496         QMacPasteboardMimeGraphics()
497                 : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL)
498         {}
499
500         QString convertorName() { return "Graphics"; }
501
502         QString flavorFor(QString const & mime)
503         {
504                 LYXERR(Debug::ACTION, "flavorFor " << mime);
505                 if (mime == pdfMimeType())
506                         return QLatin1String("com.adobe.pdf");
507                 return QString();
508         }
509
510         QString mimeFor(QString flav)
511         {
512                 LYXERR(Debug::ACTION, "mimeFor " << flav);
513                 if (flav == QLatin1String("com.adobe.pdf"))
514                         return pdfMimeType();
515                 return QString();
516         }
517
518         bool canConvert(QString const & mime, QString flav)
519         {
520                 return mimeFor(flav) == mime;
521         }
522
523         QVariant convertToMime(QString const & /*mime*/, QList<QByteArray> data,
524                 QString /*flav*/)
525         {
526                 if(data.count() > 1)
527                         qWarning("QMacPasteboardMimeGraphics: Cannot handle multiple member data");
528                 return data.first();
529         }
530
531         QList<QByteArray> convertFromMime(QString const & /*mime*/,
532                 QVariant data, QString /*flav*/)
533         {
534                 QList<QByteArray> ret;
535                 ret.append(data.toByteArray());
536                 return ret;
537         }
538 };
539 #endif
540
541 ///////////////////////////////////////////////////////////////
542 //
543 // You can find more platform specific stuff at the end of this file...
544 //
545 ///////////////////////////////////////////////////////////////
546
547 ////////////////////////////////////////////////////////////////////////
548 // Windows specific stuff goes here...
549
550 #ifdef Q_WS_WIN
551 // QWindowsMimeMetafile can only be compiled on Windows.
552
553 static FORMATETC cfFromMime(QString const & mimetype)
554 {
555         FORMATETC formatetc;
556         if (mimetype == emfMimeType()) {
557                 formatetc.cfFormat = CF_ENHMETAFILE;
558                 formatetc.tymed = TYMED_ENHMF;
559         } else if (mimetype == wmfMimeType()) {
560                 formatetc.cfFormat = CF_METAFILEPICT;
561                 formatetc.tymed = TYMED_MFPICT;
562         }
563         formatetc.ptd = 0;
564         formatetc.dwAspect = DVASPECT_CONTENT;
565         formatetc.lindex = -1;
566         return formatetc;
567 }
568
569
570 class QWindowsMimeMetafile : public QWindowsMime {
571 public:
572         QWindowsMimeMetafile() {}
573
574         bool canConvertFromMime(FORMATETC const & formatetc,
575                 QMimeData const * mimedata) const
576         {
577                 return false;
578         }
579
580         bool canConvertToMime(QString const & mimetype,
581                 IDataObject * pDataObj) const
582         {
583                 if (mimetype != emfMimeType() && mimetype != wmfMimeType())
584                         return false;
585                 FORMATETC formatetc = cfFromMime(mimetype);
586                 return pDataObj->QueryGetData(&formatetc) == S_OK;
587         }
588
589         bool convertFromMime(FORMATETC const & formatetc,
590                 const QMimeData * mimedata, STGMEDIUM * pmedium) const
591         {
592                 return false;
593         }
594
595         QVariant convertToMime(QString const & mimetype, IDataObject * pDataObj,
596                 QVariant::Type preferredType) const
597         {
598                 QByteArray data;
599                 if (!canConvertToMime(mimetype, pDataObj))
600                         return data;
601
602                 FORMATETC formatetc = cfFromMime(mimetype);
603                 STGMEDIUM s;
604                 if (pDataObj->GetData(&formatetc, &s) != S_OK)
605                         return data;
606
607                 int dataSize;
608                 if (s.tymed == TYMED_ENHMF) {
609                         dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, 0, 0);
610                         data.resize(dataSize);
611                         dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, dataSize,
612                                 (LPBYTE)data.data());
613                 } else if (s.tymed == TYMED_MFPICT) {
614                         dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, 0, 0);
615                         data.resize(dataSize);
616                         dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, dataSize,
617                                 (LPBYTE)data.data());
618                 }
619                 data.detach();
620                 ReleaseStgMedium(&s);
621
622                 return data;
623         }
624
625
626         QVector<FORMATETC> formatsForMime(QString const & mimetype,
627                 QMimeData const * mimedata) const
628         {
629                 QVector<FORMATETC> formats;
630                 if (mimetype == emfMimeType() || mimetype == wmfMimeType())
631                         formats += cfFromMime(mimetype);
632                 return formats;
633         }
634
635         QString mimeForFormat(FORMATETC const & formatetc) const
636         {
637                 switch (formatetc.cfFormat) {
638                 case CF_ENHMETAFILE:
639                         return emfMimeType(); 
640                 case CF_METAFILEPICT:
641                         return wmfMimeType();
642                 }
643                 return QString();
644         }
645 };
646
647 #endif // Q_WS_WIN
648
649 ////////////////////////////////////////////////////////////////////////
650 // GuiApplication::Private definition and implementation.
651 ////////////////////////////////////////////////////////////////////////
652
653 struct GuiApplication::Private
654 {
655         Private(): language_model_(0), meta_fake_bit(NoModifier),
656                 global_menubar_(0)
657         {
658         #ifdef Q_WS_WIN
659                 /// WMF Mime handler for Windows clipboard.
660                 wmf_mime_ = new QWindowsMimeMetafile();
661         #endif
662                 initKeySequences(&theTopLevelKeymap());
663         }
664
665         void initKeySequences(KeyMap * kb)
666         {
667                 keyseq = KeySequence(kb, kb);
668                 cancel_meta_seq = KeySequence(kb, kb);
669         }
670
671         ///
672         QSortFilterProxyModel * language_model_;
673         ///
674         GuiClipboard clipboard_;
675         ///
676         GuiSelection selection_;
677         ///
678         FontLoader font_loader_;
679         ///
680         ColorCache color_cache_;
681         ///
682         QTranslator qt_trans_;
683         ///
684         QHash<int, SocketNotifier *> socket_notifiers_;
685         ///
686         Menus menus_;
687         ///
688         /// The global instance
689         Toolbars toolbars_;
690
691         /// this timer is used for any regular events one wants to
692         /// perform. at present it is used to check if forked processes
693         /// are done.
694         QTimer general_timer_;
695
696         /// delayed FuncRequests
697         std::queue<FuncRequest> func_request_queue_;
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(GuiView * 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                 theBufferList().changed(false);
953                 // Restore current_view_
954                 current_view_ = view;
955                 break;
956         }
957
958         case LFUN_BUFFER_NEW:
959                 if (d->views_.empty()
960                     || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
961                         createView(QString(), false); // keep hidden
962                         current_view_->newDocument(to_utf8(cmd.argument()), false);
963                         current_view_->show();
964                         setActiveWindow(current_view_);
965                 } else {
966                         current_view_->newDocument(to_utf8(cmd.argument()), false);
967                 }
968                 break;
969
970         case LFUN_BUFFER_NEW_TEMPLATE:
971                 if (d->views_.empty()
972                     || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
973                         createView();
974                         current_view_->newDocument(to_utf8(cmd.argument()), true);
975                         if (!current_view_->documentBufferView())
976                                 current_view_->close();
977                 } else {
978                         current_view_->newDocument(to_utf8(cmd.argument()), true);
979                 }
980                 break;
981
982         case LFUN_FILE_OPEN:
983                 // FIXME: create a new method shared with LFUN_HELP_OPEN.
984                 if (d->views_.empty()
985                     || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
986                         string const fname = to_utf8(cmd.argument());
987                         // We want the ui session to be saved per document and not per
988                         // window number. The filename crc is a good enough identifier.
989                         boost::crc_32_type crc;
990                         crc = for_each(fname.begin(), fname.end(), crc);
991                         createView(crc.checksum());
992                         current_view_->openDocument(fname);
993                         if (current_view_ && !current_view_->documentBufferView())
994                                 current_view_->close();
995                 } else
996                         current_view_->openDocument(to_utf8(cmd.argument()));
997                 break;
998
999         case LFUN_HELP_OPEN: {
1000                 // FIXME: create a new method shared with LFUN_FILE_OPEN.
1001                 if (current_view_ == 0)
1002                         createView();
1003                 string const arg = to_utf8(cmd.argument());
1004                 if (arg.empty()) {
1005                         current_view_->message(_("Missing argument"));
1006                         break;
1007                 }
1008                 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1009                 if (fname.empty()) 
1010                         fname = i18nLibFileSearch("examples", arg, "lyx");
1011
1012                 if (fname.empty()) {
1013                         lyxerr << "LyX: unable to find documentation file `"
1014                                 << arg << "'. Bad installation?" << endl;
1015                         break;
1016                 }
1017                 current_view_->message(bformat(_("Opening help file %1$s..."),
1018                         makeDisplayPath(fname.absFilename())));
1019                 Buffer * buf = current_view_->loadDocument(fname, false);
1020                 if (buf) {
1021                         current_view_->setBuffer(buf);
1022                         buf->updateLabels();
1023                         buf->errors("Parse");
1024                 }
1025                 break;
1026         }
1027
1028         case LFUN_SET_COLOR: {
1029                 string lyx_name;
1030                 string const x11_name = split(to_utf8(cmd.argument()), lyx_name, ' ');
1031                 if (lyx_name.empty() || x11_name.empty()) {
1032                         current_view_->message(
1033                                 _("Syntax: set-color <lyx_name> <x11_name>"));
1034                         break;
1035                 }
1036
1037                 string const graphicsbg = lcolor.getLyXName(Color_graphicsbg);
1038                 bool const graphicsbg_changed = lyx_name == graphicsbg
1039                         && x11_name != graphicsbg;
1040                 if (graphicsbg_changed) {
1041                         // FIXME: The graphics cache no longer has a changeDisplay method.
1042 #if 0
1043                         graphics::GCache::get().changeDisplay(true);
1044 #endif
1045                 }
1046
1047                 if (!lcolor.setColor(lyx_name, x11_name)) {
1048                         current_view_->message(
1049                                         bformat(_("Set-color \"%1$s\" failed "
1050                                                                "- color is undefined or "
1051                                                                "may not be redefined"),
1052                                                                    from_utf8(lyx_name)));
1053                         break;
1054                 }
1055                 // Make sure we don't keep old colors in cache.
1056                 d->color_cache_.clear();
1057                 break;
1058         }
1059
1060         case LFUN_LYXRC_APPLY: {
1061                 // reset active key sequences, since the bindings
1062                 // are updated (bug 6064)
1063                 d->keyseq.reset();
1064                 LyXRC const lyxrc_orig = lyxrc;
1065
1066                 istringstream ss(to_utf8(cmd.argument()));
1067                 bool const success = lyxrc.read(ss) == 0;
1068
1069                 if (!success) {
1070                         lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1071                                 << "Unable to read lyxrc data"
1072                                 << endl;
1073                         break;
1074                 }
1075
1076                 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1077                 setSpellChecker();
1078                 resetGui();
1079
1080                 break;
1081         }
1082
1083         case LFUN_COMMAND_PREFIX:
1084                 lyx::dispatch(FuncRequest(LFUN_MESSAGE, d->keyseq.printOptions(true)));
1085                 break;
1086
1087         case LFUN_CANCEL: {
1088                 d->keyseq.reset();
1089                 d->meta_fake_bit = NoModifier;
1090                 GuiView * gv = currentView();
1091                 if (gv && gv->currentBufferView())
1092                         // cancel any selection
1093                         lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1094                 dr.setMessage(from_ascii(N_("Cancel")));
1095                 break;
1096         }
1097         case LFUN_META_PREFIX:
1098                 d->meta_fake_bit = AltModifier;
1099                 dr.setMessage(d->keyseq.print(KeySequence::ForGui));
1100                 break;
1101
1102         // --- Menus -----------------------------------------------
1103         case LFUN_RECONFIGURE:
1104                 // argument is any additional parameter to the configure.py command
1105                 reconfigure(currentView(), to_utf8(cmd.argument()));
1106                 break;
1107
1108         // --- lyxserver commands ----------------------------
1109         case LFUN_SERVER_GET_FILENAME: {
1110                 GuiView * lv = currentView();
1111                 LASSERT(lv && lv->documentBufferView(), return);
1112                 docstring const fname = from_utf8(
1113                         lv->documentBufferView()->buffer().absFileName());
1114                 dr.setMessage(fname);
1115                 LYXERR(Debug::INFO, "FNAME[" << fname << ']');
1116                 break;
1117         }
1118         case LFUN_SERVER_NOTIFY: {
1119                 docstring const dispatch_buffer = d->keyseq.print(KeySequence::Portable);
1120                 dr.setMessage(dispatch_buffer);
1121                 theServer().notifyClient(to_utf8(dispatch_buffer));
1122                 break;
1123         }
1124         default:
1125                 // Notify the caller that the action has not been dispatched.
1126                 dr.dispatched(false);
1127                 return;
1128         }
1129
1130         // The action has been dispatched.
1131         dr.dispatched(true);
1132 }
1133
1134
1135 docstring GuiApplication::viewStatusMessage()
1136 {
1137         // When meta-fake key is pressed, show the key sequence so far + "M-".
1138         if (d->meta_fake_bit != NoModifier)
1139                 return d->keyseq.print(KeySequence::ForGui) + "M-";
1140
1141         // Else, when a non-complete key sequence is pressed,
1142         // show the available options.
1143         if (d->keyseq.length() > 0 && !d->keyseq.deleted())
1144                 return d->keyseq.printOptions(true);
1145
1146         return docstring();
1147 }
1148
1149
1150 void GuiApplication::handleKeyFunc(FuncCode action)
1151 {
1152         char_type c = 0;
1153
1154         if (d->keyseq.length())
1155                 c = 0;
1156         GuiView * gv = currentView();
1157         LASSERT(gv && gv->currentBufferView(), return);
1158         BufferView * bv = gv->currentBufferView();
1159         bv->getIntl().getTransManager().deadkey(
1160                 c, get_accent(action).accent, bv->cursor().innerText(),
1161                 bv->cursor());
1162         // Need to clear, in case the minibuffer calls these
1163         // actions
1164         d->keyseq.clear();
1165         // copied verbatim from do_accent_char
1166         bv->cursor().resetAnchor();
1167         bv->processUpdateFlags(Update::FitCursor);
1168 }
1169
1170
1171 void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
1172 {
1173         LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
1174
1175         GuiView * lv = currentView();
1176
1177         // Do nothing if we have nothing (JMarc)
1178         if (!keysym.isOK()) {
1179                 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
1180                 lv->restartCursor();
1181                 return;
1182         }
1183
1184         if (keysym.isModifier()) {
1185                 LYXERR(Debug::KEY, "isModifier true");
1186                 if (lv)
1187                         lv->restartCursor();
1188                 return;
1189         }
1190
1191         char_type encoded_last_key = keysym.getUCSEncoded();
1192
1193         // Do a one-deep top-level lookup for
1194         // cancel and meta-fake keys. RVDK_PATCH_5
1195         d->cancel_meta_seq.reset();
1196
1197         FuncRequest func = d->cancel_meta_seq.addkey(keysym, state);
1198         LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
1199
1200         // When not cancel or meta-fake, do the normal lookup.
1201         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
1202         // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
1203         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
1204                 // remove Caps Lock and Mod2 as a modifiers
1205                 func = d->keyseq.addkey(keysym, (state | d->meta_fake_bit));
1206                 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
1207         }
1208
1209         // Dont remove this unless you know what you are doing.
1210         d->meta_fake_bit = NoModifier;
1211
1212         // Can this happen now ?
1213         if (func.action == LFUN_NOACTION)
1214                 func = FuncRequest(LFUN_COMMAND_PREFIX);
1215
1216         LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
1217                 << d->keyseq.print(KeySequence::Portable) << ']');
1218
1219         // already here we know if it any point in going further
1220         // why not return already here if action == -1 and
1221         // num_bytes == 0? (Lgb)
1222
1223         if (d->keyseq.length() > 1)
1224                 lv->message(d->keyseq.print(KeySequence::ForGui));
1225
1226
1227         // Maybe user can only reach the key via holding down shift.
1228         // Let's see. But only if shift is the only modifier
1229         if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
1230                 LYXERR(Debug::KEY, "Trying without shift");
1231                 func = d->keyseq.addkey(keysym, NoModifier);
1232                 LYXERR(Debug::KEY, "Action now " << func.action);
1233         }
1234
1235         if (func.action == LFUN_UNKNOWN_ACTION) {
1236                 // Hmm, we didn't match any of the keysequences. See
1237                 // if it's normal insertable text not already covered
1238                 // by a binding
1239                 if (keysym.isText() && d->keyseq.length() == 1) {
1240                         LYXERR(Debug::KEY, "isText() is true, inserting.");
1241                         func = FuncRequest(LFUN_SELF_INSERT,
1242                                            FuncRequest::KEYBOARD);
1243                 } else {
1244                         LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
1245                         lv->message(_("Unknown function."));
1246                         lv->restartCursor();
1247                         return;
1248                 }
1249         }
1250
1251         if (func.action == LFUN_SELF_INSERT) {
1252                 if (encoded_last_key != 0) {
1253                         docstring const arg(1, encoded_last_key);
1254                         lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
1255                                              FuncRequest::KEYBOARD));
1256                         LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
1257                 }
1258         } else {
1259                 lyx::dispatch(func);
1260                 if (!lv)
1261                         return;
1262         }
1263 }
1264
1265
1266 void GuiApplication::dispatchDelayed(FuncRequest const & func)
1267 {
1268         d->func_request_queue_.push(func);
1269         QTimer::singleShot(0, this, SLOT(processFuncRequestQueue()));
1270 }
1271
1272
1273 void GuiApplication::resetGui()
1274 {
1275         // Set the language defined by the user.
1276         setGuiLanguage();
1277
1278         // Read menus
1279         if (!readUIFile(toqstr(lyxrc.ui_file)))
1280                 // Gives some error box here.
1281                 return;
1282
1283         if (d->global_menubar_)
1284                 d->menus_.fillMenuBar(d->global_menubar_, 0, false);
1285
1286         QHash<int, GuiView *>::iterator it;
1287         for (it = d->views_.begin(); it != d->views_.end(); ++it) {
1288                 GuiView * gv = *it;
1289                 setCurrentView(gv);
1290                 gv->setLayoutDirection(layoutDirection());
1291                 gv->resetDialogs();
1292         }
1293
1294         lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
1295 }
1296
1297
1298 void GuiApplication::createView(int view_id)
1299 {
1300         createView(QString(), true, view_id);
1301 }
1302
1303
1304 void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
1305         int view_id)
1306 {
1307         // release the keyboard which might have been grabed by the global
1308         // menubar on Mac to catch shortcuts even without any GuiView.
1309         if (d->global_menubar_)
1310                 d->global_menubar_->releaseKeyboard();
1311
1312         // create new view
1313         int id = view_id;
1314         while (d->views_.find(id) != d->views_.end())
1315                 id++;
1316
1317         LYXERR(Debug::GUI, "About to create new window with ID " << id);
1318         GuiView * view = new GuiView(id);
1319         // register view
1320         d->views_[id] = view;
1321
1322         if (autoShow) {
1323                 view->show();
1324                 setActiveWindow(view);
1325         }
1326
1327         if (!geometry_arg.isEmpty()) {
1328 #ifdef Q_WS_WIN
1329                 int x, y;
1330                 int w, h;
1331                 QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)([+-][0-9]*)){0,1}" );
1332                 re.indexIn(geometry_arg);
1333                 w = re.cap(1).toInt();
1334                 h = re.cap(2).toInt();
1335                 x = re.cap(3).toInt();
1336                 y = re.cap(4).toInt();
1337                 view->setGeometry(x, y, w, h);
1338 #endif
1339         }
1340         view->setFocus();
1341 }
1342
1343
1344 Clipboard & GuiApplication::clipboard()
1345 {
1346         return d->clipboard_;
1347 }
1348
1349
1350 Selection & GuiApplication::selection()
1351 {
1352         return d->selection_;
1353 }
1354
1355
1356 FontLoader & GuiApplication::fontLoader() 
1357 {
1358         return d->font_loader_;
1359 }
1360
1361
1362 Toolbars const & GuiApplication::toolbars() const 
1363 {
1364         return d->toolbars_;
1365 }
1366
1367
1368 Toolbars & GuiApplication::toolbars()
1369 {
1370         return d->toolbars_; 
1371 }
1372
1373
1374 Menus const & GuiApplication::menus() const 
1375 {
1376         return d->menus_;
1377 }
1378
1379
1380 Menus & GuiApplication::menus()
1381 {
1382         return d->menus_; 
1383 }
1384
1385
1386 QList<int> GuiApplication::viewIds() const
1387 {
1388         return d->views_.keys();
1389 }
1390
1391
1392 ColorCache & GuiApplication::colorCache()
1393 {
1394         return d->color_cache_;
1395 }
1396
1397
1398 int GuiApplication::exec()
1399 {
1400         // asynchronously handle batch commands. This event will be in
1401         // the event queue in front of other asynchronous events. Hence,
1402         // we can assume in the latter that the gui is setup already.
1403         QTimer::singleShot(0, this, SLOT(execBatchCommands()));
1404
1405         return QApplication::exec();
1406 }
1407
1408
1409 void GuiApplication::exit(int status)
1410 {
1411         QApplication::exit(status);
1412 }
1413
1414
1415 void GuiApplication::setGuiLanguage()
1416 {
1417         // Set the language defined by the user.
1418         setRcGuiLanguage();
1419
1420         QString const default_language = toqstr(Messages::defaultLanguage());
1421         LYXERR(Debug::LOCALE, "Trying to set default locale to: " << default_language);
1422         QLocale const default_locale(default_language);
1423         QLocale::setDefault(default_locale);
1424
1425         // install translation file for Qt built-in dialogs
1426         QString const language_name = QString("qt_") + default_locale.name();
1427
1428         // language_name can be short (e.g. qt_zh) or long (e.g. qt_zh_CN). 
1429         // Short-named translator can be loaded from a long name, but not the
1430         // opposite. Therefore, long name should be used without truncation.
1431         // c.f. http://doc.trolltech.com/4.1/qtranslator.html#load
1432         if (!d->qt_trans_.load(language_name,
1433                         QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
1434                 LYXERR(Debug::LOCALE, "Could not find Qt translations for locale "
1435                         << language_name);
1436         } else {
1437                 LYXERR(Debug::LOCALE, "Successfully installed Qt translations for locale "
1438                         << language_name);
1439         }
1440
1441         switch (default_locale.language()) {
1442         case QLocale::Arabic :
1443         case QLocale::Hebrew :
1444         case QLocale::Persian :
1445         case QLocale::Urdu :
1446         setLayoutDirection(Qt::RightToLeft);
1447                 break;
1448         default:
1449         setLayoutDirection(Qt::LeftToRight);
1450         }
1451 }
1452
1453
1454 void GuiApplication::processFuncRequestQueue()
1455 {
1456         while (!d->func_request_queue_.empty()) {
1457                 lyx::dispatch(d->func_request_queue_.back());
1458                 d->func_request_queue_.pop();
1459         }
1460 }
1461
1462
1463 void GuiApplication::execBatchCommands()
1464 {
1465         setGuiLanguage();
1466
1467         // Read menus
1468         if (!readUIFile(toqstr(lyxrc.ui_file)))
1469                 // Gives some error box here.
1470                 return;
1471
1472 #ifdef Q_WS_MACX
1473         // Create the global default menubar which is shown for the dialogs
1474         // and if no GuiView is visible.
1475         // This must be done after the session was recovered to know the "last files".
1476         d->global_menubar_ = new GlobalMenuBar();
1477         d->menus_.fillMenuBar(d->global_menubar_, 0, true);
1478 #endif
1479
1480         lyx::execBatchCommands();
1481 }
1482
1483
1484 QAbstractItemModel * GuiApplication::languageModel()
1485 {
1486         if (d->language_model_)
1487                 return d->language_model_;
1488
1489         QStandardItemModel * lang_model = new QStandardItemModel(this);
1490         lang_model->insertColumns(0, 1);
1491         int current_row;
1492         Languages::const_iterator it = lyx::languages.begin();
1493         Languages::const_iterator end = lyx::languages.end();
1494         for (; it != end; ++it) {
1495                 current_row = lang_model->rowCount();
1496                 lang_model->insertRows(current_row, 1);
1497                 QModelIndex item = lang_model->index(current_row, 0);
1498                 lang_model->setData(item, qt_(it->second.display()), Qt::DisplayRole);
1499                 lang_model->setData(item, toqstr(it->second.lang()), Qt::UserRole);
1500         }
1501         d->language_model_ = new QSortFilterProxyModel(this);
1502         d->language_model_->setSourceModel(lang_model);
1503 #if QT_VERSION >= 0x040300
1504         d->language_model_->setSortLocaleAware(true);
1505 #endif
1506         return d->language_model_;
1507 }
1508
1509
1510 void GuiApplication::restoreGuiSession()
1511 {
1512         if (!lyxrc.load_session)
1513                 return;
1514
1515         Session & session = theSession();
1516         LastOpenedSection::LastOpened const & lastopened = 
1517                 session.lastOpened().getfiles();
1518
1519         FileName active_file;
1520         // do not add to the lastfile list since these files are restored from
1521         // last session, and should be already there (regular files), or should
1522         // not be added at all (help files).
1523         for (size_t i = 0; i < lastopened.size(); ++i) {
1524                 FileName const & file_name = lastopened[i].file_name;
1525                 if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs
1526                           && current_view_->documentBufferView() != 0)) {
1527                         boost::crc_32_type crc;
1528                         string const & fname = file_name.absFilename();
1529                         crc = for_each(fname.begin(), fname.end(), crc);
1530                         createView(crc.checksum());
1531                 }
1532                 current_view_->loadDocument(file_name, false);
1533
1534                 if (lastopened[i].active)
1535                         active_file = file_name;
1536         }
1537
1538         // Restore last active buffer
1539         Buffer * buffer = theBufferList().getBuffer(active_file);
1540         if (buffer)
1541                 current_view_->setBuffer(buffer);
1542
1543         // clear this list to save a few bytes of RAM
1544         session.lastOpened().clear();
1545 }
1546
1547
1548 QString const GuiApplication::romanFontName()
1549 {
1550         QFont font;
1551         font.setKerning(false);
1552         font.setStyleHint(QFont::Serif);
1553         font.setFamily("serif");
1554
1555         return QFontInfo(font).family();
1556 }
1557
1558
1559 QString const GuiApplication::sansFontName()
1560 {
1561         QFont font;
1562         font.setKerning(false);
1563         font.setStyleHint(QFont::SansSerif);
1564         font.setFamily("sans");
1565
1566         return QFontInfo(font).family();
1567 }
1568
1569
1570 QString const GuiApplication::typewriterFontName()
1571 {
1572         QFont font;
1573         font.setKerning(false);
1574         font.setStyleHint(QFont::TypeWriter);
1575         font.setFamily("monospace");
1576
1577         return QFontInfo(font).family();
1578 }
1579
1580
1581 void GuiApplication::handleRegularEvents()
1582 {
1583         ForkedCallsController::handleCompletedProcesses();
1584 }
1585
1586
1587 bool GuiApplication::event(QEvent * e)
1588 {
1589         switch(e->type()) {
1590         case QEvent::FileOpen: {
1591                 // Open a file; this happens only on Mac OS X for now.
1592                 //
1593                 // We do this asynchronously because on startup the batch
1594                 // commands are not executed here yet and the gui is not ready
1595                 // therefore.
1596                 QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
1597                 dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file())));
1598                 e->accept();
1599                 return true;
1600         }
1601         default:
1602                 return QApplication::event(e);
1603         }
1604 }
1605
1606
1607 bool GuiApplication::notify(QObject * receiver, QEvent * event)
1608 {
1609         try {
1610                 return QApplication::notify(receiver, event);
1611         }
1612         catch (ExceptionMessage const & e) {
1613                 switch(e.type_) { 
1614                 case ErrorException:
1615                         emergencyCleanup();
1616                         setQuitOnLastWindowClosed(false);
1617                         closeAllViews();
1618                         Alert::error(e.title_, e.details_);
1619 #ifndef NDEBUG
1620                         // Properly crash in debug mode in order to get a useful backtrace.
1621                         abort();
1622 #endif
1623                         // In release mode, try to exit gracefully.
1624                         this->exit(1);
1625
1626                 case BufferException: {
1627                         if (!current_view_->documentBufferView())
1628                                 return false;
1629                         Buffer * buf = &current_view_->documentBufferView()->buffer();
1630                         docstring details = e.details_ + '\n';
1631                         details += buf->emergencyWrite();
1632                         theBufferList().release(buf);
1633                         details += "\n" + _("The current document was closed.");
1634                         Alert::error(e.title_, details);
1635                         return false;
1636                 }
1637                 case WarningException:
1638                         Alert::warning(e.title_, e.details_);
1639                         return false;
1640                 }
1641         }
1642         catch (exception const & e) {
1643                 docstring s = _("LyX has caught an exception, it will now "
1644                         "attempt to save all unsaved documents and exit."
1645                         "\n\nException: ");
1646                 s += from_ascii(e.what());
1647                 Alert::error(_("Software exception Detected"), s);
1648                 lyx_exit(1);
1649         }
1650         catch (...) {
1651                 docstring s = _("LyX has caught some really weird exception, it will "
1652                         "now attempt to save all unsaved documents and exit.");
1653                 Alert::error(_("Software exception Detected"), s);
1654                 lyx_exit(1);
1655         }
1656
1657         return false;
1658 }
1659
1660
1661 bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
1662 {
1663         QColor const & qcol = d->color_cache_.get(col);
1664         if (!qcol.isValid()) {
1665                 rgbcol.r = 0;
1666                 rgbcol.g = 0;
1667                 rgbcol.b = 0;
1668                 return false;
1669         }
1670         rgbcol.r = qcol.red();
1671         rgbcol.g = qcol.green();
1672         rgbcol.b = qcol.blue();
1673         return true;
1674 }
1675
1676
1677 string const GuiApplication::hexName(ColorCode col)
1678 {
1679         return ltrim(fromqstr(d->color_cache_.get(col).name()), "#");
1680 }
1681
1682
1683 void GuiApplication::registerSocketCallback(int fd, SocketCallback func)
1684 {
1685         SocketNotifier * sn = new SocketNotifier(this, fd, func);
1686         d->socket_notifiers_[fd] = sn;
1687         connect(sn, SIGNAL(activated(int)), this, SLOT(socketDataReceived(int)));
1688 }
1689
1690
1691 void GuiApplication::socketDataReceived(int fd)
1692 {
1693         d->socket_notifiers_[fd]->func_();
1694 }
1695
1696
1697 void GuiApplication::unregisterSocketCallback(int fd)
1698 {
1699         d->socket_notifiers_.take(fd)->setEnabled(false);
1700 }
1701
1702
1703 void GuiApplication::commitData(QSessionManager & sm)
1704 {
1705         /// The implementation is required to avoid an application exit
1706         /// when session state save is triggered by session manager.
1707         /// The default implementation sends a close event to all
1708         /// visible top level widgets when session managment allows
1709         /// interaction.
1710         /// We are changing that to close all wiew one by one.
1711         /// FIXME: verify if the default implementation is enough now.
1712         if (sm.allowsInteraction() && !closeAllViews())
1713                 sm.cancel();
1714 }
1715
1716
1717 void GuiApplication::unregisterView(GuiView * gv)
1718 {
1719         LASSERT(d->views_[gv->id()] == gv, /**/);
1720         d->views_.remove(gv->id());
1721         if (current_view_ == gv)
1722                 current_view_ = 0;
1723 }
1724
1725
1726 bool GuiApplication::closeAllViews()
1727 {
1728         if (d->views_.empty())
1729                 return true;
1730
1731         // When a view/window was closed before without quitting LyX, there
1732         // are already entries in the lastOpened list.
1733         theSession().lastOpened().clear();
1734
1735         QList<GuiView *> views = d->views_.values();
1736         foreach (GuiView * view, views) {
1737                 if (!view->close())
1738                         return false;
1739         }
1740
1741         d->views_.clear();
1742         return true;
1743 }
1744
1745
1746 GuiView & GuiApplication::view(int id) const
1747 {
1748         LASSERT(d->views_.contains(id), /**/);
1749         return *d->views_.value(id);
1750 }
1751
1752
1753 void GuiApplication::hideDialogs(string const & name, Inset * inset) const
1754 {
1755         QList<GuiView *> views = d->views_.values();
1756         foreach (GuiView * view, views)
1757                 view->hideDialog(name, inset);
1758 }
1759
1760
1761 Buffer const * GuiApplication::updateInset(Inset const * inset) const
1762 {
1763         Buffer const * buffer_ = 0;
1764         QHash<int, GuiView *>::iterator end = d->views_.end();
1765         for (QHash<int, GuiView *>::iterator it = d->views_.begin(); it != end; ++it) {
1766                 if (Buffer const * ptr = (*it)->updateInset(inset))
1767                         buffer_ = ptr;
1768         }
1769         return buffer_;
1770 }
1771
1772
1773 bool GuiApplication::searchMenu(FuncRequest const & func,
1774         docstring_list & names) const
1775 {
1776         return d->menus_.searchMenu(func, names);
1777 }
1778
1779
1780 bool GuiApplication::readUIFile(QString const & name, bool include)
1781 {
1782         LYXERR(Debug::INIT, "About to read " << name << "...");
1783
1784         FileName ui_path;
1785         if (include) {
1786                 ui_path = libFileSearch("ui", name, "inc");
1787                 if (ui_path.empty())
1788                         ui_path = libFileSearch("ui", changeExtension(name, "inc"));
1789         } else {
1790                 ui_path = libFileSearch("ui", name, "ui");
1791         }
1792
1793         if (ui_path.empty()) {
1794                 static const QString defaultUIFile = "default";
1795                 LYXERR(Debug::INIT, "Could not find " << name);
1796                 if (include) {
1797                         Alert::warning(_("Could not find UI definition file"),
1798                                 bformat(_("Error while reading the included file\n%1$s\n"
1799                                         "Please check your installation."), qstring_to_ucs4(name)));
1800                         return false;
1801                 }
1802                 if (name == defaultUIFile) {
1803                         LYXERR(Debug::INIT, "Could not find default UI file!!");
1804                         Alert::warning(_("Could not find default UI file"),
1805                                 _("LyX could not find the default UI file!\n"
1806                                   "Please check your installation."));
1807                         return false;
1808                 }
1809                 Alert::warning(_("Could not find UI definition file"),
1810                 bformat(_("Error while reading the configuration file\n%1$s\n"
1811                         "Falling back to default.\n"
1812                         "Please look under Tools>Preferences>User Interface and\n"
1813                         "check which User Interface file you are using."), qstring_to_ucs4(name)));
1814                 return readUIFile(defaultUIFile, false);
1815         }
1816
1817         // Ensure that a file is read only once (prevents include loops)
1818         static QStringList uifiles;
1819         QString const uifile = toqstr(ui_path.absFilename());
1820         if (uifiles.contains(uifile)) {
1821                 if (!include) {
1822                         // We are reading again the top uifile so reset the safeguard:
1823                         uifiles.clear();
1824                         d->menus_.reset();
1825                         d->toolbars_.reset();
1826                 } else {
1827                         LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1828                                 << "Is this an include loop?");
1829                         return false;
1830                 }
1831         }
1832         uifiles.push_back(uifile);
1833
1834         LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1835
1836         enum {
1837                 ui_menuset = 1,
1838                 ui_toolbars,
1839                 ui_toolbarset,
1840                 ui_include,
1841                 ui_last
1842         };
1843
1844         LexerKeyword uitags[] = {
1845                 { "include", ui_include },
1846                 { "menuset", ui_menuset },
1847                 { "toolbars", ui_toolbars },
1848                 { "toolbarset", ui_toolbarset }
1849         };
1850
1851         Lexer lex(uitags);
1852         lex.setFile(ui_path);
1853         if (!lex.isOK()) {
1854                 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1855                        << endl;
1856         }
1857
1858         if (lyxerr.debugging(Debug::PARSER))
1859                 lex.printTable(lyxerr);
1860
1861         // store which ui files define Toolbars
1862         static QStringList toolbar_uifiles;
1863
1864         while (lex.isOK()) {
1865                 switch (lex.lex()) {
1866                 case ui_include: {
1867                         lex.next(true);
1868                         QString const file = toqstr(lex.getString());
1869                         if (!readUIFile(file, true))
1870                                 return false;
1871                         break;
1872                 }
1873                 case ui_menuset:
1874                         d->menus_.read(lex);
1875                         break;
1876
1877                 case ui_toolbarset:
1878                         d->toolbars_.readToolbars(lex);
1879                         break;
1880
1881                 case ui_toolbars:
1882                         d->toolbars_.readToolbarSettings(lex);
1883                         toolbar_uifiles.push_back(uifile);
1884                         break;
1885
1886                 default:
1887                         if (!rtrim(lex.getString()).empty())
1888                                 lex.printError("LyX::ReadUIFile: "
1889                                                "Unknown menu tag: `$$Token'");
1890                         break;
1891                 }
1892         }
1893
1894         if (include)
1895                 return true;
1896
1897         QSettings settings;
1898         settings.beginGroup("ui_files");
1899         bool touched = false;
1900         for (int i = 0; i != uifiles.size(); ++i) {
1901                 QFileInfo fi(uifiles[i]);
1902                 QDateTime const date_value = fi.lastModified();
1903                 QString const name_key = QString::number(i);
1904                 // if an ui file which defines Toolbars has changed,
1905                 // we have to reset the settings
1906                 if (toolbar_uifiles.contains(uifiles[i])
1907                  && (!settings.contains(name_key)
1908                  || settings.value(name_key).toString() != uifiles[i]
1909                  || settings.value(name_key + "/date").toDateTime() != date_value)) {
1910                         touched = true;
1911                         settings.setValue(name_key, uifiles[i]);
1912                         settings.setValue(name_key + "/date", date_value);
1913                 }
1914         }
1915         settings.endGroup();
1916         if (touched)
1917                 settings.remove("views");
1918
1919         return true;
1920 }
1921
1922
1923 void GuiApplication::onLastWindowClosed()
1924 {
1925         if (d->global_menubar_)
1926                 d->global_menubar_->grabKeyboard();
1927 }
1928
1929
1930 ////////////////////////////////////////////////////////////////////////
1931 //
1932 // X11 specific stuff goes here...
1933
1934 #ifdef Q_WS_X11
1935 bool GuiApplication::x11EventFilter(XEvent * xev)
1936 {
1937         if (!current_view_)
1938                 return false;
1939
1940         switch (xev->type) {
1941         case SelectionRequest: {
1942                 if (xev->xselectionrequest.selection != XA_PRIMARY)
1943                         break;
1944                 LYXERR(Debug::SELECTION, "X requested selection.");
1945                 BufferView * bv = current_view_->currentBufferView();
1946                 if (bv) {
1947                         docstring const sel = bv->requestSelection();
1948                         if (!sel.empty())
1949                                 d->selection_.put(sel);
1950                 }
1951                 break;
1952         }
1953         case SelectionClear: {
1954                 if (xev->xselectionclear.selection != XA_PRIMARY)
1955                         break;
1956                 LYXERR(Debug::SELECTION, "Lost selection.");
1957                 BufferView * bv = current_view_->currentBufferView();
1958                 if (bv)
1959                         bv->clearSelection();
1960                 break;
1961         }
1962         }
1963         return false;
1964 }
1965 #endif
1966
1967 } // namespace frontend
1968
1969
1970 void hideDialogs(std::string const & name, Inset * inset)
1971 {
1972         if (theApp())
1973                 theApp()->hideDialogs(name, inset);
1974 }
1975
1976
1977 ////////////////////////////////////////////////////////////////////
1978 //
1979 // Font stuff
1980 //
1981 ////////////////////////////////////////////////////////////////////
1982
1983 frontend::FontLoader & theFontLoader()
1984 {
1985         LASSERT(frontend::guiApp, /**/);
1986         return frontend::guiApp->fontLoader();
1987 }
1988
1989
1990 frontend::FontMetrics const & theFontMetrics(Font const & f)
1991 {
1992         return theFontMetrics(f.fontInfo());
1993 }
1994
1995
1996 frontend::FontMetrics const & theFontMetrics(FontInfo const & f)
1997 {
1998         LASSERT(frontend::guiApp, /**/);
1999         return frontend::guiApp->fontLoader().metrics(f);
2000 }
2001
2002
2003 ////////////////////////////////////////////////////////////////////
2004 //
2005 // Misc stuff
2006 //
2007 ////////////////////////////////////////////////////////////////////
2008
2009 frontend::Clipboard & theClipboard()
2010 {
2011         LASSERT(frontend::guiApp, /**/);
2012         return frontend::guiApp->clipboard();
2013 }
2014
2015
2016 frontend::Selection & theSelection()
2017 {
2018         LASSERT(frontend::guiApp, /**/);
2019         return frontend::guiApp->selection();
2020 }
2021
2022
2023 } // namespace lyx
2024
2025 #include "moc_GuiApplication.cpp"