2 * \file GuiApplication.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * \author Abdelrazak Younes
10 * Full author contact details are available in file CREDITS.
15 #include "GuiApplication.h"
17 #include "ColorCache.h"
19 #include "GuiClipboard.h"
21 #include "GuiKeySymbol.h"
22 #include "GuiSelection.h"
25 #include "qt_helpers.h"
28 #include "frontends/alert.h"
29 #include "frontends/Application.h"
30 #include "frontends/FontLoader.h"
31 #include "frontends/FontMetrics.h"
34 #include "BufferList.h"
35 #include "BufferView.h"
38 #include "FuncRequest.h"
39 #include "FuncStatus.h"
43 #include "LaTeXFeatures.h"
46 #include "LyXAction.h"
51 #include "SpellChecker.h"
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"
71 #include "support/linkback/LinkBackProxy.h"
81 #include <QFileOpenEvent>
85 #include <QImageReader>
87 #include <QLibraryInfo>
89 #include <QMacPasteboardMime>
94 #include <QPixmapCache>
96 #include <QSessionManager>
98 #include <QSocketNotifier>
99 #include <QSortFilterProxyModel>
100 #include <QStandardItemModel>
101 #include <QTextCodec>
103 #include <QTranslator>
107 #include <X11/Xatom.h>
108 #include <X11/Xlib.h>
114 #include <QWindowsMime>
121 #include <boost/bind.hpp>
122 #include <boost/crc.hpp>
128 using namespace lyx::support;
131 static void initializeResources()
133 static bool initialized = false;
135 Q_INIT_RESOURCE(Resources);
143 frontend::Application * createApplication(int & argc, char * argv[])
146 // prune -geometry argument(s) by shifting
147 // the following ones 2 places down.
148 for (int i = 0 ; i < argc ; ++i) {
149 if (strcmp(argv[i], "-geometry") == 0) {
150 int const remove = (i+1) < argc ? 2 : 1;
152 for (int j = i; j < argc; ++j)
153 argv[j] = argv[j + remove];
158 return new frontend::GuiApplication(argc, argv);
164 /// Return the list of loadable formats.
165 vector<string> loadableImageFormats()
169 QList<QByteArray> qt_formats = QImageReader::supportedImageFormats();
171 LYXERR(Debug::GRAPHICS,
172 "\nThe image loader can load the following directly:\n");
174 if (qt_formats.empty())
175 LYXERR(Debug::GRAPHICS, "\nQt4 Problem: No Format available!");
177 for (QList<QByteArray>::const_iterator it = qt_formats.begin(); it != qt_formats.end(); ++it) {
179 LYXERR(Debug::GRAPHICS, (const char *) *it << ", ");
181 string ext = ascii_lowercase((const char *) *it);
192 ////////////////////////////////////////////////////////////////////////
194 // Icon loading support code
196 ////////////////////////////////////////////////////////////////////////
206 bool operator<(PngMap const & lhs, PngMap const & rhs)
208 return lhs.key < rhs.key;
214 CompareKey(QString const & name) : name_(name) {}
215 bool operator()(PngMap const & other) const { return other.key == name_; }
221 // this must be sorted alphabetically
222 // Upper case comes before lower case
223 PngMap sorted_png_map[] = {
224 { "Bumpeq", "bumpeq2" },
227 { "Delta", "delta2" },
228 { "Downarrow", "downarrow2" },
229 { "Gamma", "gamma2" },
230 { "Lambda", "lambda2" },
231 { "Leftarrow", "leftarrow2" },
232 { "Leftrightarrow", "leftrightarrow2" },
233 { "Longleftarrow", "longleftarrow2" },
234 { "Longleftrightarrow", "longleftrightarrow2" },
235 { "Longrightarrow", "longrightarrow2" },
236 { "Omega", "omega2" },
240 { "Rightarrow", "rightarrow2" },
241 { "Sigma", "sigma2" },
242 { "Subset", "subset2" },
243 { "Supset", "supset2" },
244 { "Theta", "theta2" },
245 { "Uparrow", "uparrow2" },
246 { "Updownarrow", "updownarrow2" },
247 { "Upsilon", "upsilon2" },
248 { "Vdash", "vdash3" },
251 { "nLeftarrow", "nleftarrow2" },
252 { "nLeftrightarrow", "nleftrightarrow2" },
253 { "nRightarrow", "nrightarrow2" },
254 { "nVDash", "nvdash3" },
255 { "nvDash", "nvdash2" },
256 { "textrm \\AA", "textrm_AA"},
257 { "textrm \\O", "textrm_O"},
258 { "vDash", "vdash2" }
261 size_t const nr_sorted_png_map = sizeof(sorted_png_map) / sizeof(PngMap);
264 QString findPng(QString const & name)
266 PngMap const * const begin = sorted_png_map;
267 PngMap const * const end = begin + nr_sorted_png_map;
268 LASSERT(sorted(begin, end), /**/);
270 PngMap const * const it = find_if(begin, end, CompareKey(name));
274 png_name = it->value;
277 png_name.replace('_', "underscore");
278 png_name.replace(' ', '_');
280 // This way we can have "math-delim { }" on the toolbar.
281 png_name.replace('(', "lparen");
282 png_name.replace(')', "rparen");
283 png_name.replace('[', "lbracket");
284 png_name.replace(']', "rbracket");
285 png_name.replace('{', "lbrace");
286 png_name.replace('}', "rbrace");
287 png_name.replace('|', "bars");
288 png_name.replace(',', "thinspace");
289 png_name.replace(':', "mediumspace");
290 png_name.replace(';', "thickspace");
291 png_name.replace('!', "negthinspace");
294 LYXERR(Debug::GUI, "findPng(" << name << ")\n"
295 << "Looking for math PNG called \"" << png_name << '"');
302 QString iconName(FuncRequest const & f, bool unknown)
304 initializeResources();
309 case LFUN_MATH_INSERT:
310 if (!f.argument().empty()) {
312 name1 = findPng(toqstr(f.argument()).mid(1));
315 case LFUN_MATH_DELIM:
316 case LFUN_MATH_BIGDELIM:
318 name1 = findPng(toqstr(f.argument()));
322 name1 = toqstr(f.argument());
324 case LFUN_COMMAND_ALTERNATIVES: {
325 // use the first of the alternative commands
327 docstring dummy = split(f.argument(), firstcom, ';');
328 name1 = toqstr(firstcom);
329 name1.replace(' ', '_');
333 name2 = toqstr(lyxaction.getActionName(f.action));
336 if (!f.argument().empty()) {
337 name1 = name2 + ' ' + toqstr(f.argument());
338 name1.replace(' ', '_');
339 name1.replace('\\', "backslash");
343 FileName fname = libFileSearch("images/" + path, name1, "png");
345 return toqstr(fname.absFilename());
347 fname = libFileSearch("images/" + path, name2, "png");
349 return toqstr(fname.absFilename());
351 path = ":/images/" + path;
354 LYXERR0("Directory " << path << " not found in resource!");
358 if (res.exists(name1))
362 if (res.exists(name2))
365 LYXERR(Debug::GUI, "Cannot find icon with filename "
366 << "\"" << name1 << "\""
368 << "\"" << name2 << "\""
370 << lyxaction.getActionName(f.action)
371 << '(' << to_utf8(f.argument()) << ")\"");
374 fname = libFileSearch(QString("images/"), "unknown", "png");
376 return toqstr(fname.absFilename());
377 return QString(":/images/unknown.png");
383 QPixmap getPixmap(QString const & path, QString const & name, QString const & ext)
386 FileName fname = libFileSearch(path, name, ext);
387 QString path1 = toqstr(fname.absFilename());
388 QString path2 = ":/" + path + name + "." + ext;
390 if (pixmap.load(path1)) {
393 else if (pixmap.load(path2)) {
397 LYXERR0("Cannot load pixmap \""
398 << path << name << '.' << ext
399 << "\", please verify resource system!");
404 QIcon getIcon(FuncRequest const & f, bool unknown)
406 QString icon = iconName(f, unknown);
410 //LYXERR(Debug::GUI, "Found icon: " << icon);
412 if (!pm.load(icon)) {
413 LYXERR0("Cannot load icon " << icon << " please verify resource system!");
421 ////////////////////////////////////////////////////////////////////////
423 // LyX server support code.
425 ////////////////////////////////////////////////////////////////////////
427 class SocketNotifier : public QSocketNotifier
430 /// connect a connection notification from the LyXServerSocket
431 SocketNotifier(QObject * parent, int fd, Application::SocketCallback func)
432 : QSocketNotifier(fd, QSocketNotifier::Read, parent), func_(func)
436 /// The callback function
437 Application::SocketCallback func_;
441 ////////////////////////////////////////////////////////////////////////
443 // Mac specific stuff goes here...
445 ////////////////////////////////////////////////////////////////////////
447 class MenuTranslator : public QTranslator
450 MenuTranslator(QObject * parent)
451 : QTranslator(parent)
454 QString translate(const char * /*context*/,
455 const char * sourceText,
456 const char * /*comment*/ = 0)
458 string const s = sourceText;
459 if (s == N_("About %1") || s == N_("Preferences")
460 || s == N_("Reconfigure") || s == N_("Quit %1"))
467 class GlobalMenuBar : public QMenuBar
471 GlobalMenuBar() : QMenuBar(0) {}
474 bool event(QEvent * e)
476 if (e->type() == QEvent::ShortcutOverride) {
477 // && activeWindow() == 0) {
478 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
480 setKeySymbol(&sym, ke);
481 guiApp->processKeySym(sym, q_key_state(ke->modifiers()));
490 // QMacPasteboardMimeGraphics can only be compiled on Mac.
492 class QMacPasteboardMimeGraphics : public QMacPasteboardMime
495 QMacPasteboardMimeGraphics()
496 : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL)
499 QString convertorName() { return "Graphics"; }
501 QString flavorFor(QString const & mime)
503 LYXERR(Debug::ACTION, "flavorFor " << mime);
504 if (mime == pdfMimeType())
505 return QLatin1String("com.adobe.pdf");
509 QString mimeFor(QString flav)
511 LYXERR(Debug::ACTION, "mimeFor " << flav);
512 if (flav == QLatin1String("com.adobe.pdf"))
513 return pdfMimeType();
517 bool canConvert(QString const & mime, QString flav)
519 return mimeFor(flav) == mime;
522 QVariant convertToMime(QString const & /*mime*/, QList<QByteArray> data,
526 qWarning("QMacPasteboardMimeGraphics: Cannot handle multiple member data");
530 QList<QByteArray> convertFromMime(QString const & /*mime*/,
531 QVariant data, QString /*flav*/)
533 QList<QByteArray> ret;
534 ret.append(data.toByteArray());
540 ///////////////////////////////////////////////////////////////
542 // You can find more platform specific stuff at the end of this file...
544 ///////////////////////////////////////////////////////////////
546 ////////////////////////////////////////////////////////////////////////
547 // Windows specific stuff goes here...
550 // QWindowsMimeMetafile can only be compiled on Windows.
552 static FORMATETC cfFromMime(QString const & mimetype)
555 if (mimetype == emfMimeType()) {
556 formatetc.cfFormat = CF_ENHMETAFILE;
557 formatetc.tymed = TYMED_ENHMF;
558 } else if (mimetype == wmfMimeType()) {
559 formatetc.cfFormat = CF_METAFILEPICT;
560 formatetc.tymed = TYMED_MFPICT;
563 formatetc.dwAspect = DVASPECT_CONTENT;
564 formatetc.lindex = -1;
569 class QWindowsMimeMetafile : public QWindowsMime {
571 QWindowsMimeMetafile() {}
573 bool canConvertFromMime(FORMATETC const & formatetc,
574 QMimeData const * mimedata) const
579 bool canConvertToMime(QString const & mimetype,
580 IDataObject * pDataObj) const
582 if (mimetype != emfMimeType() && mimetype != wmfMimeType())
584 FORMATETC formatetc = cfFromMime(mimetype);
585 return pDataObj->QueryGetData(&formatetc) == S_OK;
588 bool convertFromMime(FORMATETC const & formatetc,
589 const QMimeData * mimedata, STGMEDIUM * pmedium) const
594 QVariant convertToMime(QString const & mimetype, IDataObject * pDataObj,
595 QVariant::Type preferredType) const
598 if (!canConvertToMime(mimetype, pDataObj))
601 FORMATETC formatetc = cfFromMime(mimetype);
603 if (pDataObj->GetData(&formatetc, &s) != S_OK)
607 if (s.tymed == TYMED_ENHMF) {
608 dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, 0, 0);
609 data.resize(dataSize);
610 dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, dataSize,
611 (LPBYTE)data.data());
612 } else if (s.tymed == TYMED_MFPICT) {
613 dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, 0, 0);
614 data.resize(dataSize);
615 dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, dataSize,
616 (LPBYTE)data.data());
619 ReleaseStgMedium(&s);
625 QVector<FORMATETC> formatsForMime(QString const & mimetype,
626 QMimeData const * mimedata) const
628 QVector<FORMATETC> formats;
629 if (mimetype == emfMimeType() || mimetype == wmfMimeType())
630 formats += cfFromMime(mimetype);
634 QString mimeForFormat(FORMATETC const & formatetc) const
636 switch (formatetc.cfFormat) {
638 return emfMimeType();
639 case CF_METAFILEPICT:
640 return wmfMimeType();
648 ////////////////////////////////////////////////////////////////////////
649 // GuiApplication::Private definition and implementation.
650 ////////////////////////////////////////////////////////////////////////
652 struct GuiApplication::Private
654 Private(): language_model_(0), global_menubar_(0),
655 meta_fake_bit(NoModifier)
658 /// WMF Mime handler for Windows clipboard.
659 wmf_mime_ = new QWindowsMimeMetafile();
661 initKeySequences(&theTopLevelKeymap());
664 void initKeySequences(KeyMap * kb)
666 keyseq = KeySequence(kb, kb);
667 cancel_meta_seq = KeySequence(kb, kb);
671 QSortFilterProxyModel * language_model_;
673 GuiClipboard clipboard_;
675 GuiSelection selection_;
677 FontLoader font_loader_;
679 ColorCache color_cache_;
681 QTranslator qt_trans_;
683 QHash<int, SocketNotifier *> socket_notifiers_;
687 /// The global instance
690 /// this timer is used for any regular events one wants to
691 /// perform. at present it is used to check if forked processes
693 QTimer general_timer_;
695 /// delayed FuncRequests
696 std::queue<FuncRequest> func_request_queue_;
701 KeySequence cancel_meta_seq;
703 KeyModifier meta_fake_bit;
705 /// Multiple views container.
707 * Warning: This must not be a smart pointer as the destruction of the
708 * object is handled by Qt when the view is closed
709 * \sa Qt::WA_DeleteOnClose attribute.
711 QHash<int, GuiView *> views_;
713 /// Only used on mac.
714 GlobalMenuBar * global_menubar_;
717 /// Linkback mime handler for MacOSX.
718 QMacPasteboardMimeGraphics mac_pasteboard_mime_;
722 /// WMF Mime handler for Windows clipboard.
723 QWindowsMimeMetafile * wmf_mime_;
728 GuiApplication * guiApp;
730 GuiApplication::~GuiApplication()
733 closeAllLinkBackLinks();
739 GuiApplication::GuiApplication(int & argc, char ** argv)
740 : QApplication(argc, argv), current_view_(0),
741 d(new GuiApplication::Private)
743 QString app_name = "LyX";
744 QCoreApplication::setOrganizationName(app_name);
745 QCoreApplication::setOrganizationDomain("lyx.org");
746 QCoreApplication::setApplicationName(lyx_package);
748 // Install translator for GUI elements.
749 installTranslator(&d->qt_trans_);
751 // FIXME: quitOnLastWindowClosed is true by default. We should have a
752 // lyxrc setting for this in order to let the application stay resident.
753 // But then we need some kind of dock icon, at least on Windows.
755 if (lyxrc.quit_on_last_window_closed)
756 setQuitOnLastWindowClosed(false);
759 // FIXME: Do we need a lyxrc setting for this on Mac? This behaviour
760 // seems to be the default case for applications like LyX.
761 setQuitOnLastWindowClosed(false);
763 // This allows to translate the strings that appear in the LyX menu.
764 /// A translator suitable for the entries in the LyX menu.
765 /// Only needed with Qt/Mac.
766 installTranslator(new MenuTranslator(this));
770 // doubleClickInterval() is 400 ms on X11 which is just too long.
771 // On Windows and Mac OS X, the operating system's value is used.
772 // On Microsoft Windows, calling this function sets the double
773 // click interval for all applications. So we don't!
774 QApplication::setDoubleClickInterval(300);
777 connect(this, SIGNAL(lastWindowClosed()), this, SLOT(onLastWindowClosed()));
779 // needs to be done before reading lyxrc
781 lyxrc.dpi = (w.logicalDpiX() + w.logicalDpiY()) / 2;
785 // Set the cache to 5120 kilobytes which corresponds to screen size of
786 // 1280 by 1024 pixels with a color depth of 32 bits.
787 QPixmapCache::setCacheLimit(5120);
789 // Initialize RC Fonts
790 if (lyxrc.roman_font_name.empty())
791 lyxrc.roman_font_name = fromqstr(romanFontName());
793 if (lyxrc.sans_font_name.empty())
794 lyxrc.sans_font_name = fromqstr(sansFontName());
796 if (lyxrc.typewriter_font_name.empty())
797 lyxrc.typewriter_font_name = fromqstr(typewriterFontName());
799 d->general_timer_.setInterval(500);
800 connect(&d->general_timer_, SIGNAL(timeout()),
801 this, SLOT(handleRegularEvents()));
802 d->general_timer_.start();
806 GuiApplication * theGuiApp()
808 return dynamic_cast<GuiApplication *>(theApp());
812 void GuiApplication::clearSession()
819 docstring GuiApplication::iconName(FuncRequest const & f, bool unknown)
821 return qstring_to_ucs4(lyx::frontend::iconName(f, unknown));
825 LyXView * GuiApplication::currentWindow()
828 /* In LyX/Mac, when a dialog is open, the menus of the
829 application can still be accessed without giving focus to
830 the main window. In this case, we want to disable the menu
831 entries that are buffer or view-related.
833 if (current_view_ && activeWindow() != current_view_)
836 return current_view_;
840 bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const
846 case LFUN_WINDOW_CLOSE:
847 enable = d->views_.size() > 0;
850 case LFUN_BUFFER_NEW:
851 case LFUN_BUFFER_NEW_TEMPLATE:
854 case LFUN_SCREEN_FONT_UPDATE:
856 case LFUN_WINDOW_NEW:
858 case LFUN_LYXRC_APPLY:
859 case LFUN_COMMAND_PREFIX:
861 case LFUN_META_PREFIX:
862 case LFUN_RECONFIGURE:
863 case LFUN_SERVER_GET_FILENAME:
864 case LFUN_SERVER_NOTIFY:
873 flag.setEnabled(false);
879 // This function runs "configure" and then rereads lyx.defaults to
880 // reconfigure the automatic settings.
881 static void reconfigure(LyXView * lv, string const & option)
883 // emit message signal.
885 lv->message(_("Running configure..."));
887 // Run configure in user lyx directory
888 PathChanger p(package().user_support());
889 string configure_command = package().configure_command();
890 configure_command += option;
892 int ret = one.startscript(Systemcall::Wait, configure_command);
894 // emit message signal.
896 lv->message(_("Reloading configuration..."));
897 lyxrc.read(libFileSearch(QString(), "lyxrc.defaults"));
898 // Re-read packages.lst
899 LaTeXFeatures::getAvailable();
902 Alert::information(_("System reconfiguration failed"),
903 _("The system reconfiguration has failed.\n"
904 "Default textclass is used but LyX may "
905 "not be able to work properly.\n"
906 "Please reconfigure again if needed."));
909 Alert::information(_("System reconfigured"),
910 _("The system has been reconfigured.\n"
911 "You need to restart LyX to make use of any\n"
912 "updated document class specifications."));
916 void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
918 switch (cmd.action) {
920 case LFUN_WINDOW_NEW:
921 createView(toqstr(cmd.argument()));
924 case LFUN_WINDOW_CLOSE:
925 // update bookmark pit of the current buffer before window close
926 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
927 theLyXFunc().gotoBookmark(i+1, false, false);
928 // clear the last opened list, because
929 // maybe this will end the session
930 theSession().lastOpened().clear();
931 current_view_->close();
935 // quitting is triggered by the gui code
936 // (leaving the event loop).
938 current_view_->message(from_utf8(N_("Exiting.")));
943 case LFUN_SCREEN_FONT_UPDATE: {
944 // handle the screen font changes.
945 d->font_loader_.update();
946 // Backup current_view_
947 GuiView * view = current_view_;
948 // Set current_view_ to zero to forbid GuiWorkArea::redraw()
949 // to skip the refresh.
951 BufferList::iterator it = theBufferList().begin();
952 BufferList::iterator const end = theBufferList().end();
953 for (; it != end; ++it)
955 // Restore current_view_
956 current_view_ = view;
960 case LFUN_BUFFER_NEW:
961 if (d->views_.empty()
962 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
963 createView(QString(), false); // keep hidden
964 current_view_->newDocument(to_utf8(cmd.argument()), false);
965 current_view_->show();
966 setActiveWindow(current_view_);
968 current_view_->newDocument(to_utf8(cmd.argument()), false);
972 case LFUN_BUFFER_NEW_TEMPLATE:
973 if (d->views_.empty()
974 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
976 current_view_->newDocument(to_utf8(cmd.argument()), true);
977 if (!current_view_->documentBufferView())
978 current_view_->close();
980 current_view_->newDocument(to_utf8(cmd.argument()), true);
985 // FIXME: create a new method shared with LFUN_HELP_OPEN.
986 if (d->views_.empty()
987 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
988 string const fname = to_utf8(cmd.argument());
989 // We want the ui session to be saved per document and not per
990 // window number. The filename crc is a good enough identifier.
991 boost::crc_32_type crc;
992 crc = for_each(fname.begin(), fname.end(), crc);
993 createView(crc.checksum());
994 current_view_->openDocument(fname);
995 if (current_view_ && !current_view_->documentBufferView())
996 current_view_->close();
998 current_view_->openDocument(to_utf8(cmd.argument()));
1001 case LFUN_HELP_OPEN: {
1002 // FIXME: create a new method shared with LFUN_FILE_OPEN.
1003 if (current_view_ == 0)
1005 string const arg = to_utf8(cmd.argument());
1007 current_view_->message(_("Missing argument"));
1010 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1012 fname = i18nLibFileSearch("examples", arg, "lyx");
1014 if (fname.empty()) {
1015 lyxerr << "LyX: unable to find documentation file `"
1016 << arg << "'. Bad installation?" << endl;
1019 current_view_->message(bformat(_("Opening help file %1$s..."),
1020 makeDisplayPath(fname.absFilename())));
1021 Buffer * buf = current_view_->loadDocument(fname, false);
1023 current_view_->setBuffer(buf);
1024 buf->updateLabels();
1025 buf->errors("Parse");
1030 case LFUN_SET_COLOR: {
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>"));
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.
1045 graphics::GCache::get().changeDisplay(true);
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)));
1057 // Make sure we don't keep old colors in cache.
1058 d->color_cache_.clear();
1062 case LFUN_LYXRC_APPLY: {
1063 // reset active key sequences, since the bindings
1064 // are updated (bug 6064)
1066 LyXRC const lyxrc_orig = lyxrc;
1068 istringstream ss(to_utf8(cmd.argument()));
1069 bool const success = lyxrc.read(ss) == 0;
1072 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1073 << "Unable to read lyxrc data"
1078 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1085 case LFUN_COMMAND_PREFIX:
1086 lyx::dispatch(FuncRequest(LFUN_MESSAGE, d->keyseq.printOptions(true)));
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")));
1099 case LFUN_META_PREFIX:
1100 d->meta_fake_bit = AltModifier;
1101 dr.setMessage(d->keyseq.print(KeySequence::ForGui));
1104 // --- Menus -----------------------------------------------
1105 case LFUN_RECONFIGURE:
1106 // argument is any additional parameter to the configure.py command
1107 reconfigure(currentView(), to_utf8(cmd.argument()));
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 << ']');
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));
1127 // Notify the caller that the action has not been dispatched.
1128 dr.dispatched(false);
1132 // The action has been dispatched.
1133 dr.dispatched(true);
1137 docstring GuiApplication::viewStatusMessage()
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-";
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);
1152 void GuiApplication::handleKeyFunc(FuncCode action)
1156 if (d->keyseq.length())
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(),
1164 // Need to clear, in case the minibuffer calls these
1167 // copied verbatim from do_accent_char
1168 bv->cursor().resetAnchor();
1169 bv->processUpdateFlags(Update::FitCursor);
1173 void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
1175 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
1177 GuiView * lv = currentView();
1179 // Do nothing if we have nothing (JMarc)
1180 if (!keysym.isOK()) {
1181 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
1182 lv->restartCursor();
1186 if (keysym.isModifier()) {
1187 LYXERR(Debug::KEY, "isModifier true");
1189 lv->restartCursor();
1193 char_type encoded_last_key = keysym.getUCSEncoded();
1195 // Do a one-deep top-level lookup for
1196 // cancel and meta-fake keys. RVDK_PATCH_5
1197 d->cancel_meta_seq.reset();
1199 FuncRequest func = d->cancel_meta_seq.addkey(keysym, state);
1200 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
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 << ']');
1211 // Dont remove this unless you know what you are doing.
1212 d->meta_fake_bit = NoModifier;
1214 // Can this happen now ?
1215 if (func.action == LFUN_NOACTION)
1216 func = FuncRequest(LFUN_COMMAND_PREFIX);
1218 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
1219 << d->keyseq.print(KeySequence::Portable) << ']');
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)
1225 if (d->keyseq.length() > 1)
1226 lv->message(d->keyseq.print(KeySequence::ForGui));
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);
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
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);
1246 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
1247 lv->message(_("Unknown function."));
1248 lv->restartCursor();
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) << "']");
1261 lyx::dispatch(func);
1268 void GuiApplication::dispatchDelayed(FuncRequest const & func)
1270 d->func_request_queue_.push(func);
1271 QTimer::singleShot(0, this, SLOT(processFuncRequestQueue()));
1275 void GuiApplication::resetGui()
1277 // Set the language defined by the user.
1281 if (!readUIFile(toqstr(lyxrc.ui_file)))
1282 // Gives some error box here.
1285 if (d->global_menubar_)
1286 d->menus_.fillMenuBar(d->global_menubar_, 0, false);
1288 QHash<int, GuiView *>::iterator it;
1289 for (it = d->views_.begin(); it != d->views_.end(); ++it) {
1292 gv->setLayoutDirection(layoutDirection());
1296 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
1300 void GuiApplication::createView(int view_id)
1302 createView(QString(), true, view_id);
1306 void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
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();
1316 while (d->views_.find(id) != d->views_.end())
1319 LYXERR(Debug::GUI, "About to create new window with ID " << id);
1320 GuiView * view = new GuiView(id);
1322 d->views_[id] = view;
1326 setActiveWindow(view);
1329 if (!geometry_arg.isEmpty()) {
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);
1346 Clipboard & GuiApplication::clipboard()
1348 return d->clipboard_;
1352 Selection & GuiApplication::selection()
1354 return d->selection_;
1358 FontLoader & GuiApplication::fontLoader()
1360 return d->font_loader_;
1364 Toolbars const & GuiApplication::toolbars() const
1366 return d->toolbars_;
1370 Toolbars & GuiApplication::toolbars()
1372 return d->toolbars_;
1376 Menus const & GuiApplication::menus() const
1382 Menus & GuiApplication::menus()
1388 QList<int> GuiApplication::viewIds() const
1390 return d->views_.keys();
1394 ColorCache & GuiApplication::colorCache()
1396 return d->color_cache_;
1400 int GuiApplication::exec()
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()));
1407 return QApplication::exec();
1411 void GuiApplication::exit(int status)
1413 QApplication::exit(status);
1417 void GuiApplication::setGuiLanguage()
1419 // Set the language defined by the user.
1422 QString const default_language = toqstr(Messages::defaultLanguage());
1423 LYXERR(Debug::LOCALE, "Tring to set default locale to: " << default_language);
1424 QLocale const default_locale(default_language);
1425 QLocale::setDefault(default_locale);
1427 // install translation file for Qt built-in dialogs
1428 QString const language_name = QString("qt_") + default_locale.name();
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 "
1439 LYXERR(Debug::LOCALE, "Successfully installed Qt translations for locale "
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);
1451 setLayoutDirection(Qt::LeftToRight);
1456 void GuiApplication::processFuncRequestQueue()
1458 while (!d->func_request_queue_.empty()) {
1459 lyx::dispatch(d->func_request_queue_.back());
1460 d->func_request_queue_.pop();
1465 void GuiApplication::execBatchCommands()
1470 if (!readUIFile(toqstr(lyxrc.ui_file)))
1471 // Gives some error box here.
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);
1482 lyx::execBatchCommands();
1486 QAbstractItemModel * GuiApplication::languageModel()
1488 if (d->language_model_)
1489 return d->language_model_;
1491 QStandardItemModel * lang_model = new QStandardItemModel(this);
1492 lang_model->insertColumns(0, 1);
1494 Languages::const_iterator it = languages.begin();
1495 Languages::const_iterator end = 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);
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);
1508 return d->language_model_;
1512 void GuiApplication::restoreGuiSession()
1514 if (!lyxrc.load_session)
1517 Session & session = theSession();
1518 LastOpenedSection::LastOpened const & lastopened =
1519 session.lastOpened().getfiles();
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());
1534 current_view_->loadDocument(file_name, false);
1536 if (lastopened[i].active)
1537 active_file = file_name;
1540 // Restore last active buffer
1541 Buffer * buffer = theBufferList().getBuffer(active_file);
1543 current_view_->setBuffer(buffer);
1545 // clear this list to save a few bytes of RAM
1546 session.lastOpened().clear();
1550 QString const GuiApplication::romanFontName()
1553 font.setKerning(false);
1554 font.setStyleHint(QFont::Serif);
1555 font.setFamily("serif");
1557 return QFontInfo(font).family();
1561 QString const GuiApplication::sansFontName()
1564 font.setKerning(false);
1565 font.setStyleHint(QFont::SansSerif);
1566 font.setFamily("sans");
1568 return QFontInfo(font).family();
1572 QString const GuiApplication::typewriterFontName()
1575 font.setKerning(false);
1576 font.setStyleHint(QFont::TypeWriter);
1577 font.setFamily("monospace");
1579 return QFontInfo(font).family();
1583 void GuiApplication::handleRegularEvents()
1585 ForkedCallsController::handleCompletedProcesses();
1589 bool GuiApplication::event(QEvent * e)
1592 case QEvent::FileOpen: {
1593 // Open a file; this happens only on Mac OS X for now.
1595 // We do this asynchronously because on startup the batch
1596 // commands are not executed here yet and the gui is not ready
1598 QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
1599 dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file())));
1604 return QApplication::event(e);
1609 bool GuiApplication::notify(QObject * receiver, QEvent * event)
1612 return QApplication::notify(receiver, event);
1614 catch (ExceptionMessage const & e) {
1616 case ErrorException:
1618 setQuitOnLastWindowClosed(false);
1620 Alert::error(e.title_, e.details_);
1622 // Properly crash in debug mode in order to get a useful backtrace.
1625 // In release mode, try to exit gracefully.
1628 case BufferException: {
1629 if (!current_view_->documentBufferView())
1631 Buffer * buf = ¤t_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);
1639 case WarningException:
1640 Alert::warning(e.title_, e.details_);
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."
1648 s += from_ascii(e.what());
1649 Alert::error(_("Software exception Detected"), s);
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);
1663 bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
1665 QColor const & qcol = d->color_cache_.get(col);
1666 if (!qcol.isValid()) {
1672 rgbcol.r = qcol.red();
1673 rgbcol.g = qcol.green();
1674 rgbcol.b = qcol.blue();
1679 string const GuiApplication::hexName(ColorCode col)
1681 return ltrim(fromqstr(d->color_cache_.get(col).name()), "#");
1685 void GuiApplication::registerSocketCallback(int fd, SocketCallback func)
1687 SocketNotifier * sn = new SocketNotifier(this, fd, func);
1688 d->socket_notifiers_[fd] = sn;
1689 connect(sn, SIGNAL(activated(int)), this, SLOT(socketDataReceived(int)));
1693 void GuiApplication::socketDataReceived(int fd)
1695 d->socket_notifiers_[fd]->func_();
1699 void GuiApplication::unregisterSocketCallback(int fd)
1701 d->socket_notifiers_.take(fd)->setEnabled(false);
1705 void GuiApplication::commitData(QSessionManager & sm)
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
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())
1719 void GuiApplication::unregisterView(GuiView * gv)
1721 LASSERT(d->views_[gv->id()] == gv, /**/);
1722 d->views_.remove(gv->id());
1723 if (current_view_ == gv)
1728 bool GuiApplication::closeAllViews()
1730 if (d->views_.empty())
1733 // When a view/window was closed before without quitting LyX, there
1734 // are already entries in the lastOpened list.
1735 theSession().lastOpened().clear();
1737 QList<GuiView *> views = d->views_.values();
1738 foreach (GuiView * view, views) {
1748 GuiView & GuiApplication::view(int id) const
1750 LASSERT(d->views_.contains(id), /**/);
1751 return *d->views_.value(id);
1755 void GuiApplication::hideDialogs(string const & name, Inset * inset) const
1757 QList<GuiView *> views = d->views_.values();
1758 foreach (GuiView * view, views)
1759 view->hideDialog(name, inset);
1763 Buffer const * GuiApplication::updateInset(Inset const * inset) const
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))
1775 bool GuiApplication::searchMenu(FuncRequest const & func,
1776 docstring_list & names) const
1778 return d->menus_.searchMenu(func, names);
1782 bool GuiApplication::readUIFile(QString const & name, bool include)
1784 LYXERR(Debug::INIT, "About to read " << name << "...");
1788 ui_path = libFileSearch("ui", name, "inc");
1789 if (ui_path.empty())
1790 ui_path = libFileSearch("ui", changeExtension(name, "inc"));
1792 ui_path = libFileSearch("ui", name, "ui");
1795 if (ui_path.empty()) {
1796 static const QString defaultUIFile = "default";
1797 LYXERR(Debug::INIT, "Could not find " << name);
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)));
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."));
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);
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)) {
1824 // We are reading again the top uifile so reset the safeguard:
1827 d->toolbars_.reset();
1829 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1830 << "Is this an include loop?");
1834 uifiles.push_back(uifile);
1836 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1846 LexerKeyword uitags[] = {
1847 { "include", ui_include },
1848 { "menuset", ui_menuset },
1849 { "toolbars", ui_toolbars },
1850 { "toolbarset", ui_toolbarset }
1854 lex.setFile(ui_path);
1856 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1860 if (lyxerr.debugging(Debug::PARSER))
1861 lex.printTable(lyxerr);
1863 // store which ui files define Toolbars
1864 static QStringList toolbar_uifiles;
1866 while (lex.isOK()) {
1867 switch (lex.lex()) {
1870 QString const file = toqstr(lex.getString());
1871 if (!readUIFile(file, true))
1876 d->menus_.read(lex);
1880 d->toolbars_.readToolbars(lex);
1884 d->toolbars_.readToolbarSettings(lex);
1885 toolbar_uifiles.push_back(uifile);
1889 if (!rtrim(lex.getString()).empty())
1890 lex.printError("LyX::ReadUIFile: "
1891 "Unknown menu tag: `$$Token'");
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)) {
1913 settings.setValue(name_key, uifiles[i]);
1914 settings.setValue(name_key + "/date", date_value);
1917 settings.endGroup();
1919 settings.remove("views");
1925 void GuiApplication::onLastWindowClosed()
1927 if (d->global_menubar_)
1928 d->global_menubar_->grabKeyboard();
1932 ////////////////////////////////////////////////////////////////////////
1934 // X11 specific stuff goes here...
1937 bool GuiApplication::x11EventFilter(XEvent * xev)
1942 switch (xev->type) {
1943 case SelectionRequest: {
1944 if (xev->xselectionrequest.selection != XA_PRIMARY)
1946 LYXERR(Debug::SELECTION, "X requested selection.");
1947 BufferView * bv = current_view_->currentBufferView();
1949 docstring const sel = bv->requestSelection();
1951 d->selection_.put(sel);
1955 case SelectionClear: {
1956 if (xev->xselectionclear.selection != XA_PRIMARY)
1958 LYXERR(Debug::SELECTION, "Lost selection.");
1959 BufferView * bv = current_view_->currentBufferView();
1961 bv->clearSelection();
1969 } // namespace frontend
1972 void hideDialogs(std::string const & name, Inset * inset)
1975 theApp()->hideDialogs(name, inset);
1979 ////////////////////////////////////////////////////////////////////
1983 ////////////////////////////////////////////////////////////////////
1985 frontend::FontLoader & theFontLoader()
1987 LASSERT(frontend::guiApp, /**/);
1988 return frontend::guiApp->fontLoader();
1992 frontend::FontMetrics const & theFontMetrics(Font const & f)
1994 return theFontMetrics(f.fontInfo());
1998 frontend::FontMetrics const & theFontMetrics(FontInfo const & f)
2000 LASSERT(frontend::guiApp, /**/);
2001 return frontend::guiApp->fontLoader().metrics(f);
2005 ////////////////////////////////////////////////////////////////////
2009 ////////////////////////////////////////////////////////////////////
2011 frontend::Clipboard & theClipboard()
2013 LASSERT(frontend::guiApp, /**/);
2014 return frontend::guiApp->clipboard();
2018 frontend::Selection & theSelection()
2020 LASSERT(frontend::guiApp, /**/);
2021 return frontend::guiApp->selection();
2027 #include "moc_GuiApplication.cpp"