]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiApplication.cpp
Work around bug http://bugzilla.lyx.org/show_bug.cgi?id=4846
[lyx.git] / src / frontends / qt4 / GuiApplication.cpp
index 104f69a0b352a9f7f695e5b1f427a65b386b3121..f1bbad25e3faea2b7d0db574701852f81eeab16b 100644 (file)
@@ -39,6 +39,7 @@
 #include "Language.h"
 #include "Lexer.h"
 #include "LyX.h"
+#include "LyXAction.h"
 #include "LyXFunc.h"
 #include "LyXRC.h"
 #include "Session.h"
@@ -52,6 +53,7 @@
 #include "support/ForkedCalls.h"
 #include "support/gettext.h"
 #include "support/lstrings.h"
+#include "support/lyxalgo.h" // sorted
 #include "support/os.h"
 #include "support/Package.h"
 
 #include "support/linkback/LinkBackProxy.h"
 #endif
 
+#include <QByteArray>
 #include <QClipboard>
+#include <QDir>
 #include <QEventLoop>
 #include <QFileOpenEvent>
 #include <QHash>
+#include <QIcon>
+#include <QImageReader>
 #include <QLocale>
 #include <QLibraryInfo>
+#include <QList>
 #include <QMacPasteboardMime>
 #include <QMenuBar>
 #include <QMimeData>
+#include <QObject>
+#include <QPixmap>
 #include <QPixmapCache>
 #include <QRegExp>
 #include <QSessionManager>
@@ -87,7 +96,6 @@
 #endif
 
 #ifdef Q_WS_WIN
-#include <QList>
 #include <QWindowsMime>
 #if defined(Q_CYGWIN_WIN) || defined(Q_CC_MINGW)
 #include <wtypes.h>
 #endif // Q_WS_WIN
 
 #include <boost/bind.hpp>
+#include <boost/crc.hpp>
 
 #include <exception>
+#include <vector>
 
 using namespace std;
 using namespace lyx::support;
 
 
+static void initializeResources()
+{
+       static bool initialized = false;
+       if (!initialized) {
+               Q_INIT_RESOURCE(Resources); 
+               initialized = true;
+       }
+}
+
+
 namespace lyx {
 
 frontend::Application * createApplication(int & argc, char * argv[])
@@ -112,6 +132,227 @@ frontend::Application * createApplication(int & argc, char * argv[])
 
 namespace frontend {
 
+
+/// Return the list of loadable formats.
+vector<string> loadableImageFormats()
+{
+       vector<string> fmts;
+
+       QList<QByteArray> qt_formats = QImageReader::supportedImageFormats();
+
+       LYXERR(Debug::GRAPHICS,
+               "\nThe image loader can load the following directly:\n");
+
+       if (qt_formats.empty())
+               LYXERR(Debug::GRAPHICS, "\nQt4 Problem: No Format available!");
+
+       for (QList<QByteArray>::const_iterator it = qt_formats.begin(); it != qt_formats.end(); ++it) {
+
+               LYXERR(Debug::GRAPHICS, (const char *) *it << ", ");
+
+               string ext = ascii_lowercase((const char *) *it);
+               // special case
+               if (ext == "jpeg")
+                       ext = "jpg";
+               fmts.push_back(ext);
+       }
+
+       return fmts;
+}
+       
+////////////////////////////////////////////////////////////////////////
+// Icon loading support code.
+////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+struct PngMap {
+       QString key;
+       QString value;
+};
+
+
+bool operator<(PngMap const & lhs, PngMap const & rhs)
+{
+       return lhs.key < rhs.key;
+}
+
+
+class CompareKey {
+public:
+       CompareKey(QString const & name) : name_(name) {}
+       bool operator()(PngMap const & other) const { return other.key == name_; }
+private:
+       QString const name_;
+};
+
+
+// this must be sorted alphabetically
+// Upper case comes before lower case
+PngMap sorted_png_map[] = {
+       { "Bumpeq", "bumpeq2" },
+       { "Cap", "cap2" },
+       { "Cup", "cup2" },
+       { "Delta", "delta2" },
+       { "Downarrow", "downarrow2" },
+       { "Gamma", "gamma2" },
+       { "Lambda", "lambda2" },
+       { "Leftarrow", "leftarrow2" },
+       { "Leftrightarrow", "leftrightarrow2" },
+       { "Longleftarrow", "longleftarrow2" },
+       { "Longleftrightarrow", "longleftrightarrow2" },
+       { "Longrightarrow", "longrightarrow2" },
+       { "Omega", "omega2" },
+       { "Phi", "phi2" },
+       { "Pi", "pi2" },
+       { "Psi", "psi2" },
+       { "Rightarrow", "rightarrow2" },
+       { "Sigma", "sigma2" },
+       { "Subset", "subset2" },
+       { "Supset", "supset2" },
+       { "Theta", "theta2" },
+       { "Uparrow", "uparrow2" },
+       { "Updownarrow", "updownarrow2" },
+       { "Upsilon", "upsilon2" },
+       { "Vdash", "vdash3" },
+       { "Vert", "vert2" },
+       { "Xi", "xi2" },
+       { "nLeftarrow", "nleftarrow2" },
+       { "nLeftrightarrow", "nleftrightarrow2" },
+       { "nRightarrow", "nrightarrow2" },
+       { "nVDash", "nvdash3" },
+       { "nvDash", "nvdash2" },
+       { "textrm \\AA", "textrm_AA"},
+       { "textrm \\O", "textrm_O"},
+       { "vDash", "vdash2" }
+};
+
+size_t const nr_sorted_png_map = sizeof(sorted_png_map) / sizeof(PngMap);
+
+
+QString findPng(QString const & name)
+{
+       PngMap const * const begin = sorted_png_map;
+       PngMap const * const end = begin + nr_sorted_png_map;
+       LASSERT(sorted(begin, end), /**/);
+
+       PngMap const * const it = find_if(begin, end, CompareKey(name));
+
+       QString png_name;
+       if (it != end) {
+               png_name = it->value;
+       } else {
+               png_name = name;
+               png_name.replace('_', "underscore");
+               png_name.replace(' ', '_');
+
+               // This way we can have "math-delim { }" on the toolbar.
+               png_name.replace('(', "lparen");
+               png_name.replace(')', "rparen");
+               png_name.replace('[', "lbracket");
+               png_name.replace(']', "rbracket");
+               png_name.replace('{', "lbrace");
+               png_name.replace('}', "rbrace");
+               png_name.replace('|', "bars");
+               png_name.replace(',', "thinspace");
+               png_name.replace(':', "mediumspace");
+               png_name.replace(';', "thickspace");
+               png_name.replace('!', "negthinspace");
+       }
+
+       LYXERR(Debug::GUI, "findPng(" << name << ")\n"
+               << "Looking for math PNG called \"" << png_name << '"');
+       return png_name;
+}
+
+} // namespace anon
+
+
+QString iconName(FuncRequest const & f, bool unknown)
+{
+       initializeResources();
+       QString name1;
+       QString name2;
+       QString path;
+       switch (f.action) {
+       case LFUN_MATH_INSERT:
+               if (!f.argument().empty()) {
+                       path = "math/";
+                       name1 = findPng(toqstr(f.argument()).mid(1));
+               }
+               break;
+       case LFUN_MATH_DELIM:
+       case LFUN_MATH_BIGDELIM:
+               path = "math/";
+               name1 = findPng(toqstr(f.argument()));
+               break;
+       case LFUN_CALL:
+               path = "commands/";
+               name1 = toqstr(f.argument());
+               break;
+       default:
+               name2 = toqstr(lyxaction.getActionName(f.action));
+               name1 = name2;
+
+               if (!f.argument().empty()) {
+                       name1 = name2 + ' ' + toqstr(f.argument());
+                       name1.replace(' ', '_');
+               }
+       }
+
+       FileName fname = libFileSearch("images/" + path, name1, "png");
+       if (fname.exists())
+               return toqstr(fname.absFilename());
+
+       fname = libFileSearch("images/" + path, name2, "png");
+       if (fname.exists())
+               return toqstr(fname.absFilename());
+
+       path = ":/images/" + path;
+       QDir res(path);
+       if (!res.exists()) {
+               LYXERR0("Directory " << path << " not found in resource!"); 
+               return QString();
+       }
+       name1 += ".png";
+       if (res.exists(name1))
+               return path + name1;
+
+       name2 += ".png";
+       if (res.exists(name2))
+               return path + name2;
+
+       LYXERR(Debug::GUI, "Cannot find icon for command \""
+                          << lyxaction.getActionName(f.action)
+                          << '(' << to_utf8(f.argument()) << ")\"");
+
+       if (unknown)
+               return QString(":/images/unknown.png");
+
+       return QString();
+}
+
+
+QIcon getIcon(FuncRequest const & f, bool unknown)
+{
+       QString icon = iconName(f, unknown);
+       if (icon.isEmpty())
+               return QIcon();
+
+       //LYXERR(Debug::GUI, "Found icon: " << icon);
+       QPixmap pm;
+       if (!pm.load(icon)) {
+               LYXERR0("Cannot load icon " << icon << " please verify resource system!");
+               return QIcon();
+       }
+
+       return QIcon(pm);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// LyX server support code.
+////////////////////////////////////////////////////////////////////////
 class SocketNotifier : public QSocketNotifier
 {
 public:
@@ -128,6 +369,7 @@ public:
 
 ////////////////////////////////////////////////////////////////////////
 // Mac specific stuff goes here...
+////////////////////////////////////////////////////////////////////////
 
 class MenuTranslator : public QTranslator
 {
@@ -204,14 +446,16 @@ public:
                return mimeFor(flav) == mime;
        }
 
-       QVariant convertToMime(QString const & mime, QList<QByteArray> data, QString flav)
+       QVariant convertToMime(QString const & /*mime*/, QList<QByteArray> data,
+               QString /*flav*/)
        {
                if(data.count() > 1)
                        qWarning("QMacPasteboardMimeGraphics: Cannot handle multiple member data");
                return data.first();
        }
 
-       QList<QByteArray> convertFromMime(QString const & mime, QVariant data, QString flav)
+       QList<QByteArray> convertFromMime(QString const & /*mime*/,
+               QVariant data, QString /*flav*/)
        {
                QList<QByteArray> ret;
                ret.append(data.toByteArray());
@@ -385,9 +629,7 @@ struct GuiApplication::Private
 
 #ifdef Q_WS_WIN
        /// WMF Mime handler for Windows clipboard.
-       // FIXME for Windows Vista and Qt4 (see http://bugzilla.lyx.org/show_bug.cgi?id=4846)
-       // But this makes LyX crash on exit when LyX is compiled in release mode and if there
-       // is something in the clipboard.
+       /// \warning: see comment in ~GuiApplication and in bug 4846.
        QWindowsMimeMetafile wmf_mime_;
 #endif
 };
@@ -400,6 +642,13 @@ GuiApplication::~GuiApplication()
 #ifdef Q_WS_MACX
        closeAllLinkBackLinks();
 #endif
+       // FIXME: Work around bug 4846 for Windows Vista and Qt4
+       // (see http://bugzilla.lyx.org/show_bug.cgi?id=4846)
+       // If the clipboard is not cleared, LyX crashes on exit when it is
+       // compiled in release mode and if there is something in the clipboard.
+       // This is related to QWindowsMimeMetafile which is apparently not 
+       // properly destroyed.
+       qApp->clipboard()->clear(QClipboard::Clipboard);
        delete d;
 }
 
@@ -463,7 +712,6 @@ GuiApplication::GuiApplication(int & argc, char ** argv)
        using namespace lyx::graphics;
 
        Image::newImage = boost::bind(&GuiImage::newImage);
-       Image::loadableFormats = boost::bind(&GuiImage::loadableFormats);
 
        // needs to be done before reading lyxrc
        QWidget w;
@@ -492,6 +740,13 @@ GuiApplication::GuiApplication(int & argc, char ** argv)
 }
 
 
+docstring GuiApplication::iconName(FuncRequest const & f, bool unknown)
+{
+       return qstring_to_ucs4(lyx::frontend::iconName(f, unknown));
+}
+
+
+
 bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const
 {
        bool enable = true;
@@ -591,8 +846,13 @@ bool GuiApplication::dispatch(FuncRequest const & cmd)
        case LFUN_FILE_OPEN:
                if (d->views_.empty()
                    || (!lyxrc.open_buffers_in_tabs && current_view_->buffer() != 0)) {
-                       createView();
-                       current_view_->openDocument(to_utf8(cmd.argument()));
+                       string const fname = to_utf8(cmd.argument());
+                       // We want the ui session to be saved per document and not per
+                       // window number. The filename crc is a good enough identifier.
+                       boost::crc_32_type crc;
+                       crc = for_each(fname.begin(), fname.end(), crc);
+                       createView(crc.checksum());
+                       current_view_->openDocument(fname);
                        if (!current_view_->buffer())
                                current_view_->close();
                } else
@@ -643,6 +903,19 @@ bool GuiApplication::dispatch(FuncRequest const & cmd)
 
 void GuiApplication::resetGui()
 {
+       // Set the language defined by the user.
+       LyX::ref().setRcGuiLanguage();
+
+       // Read menus
+       if (!readUIFile(toqstr(lyxrc.ui_file)))
+               // Gives some error box here.
+               return;
+
+       // init the global menubar on Mac. This must be done after the session
+       // was recovered to know the "last files".
+       if (d->global_menubar_)
+               d->menus_.fillMenuBar(d->global_menubar_, 0, true);
+
        QHash<int, GuiView *>::iterator it;
        for (it = d->views_.begin(); it != d->views_.end(); ++it)
                (*it)->resetDialogs();
@@ -651,7 +924,14 @@ void GuiApplication::resetGui()
 }
 
 
-void GuiApplication::createView(QString const & geometry_arg, bool autoShow)
+void GuiApplication::createView(int view_id)
+{
+       createView(QString(), true, view_id);
+}
+
+
+void GuiApplication::createView(QString const & geometry_arg, bool autoShow,
+       int view_id)
 {
        // release the keyboard which might have been grabed by the global
        // menubar on Mac to catch shortcuts even without any GuiView.
@@ -659,15 +939,13 @@ void GuiApplication::createView(QString const & geometry_arg, bool autoShow)
                d->global_menubar_->releaseKeyboard();
 
        // create new view
-       int id = 0;
-       while (d->views_.find(id) != d->views_.end())
-               id++;
+       int id = view_id;
+       if (id == 0) {
+               while (d->views_.find(id) != d->views_.end())
+                       id++;
+       }
+       LYXERR(Debug::GUI, "About to create new window with ID " << id);
        GuiView * view = new GuiView(id);
-       
-       // copy the icon size from old view
-       if (current_view_)
-               view->setIconSize(current_view_->iconSize());
-
        // register view
        d->views_[id] = view;
 
@@ -763,6 +1041,9 @@ void GuiApplication::exit(int status)
 
 void GuiApplication::execBatchCommands()
 {
+       // Set the language defined by the user.
+       LyX::ref().setRcGuiLanguage();
+
        // Read menus
        if (!readUIFile(toqstr(lyxrc.ui_file)))
                // Gives some error box here.
@@ -865,17 +1146,8 @@ bool GuiApplication::event(QEvent * e)
        case QEvent::FileOpen: {
                // Open a file; this happens only on Mac OS X for now
                QFileOpenEvent * foe = static_cast<QFileOpenEvent *>(e);
-
-               if (!current_view_ || !current_view_->view())
-                       // The application is not properly initialized yet.
-                       // So we acknowledge the event and delay the file opening
-                       // until LyX is ready.
-                       // FIXME UNICODE: FileName accept an utf8 encoded string.
-                       LyX::ref().addFileToLoad(fromqstr(foe->file()));
-               else
-                       lyx::dispatch(FuncRequest(LFUN_FILE_OPEN,
-                               qstring_to_ucs4(foe->file())));
-
+               lyx::dispatch(FuncRequest(LFUN_FILE_OPEN,
+                       qstring_to_ucs4(foe->file())));
                e->accept();
                return true;
        }
@@ -1074,9 +1346,16 @@ bool GuiApplication::readUIFile(QString const & name, bool include)
        // Ensure that a file is read only once (prevents include loops)
        static QStringList uifiles;
        if (uifiles.contains(name)) {
-               LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
-                                   << "Is this an include loop?");
-               return false;
+               if (!include) {
+                       // We are reading again the top uifile so reset the safeguard:
+                       uifiles.clear();
+                       d->menus_.reset();
+                       d->toolbars_.reset();
+               } else {
+                       LYXERR(Debug::INIT, "UI file '" << name << "' has been read already. "
+                               << "Is this an include loop?");
+                       return false;
+               }
        }
 
        LYXERR(Debug::INIT, "About to read " << name << "...");
@@ -1092,7 +1371,7 @@ bool GuiApplication::readUIFile(QString const & name, bool include)
 
        if (ui_path.empty()) {
                LYXERR(Debug::INIT, "Could not find " << name);
-               Alert::warning(_("Could not find UI defintion file"),
+               Alert::warning(_("Could not find UI definition file"),
                               bformat(_("Error while reading the configuration file\n%1$s.\n"
                                   "Please check your installation."), qstring_to_ucs4(name)));
                return false;