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->setReadonly(true);
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, "Trying 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 = lyx::languages.begin();
1495 Languages::const_iterator end = lyx::languages.end();
1496 for (; it != end; ++it) {
1497 current_row = lang_model->rowCount();
1498 lang_model->insertRows(current_row, 1);
1499 QModelIndex item = lang_model->index(current_row, 0);
1500 lang_model->setData(item, qt_(it->second.display()), Qt::DisplayRole);
1501 lang_model->setData(item, toqstr(it->second.lang()), Qt::UserRole);
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"