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 theBufferList().changed(false);
953 // Restore current_view_
954 current_view_ = view;
958 case LFUN_BUFFER_NEW:
959 if (d->views_.empty()
960 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
961 createView(QString(), false); // keep hidden
962 current_view_->newDocument(to_utf8(cmd.argument()), false);
963 current_view_->show();
964 setActiveWindow(current_view_);
966 current_view_->newDocument(to_utf8(cmd.argument()), false);
970 case LFUN_BUFFER_NEW_TEMPLATE:
971 if (d->views_.empty()
972 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
974 current_view_->newDocument(to_utf8(cmd.argument()), true);
975 if (!current_view_->documentBufferView())
976 current_view_->close();
978 current_view_->newDocument(to_utf8(cmd.argument()), true);
983 // FIXME: create a new method shared with LFUN_HELP_OPEN.
984 if (d->views_.empty()
985 || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) {
986 string const fname = to_utf8(cmd.argument());
987 // We want the ui session to be saved per document and not per
988 // window number. The filename crc is a good enough identifier.
989 boost::crc_32_type crc;
990 crc = for_each(fname.begin(), fname.end(), crc);
991 createView(crc.checksum());
992 current_view_->openDocument(fname);
993 if (current_view_ && !current_view_->documentBufferView())
994 current_view_->close();
996 current_view_->openDocument(to_utf8(cmd.argument()));
999 case LFUN_HELP_OPEN: {
1000 // FIXME: create a new method shared with LFUN_FILE_OPEN.
1001 if (current_view_ == 0)
1003 string const arg = to_utf8(cmd.argument());
1005 current_view_->message(_("Missing argument"));
1008 FileName fname = i18nLibFileSearch("doc", arg, "lyx");
1010 fname = i18nLibFileSearch("examples", arg, "lyx");
1012 if (fname.empty()) {
1013 lyxerr << "LyX: unable to find documentation file `"
1014 << arg << "'. Bad installation?" << endl;
1017 current_view_->message(bformat(_("Opening help file %1$s..."),
1018 makeDisplayPath(fname.absFilename())));
1019 Buffer * buf = current_view_->loadDocument(fname, false);
1021 current_view_->setBuffer(buf);
1022 buf->updateLabels();
1023 buf->errors("Parse");
1028 case LFUN_SET_COLOR: {
1030 string const x11_name = split(to_utf8(cmd.argument()), lyx_name, ' ');
1031 if (lyx_name.empty() || x11_name.empty()) {
1032 current_view_->message(
1033 _("Syntax: set-color <lyx_name> <x11_name>"));
1037 string const graphicsbg = lcolor.getLyXName(Color_graphicsbg);
1038 bool const graphicsbg_changed = lyx_name == graphicsbg
1039 && x11_name != graphicsbg;
1040 if (graphicsbg_changed) {
1041 // FIXME: The graphics cache no longer has a changeDisplay method.
1043 graphics::GCache::get().changeDisplay(true);
1047 if (!lcolor.setColor(lyx_name, x11_name)) {
1048 current_view_->message(
1049 bformat(_("Set-color \"%1$s\" failed "
1050 "- color is undefined or "
1051 "may not be redefined"),
1052 from_utf8(lyx_name)));
1055 // Make sure we don't keep old colors in cache.
1056 d->color_cache_.clear();
1060 case LFUN_LYXRC_APPLY: {
1061 // reset active key sequences, since the bindings
1062 // are updated (bug 6064)
1064 LyXRC const lyxrc_orig = lyxrc;
1066 istringstream ss(to_utf8(cmd.argument()));
1067 bool const success = lyxrc.read(ss) == 0;
1070 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1071 << "Unable to read lyxrc data"
1076 actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1083 case LFUN_COMMAND_PREFIX:
1084 lyx::dispatch(FuncRequest(LFUN_MESSAGE, d->keyseq.printOptions(true)));
1089 d->meta_fake_bit = NoModifier;
1090 GuiView * gv = currentView();
1091 if (gv && gv->currentBufferView())
1092 // cancel any selection
1093 lyx::dispatch(FuncRequest(LFUN_MARK_OFF));
1094 dr.setMessage(from_ascii(N_("Cancel")));
1097 case LFUN_META_PREFIX:
1098 d->meta_fake_bit = AltModifier;
1099 dr.setMessage(d->keyseq.print(KeySequence::ForGui));
1102 // --- Menus -----------------------------------------------
1103 case LFUN_RECONFIGURE:
1104 // argument is any additional parameter to the configure.py command
1105 reconfigure(currentView(), to_utf8(cmd.argument()));
1108 // --- lyxserver commands ----------------------------
1109 case LFUN_SERVER_GET_FILENAME: {
1110 GuiView * lv = currentView();
1111 LASSERT(lv && lv->documentBufferView(), return);
1112 docstring const fname = from_utf8(
1113 lv->documentBufferView()->buffer().absFileName());
1114 dr.setMessage(fname);
1115 LYXERR(Debug::INFO, "FNAME[" << fname << ']');
1118 case LFUN_SERVER_NOTIFY: {
1119 docstring const dispatch_buffer = d->keyseq.print(KeySequence::Portable);
1120 dr.setMessage(dispatch_buffer);
1121 theServer().notifyClient(to_utf8(dispatch_buffer));
1125 // Notify the caller that the action has not been dispatched.
1126 dr.dispatched(false);
1130 // The action has been dispatched.
1131 dr.dispatched(true);
1135 docstring GuiApplication::viewStatusMessage()
1137 // When meta-fake key is pressed, show the key sequence so far + "M-".
1138 if (d->meta_fake_bit != NoModifier)
1139 return d->keyseq.print(KeySequence::ForGui) + "M-";
1141 // Else, when a non-complete key sequence is pressed,
1142 // show the available options.
1143 if (d->keyseq.length() > 0 && !d->keyseq.deleted())
1144 return d->keyseq.printOptions(true);
1150 void GuiApplication::handleKeyFunc(FuncCode action)
1154 if (d->keyseq.length())
1156 GuiView * gv = currentView();
1157 LASSERT(gv && gv->currentBufferView(), return);
1158 BufferView * bv = gv->currentBufferView();
1159 bv->getIntl().getTransManager().deadkey(
1160 c, get_accent(action).accent, bv->cursor().innerText(),
1162 // Need to clear, in case the minibuffer calls these
1165 // copied verbatim from do_accent_char
1166 bv->cursor().resetAnchor();
1167 bv->processUpdateFlags(Update::FitCursor);
1171 void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state)
1173 LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
1175 GuiView * lv = currentView();
1177 // Do nothing if we have nothing (JMarc)
1178 if (!keysym.isOK()) {
1179 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
1180 lv->restartCursor();
1184 if (keysym.isModifier()) {
1185 LYXERR(Debug::KEY, "isModifier true");
1187 lv->restartCursor();
1191 char_type encoded_last_key = keysym.getUCSEncoded();
1193 // Do a one-deep top-level lookup for
1194 // cancel and meta-fake keys. RVDK_PATCH_5
1195 d->cancel_meta_seq.reset();
1197 FuncRequest func = d->cancel_meta_seq.addkey(keysym, state);
1198 LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
1200 // When not cancel or meta-fake, do the normal lookup.
1201 // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
1202 // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
1203 if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
1204 // remove Caps Lock and Mod2 as a modifiers
1205 func = d->keyseq.addkey(keysym, (state | d->meta_fake_bit));
1206 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
1209 // Dont remove this unless you know what you are doing.
1210 d->meta_fake_bit = NoModifier;
1212 // Can this happen now ?
1213 if (func.action == LFUN_NOACTION)
1214 func = FuncRequest(LFUN_COMMAND_PREFIX);
1216 LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
1217 << d->keyseq.print(KeySequence::Portable) << ']');
1219 // already here we know if it any point in going further
1220 // why not return already here if action == -1 and
1221 // num_bytes == 0? (Lgb)
1223 if (d->keyseq.length() > 1)
1224 lv->message(d->keyseq.print(KeySequence::ForGui));
1227 // Maybe user can only reach the key via holding down shift.
1228 // Let's see. But only if shift is the only modifier
1229 if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
1230 LYXERR(Debug::KEY, "Trying without shift");
1231 func = d->keyseq.addkey(keysym, NoModifier);
1232 LYXERR(Debug::KEY, "Action now " << func.action);
1235 if (func.action == LFUN_UNKNOWN_ACTION) {
1236 // Hmm, we didn't match any of the keysequences. See
1237 // if it's normal insertable text not already covered
1239 if (keysym.isText() && d->keyseq.length() == 1) {
1240 LYXERR(Debug::KEY, "isText() is true, inserting.");
1241 func = FuncRequest(LFUN_SELF_INSERT,
1242 FuncRequest::KEYBOARD);
1244 LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
1245 lv->message(_("Unknown function."));
1246 lv->restartCursor();
1251 if (func.action == LFUN_SELF_INSERT) {
1252 if (encoded_last_key != 0) {
1253 docstring const arg(1, encoded_last_key);
1254 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
1255 FuncRequest::KEYBOARD));
1256 LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
1259 lyx::dispatch(func);
1266 void GuiApplication::dispatchDelayed(FuncRequest const & func)
1268 d->func_request_queue_.push(func);
1269 QTimer::singleShot(0, this, SLOT(processFuncRequestQueue()));
1273 void GuiApplication::resetGui()
1275 // Set the language defined by the user.
1279 if (!readUIFile(toqstr(lyxrc.ui_file)))
1280 // Gives some error box here.
1283 if (d->global_menubar_)
1284 d->menus_.fillMenuBar(d->global_menubar_, 0, false);
1286 QHash<int, GuiView *>::iterator it;
1287 for (it = d->views_.begin(); it != d->views_.end(); ++it) {
1290 gv->setLayoutDirection(layoutDirection());
1294 lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE));
1298 void GuiApplication::createView(int view_id)
1300 createView(QString(), true, view_id);
1304 void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
1307 // release the keyboard which might have been grabed by the global
1308 // menubar on Mac to catch shortcuts even without any GuiView.
1309 if (d->global_menubar_)
1310 d->global_menubar_->releaseKeyboard();
1314 while (d->views_.find(id) != d->views_.end())
1317 LYXERR(Debug::GUI, "About to create new window with ID " << id);
1318 GuiView * view = new GuiView(id);
1320 d->views_[id] = view;
1324 setActiveWindow(view);
1327 if (!geometry_arg.isEmpty()) {
1331 QRegExp re( "[=]*(?:([0-9]+)[xX]([0-9]+)){0,1}[ ]*(?:([+-][0-9]*)([+-][0-9]*)){0,1}" );
1332 re.indexIn(geometry_arg);
1333 w = re.cap(1).toInt();
1334 h = re.cap(2).toInt();
1335 x = re.cap(3).toInt();
1336 y = re.cap(4).toInt();
1337 view->setGeometry(x, y, w, h);
1344 Clipboard & GuiApplication::clipboard()
1346 return d->clipboard_;
1350 Selection & GuiApplication::selection()
1352 return d->selection_;
1356 FontLoader & GuiApplication::fontLoader()
1358 return d->font_loader_;
1362 Toolbars const & GuiApplication::toolbars() const
1364 return d->toolbars_;
1368 Toolbars & GuiApplication::toolbars()
1370 return d->toolbars_;
1374 Menus const & GuiApplication::menus() const
1380 Menus & GuiApplication::menus()
1386 QList<int> GuiApplication::viewIds() const
1388 return d->views_.keys();
1392 ColorCache & GuiApplication::colorCache()
1394 return d->color_cache_;
1398 int GuiApplication::exec()
1400 // asynchronously handle batch commands. This event will be in
1401 // the event queue in front of other asynchronous events. Hence,
1402 // we can assume in the latter that the gui is setup already.
1403 QTimer::singleShot(0, this, SLOT(execBatchCommands()));
1405 return QApplication::exec();
1409 void GuiApplication::exit(int status)
1411 QApplication::exit(status);
1415 void GuiApplication::setGuiLanguage()
1417 // Set the language defined by the user.
1420 QString const default_language = toqstr(Messages::defaultLanguage());
1421 LYXERR(Debug::LOCALE, "Trying to set default locale to: " << default_language);
1422 QLocale const default_locale(default_language);
1423 QLocale::setDefault(default_locale);
1425 // install translation file for Qt built-in dialogs
1426 QString const language_name = QString("qt_") + default_locale.name();
1428 // language_name can be short (e.g. qt_zh) or long (e.g. qt_zh_CN).
1429 // Short-named translator can be loaded from a long name, but not the
1430 // opposite. Therefore, long name should be used without truncation.
1431 // c.f. http://doc.trolltech.com/4.1/qtranslator.html#load
1432 if (!d->qt_trans_.load(language_name,
1433 QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
1434 LYXERR(Debug::LOCALE, "Could not find Qt translations for locale "
1437 LYXERR(Debug::LOCALE, "Successfully installed Qt translations for locale "
1441 switch (default_locale.language()) {
1442 case QLocale::Arabic :
1443 case QLocale::Hebrew :
1444 case QLocale::Persian :
1445 case QLocale::Urdu :
1446 setLayoutDirection(Qt::RightToLeft);
1449 setLayoutDirection(Qt::LeftToRight);
1454 void GuiApplication::processFuncRequestQueue()
1456 while (!d->func_request_queue_.empty()) {
1457 lyx::dispatch(d->func_request_queue_.back());
1458 d->func_request_queue_.pop();
1463 void GuiApplication::execBatchCommands()
1468 if (!readUIFile(toqstr(lyxrc.ui_file)))
1469 // Gives some error box here.
1473 // Create the global default menubar which is shown for the dialogs
1474 // and if no GuiView is visible.
1475 // This must be done after the session was recovered to know the "last files".
1476 d->global_menubar_ = new GlobalMenuBar();
1477 d->menus_.fillMenuBar(d->global_menubar_, 0, true);
1480 lyx::execBatchCommands();
1484 QAbstractItemModel * GuiApplication::languageModel()
1486 if (d->language_model_)
1487 return d->language_model_;
1489 QStandardItemModel * lang_model = new QStandardItemModel(this);
1490 lang_model->insertColumns(0, 1);
1492 Languages::const_iterator it = lyx::languages.begin();
1493 Languages::const_iterator end = lyx::languages.end();
1494 for (; it != end; ++it) {
1495 current_row = lang_model->rowCount();
1496 lang_model->insertRows(current_row, 1);
1497 QModelIndex item = lang_model->index(current_row, 0);
1498 lang_model->setData(item, qt_(it->second.display()), Qt::DisplayRole);
1499 lang_model->setData(item, toqstr(it->second.lang()), Qt::UserRole);
1501 d->language_model_ = new QSortFilterProxyModel(this);
1502 d->language_model_->setSourceModel(lang_model);
1503 #if QT_VERSION >= 0x040300
1504 d->language_model_->setSortLocaleAware(true);
1506 return d->language_model_;
1510 void GuiApplication::restoreGuiSession()
1512 if (!lyxrc.load_session)
1515 Session & session = theSession();
1516 LastOpenedSection::LastOpened const & lastopened =
1517 session.lastOpened().getfiles();
1519 FileName active_file;
1520 // do not add to the lastfile list since these files are restored from
1521 // last session, and should be already there (regular files), or should
1522 // not be added at all (help files).
1523 for (size_t i = 0; i < lastopened.size(); ++i) {
1524 FileName const & file_name = lastopened[i].file_name;
1525 if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs
1526 && current_view_->documentBufferView() != 0)) {
1527 boost::crc_32_type crc;
1528 string const & fname = file_name.absFilename();
1529 crc = for_each(fname.begin(), fname.end(), crc);
1530 createView(crc.checksum());
1532 current_view_->loadDocument(file_name, false);
1534 if (lastopened[i].active)
1535 active_file = file_name;
1538 // Restore last active buffer
1539 Buffer * buffer = theBufferList().getBuffer(active_file);
1541 current_view_->setBuffer(buffer);
1543 // clear this list to save a few bytes of RAM
1544 session.lastOpened().clear();
1548 QString const GuiApplication::romanFontName()
1551 font.setKerning(false);
1552 font.setStyleHint(QFont::Serif);
1553 font.setFamily("serif");
1555 return QFontInfo(font).family();
1559 QString const GuiApplication::sansFontName()
1562 font.setKerning(false);
1563 font.setStyleHint(QFont::SansSerif);
1564 font.setFamily("sans");
1566 return QFontInfo(font).family();
1570 QString const GuiApplication::typewriterFontName()
1573 font.setKerning(false);
1574 font.setStyleHint(QFont::TypeWriter);
1575 font.setFamily("monospace");
1577 return QFontInfo(font).family();
1581 void GuiApplication::handleRegularEvents()
1583 ForkedCallsController::handleCompletedProcesses();
1587 bool GuiApplication::event(QEvent * e)
1590 case QEvent::FileOpen: {
1591 // Open a file; this happens only on Mac OS X for now.
1593 // We do this asynchronously because on startup the batch
1594 // commands are not executed here yet and the gui is not ready
1596 QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
1597 dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file())));
1602 return QApplication::event(e);
1607 bool GuiApplication::notify(QObject * receiver, QEvent * event)
1610 return QApplication::notify(receiver, event);
1612 catch (ExceptionMessage const & e) {
1614 case ErrorException:
1616 setQuitOnLastWindowClosed(false);
1618 Alert::error(e.title_, e.details_);
1620 // Properly crash in debug mode in order to get a useful backtrace.
1623 // In release mode, try to exit gracefully.
1626 case BufferException: {
1627 if (!current_view_->documentBufferView())
1629 Buffer * buf = ¤t_view_->documentBufferView()->buffer();
1630 docstring details = e.details_ + '\n';
1631 details += buf->emergencyWrite();
1632 theBufferList().release(buf);
1633 details += "\n" + _("The current document was closed.");
1634 Alert::error(e.title_, details);
1637 case WarningException:
1638 Alert::warning(e.title_, e.details_);
1642 catch (exception const & e) {
1643 docstring s = _("LyX has caught an exception, it will now "
1644 "attempt to save all unsaved documents and exit."
1646 s += from_ascii(e.what());
1647 Alert::error(_("Software exception Detected"), s);
1651 docstring s = _("LyX has caught some really weird exception, it will "
1652 "now attempt to save all unsaved documents and exit.");
1653 Alert::error(_("Software exception Detected"), s);
1661 bool GuiApplication::getRgbColor(ColorCode col, RGBColor & rgbcol)
1663 QColor const & qcol = d->color_cache_.get(col);
1664 if (!qcol.isValid()) {
1670 rgbcol.r = qcol.red();
1671 rgbcol.g = qcol.green();
1672 rgbcol.b = qcol.blue();
1677 string const GuiApplication::hexName(ColorCode col)
1679 return ltrim(fromqstr(d->color_cache_.get(col).name()), "#");
1683 void GuiApplication::registerSocketCallback(int fd, SocketCallback func)
1685 SocketNotifier * sn = new SocketNotifier(this, fd, func);
1686 d->socket_notifiers_[fd] = sn;
1687 connect(sn, SIGNAL(activated(int)), this, SLOT(socketDataReceived(int)));
1691 void GuiApplication::socketDataReceived(int fd)
1693 d->socket_notifiers_[fd]->func_();
1697 void GuiApplication::unregisterSocketCallback(int fd)
1699 d->socket_notifiers_.take(fd)->setEnabled(false);
1703 void GuiApplication::commitData(QSessionManager & sm)
1705 /// The implementation is required to avoid an application exit
1706 /// when session state save is triggered by session manager.
1707 /// The default implementation sends a close event to all
1708 /// visible top level widgets when session managment allows
1710 /// We are changing that to close all wiew one by one.
1711 /// FIXME: verify if the default implementation is enough now.
1712 if (sm.allowsInteraction() && !closeAllViews())
1717 void GuiApplication::unregisterView(GuiView * gv)
1719 LASSERT(d->views_[gv->id()] == gv, /**/);
1720 d->views_.remove(gv->id());
1721 if (current_view_ == gv)
1726 bool GuiApplication::closeAllViews()
1728 if (d->views_.empty())
1731 // When a view/window was closed before without quitting LyX, there
1732 // are already entries in the lastOpened list.
1733 theSession().lastOpened().clear();
1735 QList<GuiView *> views = d->views_.values();
1736 foreach (GuiView * view, views) {
1746 GuiView & GuiApplication::view(int id) const
1748 LASSERT(d->views_.contains(id), /**/);
1749 return *d->views_.value(id);
1753 void GuiApplication::hideDialogs(string const & name, Inset * inset) const
1755 QList<GuiView *> views = d->views_.values();
1756 foreach (GuiView * view, views)
1757 view->hideDialog(name, inset);
1761 Buffer const * GuiApplication::updateInset(Inset const * inset) const
1763 Buffer const * buffer_ = 0;
1764 QHash<int, GuiView *>::iterator end = d->views_.end();
1765 for (QHash<int, GuiView *>::iterator it = d->views_.begin(); it != end; ++it) {
1766 if (Buffer const * ptr = (*it)->updateInset(inset))
1773 bool GuiApplication::searchMenu(FuncRequest const & func,
1774 docstring_list & names) const
1776 return d->menus_.searchMenu(func, names);
1780 bool GuiApplication::readUIFile(QString const & name, bool include)
1782 LYXERR(Debug::INIT, "About to read " << name << "...");
1786 ui_path = libFileSearch("ui", name, "inc");
1787 if (ui_path.empty())
1788 ui_path = libFileSearch("ui", changeExtension(name, "inc"));
1790 ui_path = libFileSearch("ui", name, "ui");
1793 if (ui_path.empty()) {
1794 static const QString defaultUIFile = "default";
1795 LYXERR(Debug::INIT, "Could not find " << name);
1797 Alert::warning(_("Could not find UI definition file"),
1798 bformat(_("Error while reading the included file\n%1$s\n"
1799 "Please check your installation."), qstring_to_ucs4(name)));
1802 if (name == defaultUIFile) {
1803 LYXERR(Debug::INIT, "Could not find default UI file!!");
1804 Alert::warning(_("Could not find default UI file"),
1805 _("LyX could not find the default UI file!\n"
1806 "Please check your installation."));
1809 Alert::warning(_("Could not find UI definition file"),
1810 bformat(_("Error while reading the configuration file\n%1$s\n"
1811 "Falling back to default.\n"
1812 "Please look under Tools>Preferences>User Interface and\n"
1813 "check which User Interface file you are using."), qstring_to_ucs4(name)));
1814 return readUIFile(defaultUIFile, false);
1817 // Ensure that a file is read only once (prevents include loops)
1818 static QStringList uifiles;
1819 QString const uifile = toqstr(ui_path.absFilename());
1820 if (uifiles.contains(uifile)) {
1822 // We are reading again the top uifile so reset the safeguard:
1825 d->toolbars_.reset();
1827 LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
1828 << "Is this an include loop?");
1832 uifiles.push_back(uifile);
1834 LYXERR(Debug::INIT, "Found " << name << " in " << ui_path);
1844 LexerKeyword uitags[] = {
1845 { "include", ui_include },
1846 { "menuset", ui_menuset },
1847 { "toolbars", ui_toolbars },
1848 { "toolbarset", ui_toolbarset }
1852 lex.setFile(ui_path);
1854 lyxerr << "Unable to set LyXLeX for ui file: " << ui_path
1858 if (lyxerr.debugging(Debug::PARSER))
1859 lex.printTable(lyxerr);
1861 // store which ui files define Toolbars
1862 static QStringList toolbar_uifiles;
1864 while (lex.isOK()) {
1865 switch (lex.lex()) {
1868 QString const file = toqstr(lex.getString());
1869 if (!readUIFile(file, true))
1874 d->menus_.read(lex);
1878 d->toolbars_.readToolbars(lex);
1882 d->toolbars_.readToolbarSettings(lex);
1883 toolbar_uifiles.push_back(uifile);
1887 if (!rtrim(lex.getString()).empty())
1888 lex.printError("LyX::ReadUIFile: "
1889 "Unknown menu tag: `$$Token'");
1898 settings.beginGroup("ui_files");
1899 bool touched = false;
1900 for (int i = 0; i != uifiles.size(); ++i) {
1901 QFileInfo fi(uifiles[i]);
1902 QDateTime const date_value = fi.lastModified();
1903 QString const name_key = QString::number(i);
1904 // if an ui file which defines Toolbars has changed,
1905 // we have to reset the settings
1906 if (toolbar_uifiles.contains(uifiles[i])
1907 && (!settings.contains(name_key)
1908 || settings.value(name_key).toString() != uifiles[i]
1909 || settings.value(name_key + "/date").toDateTime() != date_value)) {
1911 settings.setValue(name_key, uifiles[i]);
1912 settings.setValue(name_key + "/date", date_value);
1915 settings.endGroup();
1917 settings.remove("views");
1923 void GuiApplication::onLastWindowClosed()
1925 if (d->global_menubar_)
1926 d->global_menubar_->grabKeyboard();
1930 ////////////////////////////////////////////////////////////////////////
1932 // X11 specific stuff goes here...
1935 bool GuiApplication::x11EventFilter(XEvent * xev)
1940 switch (xev->type) {
1941 case SelectionRequest: {
1942 if (xev->xselectionrequest.selection != XA_PRIMARY)
1944 LYXERR(Debug::SELECTION, "X requested selection.");
1945 BufferView * bv = current_view_->currentBufferView();
1947 docstring const sel = bv->requestSelection();
1949 d->selection_.put(sel);
1953 case SelectionClear: {
1954 if (xev->xselectionclear.selection != XA_PRIMARY)
1956 LYXERR(Debug::SELECTION, "Lost selection.");
1957 BufferView * bv = current_view_->currentBufferView();
1959 bv->clearSelection();
1967 } // namespace frontend
1970 void hideDialogs(std::string const & name, Inset * inset)
1973 theApp()->hideDialogs(name, inset);
1977 ////////////////////////////////////////////////////////////////////
1981 ////////////////////////////////////////////////////////////////////
1983 frontend::FontLoader & theFontLoader()
1985 LASSERT(frontend::guiApp, /**/);
1986 return frontend::guiApp->fontLoader();
1990 frontend::FontMetrics const & theFontMetrics(Font const & f)
1992 return theFontMetrics(f.fontInfo());
1996 frontend::FontMetrics const & theFontMetrics(FontInfo const & f)
1998 LASSERT(frontend::guiApp, /**/);
1999 return frontend::guiApp->fontLoader().metrics(f);
2003 ////////////////////////////////////////////////////////////////////
2007 ////////////////////////////////////////////////////////////////////
2009 frontend::Clipboard & theClipboard()
2011 LASSERT(frontend::guiApp, /**/);
2012 return frontend::guiApp->clipboard();
2016 frontend::Selection & theSelection()
2018 LASSERT(frontend::guiApp, /**/);
2019 return frontend::guiApp->selection();
2025 #include "moc_GuiApplication.cpp"