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>
129 using namespace lyx::support;
132 static void initializeResources()
134 static bool initialized = false;
136 Q_INIT_RESOURCE(Resources);
144 frontend::Application * createApplication(int & argc, char * argv[])
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;
153 for (int j = i; j < argc; ++j)
154 argv[j] = argv[j + remove];
159 return new frontend::GuiApplication(argc, argv);
165 /// Return the list of loadable formats.
166 vector<string> loadableImageFormats()
170 QList<QByteArray> qt_formats = QImageReader::supportedImageFormats();
172 LYXERR(Debug::GRAPHICS,
173 "\nThe image loader can load the following directly:\n");
175 if (qt_formats.empty())
176 LYXERR(Debug::GRAPHICS, "\nQt4 Problem: No Format available!");
178 for (QList<QByteArray>::const_iterator it = qt_formats.begin(); it != qt_formats.end(); ++it) {
180 LYXERR(Debug::GRAPHICS, (const char *) *it << ", ");
182 string ext = ascii_lowercase((const char *) *it);
193 ////////////////////////////////////////////////////////////////////////
195 // Icon loading support code
197 ////////////////////////////////////////////////////////////////////////
207 bool operator<(PngMap const & lhs, PngMap const & rhs)
209 return lhs.key < rhs.key;
215 CompareKey(QString const & name) : name_(name) {}
216 bool operator()(PngMap const & other) const { return other.key == name_; }
222 // this must be sorted alphabetically
223 // Upper case comes before lower case
224 PngMap sorted_png_map[] = {
225 { "Bumpeq", "bumpeq2" },
228 { "Delta", "delta2" },
229 { "Diamond", "diamond2" },
230 { "Downarrow", "downarrow2" },
231 { "Gamma", "gamma2" },
232 { "Lambda", "lambda2" },
233 { "Leftarrow", "leftarrow2" },
234 { "Leftrightarrow", "leftrightarrow2" },
235 { "Longleftarrow", "longleftarrow2" },
236 { "Longleftrightarrow", "longleftrightarrow2" },
237 { "Longrightarrow", "longrightarrow2" },
238 { "Omega", "omega2" },
242 { "Rightarrow", "rightarrow2" },
243 { "Sigma", "sigma2" },
244 { "Subset", "subset2" },
245 { "Supset", "supset2" },
246 { "Theta", "theta2" },
247 { "Uparrow", "uparrow2" },
248 { "Updownarrow", "updownarrow2" },
249 { "Upsilon", "upsilon2" },
250 { "Vdash", "vdash3" },
253 { "nLeftarrow", "nleftarrow2" },
254 { "nLeftrightarrow", "nleftrightarrow2" },
255 { "nRightarrow", "nrightarrow2" },
256 { "nVDash", "nvdash3" },
257 { "nvDash", "nvdash2" },
258 { "textrm \\AA", "textrm_AA"},
259 { "textrm \\O", "textrm_O"},
260 { "vDash", "vdash2" }
263 size_t const nr_sorted_png_map = sizeof(sorted_png_map) / sizeof(PngMap);
266 QString findPng(QString const & name)
268 PngMap const * const begin = sorted_png_map;
269 PngMap const * const end = begin + nr_sorted_png_map;
270 LASSERT(sorted(begin, end), /**/);
272 PngMap const * const it = find_if(begin, end, CompareKey(name));
276 png_name = it->value;
279 png_name.replace('_', "underscore");
280 png_name.replace(' ', '_');
282 // This way we can have "math-delim { }" on the toolbar.
283 png_name.replace('(', "lparen");
284 png_name.replace(')', "rparen");
285 png_name.replace('[', "lbracket");
286 png_name.replace(']', "rbracket");
287 png_name.replace('{', "lbrace");
288 png_name.replace('}', "rbrace");
289 png_name.replace('|', "bars");
290 png_name.replace(',', "thinspace");
291 png_name.replace(':', "mediumspace");
292 png_name.replace(';', "thickspace");
293 png_name.replace('!', "negthinspace");
296 LYXERR(Debug::GUI, "findPng(" << name << ")\n"
297 << "Looking for math PNG called \"" << png_name << '"');
304 QString iconName(FuncRequest const & f, bool unknown)
306 initializeResources();
311 case LFUN_MATH_INSERT:
312 if (!f.argument().empty()) {
314 name1 = findPng(toqstr(f.argument()).mid(1));
317 case LFUN_MATH_DELIM:
318 case LFUN_MATH_BIGDELIM:
320 name1 = findPng(toqstr(f.argument()));
324 name1 = toqstr(f.argument());
326 case LFUN_COMMAND_ALTERNATIVES: {
327 // use the first of the alternative commands
329 docstring dummy = split(f.argument(), firstcom, ';');
330 name1 = toqstr(firstcom);
331 name1.replace(' ', '_');
335 name2 = toqstr(lyxaction.getActionName(f.action));
338 if (!f.argument().empty()) {
339 name1 = name2 + ' ' + toqstr(f.argument());
340 name1.replace(' ', '_');
341 name1.replace('\\', "backslash");
345 FileName fname = libFileSearch("images/" + path, name1, "png");
347 return toqstr(fname.absFilename());
349 fname = libFileSearch("images/" + path, name2, "png");
351 return toqstr(fname.absFilename());
353 path = ":/images/" + path;
356 LYXERR0("Directory " << path << " not found in resource!");
360 if (res.exists(name1))
364 if (res.exists(name2))
367 LYXERR(Debug::GUI, "Cannot find icon with filename "
368 << "\"" << name1 << "\""
370 << "\"" << name2 << "\""
372 << lyxaction.getActionName(f.action)
373 << '(' << to_utf8(f.argument()) << ")\"");
376 fname = libFileSearch(QString("images/"), "unknown", "png");
378 return toqstr(fname.absFilename());
379 return QString(":/images/unknown.png");
385 QPixmap getPixmap(QString const & path, QString const & name, QString const & ext)
388 FileName fname = libFileSearch(path, name, ext);
389 QString path1 = toqstr(fname.absFilename());
390 QString path2 = ":/" + path + name + "." + ext;
392 if (pixmap.load(path1)) {
395 else if (pixmap.load(path2)) {
399 LYXERR0("Cannot load pixmap \""
400 << path << name << '.' << ext
401 << "\", please verify resource system!");
406 QIcon getIcon(FuncRequest const & f, bool unknown)
408 QString icon = iconName(f, unknown);
412 //LYXERR(Debug::GUI, "Found icon: " << icon);
414 if (!pm.load(icon)) {
415 LYXERR0("Cannot load icon " << icon << " please verify resource system!");
423 ////////////////////////////////////////////////////////////////////////
425 // LyX server support code.
427 ////////////////////////////////////////////////////////////////////////
429 class SocketNotifier : public QSocketNotifier
432 /// connect a connection notification from the LyXServerSocket
433 SocketNotifier(QObject * parent, int fd, Application::SocketCallback func)
434 : QSocketNotifier(fd, QSocketNotifier::Read, parent), func_(func)
438 /// The callback function
439 Application::SocketCallback func_;
443 ////////////////////////////////////////////////////////////////////////
445 // Mac specific stuff goes here...
447 ////////////////////////////////////////////////////////////////////////
449 class MenuTranslator : public QTranslator
452 MenuTranslator(QObject * parent)
453 : QTranslator(parent)
456 QString translate(const char * /*context*/,
457 const char * sourceText,
458 const char * /*comment*/ = 0)
460 string const s = sourceText;
461 if (s == N_("About %1") || s == N_("Preferences")
462 || s == N_("Reconfigure") || s == N_("Quit %1"))
469 class GlobalMenuBar : public QMenuBar
473 GlobalMenuBar() : QMenuBar(0) {}
476 bool event(QEvent * e)
478 if (e->type() == QEvent::ShortcutOverride) {
479 // && activeWindow() == 0) {
480 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
482 setKeySymbol(&sym, ke);
483 guiApp->processKeySym(sym, q_key_state(ke->modifiers()));
492 // QMacPasteboardMimeGraphics can only be compiled on Mac.
494 class QMacPasteboardMimeGraphics : public QMacPasteboardMime
497 QMacPasteboardMimeGraphics()
498 : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL)
501 QString convertorName() { return "Graphics"; }
503 QString flavorFor(QString const & mime)
505 LYXERR(Debug::ACTION, "flavorFor " << mime);
506 if (mime == pdfMimeType())
507 return QLatin1String("com.adobe.pdf");
511 QString mimeFor(QString flav)
513 LYXERR(Debug::ACTION, "mimeFor " << flav);
514 if (flav == QLatin1String("com.adobe.pdf"))
515 return pdfMimeType();
519 bool canConvert(QString const & mime, QString flav)
521 return mimeFor(flav) == mime;
524 QVariant convertToMime(QString const & /*mime*/, QList<QByteArray> data,
528 qWarning("QMacPasteboardMimeGraphics: Cannot handle multiple member data");
532 QList<QByteArray> convertFromMime(QString const & /*mime*/,
533 QVariant data, QString /*flav*/)
535 QList<QByteArray> ret;
536 ret.append(data.toByteArray());
542 ///////////////////////////////////////////////////////////////
544 // You can find more platform specific stuff at the end of this file...
546 ///////////////////////////////////////////////////////////////
548 ////////////////////////////////////////////////////////////////////////
549 // Windows specific stuff goes here...
552 // QWindowsMimeMetafile can only be compiled on Windows.
554 static FORMATETC cfFromMime(QString const & mimetype)
557 if (mimetype == emfMimeType()) {
558 formatetc.cfFormat = CF_ENHMETAFILE;
559 formatetc.tymed = TYMED_ENHMF;
560 } else if (mimetype == wmfMimeType()) {
561 formatetc.cfFormat = CF_METAFILEPICT;
562 formatetc.tymed = TYMED_MFPICT;
565 formatetc.dwAspect = DVASPECT_CONTENT;
566 formatetc.lindex = -1;
571 class QWindowsMimeMetafile : public QWindowsMime {
573 QWindowsMimeMetafile() {}
575 bool canConvertFromMime(FORMATETC const & formatetc,
576 QMimeData const * mimedata) const
581 bool canConvertToMime(QString const & mimetype,
582 IDataObject * pDataObj) const
584 if (mimetype != emfMimeType() && mimetype != wmfMimeType())
586 FORMATETC formatetc = cfFromMime(mimetype);
587 return pDataObj->QueryGetData(&formatetc) == S_OK;
590 bool convertFromMime(FORMATETC const & formatetc,
591 const QMimeData * mimedata, STGMEDIUM * pmedium) const
596 QVariant convertToMime(QString const & mimetype, IDataObject * pDataObj,
597 QVariant::Type preferredType) const
600 if (!canConvertToMime(mimetype, pDataObj))
603 FORMATETC formatetc = cfFromMime(mimetype);
605 if (pDataObj->GetData(&formatetc, &s) != S_OK)
609 if (s.tymed == TYMED_ENHMF) {
610 dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, 0, 0);
611 data.resize(dataSize);
612 dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, dataSize,
613 (LPBYTE)data.data());
614 } else if (s.tymed == TYMED_MFPICT) {
615 dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, 0, 0);
616 data.resize(dataSize);
617 dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, dataSize,
618 (LPBYTE)data.data());
621 ReleaseStgMedium(&s);
627 QVector<FORMATETC> formatsForMime(QString const & mimetype,
628 QMimeData const * mimedata) const
630 QVector<FORMATETC> formats;
631 if (mimetype == emfMimeType() || mimetype == wmfMimeType())
632 formats += cfFromMime(mimetype);
636 QString mimeForFormat(FORMATETC const & formatetc) const
638 switch (formatetc.cfFormat) {
640 return emfMimeType();
641 case CF_METAFILEPICT:
642 return wmfMimeType();
650 ////////////////////////////////////////////////////////////////////////
651 // GuiApplication::Private definition and implementation.
652 ////////////////////////////////////////////////////////////////////////
654 struct GuiApplication::Private
656 Private(): language_model_(0), meta_fake_bit(NoModifier),
660 /// WMF Mime handler for Windows clipboard.
661 wmf_mime_ = new QWindowsMimeMetafile();
663 initKeySequences(&theTopLevelKeymap());
666 void initKeySequences(KeyMap * kb)
668 keyseq = KeySequence(kb, kb);
669 cancel_meta_seq = KeySequence(kb, kb);
673 QSortFilterProxyModel * language_model_;
675 GuiClipboard clipboard_;
677 GuiSelection selection_;
679 FontLoader font_loader_;
681 ColorCache color_cache_;
683 QTranslator qt_trans_;
685 QHash<int, SocketNotifier *> socket_notifiers_;
689 /// The global instance
692 /// this timer is used for any regular events one wants to
693 /// perform. at present it is used to check if forked processes
695 QTimer general_timer_;
697 /// delayed FuncRequests
698 std::queue<FuncRequest> func_request_queue_;
703 KeySequence cancel_meta_seq;
705 KeyModifier meta_fake_bit;
707 /// Multiple views container.
709 * Warning: This must not be a smart pointer as the destruction of the
710 * object is handled by Qt when the view is closed
711 * \sa Qt::WA_DeleteOnClose attribute.
713 QHash<int, GuiView *> views_;
715 /// Only used on mac.
716 GlobalMenuBar * global_menubar_;
719 /// Linkback mime handler for MacOSX.
720 QMacPasteboardMimeGraphics mac_pasteboard_mime_;
724 /// WMF Mime handler for Windows clipboard.
725 QWindowsMimeMetafile * wmf_mime_;
730 GuiApplication * guiApp;
732 GuiApplication::~GuiApplication()
735 closeAllLinkBackLinks();
741 GuiApplication::GuiApplication(int & argc, char ** argv)
742 : QApplication(argc, argv), current_view_(0),
743 d(new GuiApplication::Private)
745 QString app_name = "LyX";
746 QCoreApplication::setOrganizationName(app_name);
747 QCoreApplication::setOrganizationDomain("lyx.org");
748 QCoreApplication::setApplicationName(lyx_package);
750 // Install translator for GUI elements.
751 installTranslator(&d->qt_trans_);
753 // FIXME: quitOnLastWindowClosed is true by default. We should have a
754 // lyxrc setting for this in order to let the application stay resident.
755 // But then we need some kind of dock icon, at least on Windows.
757 if (lyxrc.quit_on_last_window_closed)
758 setQuitOnLastWindowClosed(false);
761 // FIXME: Do we need a lyxrc setting for this on Mac? This behaviour
762 // seems to be the default case for applications like LyX.
763 setQuitOnLastWindowClosed(false);
765 // This allows to translate the strings that appear in the LyX menu.
766 /// A translator suitable for the entries in the LyX menu.
767 /// Only needed with Qt/Mac.
768 installTranslator(new MenuTranslator(this));
772 // doubleClickInterval() is 400 ms on X11 which is just too long.
773 // On Windows and Mac OS X, the operating system's value is used.
774 // On Microsoft Windows, calling this function sets the double
775 // click interval for all applications. So we don't!
776 QApplication::setDoubleClickInterval(300);
779 connect(this, SIGNAL(lastWindowClosed()), this, SLOT(onLastWindowClosed()));
781 // needs to be done before reading lyxrc
783 lyxrc.dpi = (w.logicalDpiX() + w.logicalDpiY()) / 2;
787 // Set the cache to 5120 kilobytes which corresponds to screen size of
788 // 1280 by 1024 pixels with a color depth of 32 bits.
789 QPixmapCache::setCacheLimit(5120);
791 // Initialize RC Fonts
792 if (lyxrc.roman_font_name.empty())
793 lyxrc.roman_font_name = fromqstr(romanFontName());
795 if (lyxrc.sans_font_name.empty())
796 lyxrc.sans_font_name = fromqstr(sansFontName());
798 if (lyxrc.typewriter_font_name.empty())
799 lyxrc.typewriter_font_name = fromqstr(typewriterFontName());
801 d->general_timer_.setInterval(500);
802 connect(&d->general_timer_, SIGNAL(timeout()),
803 this, SLOT(handleRegularEvents()));
804 d->general_timer_.start();
808 GuiApplication * theGuiApp()
810 return dynamic_cast<GuiApplication *>(theApp());
814 void GuiApplication::clearSession()
821 docstring GuiApplication::iconName(FuncRequest const & f, bool unknown)
823 return qstring_to_ucs4(lyx::frontend::iconName(f, unknown));
827 LyXView * GuiApplication::currentWindow()
830 /* In LyX/Mac, when a dialog is open, the menus of the
831 application can still be accessed without giving focus to
832 the main window. In this case, we want to disable the menu
833 entries that are buffer or view-related.
835 if (current_view_ && activeWindow() != current_view_)
838 return current_view_;
842 bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const
848 case LFUN_WINDOW_CLOSE:
849 enable = d->views_.size() > 0;
852 case LFUN_BUFFER_NEW:
853 case LFUN_BUFFER_NEW_TEMPLATE:
856 case LFUN_SCREEN_FONT_UPDATE:
858 case LFUN_WINDOW_NEW:
860 case LFUN_LYXRC_APPLY:
861 case LFUN_COMMAND_PREFIX:
863 case LFUN_META_PREFIX:
864 case LFUN_RECONFIGURE:
865 case LFUN_SERVER_GET_FILENAME:
866 case LFUN_SERVER_NOTIFY:
875 flag.setEnabled(false);
881 // This function runs "configure" and then rereads lyx.defaults to
882 // reconfigure the automatic settings.
883 static void reconfigure(GuiView * lv, string const & option)
885 // emit message signal.
887 lv->message(_("Running configure..."));
889 // Run configure in user lyx directory
890 PathChanger p(package().user_support());
891 string configure_command = package().configure_command();
892 configure_command += option;
894 int ret = one.startscript(Systemcall::Wait, configure_command);
896 // emit message signal.
898 lv->message(_("Reloading configuration..."));
899 lyxrc.read(libFileSearch(QString(), "lyxrc.defaults"));
900 // Re-read packages.lst
901 LaTeXFeatures::getAvailable();
904 Alert::information(_("System reconfiguration failed"),
905 _("The system reconfiguration has failed.\n"
906 "Default textclass is used but LyX may "
907 "not be able to work properly.\n"
908 "Please reconfigure again if needed."));
911 Alert::information(_("System reconfigured"),
912 _("The system has been reconfigured.\n"
913 "You need to restart LyX to make use of any\n"
914 "updated document class specifications."));
918 void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
920 switch (cmd.action) {
922 case LFUN_WINDOW_NEW:
923 createView(toqstr(cmd.argument()));
926 case LFUN_WINDOW_CLOSE:
927 // update bookmark pit of the current buffer before window close
928 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
929 theLyXFunc().gotoBookmark(i+1, false, false);
930 // clear the last opened list, because
931 // maybe this will end the session
932 theSession().lastOpened().clear();
933 current_view_->close();
937 // quitting is triggered by the gui code
938 // (leaving the event loop).
940 current_view_->message(from_utf8(N_("Exiting.")));
945 case LFUN_SCREEN_FONT_UPDATE: {
946 // handle the screen font changes.
947 d->font_loader_.update();
948 // Backup current_view_
949 GuiView * view = current_view_;
950 // Set current_view_ to zero to forbid GuiWorkArea::redraw()
951 // to skip the refresh.
953 theBufferList().changed(false);
954 // Restore current_view_
955 current_view_ = view;
959 case LFUN_BUFFER_NEW:
960 if (d->views_.empty()
961 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
962 createView(QString(), false); // keep hidden
963 current_view_->newDocument(to_utf8(cmd.argument()), false);
964 current_view_->show();
965 setActiveWindow(current_view_);
967 current_view_->newDocument(to_utf8(cmd.argument()), false);
971 case LFUN_BUFFER_NEW_TEMPLATE:
972 if (d->views_.empty()
973 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
975 current_view_->newDocument(to_utf8(cmd.argument()), true);
976 if (!current_view_->documentBufferView())
977 current_view_->close();
979 current_view_->newDocument(to_utf8(cmd.argument()), true);
984 // FIXME: create a new method shared with LFUN_HELP_OPEN.
985 if (d->views_.empty()
986 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
987 string const fname = to_utf8(cmd.argument());
988 // We want the ui session to be saved per document and not per
989 // window number. The filename crc is a good enough identifier.
990 boost::crc_32_type crc;
991 crc = for_each(fname.begin(), fname.end(), crc);
992 createView(crc.checksum());
993 current_view_->openDocument(fname);
994 if (current_view_ && !current_view_->documentBufferView())
995 current_view_->close();
997 current_view_->openDocument(to_utf8(cmd.argument()));
1000 case LFUN_HELP_OPEN: {
1001 // FIXME: create a new method shared with LFUN_FILE_OPEN.
1002 if (current_view_ == 0)
1004 string const arg = to_utf8(cmd.argument());
1006 current_view_->message(_("Missing argument"));
1009 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1011 fname = i18nLibFileSearch("examples", arg, "lyx");
1013 if (fname.empty()) {
1014 lyxerr << "LyX: unable to find documentation file `"
1015 << arg << "'. Bad installation?" << endl;
1018 current_view_->message(bformat(_("Opening help file %1$s..."),
1019 makeDisplayPath(fname.absFilename())));
1020 Buffer * buf = current_view_->loadDocument(fname, false);
1022 current_view_->setBuffer(buf);
1023 buf->updateLabels();
1024 buf->errors("Parse");
1029 case LFUN_SET_COLOR: {
1031 string const x11_name = split(to_utf8(cmd.argument()), lyx_name, ' ');
1032 if (lyx_name.empty() || x11_name.empty()) {
1033 current_view_->message(
1034 _("Syntax: set-color <lyx_name> <x11_name>"));
1038 string const graphicsbg = lcolor.getLyXName(Color_graphicsbg);
1039 bool const graphicsbg_changed = lyx_name == graphicsbg
1040 && x11_name != graphicsbg;
1041 if (graphicsbg_changed) {
1042 // FIXME: The graphics cache no longer has a changeDisplay method.
1044 graphics::GCache::get().changeDisplay(true);
1048 if (!lcolor.setColor(lyx_name, x11_name)) {
1049 current_view_->message(
1050 bformat(_("Set-color \"%1$s\" failed "
1051 "- color is undefined or "
1052 "may not be redefined"),
1053 from_utf8(lyx_name)));
1056 // Make sure we don't keep old colors in cache.
1057 d->color_cache_.clear();
1061 case LFUN_LYXRC_APPLY: {
1062 // reset active key sequences, since the bindings
1063 // are updated (bug 6064)
1065 LyXRC const lyxrc_orig = lyxrc;
1067 istringstream ss(to_utf8(cmd.argument()));
1068 bool const success = lyxrc.read(ss) == 0;
1071 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1072 << "Unable to read lyxrc data"
1077 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1084 case LFUN_COMMAND_PREFIX:
1085 lyx::dispatch(FuncRequest(LFUN_MESSAGE, d->keyseq.printOptions(true)));
1090 d->meta_fake_bit = NoModifier;
1091 GuiView * gv = currentView();
1092 if (gv && gv->currentBufferView())
1093 // cancel any selection
1094 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1095 dr.setMessage(from_ascii(N_("Cancel")));
1098 case LFUN_META_PREFIX:
1099 d->meta_fake_bit = AltModifier;
1100 dr.setMessage(d->keyseq.print(KeySequence::ForGui));
1103 // --- Menus -----------------------------------------------
1104 case LFUN_RECONFIGURE:
1105 // argument is any additional parameter to the configure.py command
1106 reconfigure(currentView(), to_utf8(cmd.argument()));
1109 // --- lyxserver commands ----------------------------
1110 case LFUN_SERVER_GET_FILENAME: {
1111 GuiView * lv = currentView();
1112 LASSERT(lv && lv->documentBufferView(), return);
1113 docstring const fname = from_utf8(
1114 lv->documentBufferView()->buffer().absFileName());
1115 dr.setMessage(fname);
1116 LYXERR(Debug::INFO, "FNAME[" << fname << ']');
1119 case LFUN_SERVER_NOTIFY: {
1120 docstring const dispatch_buffer = d->keyseq.print(KeySequence::Portable);
1121 dr.setMessage(dispatch_buffer);
1122 theServer().notifyClient(to_utf8(dispatch_buffer));
1126 // Notify the caller that the action has not been dispatched.
1127 dr.dispatched(false);
1131 // The action has been dispatched.
1132 dr.dispatched(true);
1136 docstring GuiApplication::viewStatusMessage()
1138 // When meta-fake key is pressed, show the key sequence so far + "M-".
1139 if (d->meta_fake_bit != NoModifier)
1140 return d->keyseq.print(KeySequence::ForGui) + "M-";
1142 // Else, when a non-complete key sequence is pressed,
1143 // show the available options.
1144 if (d->keyseq.length() > 0 && !d->keyseq.deleted())
1145 return d->keyseq.printOptions(true);
1151 void GuiApplication::handleKeyFunc(FuncCode action)
1155 if (d->keyseq.length())
1157 GuiView * gv = currentView();
1158 LASSERT(gv && gv->currentBufferView(), return);
1159 BufferView * bv = gv->currentBufferView();
1160 bv->getIntl().getTransManager().deadkey(
1161 c, get_accent(action).accent, bv->cursor().innerText(),
1163 // Need to clear, in case the minibuffer calls these
1166 // copied verbatim from do_accent_char
1167 bv->cursor().resetAnchor();
1168 bv->processUpdateFlags(Update::FitCursor);
1172 void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
1174 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
1176 GuiView * lv = currentView();
1178 // Do nothing if we have nothing (JMarc)
1179 if (!keysym.isOK()) {
1180 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
1181 lv->restartCursor();
1185 if (keysym.isModifier()) {
1186 LYXERR(Debug::KEY, "isModifier true");
1188 lv->restartCursor();
1192 char_type encoded_last_key = keysym.getUCSEncoded();
1194 // Do a one-deep top-level lookup for
1195 // cancel and meta-fake keys. RVDK_PATCH_5
1196 d->cancel_meta_seq.reset();
1198 FuncRequest func = d->cancel_meta_seq.addkey(keysym, state);
1199 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
1201 // When not cancel or meta-fake, do the normal lookup.
1202 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
1203 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
1204 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
1205 // remove Caps Lock and Mod2 as a modifiers
1206 func = d->keyseq.addkey(keysym, (state | d->meta_fake_bit));
1207 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
1210 // Dont remove this unless you know what you are doing.
1211 d->meta_fake_bit = NoModifier;
1213 // Can this happen now ?
1214 if (func.action == LFUN_NOACTION)
1215 func = FuncRequest(LFUN_COMMAND_PREFIX);
1217 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
1218 << d->keyseq.print(KeySequence::Portable) << ']');
1220 // already here we know if it any point in going further
1221 // why not return already here if action == -1 and
1222 // num_bytes == 0? (Lgb)
1224 if (d->keyseq.length() > 1)
1225 lv->message(d->keyseq.print(KeySequence::ForGui));
1228 // Maybe user can only reach the key via holding down shift.
1229 // Let's see. But only if shift is the only modifier
1230 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
1231 LYXERR(Debug::KEY, "Trying without shift");
1232 func = d->keyseq.addkey(keysym, NoModifier);
1233 LYXERR(Debug::KEY, "Action now " << func.action);
1236 if (func.action == LFUN_UNKNOWN_ACTION) {
1237 // Hmm, we didn't match any of the keysequences. See
1238 // if it's normal insertable text not already covered
1240 if (keysym.isText() && d->keyseq.length() == 1) {
1241 LYXERR(Debug::KEY, "isText() is true, inserting.");
1242 func = FuncRequest(LFUN_SELF_INSERT,
1243 FuncRequest::KEYBOARD);
1245 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
1246 lv->message(_("Unknown function."));
1247 lv->restartCursor();
1252 if (func.action == LFUN_SELF_INSERT) {
1253 if (encoded_last_key != 0) {
1254 docstring const arg(1, encoded_last_key);
1255 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
1256 FuncRequest::KEYBOARD));
1257 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
1260 lyx::dispatch(func);
1267 void GuiApplication::dispatchDelayed(FuncRequest const & func)
1269 d->func_request_queue_.push(func);
1270 QTimer::singleShot(0, this, SLOT(processFuncRequestQueue()));
1274 void GuiApplication::resetGui()
1276 // Set the language defined by the user.
1280 if (!readUIFile(toqstr(lyxrc.ui_file)))
1281 // Gives some error box here.
1284 if (d->global_menubar_)
1285 d->menus_.fillMenuBar(d->global_menubar_, 0, false);
1287 QHash<int, GuiView *>::iterator it;
1288 for (it = d->views_.begin(); it != d->views_.end(); ++it) {
1291 gv->setLayoutDirection(layoutDirection());
1295 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
1299 void GuiApplication::createView(int view_id)
1301 createView(QString(), true, view_id);
1305 void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
1308 // release the keyboard which might have been grabed by the global
1309 // menubar on Mac to catch shortcuts even without any GuiView.
1310 if (d->global_menubar_)
1311 d->global_menubar_->releaseKeyboard();
1315 while (d->views_.find(id) != d->views_.end())
1318 LYXERR(Debug::GUI, "About to create new window with ID " << id);
1319 GuiView * view = new GuiView(id);
1321 d->views_[id] = view;
1325 setActiveWindow(view);
1328 if (!geometry_arg.isEmpty()) {
1332 QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)([+-][0-9]*)){0,1}" );
1333 re.indexIn(geometry_arg);
1334 w = re.cap(1).toInt();
1335 h = re.cap(2).toInt();
1336 x = re.cap(3).toInt();
1337 y = re.cap(4).toInt();
1338 view->setGeometry(x, y, w, h);
1345 Clipboard & GuiApplication::clipboard()
1347 return d->clipboard_;
1351 Selection & GuiApplication::selection()
1353 return d->selection_;
1357 FontLoader & GuiApplication::fontLoader()
1359 return d->font_loader_;
1363 Toolbars const & GuiApplication::toolbars() const
1365 return d->toolbars_;
1369 Toolbars & GuiApplication::toolbars()
1371 return d->toolbars_;
1375 Menus const & GuiApplication::menus() const
1381 Menus & GuiApplication::menus()
1387 QList<int> GuiApplication::viewIds() const
1389 return d->views_.keys();
1393 ColorCache & GuiApplication::colorCache()
1395 return d->color_cache_;
1399 int GuiApplication::exec()
1401 // asynchronously handle batch commands. This event will be in
1402 // the event queue in front of other asynchronous events. Hence,
1403 // we can assume in the latter that the gui is setup already.
1404 QTimer::singleShot(0, this, SLOT(execBatchCommands()));
1406 return QApplication::exec();
1410 void GuiApplication::exit(int status)
1412 QApplication::exit(status);
1416 void GuiApplication::setGuiLanguage()
1418 // Set the language defined by the user.
1421 QString const default_language = toqstr(Messages::defaultLanguage());
1422 LYXERR(Debug::LOCALE, "Trying to set default locale to: " << default_language);
1423 QLocale const default_locale(default_language);
1424 QLocale::setDefault(default_locale);
1426 // install translation file for Qt built-in dialogs
1427 QString const language_name = QString("qt_") + default_locale.name();
1429 // language_name can be short (e.g. qt_zh) or long (e.g. qt_zh_CN).
1430 // Short-named translator can be loaded from a long name, but not the
1431 // opposite. Therefore, long name should be used without truncation.
1432 // c.f. http://doc.trolltech.com/4.1/qtranslator.html#load
1433 if (!d->qt_trans_.load(language_name,
1434 QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
1435 LYXERR(Debug::LOCALE, "Could not find Qt translations for locale "
1438 LYXERR(Debug::LOCALE, "Successfully installed Qt translations for locale "
1442 switch (default_locale.language()) {
1443 case QLocale::Arabic :
1444 case QLocale::Hebrew :
1445 case QLocale::Persian :
1446 case QLocale::Urdu :
1447 setLayoutDirection(Qt::RightToLeft);
1450 setLayoutDirection(Qt::LeftToRight);
1455 void GuiApplication::processFuncRequestQueue()
1457 while (!d->func_request_queue_.empty()) {
1458 lyx::dispatch(d->func_request_queue_.back());
1459 d->func_request_queue_.pop();
1464 void GuiApplication::execBatchCommands()
1469 if (!readUIFile(toqstr(lyxrc.ui_file)))
1470 // Gives some error box here.
1474 // Create the global default menubar which is shown for the dialogs
1475 // and if no GuiView is visible.
1476 // This must be done after the session was recovered to know the "last files".
1477 d->global_menubar_ = new GlobalMenuBar();
1478 d->menus_.fillMenuBar(d->global_menubar_, 0, true);
1481 lyx::execBatchCommands();
1485 QAbstractItemModel * GuiApplication::languageModel()
1487 if (d->language_model_)
1488 return d->language_model_;
1490 QStandardItemModel * lang_model = new QStandardItemModel(this);
1491 lang_model->insertColumns(0, 1);
1493 Languages::const_iterator it = lyx::languages.begin();
1494 Languages::const_iterator end = lyx::languages.end();
1495 for (; it != end; ++it) {
1496 current_row = lang_model->rowCount();
1497 lang_model->insertRows(current_row, 1);
1498 QModelIndex item = lang_model->index(current_row, 0);
1499 lang_model->setData(item, qt_(it->second.display()), Qt::DisplayRole);
1500 lang_model->setData(item, toqstr(it->second.lang()), Qt::UserRole);
1502 d->language_model_ = new QSortFilterProxyModel(this);
1503 d->language_model_->setSourceModel(lang_model);
1504 #if QT_VERSION >= 0x040300
1505 d->language_model_->setSortLocaleAware(true);
1507 return d->language_model_;
1511 void GuiApplication::restoreGuiSession()
1513 if (!lyxrc.load_session)
1516 Session & session = theSession();
1517 LastOpenedSection::LastOpened const & lastopened =
1518 session.lastOpened().getfiles();
1520 FileName active_file;
1521 // do not add to the lastfile list since these files are restored from
1522 // last session, and should be already there (regular files), or should
1523 // not be added at all (help files).
1524 for (size_t i = 0; i < lastopened.size(); ++i) {
1525 FileName const & file_name = lastopened[i].file_name;
1526 if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs
1527 && current_view_->documentBufferView() != 0)) {
1528 boost::crc_32_type crc;
1529 string const & fname = file_name.absFilename();
1530 crc = for_each(fname.begin(), fname.end(), crc);
1531 createView(crc.checksum());
1533 current_view_->loadDocument(file_name, false);
1535 if (lastopened[i].active)
1536 active_file = file_name;
1539 // Restore last active buffer
1540 Buffer * buffer = theBufferList().getBuffer(active_file);
1542 current_view_->setBuffer(buffer);
1544 // clear this list to save a few bytes of RAM
1545 session.lastOpened().clear();
1549 QString const GuiApplication::romanFontName()
1552 font.setKerning(false);
1553 font.setStyleHint(QFont::Serif);
1554 font.setFamily("serif");
1556 return QFontInfo(font).family();
1560 QString const GuiApplication::sansFontName()
1563 font.setKerning(false);
1564 font.setStyleHint(QFont::SansSerif);
1565 font.setFamily("sans");
1567 return QFontInfo(font).family();
1571 QString const GuiApplication::typewriterFontName()
1574 font.setKerning(false);
1575 font.setStyleHint(QFont::TypeWriter);
1576 font.setFamily("monospace");
1578 return QFontInfo(font).family();
1582 void GuiApplication::handleRegularEvents()
1584 ForkedCallsController::handleCompletedProcesses();
1588 bool GuiApplication::event(QEvent * e)
1591 case QEvent::FileOpen: {
1592 // Open a file; this happens only on Mac OS X for now.
1594 // We do this asynchronously because on startup the batch
1595 // commands are not executed here yet and the gui is not ready
1597 QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
1598 dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file())));
1603 return QApplication::event(e);
1608 bool GuiApplication::notify(QObject * receiver, QEvent * event)
1611 return QApplication::notify(receiver, event);
1613 catch (ExceptionMessage const & e) {
1615 case ErrorException:
1617 setQuitOnLastWindowClosed(false);
1619 Alert::error(e.title_, e.details_);
1621 // Properly crash in debug mode in order to get a useful backtrace.
1624 // In release mode, try to exit gracefully.
1627 case BufferException: {
1628 if (!current_view_->documentBufferView())
1630 Buffer * buf = ¤t_view_->documentBufferView()->buffer();
1631 docstring details = e.details_ + '\n';
1632 details += buf->emergencyWrite();
1633 theBufferList().release(buf);
1634 details += "\n" + _("The current document was closed.");
1635 Alert::error(e.title_, details);
1638 case WarningException:
1639 Alert::warning(e.title_, e.details_);
1643 catch (exception const & e) {
1644 docstring s = _("LyX has caught an exception, it will now "
1645 "attempt to save all unsaved documents and exit."
1647 s += from_ascii(e.what());
1648 Alert::error(_("Software exception Detected"), s);
1652 docstring s = _("LyX has caught some really weird exception, it will "
1653 "now attempt to save all unsaved documents and exit.");
1654 Alert::error(_("Software exception Detected"), s);
1662 bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
1664 QColor const & qcol = d->color_cache_.get(col);
1665 if (!qcol.isValid()) {
1671 rgbcol.r = qcol.red();
1672 rgbcol.g = qcol.green();
1673 rgbcol.b = qcol.blue();
1678 string const GuiApplication::hexName(ColorCode col)
1680 return ltrim(fromqstr(d->color_cache_.get(col).name()), "#");
1684 void GuiApplication::registerSocketCallback(int fd, SocketCallback func)
1686 SocketNotifier * sn = new SocketNotifier(this, fd, func);
1687 d->socket_notifiers_[fd] = sn;
1688 connect(sn, SIGNAL(activated(int)), this, SLOT(socketDataReceived(int)));
1692 void GuiApplication::socketDataReceived(int fd)
1694 d->socket_notifiers_[fd]->func_();
1698 void GuiApplication::unregisterSocketCallback(int fd)
1700 d->socket_notifiers_.take(fd)->setEnabled(false);
1704 void GuiApplication::commitData(QSessionManager & sm)
1706 /// The implementation is required to avoid an application exit
1707 /// when session state save is triggered by session manager.
1708 /// The default implementation sends a close event to all
1709 /// visible top level widgets when session managment allows
1711 /// We are changing that to close all wiew one by one.
1712 /// FIXME: verify if the default implementation is enough now.
1713 if (sm.allowsInteraction() && !closeAllViews())
1718 void GuiApplication::unregisterView(GuiView * gv)
1720 LASSERT(d->views_[gv->id()] == gv, /**/);
1721 d->views_.remove(gv->id());
1722 if (current_view_ == gv)
1727 bool GuiApplication::closeAllViews()
1729 if (d->views_.empty())
1732 // When a view/window was closed before without quitting LyX, there
1733 // are already entries in the lastOpened list.
1734 theSession().lastOpened().clear();
1736 QList<GuiView *> views = d->views_.values();
1737 foreach (GuiView * view, views) {
1747 GuiView & GuiApplication::view(int id) const
1749 LASSERT(d->views_.contains(id), /**/);
1750 return *d->views_.value(id);
1754 void GuiApplication::hideDialogs(string const & name, Inset * inset) const
1756 QList<GuiView *> views = d->views_.values();
1757 foreach (GuiView * view, views)
1758 view->hideDialog(name, inset);
1762 Buffer const * GuiApplication::updateInset(Inset const * inset) const
1764 Buffer const * buffer_ = 0;
1765 QHash<int, GuiView *>::iterator end = d->views_.end();
1766 for (QHash<int, GuiView *>::iterator it = d->views_.begin(); it != end; ++it) {
1767 if (Buffer const * ptr = (*it)->updateInset(inset))
1774 bool GuiApplication::searchMenu(FuncRequest const & func,
1775 docstring_list & names) const
1777 return d->menus_.searchMenu(func, names);
1781 bool GuiApplication::readUIFile(QString const & name, bool include)
1783 LYXERR(Debug::INIT, "About to read " << name << "...");
1787 ui_path = libFileSearch("ui", name, "inc");
1788 if (ui_path.empty())
1789 ui_path = libFileSearch("ui", changeExtension(name, "inc"));
1791 ui_path = libFileSearch("ui", name, "ui");
1794 if (ui_path.empty()) {
1795 static const QString defaultUIFile = "default";
1796 LYXERR(Debug::INIT, "Could not find " << name);
1798 Alert::warning(_("Could not find UI definition file"),
1799 bformat(_("Error while reading the included file\n%1$s\n"
1800 "Please check your installation."), qstring_to_ucs4(name)));
1803 if (name == defaultUIFile) {
1804 LYXERR(Debug::INIT, "Could not find default UI file!!");
1805 Alert::warning(_("Could not find default UI file"),
1806 _("LyX could not find the default UI file!\n"
1807 "Please check your installation."));
1810 Alert::warning(_("Could not find UI definition file"),
1811 bformat(_("Error while reading the configuration file\n%1$s\n"
1812 "Falling back to default.\n"
1813 "Please look under Tools>Preferences>User Interface and\n"
1814 "check which User Interface file you are using."), qstring_to_ucs4(name)));
1815 return readUIFile(defaultUIFile, false);
1818 // Ensure that a file is read only once (prevents include loops)
1819 static QStringList uifiles;
1820 QString const uifile = toqstr(ui_path.absFilename());
1821 if (uifiles.contains(uifile)) {
1823 // We are reading again the top uifile so reset the safeguard:
1826 d->toolbars_.reset();
1828 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1829 << "Is this an include loop?");
1833 uifiles.push_back(uifile);
1835 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1845 LexerKeyword uitags[] = {
1846 { "include", ui_include },
1847 { "menuset", ui_menuset },
1848 { "toolbars", ui_toolbars },
1849 { "toolbarset", ui_toolbarset }
1853 lex.setFile(ui_path);
1855 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1859 if (lyxerr.debugging(Debug::PARSER))
1860 lex.printTable(lyxerr);
1862 // store which ui files define Toolbars
1863 static QStringList toolbar_uifiles;
1865 while (lex.isOK()) {
1866 switch (lex.lex()) {
1869 QString const file = toqstr(lex.getString());
1870 if (!readUIFile(file, true))
1875 d->menus_.read(lex);
1879 d->toolbars_.readToolbars(lex);
1883 d->toolbars_.readToolbarSettings(lex);
1884 toolbar_uifiles.push_back(uifile);
1888 if (!rtrim(lex.getString()).empty())
1889 lex.printError("LyX::ReadUIFile: "
1890 "Unknown menu tag: `$$Token'");
1899 settings.beginGroup("ui_files");
1900 bool touched = false;
1901 for (int i = 0; i != uifiles.size(); ++i) {
1902 QFileInfo fi(uifiles[i]);
1903 QDateTime const date_value = fi.lastModified();
1904 QString const name_key = QString::number(i);
1905 // if an ui file which defines Toolbars has changed,
1906 // we have to reset the settings
1907 if (toolbar_uifiles.contains(uifiles[i])
1908 && (!settings.contains(name_key)
1909 || settings.value(name_key).toString() != uifiles[i]
1910 || settings.value(name_key + "/date").toDateTime() != date_value)) {
1912 settings.setValue(name_key, uifiles[i]);
1913 settings.setValue(name_key + "/date", date_value);
1916 settings.endGroup();
1918 settings.remove("views");
1924 void GuiApplication::onLastWindowClosed()
1926 if (d->global_menubar_)
1927 d->global_menubar_->grabKeyboard();
1931 ////////////////////////////////////////////////////////////////////////
1933 // X11 specific stuff goes here...
1936 bool GuiApplication::x11EventFilter(XEvent * xev)
1941 switch (xev->type) {
1942 case SelectionRequest: {
1943 if (xev->xselectionrequest.selection != XA_PRIMARY)
1945 LYXERR(Debug::SELECTION, "X requested selection.");
1946 BufferView * bv = current_view_->currentBufferView();
1948 docstring const sel = bv->requestSelection();
1950 d->selection_.put(sel);
1954 case SelectionClear: {
1955 if (xev->xselectionclear.selection != XA_PRIMARY)
1957 LYXERR(Debug::SELECTION, "Lost selection.");
1958 BufferView * bv = current_view_->currentBufferView();
1960 bv->clearSelection();
1968 } // namespace frontend
1971 void hideDialogs(std::string const & name, Inset * inset)
1974 theApp()->hideDialogs(name, inset);
1978 ////////////////////////////////////////////////////////////////////
1982 ////////////////////////////////////////////////////////////////////
1984 frontend::FontLoader & theFontLoader()
1986 LASSERT(frontend::guiApp, /**/);
1987 return frontend::guiApp->fontLoader();
1991 frontend::FontMetrics const & theFontMetrics(Font const & f)
1993 return theFontMetrics(f.fontInfo());
1997 frontend::FontMetrics const & theFontMetrics(FontInfo const & f)
1999 LASSERT(frontend::guiApp, /**/);
2000 return frontend::guiApp->fontLoader().metrics(f);
2004 ////////////////////////////////////////////////////////////////////
2008 ////////////////////////////////////////////////////////////////////
2010 frontend::Clipboard & theClipboard()
2012 LASSERT(frontend::guiApp, /**/);
2013 return frontend::guiApp->clipboard();
2017 frontend::Selection & theSelection()
2019 LASSERT(frontend::guiApp, /**/);
2020 return frontend::guiApp->selection();
2026 #include "moc_GuiApplication.cpp"