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 { "Downarrow", "downarrow2" },
230 { "Gamma", "gamma2" },
231 { "Lambda", "lambda2" },
232 { "Leftarrow", "leftarrow2" },
233 { "Leftrightarrow", "leftrightarrow2" },
234 { "Longleftarrow", "longleftarrow2" },
235 { "Longleftrightarrow", "longleftrightarrow2" },
236 { "Longrightarrow", "longrightarrow2" },
237 { "Omega", "omega2" },
241 { "Rightarrow", "rightarrow2" },
242 { "Sigma", "sigma2" },
243 { "Subset", "subset2" },
244 { "Supset", "supset2" },
245 { "Theta", "theta2" },
246 { "Uparrow", "uparrow2" },
247 { "Updownarrow", "updownarrow2" },
248 { "Upsilon", "upsilon2" },
249 { "Vdash", "vdash3" },
252 { "nLeftarrow", "nleftarrow2" },
253 { "nLeftrightarrow", "nleftrightarrow2" },
254 { "nRightarrow", "nrightarrow2" },
255 { "nVDash", "nvdash3" },
256 { "nvDash", "nvdash2" },
257 { "textrm \\AA", "textrm_AA"},
258 { "textrm \\O", "textrm_O"},
259 { "vDash", "vdash2" }
262 size_t const nr_sorted_png_map = sizeof(sorted_png_map) / sizeof(PngMap);
265 QString findPng(QString const & name)
267 PngMap const * const begin = sorted_png_map;
268 PngMap const * const end = begin + nr_sorted_png_map;
269 LASSERT(sorted(begin, end), /**/);
271 PngMap const * const it = find_if(begin, end, CompareKey(name));
275 png_name = it->value;
278 png_name.replace('_', "underscore");
279 png_name.replace(' ', '_');
281 // This way we can have "math-delim { }" on the toolbar.
282 png_name.replace('(', "lparen");
283 png_name.replace(')', "rparen");
284 png_name.replace('[', "lbracket");
285 png_name.replace(']', "rbracket");
286 png_name.replace('{', "lbrace");
287 png_name.replace('}', "rbrace");
288 png_name.replace('|', "bars");
289 png_name.replace(',', "thinspace");
290 png_name.replace(':', "mediumspace");
291 png_name.replace(';', "thickspace");
292 png_name.replace('!', "negthinspace");
295 LYXERR(Debug::GUI, "findPng(" << name << ")\n"
296 << "Looking for math PNG called \"" << png_name << '"');
303 QString iconName(FuncRequest const & f, bool unknown)
305 initializeResources();
310 case LFUN_MATH_INSERT:
311 if (!f.argument().empty()) {
313 name1 = findPng(toqstr(f.argument()).mid(1));
316 case LFUN_MATH_DELIM:
317 case LFUN_MATH_BIGDELIM:
319 name1 = findPng(toqstr(f.argument()));
323 name1 = toqstr(f.argument());
325 case LFUN_COMMAND_ALTERNATIVES: {
326 // use the first of the alternative commands
328 docstring dummy = split(f.argument(), firstcom, ';');
329 name1 = toqstr(firstcom);
330 name1.replace(' ', '_');
334 name2 = toqstr(lyxaction.getActionName(f.action));
337 if (!f.argument().empty()) {
338 name1 = name2 + ' ' + toqstr(f.argument());
339 name1.replace(' ', '_');
340 name1.replace('\\', "backslash");
344 FileName fname = libFileSearch("images/" + path, name1, "png");
346 return toqstr(fname.absFilename());
348 fname = libFileSearch("images/" + path, name2, "png");
350 return toqstr(fname.absFilename());
352 path = ":/images/" + path;
355 LYXERR0("Directory " << path << " not found in resource!");
359 if (res.exists(name1))
363 if (res.exists(name2))
366 LYXERR(Debug::GUI, "Cannot find icon with filename "
367 << "\"" << name1 << "\""
369 << "\"" << name2 << "\""
371 << lyxaction.getActionName(f.action)
372 << '(' << to_utf8(f.argument()) << ")\"");
375 fname = libFileSearch(QString("images/"), "unknown", "png");
377 return toqstr(fname.absFilename());
378 return QString(":/images/unknown.png");
384 QPixmap getPixmap(QString const & path, QString const & name, QString const & ext)
387 FileName fname = libFileSearch(path, name, ext);
388 QString path1 = toqstr(fname.absFilename());
389 QString path2 = ":/" + path + name + "." + ext;
391 if (pixmap.load(path1)) {
394 else if (pixmap.load(path2)) {
398 LYXERR0("Cannot load pixmap \""
399 << path << name << '.' << ext
400 << "\", please verify resource system!");
405 QIcon getIcon(FuncRequest const & f, bool unknown)
407 QString icon = iconName(f, unknown);
411 //LYXERR(Debug::GUI, "Found icon: " << icon);
413 if (!pm.load(icon)) {
414 LYXERR0("Cannot load icon " << icon << " please verify resource system!");
422 ////////////////////////////////////////////////////////////////////////
424 // LyX server support code.
426 ////////////////////////////////////////////////////////////////////////
428 class SocketNotifier : public QSocketNotifier
431 /// connect a connection notification from the LyXServerSocket
432 SocketNotifier(QObject * parent, int fd, Application::SocketCallback func)
433 : QSocketNotifier(fd, QSocketNotifier::Read, parent), func_(func)
437 /// The callback function
438 Application::SocketCallback func_;
442 ////////////////////////////////////////////////////////////////////////
444 // Mac specific stuff goes here...
446 ////////////////////////////////////////////////////////////////////////
448 class MenuTranslator : public QTranslator
451 MenuTranslator(QObject * parent)
452 : QTranslator(parent)
455 QString translate(const char * /*context*/,
456 const char * sourceText,
457 const char * /*comment*/ = 0)
459 string const s = sourceText;
460 if (s == N_("About %1") || s == N_("Preferences")
461 || s == N_("Reconfigure") || s == N_("Quit %1"))
468 class GlobalMenuBar : public QMenuBar
472 GlobalMenuBar() : QMenuBar(0) {}
475 bool event(QEvent * e)
477 if (e->type() == QEvent::ShortcutOverride) {
478 // && activeWindow() == 0) {
479 QKeyEvent * ke = static_cast<QKeyEvent*>(e);
481 setKeySymbol(&sym, ke);
482 guiApp->processKeySym(sym, q_key_state(ke->modifiers()));
491 // QMacPasteboardMimeGraphics can only be compiled on Mac.
493 class QMacPasteboardMimeGraphics : public QMacPasteboardMime
496 QMacPasteboardMimeGraphics()
497 : QMacPasteboardMime(MIME_QT_CONVERTOR|MIME_ALL)
500 QString convertorName() { return "Graphics"; }
502 QString flavorFor(QString const & mime)
504 LYXERR(Debug::ACTION, "flavorFor " << mime);
505 if (mime == pdfMimeType())
506 return QLatin1String("com.adobe.pdf");
510 QString mimeFor(QString flav)
512 LYXERR(Debug::ACTION, "mimeFor " << flav);
513 if (flav == QLatin1String("com.adobe.pdf"))
514 return pdfMimeType();
518 bool canConvert(QString const & mime, QString flav)
520 return mimeFor(flav) == mime;
523 QVariant convertToMime(QString const & /*mime*/, QList<QByteArray> data,
527 qWarning("QMacPasteboardMimeGraphics: Cannot handle multiple member data");
531 QList<QByteArray> convertFromMime(QString const & /*mime*/,
532 QVariant data, QString /*flav*/)
534 QList<QByteArray> ret;
535 ret.append(data.toByteArray());
541 ///////////////////////////////////////////////////////////////
543 // You can find more platform specific stuff at the end of this file...
545 ///////////////////////////////////////////////////////////////
547 ////////////////////////////////////////////////////////////////////////
548 // Windows specific stuff goes here...
551 // QWindowsMimeMetafile can only be compiled on Windows.
553 static FORMATETC cfFromMime(QString const & mimetype)
556 if (mimetype == emfMimeType()) {
557 formatetc.cfFormat = CF_ENHMETAFILE;
558 formatetc.tymed = TYMED_ENHMF;
559 } else if (mimetype == wmfMimeType()) {
560 formatetc.cfFormat = CF_METAFILEPICT;
561 formatetc.tymed = TYMED_MFPICT;
564 formatetc.dwAspect = DVASPECT_CONTENT;
565 formatetc.lindex = -1;
570 class QWindowsMimeMetafile : public QWindowsMime {
572 QWindowsMimeMetafile() {}
574 bool canConvertFromMime(FORMATETC const & formatetc,
575 QMimeData const * mimedata) const
580 bool canConvertToMime(QString const & mimetype,
581 IDataObject * pDataObj) const
583 if (mimetype != emfMimeType() && mimetype != wmfMimeType())
585 FORMATETC formatetc = cfFromMime(mimetype);
586 return pDataObj->QueryGetData(&formatetc) == S_OK;
589 bool convertFromMime(FORMATETC const & formatetc,
590 const QMimeData * mimedata, STGMEDIUM * pmedium) const
595 QVariant convertToMime(QString const & mimetype, IDataObject * pDataObj,
596 QVariant::Type preferredType) const
599 if (!canConvertToMime(mimetype, pDataObj))
602 FORMATETC formatetc = cfFromMime(mimetype);
604 if (pDataObj->GetData(&formatetc, &s) != S_OK)
608 if (s.tymed == TYMED_ENHMF) {
609 dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, 0, 0);
610 data.resize(dataSize);
611 dataSize = GetEnhMetaFileBits(s.hEnhMetaFile, dataSize,
612 (LPBYTE)data.data());
613 } else if (s.tymed == TYMED_MFPICT) {
614 dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, 0, 0);
615 data.resize(dataSize);
616 dataSize = GetMetaFileBitsEx((HMETAFILE)s.hMetaFilePict, dataSize,
617 (LPBYTE)data.data());
620 ReleaseStgMedium(&s);
626 QVector<FORMATETC> formatsForMime(QString const & mimetype,
627 QMimeData const * mimedata) const
629 QVector<FORMATETC> formats;
630 if (mimetype == emfMimeType() || mimetype == wmfMimeType())
631 formats += cfFromMime(mimetype);
635 QString mimeForFormat(FORMATETC const & formatetc) const
637 switch (formatetc.cfFormat) {
639 return emfMimeType();
640 case CF_METAFILEPICT:
641 return wmfMimeType();
649 ////////////////////////////////////////////////////////////////////////
650 // GuiApplication::Private definition and implementation.
651 ////////////////////////////////////////////////////////////////////////
653 struct GuiApplication::Private
655 Private(): language_model_(0), meta_fake_bit(NoModifier),
659 /// WMF Mime handler for Windows clipboard.
660 wmf_mime_ = new QWindowsMimeMetafile();
662 initKeySequences(&theTopLevelKeymap());
665 void initKeySequences(KeyMap * kb)
667 keyseq = KeySequence(kb, kb);
668 cancel_meta_seq = KeySequence(kb, kb);
672 QSortFilterProxyModel * language_model_;
674 GuiClipboard clipboard_;
676 GuiSelection selection_;
678 FontLoader font_loader_;
680 ColorCache color_cache_;
682 QTranslator qt_trans_;
684 QHash<int, SocketNotifier *> socket_notifiers_;
688 /// The global instance
691 /// this timer is used for any regular events one wants to
692 /// perform. at present it is used to check if forked processes
694 QTimer general_timer_;
696 /// delayed FuncRequests
697 std::queue<FuncRequest> func_request_queue_;
702 KeySequence cancel_meta_seq;
704 KeyModifier meta_fake_bit;
706 /// Multiple views container.
708 * Warning: This must not be a smart pointer as the destruction of the
709 * object is handled by Qt when the view is closed
710 * \sa Qt::WA_DeleteOnClose attribute.
712 QHash<int, GuiView *> views_;
714 /// Only used on mac.
715 GlobalMenuBar * global_menubar_;
718 /// Linkback mime handler for MacOSX.
719 QMacPasteboardMimeGraphics mac_pasteboard_mime_;
723 /// WMF Mime handler for Windows clipboard.
724 QWindowsMimeMetafile * wmf_mime_;
729 GuiApplication * guiApp;
731 GuiApplication::~GuiApplication()
734 closeAllLinkBackLinks();
740 GuiApplication::GuiApplication(int & argc, char ** argv)
741 : QApplication(argc, argv), current_view_(0),
742 d(new GuiApplication::Private)
744 QString app_name = "LyX";
745 QCoreApplication::setOrganizationName(app_name);
746 QCoreApplication::setOrganizationDomain("lyx.org");
747 QCoreApplication::setApplicationName(lyx_package);
749 // Install translator for GUI elements.
750 installTranslator(&d->qt_trans_);
752 // FIXME: quitOnLastWindowClosed is true by default. We should have a
753 // lyxrc setting for this in order to let the application stay resident.
754 // But then we need some kind of dock icon, at least on Windows.
756 if (lyxrc.quit_on_last_window_closed)
757 setQuitOnLastWindowClosed(false);
760 // FIXME: Do we need a lyxrc setting for this on Mac? This behaviour
761 // seems to be the default case for applications like LyX.
762 setQuitOnLastWindowClosed(false);
764 // This allows to translate the strings that appear in the LyX menu.
765 /// A translator suitable for the entries in the LyX menu.
766 /// Only needed with Qt/Mac.
767 installTranslator(new MenuTranslator(this));
771 // doubleClickInterval() is 400 ms on X11 which is just too long.
772 // On Windows and Mac OS X, the operating system's value is used.
773 // On Microsoft Windows, calling this function sets the double
774 // click interval for all applications. So we don't!
775 QApplication::setDoubleClickInterval(300);
778 connect(this, SIGNAL(lastWindowClosed()), this, SLOT(onLastWindowClosed()));
780 // needs to be done before reading lyxrc
782 lyxrc.dpi = (w.logicalDpiX() + w.logicalDpiY()) / 2;
786 // Set the cache to 5120 kilobytes which corresponds to screen size of
787 // 1280 by 1024 pixels with a color depth of 32 bits.
788 QPixmapCache::setCacheLimit(5120);
790 // Initialize RC Fonts
791 if (lyxrc.roman_font_name.empty())
792 lyxrc.roman_font_name = fromqstr(romanFontName());
794 if (lyxrc.sans_font_name.empty())
795 lyxrc.sans_font_name = fromqstr(sansFontName());
797 if (lyxrc.typewriter_font_name.empty())
798 lyxrc.typewriter_font_name = fromqstr(typewriterFontName());
800 d->general_timer_.setInterval(500);
801 connect(&d->general_timer_, SIGNAL(timeout()),
802 this, SLOT(handleRegularEvents()));
803 d->general_timer_.start();
807 GuiApplication * theGuiApp()
809 return dynamic_cast<GuiApplication *>(theApp());
813 void GuiApplication::clearSession()
820 docstring GuiApplication::iconName(FuncRequest const & f, bool unknown)
822 return qstring_to_ucs4(lyx::frontend::iconName(f, unknown));
826 LyXView * GuiApplication::currentWindow()
829 /* In LyX/Mac, when a dialog is open, the menus of the
830 application can still be accessed without giving focus to
831 the main window. In this case, we want to disable the menu
832 entries that are buffer or view-related.
834 if (current_view_ && activeWindow() != current_view_)
837 return current_view_;
841 bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const
847 case LFUN_WINDOW_CLOSE:
848 enable = d->views_.size() > 0;
851 case LFUN_BUFFER_NEW:
852 case LFUN_BUFFER_NEW_TEMPLATE:
855 case LFUN_SCREEN_FONT_UPDATE:
857 case LFUN_WINDOW_NEW:
859 case LFUN_LYXRC_APPLY:
860 case LFUN_COMMAND_PREFIX:
862 case LFUN_META_PREFIX:
863 case LFUN_RECONFIGURE:
864 case LFUN_SERVER_GET_FILENAME:
865 case LFUN_SERVER_NOTIFY:
874 flag.setEnabled(false);
880 // This function runs "configure" and then rereads lyx.defaults to
881 // reconfigure the automatic settings.
882 static void reconfigure(GuiView * lv, string const & option)
884 // emit message signal.
886 lv->message(_("Running configure..."));
888 // Run configure in user lyx directory
889 PathChanger p(package().user_support());
890 string configure_command = package().configure_command();
891 configure_command += option;
893 int ret = one.startscript(Systemcall::Wait, configure_command);
895 // emit message signal.
897 lv->message(_("Reloading configuration..."));
898 lyxrc.read(libFileSearch(QString(), "lyxrc.defaults"));
899 // Re-read packages.lst
900 LaTeXFeatures::getAvailable();
903 Alert::information(_("System reconfiguration failed"),
904 _("The system reconfiguration has failed.\n"
905 "Default textclass is used but LyX may "
906 "not be able to work properly.\n"
907 "Please reconfigure again if needed."));
910 Alert::information(_("System reconfigured"),
911 _("The system has been reconfigured.\n"
912 "You need to restart LyX to make use of any\n"
913 "updated document class specifications."));
917 void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr)
919 switch (cmd.action) {
921 case LFUN_WINDOW_NEW:
922 createView(toqstr(cmd.argument()));
925 case LFUN_WINDOW_CLOSE:
926 // update bookmark pit of the current buffer before window close
927 for (size_t i = 0; i < theSession().bookmarks().size(); ++i)
928 theLyXFunc().gotoBookmark(i+1, false, false);
929 // clear the last opened list, because
930 // maybe this will end the session
931 theSession().lastOpened().clear();
932 current_view_->close();
936 // quitting is triggered by the gui code
937 // (leaving the event loop).
939 current_view_->message(from_utf8(N_("Exiting.")));
944 case LFUN_SCREEN_FONT_UPDATE: {
945 // handle the screen font changes.
946 d->font_loader_.update();
947 // Backup current_view_
948 GuiView * view = current_view_;
949 // Set current_view_ to zero to forbid GuiWorkArea::redraw()
950 // to skip the refresh.
952 BufferList::iterator it = theBufferList().begin();
953 BufferList::iterator const end = theBufferList().end();
954 for (; it != end; ++it)
956 // Restore current_view_
957 current_view_ = view;
961 case LFUN_BUFFER_NEW:
962 if (d->views_.empty()
963 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
964 createView(QString(), false); // keep hidden
965 current_view_->newDocument(to_utf8(cmd.argument()), false);
966 current_view_->show();
967 setActiveWindow(current_view_);
969 current_view_->newDocument(to_utf8(cmd.argument()), false);
973 case LFUN_BUFFER_NEW_TEMPLATE:
974 if (d->views_.empty()
975 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
977 current_view_->newDocument(to_utf8(cmd.argument()), true);
978 if (!current_view_->documentBufferView())
979 current_view_->close();
981 current_view_->newDocument(to_utf8(cmd.argument()), true);
986 // FIXME: create a new method shared with LFUN_HELP_OPEN.
987 if (d->views_.empty()
988 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
989 string const fname = to_utf8(cmd.argument());
990 // We want the ui session to be saved per document and not per
991 // window number. The filename crc is a good enough identifier.
992 boost::crc_32_type crc;
993 crc = for_each(fname.begin(), fname.end(), crc);
994 createView(crc.checksum());
995 current_view_->openDocument(fname);
996 if (current_view_ && !current_view_->documentBufferView())
997 current_view_->close();
999 current_view_->openDocument(to_utf8(cmd.argument()));
1002 case LFUN_HELP_OPEN: {
1003 // FIXME: create a new method shared with LFUN_FILE_OPEN.
1004 if (current_view_ == 0)
1006 string const arg = to_utf8(cmd.argument());
1008 current_view_->message(_("Missing argument"));
1011 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1013 fname = i18nLibFileSearch("examples", arg, "lyx");
1015 if (fname.empty()) {
1016 lyxerr << "LyX: unable to find documentation file `"
1017 << arg << "'. Bad installation?" << endl;
1020 current_view_->message(bformat(_("Opening help file %1$s..."),
1021 makeDisplayPath(fname.absFilename())));
1022 Buffer * buf = current_view_->loadDocument(fname, false);
1024 current_view_->setBuffer(buf);
1025 buf->updateLabels();
1026 buf->errors("Parse");
1031 case LFUN_SET_COLOR: {
1033 string const x11_name = split(to_utf8(cmd.argument()), lyx_name, ' ');
1034 if (lyx_name.empty() || x11_name.empty()) {
1035 current_view_->message(
1036 _("Syntax: set-color <lyx_name> <x11_name>"));
1040 string const graphicsbg = lcolor.getLyXName(Color_graphicsbg);
1041 bool const graphicsbg_changed = lyx_name == graphicsbg
1042 && x11_name != graphicsbg;
1043 if (graphicsbg_changed) {
1044 // FIXME: The graphics cache no longer has a changeDisplay method.
1046 graphics::GCache::get().changeDisplay(true);
1050 if (!lcolor.setColor(lyx_name, x11_name)) {
1051 current_view_->message(
1052 bformat(_("Set-color \"%1$s\" failed "
1053 "- color is undefined or "
1054 "may not be redefined"),
1055 from_utf8(lyx_name)));
1058 // Make sure we don't keep old colors in cache.
1059 d->color_cache_.clear();
1063 case LFUN_LYXRC_APPLY: {
1064 // reset active key sequences, since the bindings
1065 // are updated (bug 6064)
1067 LyXRC const lyxrc_orig = lyxrc;
1069 istringstream ss(to_utf8(cmd.argument()));
1070 bool const success = lyxrc.read(ss) == 0;
1073 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1074 << "Unable to read lyxrc data"
1079 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1086 case LFUN_COMMAND_PREFIX:
1087 lyx::dispatch(FuncRequest(LFUN_MESSAGE, d->keyseq.printOptions(true)));
1092 d->meta_fake_bit = NoModifier;
1093 GuiView * gv = currentView();
1094 if (gv && gv->currentBufferView())
1095 // cancel any selection
1096 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1097 dr.setMessage(from_ascii(N_("Cancel")));
1100 case LFUN_META_PREFIX:
1101 d->meta_fake_bit = AltModifier;
1102 dr.setMessage(d->keyseq.print(KeySequence::ForGui));
1105 // --- Menus -----------------------------------------------
1106 case LFUN_RECONFIGURE:
1107 // argument is any additional parameter to the configure.py command
1108 reconfigure(currentView(), to_utf8(cmd.argument()));
1111 // --- lyxserver commands ----------------------------
1112 case LFUN_SERVER_GET_FILENAME: {
1113 GuiView * lv = currentView();
1114 LASSERT(lv && lv->documentBufferView(), return);
1115 docstring const fname = from_utf8(
1116 lv->documentBufferView()->buffer().absFileName());
1117 dr.setMessage(fname);
1118 LYXERR(Debug::INFO, "FNAME[" << fname << ']');
1121 case LFUN_SERVER_NOTIFY: {
1122 docstring const dispatch_buffer = d->keyseq.print(KeySequence::Portable);
1123 dr.setMessage(dispatch_buffer);
1124 theServer().notifyClient(to_utf8(dispatch_buffer));
1128 // Notify the caller that the action has not been dispatched.
1129 dr.dispatched(false);
1133 // The action has been dispatched.
1134 dr.dispatched(true);
1138 docstring GuiApplication::viewStatusMessage()
1140 // When meta-fake key is pressed, show the key sequence so far + "M-".
1141 if (d->meta_fake_bit != NoModifier)
1142 return d->keyseq.print(KeySequence::ForGui) + "M-";
1144 // Else, when a non-complete key sequence is pressed,
1145 // show the available options.
1146 if (d->keyseq.length() > 0 && !d->keyseq.deleted())
1147 return d->keyseq.printOptions(true);
1153 void GuiApplication::handleKeyFunc(FuncCode action)
1157 if (d->keyseq.length())
1159 GuiView * gv = currentView();
1160 LASSERT(gv && gv->currentBufferView(), return);
1161 BufferView * bv = gv->currentBufferView();
1162 bv->getIntl().getTransManager().deadkey(
1163 c, get_accent(action).accent, bv->cursor().innerText(),
1165 // Need to clear, in case the minibuffer calls these
1168 // copied verbatim from do_accent_char
1169 bv->cursor().resetAnchor();
1170 bv->processUpdateFlags(Update::FitCursor);
1174 void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
1176 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
1178 GuiView * lv = currentView();
1180 // Do nothing if we have nothing (JMarc)
1181 if (!keysym.isOK()) {
1182 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
1183 lv->restartCursor();
1187 if (keysym.isModifier()) {
1188 LYXERR(Debug::KEY, "isModifier true");
1190 lv->restartCursor();
1194 char_type encoded_last_key = keysym.getUCSEncoded();
1196 // Do a one-deep top-level lookup for
1197 // cancel and meta-fake keys. RVDK_PATCH_5
1198 d->cancel_meta_seq.reset();
1200 FuncRequest func = d->cancel_meta_seq.addkey(keysym, state);
1201 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
1203 // When not cancel or meta-fake, do the normal lookup.
1204 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
1205 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
1206 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
1207 // remove Caps Lock and Mod2 as a modifiers
1208 func = d->keyseq.addkey(keysym, (state | d->meta_fake_bit));
1209 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
1212 // Dont remove this unless you know what you are doing.
1213 d->meta_fake_bit = NoModifier;
1215 // Can this happen now ?
1216 if (func.action == LFUN_NOACTION)
1217 func = FuncRequest(LFUN_COMMAND_PREFIX);
1219 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
1220 << d->keyseq.print(KeySequence::Portable) << ']');
1222 // already here we know if it any point in going further
1223 // why not return already here if action == -1 and
1224 // num_bytes == 0? (Lgb)
1226 if (d->keyseq.length() > 1)
1227 lv->message(d->keyseq.print(KeySequence::ForGui));
1230 // Maybe user can only reach the key via holding down shift.
1231 // Let's see. But only if shift is the only modifier
1232 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
1233 LYXERR(Debug::KEY, "Trying without shift");
1234 func = d->keyseq.addkey(keysym, NoModifier);
1235 LYXERR(Debug::KEY, "Action now " << func.action);
1238 if (func.action == LFUN_UNKNOWN_ACTION) {
1239 // Hmm, we didn't match any of the keysequences. See
1240 // if it's normal insertable text not already covered
1242 if (keysym.isText() && d->keyseq.length() == 1) {
1243 LYXERR(Debug::KEY, "isText() is true, inserting.");
1244 func = FuncRequest(LFUN_SELF_INSERT,
1245 FuncRequest::KEYBOARD);
1247 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
1248 lv->message(_("Unknown function."));
1249 lv->restartCursor();
1254 if (func.action == LFUN_SELF_INSERT) {
1255 if (encoded_last_key != 0) {
1256 docstring const arg(1, encoded_last_key);
1257 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
1258 FuncRequest::KEYBOARD));
1259 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
1262 lyx::dispatch(func);
1269 void GuiApplication::dispatchDelayed(FuncRequest const & func)
1271 d->func_request_queue_.push(func);
1272 QTimer::singleShot(0, this, SLOT(processFuncRequestQueue()));
1276 void GuiApplication::resetGui()
1278 // Set the language defined by the user.
1282 if (!readUIFile(toqstr(lyxrc.ui_file)))
1283 // Gives some error box here.
1286 if (d->global_menubar_)
1287 d->menus_.fillMenuBar(d->global_menubar_, 0, false);
1289 QHash<int, GuiView *>::iterator it;
1290 for (it = d->views_.begin(); it != d->views_.end(); ++it) {
1293 gv->setLayoutDirection(layoutDirection());
1297 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
1301 void GuiApplication::createView(int view_id)
1303 createView(QString(), true, view_id);
1307 void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
1310 // release the keyboard which might have been grabed by the global
1311 // menubar on Mac to catch shortcuts even without any GuiView.
1312 if (d->global_menubar_)
1313 d->global_menubar_->releaseKeyboard();
1317 while (d->views_.find(id) != d->views_.end())
1320 LYXERR(Debug::GUI, "About to create new window with ID " << id);
1321 GuiView * view = new GuiView(id);
1323 d->views_[id] = view;
1327 setActiveWindow(view);
1330 if (!geometry_arg.isEmpty()) {
1334 QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)([+-][0-9]*)){0,1}" );
1335 re.indexIn(geometry_arg);
1336 w = re.cap(1).toInt();
1337 h = re.cap(2).toInt();
1338 x = re.cap(3).toInt();
1339 y = re.cap(4).toInt();
1340 view->setGeometry(x, y, w, h);
1347 Clipboard & GuiApplication::clipboard()
1349 return d->clipboard_;
1353 Selection & GuiApplication::selection()
1355 return d->selection_;
1359 FontLoader & GuiApplication::fontLoader()
1361 return d->font_loader_;
1365 Toolbars const & GuiApplication::toolbars() const
1367 return d->toolbars_;
1371 Toolbars & GuiApplication::toolbars()
1373 return d->toolbars_;
1377 Menus const & GuiApplication::menus() const
1383 Menus & GuiApplication::menus()
1389 QList<int> GuiApplication::viewIds() const
1391 return d->views_.keys();
1395 ColorCache & GuiApplication::colorCache()
1397 return d->color_cache_;
1401 int GuiApplication::exec()
1403 // asynchronously handle batch commands. This event will be in
1404 // the event queue in front of other asynchronous events. Hence,
1405 // we can assume in the latter that the gui is setup already.
1406 QTimer::singleShot(0, this, SLOT(execBatchCommands()));
1408 return QApplication::exec();
1412 void GuiApplication::exit(int status)
1414 QApplication::exit(status);
1418 void GuiApplication::setGuiLanguage()
1420 // Set the language defined by the user.
1423 QString const default_language = toqstr(Messages::defaultLanguage());
1424 LYXERR(Debug::LOCALE, "Trying to set default locale to: " << default_language);
1425 QLocale const default_locale(default_language);
1426 QLocale::setDefault(default_locale);
1428 // install translation file for Qt built-in dialogs
1429 QString const language_name = QString("qt_") + default_locale.name();
1431 // language_name can be short (e.g. qt_zh) or long (e.g. qt_zh_CN).
1432 // Short-named translator can be loaded from a long name, but not the
1433 // opposite. Therefore, long name should be used without truncation.
1434 // c.f. http://doc.trolltech.com/4.1/qtranslator.html#load
1435 if (!d->qt_trans_.load(language_name,
1436 QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
1437 LYXERR(Debug::LOCALE, "Could not find Qt translations for locale "
1440 LYXERR(Debug::LOCALE, "Successfully installed Qt translations for locale "
1444 switch (default_locale.language()) {
1445 case QLocale::Arabic :
1446 case QLocale::Hebrew :
1447 case QLocale::Persian :
1448 case QLocale::Urdu :
1449 setLayoutDirection(Qt::RightToLeft);
1452 setLayoutDirection(Qt::LeftToRight);
1457 void GuiApplication::processFuncRequestQueue()
1459 while (!d->func_request_queue_.empty()) {
1460 lyx::dispatch(d->func_request_queue_.back());
1461 d->func_request_queue_.pop();
1466 void GuiApplication::execBatchCommands()
1471 if (!readUIFile(toqstr(lyxrc.ui_file)))
1472 // Gives some error box here.
1476 // Create the global default menubar which is shown for the dialogs
1477 // and if no GuiView is visible.
1478 // This must be done after the session was recovered to know the "last files".
1479 d->global_menubar_ = new GlobalMenuBar();
1480 d->menus_.fillMenuBar(d->global_menubar_, 0, true);
1483 lyx::execBatchCommands();
1487 QAbstractItemModel * GuiApplication::languageModel()
1489 if (d->language_model_)
1490 return d->language_model_;
1492 QStandardItemModel * lang_model = new QStandardItemModel(this);
1493 lang_model->insertColumns(0, 1);
1495 Languages::const_iterator it = lyx::languages.begin();
1496 Languages::const_iterator end = lyx::languages.end();
1497 for (; it != end; ++it) {
1498 current_row = lang_model->rowCount();
1499 lang_model->insertRows(current_row, 1);
1500 QModelIndex item = lang_model->index(current_row, 0);
1501 lang_model->setData(item, qt_(it->second.display()), Qt::DisplayRole);
1502 lang_model->setData(item, toqstr(it->second.lang()), Qt::UserRole);
1504 d->language_model_ = new QSortFilterProxyModel(this);
1505 d->language_model_->setSourceModel(lang_model);
1506 #if QT_VERSION >= 0x040300
1507 d->language_model_->setSortLocaleAware(true);
1509 return d->language_model_;
1513 void GuiApplication::restoreGuiSession()
1515 if (!lyxrc.load_session)
1518 Session & session = theSession();
1519 LastOpenedSection::LastOpened const & lastopened =
1520 session.lastOpened().getfiles();
1522 FileName active_file;
1523 // do not add to the lastfile list since these files are restored from
1524 // last session, and should be already there (regular files), or should
1525 // not be added at all (help files).
1526 for (size_t i = 0; i < lastopened.size(); ++i) {
1527 FileName const & file_name = lastopened[i].file_name;
1528 if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs
1529 && current_view_->documentBufferView() != 0)) {
1530 boost::crc_32_type crc;
1531 string const & fname = file_name.absFilename();
1532 crc = for_each(fname.begin(), fname.end(), crc);
1533 createView(crc.checksum());
1535 current_view_->loadDocument(file_name, false);
1537 if (lastopened[i].active)
1538 active_file = file_name;
1541 // Restore last active buffer
1542 Buffer * buffer = theBufferList().getBuffer(active_file);
1544 current_view_->setBuffer(buffer);
1546 // clear this list to save a few bytes of RAM
1547 session.lastOpened().clear();
1551 QString const GuiApplication::romanFontName()
1554 font.setKerning(false);
1555 font.setStyleHint(QFont::Serif);
1556 font.setFamily("serif");
1558 return QFontInfo(font).family();
1562 QString const GuiApplication::sansFontName()
1565 font.setKerning(false);
1566 font.setStyleHint(QFont::SansSerif);
1567 font.setFamily("sans");
1569 return QFontInfo(font).family();
1573 QString const GuiApplication::typewriterFontName()
1576 font.setKerning(false);
1577 font.setStyleHint(QFont::TypeWriter);
1578 font.setFamily("monospace");
1580 return QFontInfo(font).family();
1584 void GuiApplication::handleRegularEvents()
1586 ForkedCallsController::handleCompletedProcesses();
1590 bool GuiApplication::event(QEvent * e)
1593 case QEvent::FileOpen: {
1594 // Open a file; this happens only on Mac OS X for now.
1596 // We do this asynchronously because on startup the batch
1597 // commands are not executed here yet and the gui is not ready
1599 QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
1600 dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file())));
1605 return QApplication::event(e);
1610 bool GuiApplication::notify(QObject * receiver, QEvent * event)
1613 return QApplication::notify(receiver, event);
1615 catch (ExceptionMessage const & e) {
1617 case ErrorException:
1619 setQuitOnLastWindowClosed(false);
1621 Alert::error(e.title_, e.details_);
1623 // Properly crash in debug mode in order to get a useful backtrace.
1626 // In release mode, try to exit gracefully.
1629 case BufferException: {
1630 if (!current_view_->documentBufferView())
1632 Buffer * buf = ¤t_view_->documentBufferView()->buffer();
1633 docstring details = e.details_ + '\n';
1634 details += buf->emergencyWrite();
1635 theBufferList().release(buf);
1636 details += "\n" + _("The current document was closed.");
1637 Alert::error(e.title_, details);
1640 case WarningException:
1641 Alert::warning(e.title_, e.details_);
1645 catch (exception const & e) {
1646 docstring s = _("LyX has caught an exception, it will now "
1647 "attempt to save all unsaved documents and exit."
1649 s += from_ascii(e.what());
1650 Alert::error(_("Software exception Detected"), s);
1654 docstring s = _("LyX has caught some really weird exception, it will "
1655 "now attempt to save all unsaved documents and exit.");
1656 Alert::error(_("Software exception Detected"), s);
1664 bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
1666 QColor const & qcol = d->color_cache_.get(col);
1667 if (!qcol.isValid()) {
1673 rgbcol.r = qcol.red();
1674 rgbcol.g = qcol.green();
1675 rgbcol.b = qcol.blue();
1680 string const GuiApplication::hexName(ColorCode col)
1682 return ltrim(fromqstr(d->color_cache_.get(col).name()), "#");
1686 void GuiApplication::registerSocketCallback(int fd, SocketCallback func)
1688 SocketNotifier * sn = new SocketNotifier(this, fd, func);
1689 d->socket_notifiers_[fd] = sn;
1690 connect(sn, SIGNAL(activated(int)), this, SLOT(socketDataReceived(int)));
1694 void GuiApplication::socketDataReceived(int fd)
1696 d->socket_notifiers_[fd]->func_();
1700 void GuiApplication::unregisterSocketCallback(int fd)
1702 d->socket_notifiers_.take(fd)->setEnabled(false);
1706 void GuiApplication::commitData(QSessionManager & sm)
1708 /// The implementation is required to avoid an application exit
1709 /// when session state save is triggered by session manager.
1710 /// The default implementation sends a close event to all
1711 /// visible top level widgets when session managment allows
1713 /// We are changing that to close all wiew one by one.
1714 /// FIXME: verify if the default implementation is enough now.
1715 if (sm.allowsInteraction() && !closeAllViews())
1720 void GuiApplication::unregisterView(GuiView * gv)
1722 LASSERT(d->views_[gv->id()] == gv, /**/);
1723 d->views_.remove(gv->id());
1724 if (current_view_ == gv)
1729 bool GuiApplication::closeAllViews()
1731 if (d->views_.empty())
1734 // When a view/window was closed before without quitting LyX, there
1735 // are already entries in the lastOpened list.
1736 theSession().lastOpened().clear();
1738 QList<GuiView *> views = d->views_.values();
1739 foreach (GuiView * view, views) {
1749 GuiView & GuiApplication::view(int id) const
1751 LASSERT(d->views_.contains(id), /**/);
1752 return *d->views_.value(id);
1756 void GuiApplication::hideDialogs(string const & name, Inset * inset) const
1758 QList<GuiView *> views = d->views_.values();
1759 foreach (GuiView * view, views)
1760 view->hideDialog(name, inset);
1764 Buffer const * GuiApplication::updateInset(Inset const * inset) const
1766 Buffer const * buffer_ = 0;
1767 QHash<int, GuiView *>::iterator end = d->views_.end();
1768 for (QHash<int, GuiView *>::iterator it = d->views_.begin(); it != end; ++it) {
1769 if (Buffer const * ptr = (*it)->updateInset(inset))
1776 bool GuiApplication::searchMenu(FuncRequest const & func,
1777 docstring_list & names) const
1779 return d->menus_.searchMenu(func, names);
1783 bool GuiApplication::readUIFile(QString const & name, bool include)
1785 LYXERR(Debug::INIT, "About to read " << name << "...");
1789 ui_path = libFileSearch("ui", name, "inc");
1790 if (ui_path.empty())
1791 ui_path = libFileSearch("ui", changeExtension(name, "inc"));
1793 ui_path = libFileSearch("ui", name, "ui");
1796 if (ui_path.empty()) {
1797 static const QString defaultUIFile = "default";
1798 LYXERR(Debug::INIT, "Could not find " << name);
1800 Alert::warning(_("Could not find UI definition file"),
1801 bformat(_("Error while reading the included file\n%1$s\n"
1802 "Please check your installation."), qstring_to_ucs4(name)));
1805 if (name == defaultUIFile) {
1806 LYXERR(Debug::INIT, "Could not find default UI file!!");
1807 Alert::warning(_("Could not find default UI file"),
1808 _("LyX could not find the default UI file!\n"
1809 "Please check your installation."));
1812 Alert::warning(_("Could not find UI definition file"),
1813 bformat(_("Error while reading the configuration file\n%1$s\n"
1814 "Falling back to default.\n"
1815 "Please look under Tools>Preferences>User Interface and\n"
1816 "check which User Interface file you are using."), qstring_to_ucs4(name)));
1817 return readUIFile(defaultUIFile, false);
1820 // Ensure that a file is read only once (prevents include loops)
1821 static QStringList uifiles;
1822 QString const uifile = toqstr(ui_path.absFilename());
1823 if (uifiles.contains(uifile)) {
1825 // We are reading again the top uifile so reset the safeguard:
1828 d->toolbars_.reset();
1830 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1831 << "Is this an include loop?");
1835 uifiles.push_back(uifile);
1837 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1847 LexerKeyword uitags[] = {
1848 { "include", ui_include },
1849 { "menuset", ui_menuset },
1850 { "toolbars", ui_toolbars },
1851 { "toolbarset", ui_toolbarset }
1855 lex.setFile(ui_path);
1857 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1861 if (lyxerr.debugging(Debug::PARSER))
1862 lex.printTable(lyxerr);
1864 // store which ui files define Toolbars
1865 static QStringList toolbar_uifiles;
1867 while (lex.isOK()) {
1868 switch (lex.lex()) {
1871 QString const file = toqstr(lex.getString());
1872 if (!readUIFile(file, true))
1877 d->menus_.read(lex);
1881 d->toolbars_.readToolbars(lex);
1885 d->toolbars_.readToolbarSettings(lex);
1886 toolbar_uifiles.push_back(uifile);
1890 if (!rtrim(lex.getString()).empty())
1891 lex.printError("LyX::ReadUIFile: "
1892 "Unknown menu tag: `$$Token'");
1901 settings.beginGroup("ui_files");
1902 bool touched = false;
1903 for (int i = 0; i != uifiles.size(); ++i) {
1904 QFileInfo fi(uifiles[i]);
1905 QDateTime const date_value = fi.lastModified();
1906 QString const name_key = QString::number(i);
1907 // if an ui file which defines Toolbars has changed,
1908 // we have to reset the settings
1909 if (toolbar_uifiles.contains(uifiles[i])
1910 && (!settings.contains(name_key)
1911 || settings.value(name_key).toString() != uifiles[i]
1912 || settings.value(name_key + "/date").toDateTime() != date_value)) {
1914 settings.setValue(name_key, uifiles[i]);
1915 settings.setValue(name_key + "/date", date_value);
1918 settings.endGroup();
1920 settings.remove("views");
1926 void GuiApplication::onLastWindowClosed()
1928 if (d->global_menubar_)
1929 d->global_menubar_->grabKeyboard();
1933 ////////////////////////////////////////////////////////////////////////
1935 // X11 specific stuff goes here...
1938 bool GuiApplication::x11EventFilter(XEvent * xev)
1943 switch (xev->type) {
1944 case SelectionRequest: {
1945 if (xev->xselectionrequest.selection != XA_PRIMARY)
1947 LYXERR(Debug::SELECTION, "X requested selection.");
1948 BufferView * bv = current_view_->currentBufferView();
1950 docstring const sel = bv->requestSelection();
1952 d->selection_.put(sel);
1956 case SelectionClear: {
1957 if (xev->xselectionclear.selection != XA_PRIMARY)
1959 LYXERR(Debug::SELECTION, "Lost selection.");
1960 BufferView * bv = current_view_->currentBufferView();
1962 bv->clearSelection();
1970 } // namespace frontend
1973 void hideDialogs(std::string const & name, Inset * inset)
1976 theApp()->hideDialogs(name, inset);
1980 ////////////////////////////////////////////////////////////////////
1984 ////////////////////////////////////////////////////////////////////
1986 frontend::FontLoader & theFontLoader()
1988 LASSERT(frontend::guiApp, /**/);
1989 return frontend::guiApp->fontLoader();
1993 frontend::FontMetrics const & theFontMetrics(Font const & f)
1995 return theFontMetrics(f.fontInfo());
1999 frontend::FontMetrics const & theFontMetrics(FontInfo const & f)
2001 LASSERT(frontend::guiApp, /**/);
2002 return frontend::guiApp->fontLoader().metrics(f);
2006 ////////////////////////////////////////////////////////////////////
2010 ////////////////////////////////////////////////////////////////////
2012 frontend::Clipboard & theClipboard()
2014 LASSERT(frontend::guiApp, /**/);
2015 return frontend::guiApp->clipboard();
2019 frontend::Selection & theSelection()
2021 LASSERT(frontend::guiApp, /**/);
2022 return frontend::guiApp->selection();
2028 #include "moc_GuiApplication.cpp"