X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiApplication.cpp;h=4656e63a69f5855ce8593e043a099075055a19ab;hb=28896d1bddcfcd358c863603334c0a013fb601ea;hp=db7b4042cd7aebae0a6d32acca0f2e533b8d2e0c;hpb=b9a507932f971ca1a0aaf76f7563c4b1bda9af7f;p=lyx.git diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp index db7b4042cd..4656e63a69 100644 --- a/src/frontends/qt4/GuiApplication.cpp +++ b/src/frontends/qt4/GuiApplication.cpp @@ -33,6 +33,7 @@ #include "Buffer.h" #include "BufferList.h" #include "BufferView.h" +#include "CmdDef.h" #include "Color.h" #include "Font.h" #include "FuncRequest.h" @@ -44,14 +45,15 @@ #include "Lexer.h" #include "LyX.h" #include "LyXAction.h" -#include "LyXFunc.h" #include "LyXRC.h" +#include "Paragraph.h" #include "Server.h" #include "Session.h" #include "SpellChecker.h" +#include "Thesaurus.h" #include "version.h" -#include "support/lassert.h" +#include "support/convert.h" #include "support/debug.h" #include "support/ExceptionMessage.h" #include "support/FileName.h" @@ -59,6 +61,7 @@ #include "support/foreach.h" #include "support/ForkedCalls.h" #include "support/gettext.h" +#include "support/lassert.h" #include "support/lstrings.h" #include "support/lyxalgo.h" // sorted #include "support/Messages.h" @@ -101,6 +104,9 @@ #include #include #include +#if QT_VERSION >= 0x040400 +#include +#endif #include #ifdef Q_WS_X11 @@ -118,7 +124,7 @@ #include #endif // Q_WS_WIN -#include +#include "support/bind.h" #include #include @@ -156,7 +162,11 @@ frontend::Application * createApplication(int & argc, char * argv[]) } } #endif - return new frontend::GuiApplication(argc, argv); + frontend::GuiApplication * guiApp = new frontend::GuiApplication(argc, argv); + // I'd rather do that in the constructor, but I do not think that + // the palette is accessible there. + guiApp->colorCache().setPalette(guiApp->palette()); + return guiApp; } namespace frontend { @@ -226,6 +236,7 @@ PngMap sorted_png_map[] = { { "Cap", "cap2" }, { "Cup", "cup2" }, { "Delta", "delta2" }, + { "Diamond", "diamond2" }, { "Downarrow", "downarrow2" }, { "Gamma", "gamma2" }, { "Lambda", "lambda2" }, @@ -256,7 +267,11 @@ PngMap sorted_png_map[] = { { "nvDash", "nvdash2" }, { "textrm \\AA", "textrm_AA"}, { "textrm \\O", "textrm_O"}, - { "vDash", "vdash2" } + { "vDash", "vdash2" }, + { "varPhi", "varphi2" }, + { "varPi", "varpi2" }, + { "varSigma", "varsigma2" }, + { "varTheta", "vartheta2" } }; size_t const nr_sorted_png_map = sizeof(sorted_png_map) / sizeof(PngMap); @@ -306,7 +321,7 @@ QString iconName(FuncRequest const & f, bool unknown) QString name1; QString name2; QString path; - switch (f.action) { + switch (f.action()) { case LFUN_MATH_INSERT: if (!f.argument().empty()) { path = "math/"; @@ -327,11 +342,26 @@ QString iconName(FuncRequest const & f, bool unknown) docstring firstcom; docstring dummy = split(f.argument(), firstcom, ';'); name1 = toqstr(firstcom); + // FIXME: we should rename the icons to tabular-xxx instead of + // "tabular-feature-xxx" + name1.replace("inset-modify tabular", "tabular-feature"); name1.replace(' ', '_'); break; } + case LFUN_INSET_MODIFY: { + // FIXME: we should rename the icons to tabular-xxx instead of + // "tabular-feature-xxx" and generalize this naming to all + // insets, not to tabular using ones. + string inset_name; + string const command = split(to_utf8(f.argument()), inset_name, ' '); + if (insetCode(inset_name) == TABULAR_CODE) { + name1 = "tabular-feature "+ toqstr(command); + name1.replace(' ', '_'); + break; + } + } default: - name2 = toqstr(lyxaction.getActionName(f.action)); + name2 = toqstr(lyxaction.getActionName(f.action())); name1 = name2; if (!f.argument().empty()) { @@ -343,11 +373,11 @@ QString iconName(FuncRequest const & f, bool unknown) FileName fname = libFileSearch("images/" + path, name1, "png"); if (fname.exists()) - return toqstr(fname.absFilename()); + return toqstr(fname.absFileName()); fname = libFileSearch("images/" + path, name2, "png"); if (fname.exists()) - return toqstr(fname.absFilename()); + return toqstr(fname.absFileName()); path = ":/images/" + path; QDir res(path); @@ -368,13 +398,13 @@ QString iconName(FuncRequest const & f, bool unknown) << " or filename " << "\"" << name2 << "\"" << " for command \"" - << lyxaction.getActionName(f.action) + << lyxaction.getActionName(f.action()) << '(' << to_utf8(f.argument()) << ")\""); if (unknown) { fname = libFileSearch(QString("images/"), "unknown", "png"); if (fname.exists()) - return toqstr(fname.absFilename()); + return toqstr(fname.absFileName()); return QString(":/images/unknown.png"); } @@ -385,7 +415,7 @@ QPixmap getPixmap(QString const & path, QString const & name, QString const & ex { QPixmap pixmap; FileName fname = libFileSearch(path, name, ext); - QString path1 = toqstr(fname.absFilename()); + QString path1 = toqstr(fname.absFileName()); QString path2 = ":/" + path + name + "." + ext; if (pixmap.load(path1)) { @@ -760,7 +790,6 @@ GuiApplication::GuiApplication(int & argc, char ** argv) // FIXME: Do we need a lyxrc setting for this on Mac? This behaviour // seems to be the default case for applications like LyX. setQuitOnLastWindowClosed(false); - // This allows to translate the strings that appear in the LyX menu. /// A translator suitable for the entries in the LyX menu. /// Only needed with Qt/Mac. @@ -801,6 +830,13 @@ GuiApplication::GuiApplication(int & argc, char ** argv) connect(&d->general_timer_, SIGNAL(timeout()), this, SLOT(handleRegularEvents())); d->general_timer_.start(); + +#if QT_VERSION >= 0x040400 + // maxThreadCount() defaults in general to 2 on single or dual-processor. + // This is clearly not enough in a time where we use threads for + // document preview and/or export. 20 should be OK. + QThreadPool::globalInstance()->setMaxThreadCount(20); +#endif } @@ -823,26 +859,159 @@ docstring GuiApplication::iconName(FuncRequest const & f, bool unknown) } -LyXView * GuiApplication::currentWindow() +FuncStatus GuiApplication::getStatus(FuncRequest const & cmd) const { -#ifdef Q_WS_MACX - /* In LyX/Mac, when a dialog is open, the menus of the - application can still be accessed without giving focus to - the main window. In this case, we want to disable the menu - entries that are buffer or view-related. + FuncStatus status; + + BufferView * bv = 0; + BufferView * doc_bv = 0; + + if (cmd.action() == LFUN_NOACTION) { + status.message(from_utf8(N_("Nothing to do"))); + status.setEnabled(false); + } + + else if (cmd.action() == LFUN_UNKNOWN_ACTION) { + status.setUnknown(true); + status.message(from_utf8(N_("Unknown action"))); + status.setEnabled(false); + } + + // Does the GuiApplication know something? + else if (getStatus(cmd, status)) { } + + // If we do not have a GuiView, then other functions are disabled + else if (!current_view_) + status.setEnabled(false); + + // Does the GuiView know something? + else if (current_view_->getStatus(cmd, status)) { } + + // In LyX/Mac, when a dialog is open, the menus of the + // application can still be accessed without giving focus to + // the main window. In this case, we want to disable the menu + // entries that are buffer or view-related. + //FIXME: Abdel (09/02/10) This has very bad effect on Linux, don't know why... + /* + else if (cmd.origin() == FuncRequest::MENU && !current_view_->hasFocus()) + status.setEnabled(false); */ - if (current_view_ && activeWindow() != current_view_) - return 0; -#endif - return current_view_; + + // If we do not have a BufferView, then other functions are disabled + else if (!(bv = current_view_->currentBufferView())) + status.setEnabled(false); + + // Does the current BufferView know something? + else if (bv->getStatus(cmd, status)) { } + + // Does the current Buffer know something? + else if (bv->buffer().getStatus(cmd, status)) { } + + // If we do not have a document BufferView, different from the + // current BufferView, then other functions are disabled + else if (!(doc_bv = current_view_->documentBufferView()) || doc_bv == bv) + status.setEnabled(false); + + // Does the document Buffer know something? + else if (doc_bv->buffer().getStatus(cmd, status)) { } + + else { + LYXERR(Debug::ACTION, "LFUN not handled in getStatus(): " << cmd); + status.message(from_utf8(N_("Command not handled"))); + status.setEnabled(false); + } + + // the default error message if we disable the command + if (!status.enabled() && status.message().empty()) + status.message(from_utf8(N_("Command disabled"))); + + return status; } bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const { + // I would really like to avoid having this switch and rather try to + // encode this in the function itself. + // -- And I'd rather let an inset decide which LFUNs it is willing + // to handle (Andre') bool enable = true; + switch (cmd.action()) { + + // This could be used for the no-GUI version. The GUI version is handled in + // GuiView::getStatus(). See above. + /* + case LFUN_BUFFER_WRITE: + case LFUN_BUFFER_WRITE_AS: { + Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0))); + enable = b && (b->isUnnamed() || !b->isClean()); + break; + } + */ + + case LFUN_BOOKMARK_GOTO: { + const unsigned int num = convert(to_utf8(cmd.argument())); + enable = theSession().bookmarks().isValid(num); + break; + } - switch(cmd.action) { + case LFUN_BOOKMARK_CLEAR: + enable = theSession().bookmarks().hasValid(); + break; + + // this one is difficult to get right. As a half-baked + // solution, we consider only the first action of the sequence + case LFUN_COMMAND_SEQUENCE: { + // argument contains ';'-terminated commands + string const firstcmd = token(to_utf8(cmd.argument()), ';', 0); + FuncRequest func(lyxaction.lookupFunc(firstcmd)); + func.setOrigin(cmd.origin()); + flag = getStatus(func); + break; + } + + // we want to check if at least one of these is enabled + case LFUN_COMMAND_ALTERNATIVES: { + // argument contains ';'-terminated commands + string arg = to_utf8(cmd.argument()); + while (!arg.empty()) { + string first; + arg = split(arg, first, ';'); + FuncRequest func(lyxaction.lookupFunc(first)); + func.setOrigin(cmd.origin()); + flag = getStatus(func); + // if this one is enabled, the whole thing is + if (flag.enabled()) + break; + } + break; + } + + case LFUN_CALL: { + FuncRequest func; + string name = to_utf8(cmd.argument()); + if (theTopLevelCmdDef().lock(name, func)) { + func.setOrigin(cmd.origin()); + flag = getStatus(func); + theTopLevelCmdDef().release(name); + } else { + // catch recursion or unknown command + // definition. all operations until the + // recursion or unknown command definition + // occurs are performed, so set the state to + // enabled + enable = true; + } + break; + } + + case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE: + case LFUN_REPEAT: + case LFUN_PREFERENCES_SAVE: + case LFUN_BUFFER_SAVE_AS_DEFAULT: + case LFUN_DEBUG_LEVEL_SET: + // these are handled in our dispatch() + break; case LFUN_WINDOW_CLOSE: enable = d->views_.size() > 0; @@ -872,18 +1041,178 @@ bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const if (!enable) flag.setEnabled(false); - return true; } +/// make a post-dispatch status message +static docstring makeDispatchMessage(docstring const & msg, + FuncRequest const & cmd) +{ + const bool verbose = (cmd.origin() == FuncRequest::MENU + || cmd.origin() == FuncRequest::TOOLBAR + || cmd.origin() == FuncRequest::COMMANDBUFFER); + + if (cmd.action() == LFUN_SELF_INSERT || !verbose) { + LYXERR(Debug::ACTION, "dispatch msg is " << msg); + return msg; + } + + docstring dispatch_msg = msg; + if (!dispatch_msg.empty()) + dispatch_msg += ' '; + + docstring comname = from_utf8(lyxaction.getActionName(cmd.action())); + + bool argsadded = false; + + if (!cmd.argument().empty()) { + if (cmd.action() != LFUN_UNKNOWN_ACTION) { + comname += ' ' + cmd.argument(); + argsadded = true; + } + } + docstring const shortcuts = theTopLevelKeymap(). + printBindings(cmd, KeySequence::ForGui); + + if (!shortcuts.empty()) + comname += ": " + shortcuts; + else if (!argsadded && !cmd.argument().empty()) + comname += ' ' + cmd.argument(); + + if (!comname.empty()) { + comname = rtrim(comname); + dispatch_msg += '(' + rtrim(comname) + ')'; + } + LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg)); + return dispatch_msg; +} + + +void GuiApplication::dispatch(FuncRequest const & cmd) +{ + if (current_view_ && current_view_->currentBufferView()) + current_view_->currentBufferView()->cursor().saveBeforeDispatchPosXY(); + + DispatchResult dr; + // redraw the screen at the end (first of the two drawing steps). + // This is done unless explicitly requested otherwise + dr.screenUpdate(Update::FitCursor); + dispatch(cmd, dr); + + if (!current_view_) + return; + + BufferView * bv = current_view_->currentBufferView(); + if (bv) { + Cursor & cursor = bv->cursor(); + Buffer & buf = bv->buffer(); + + // FIXME + // This check out to be done somewhere else. It got moved here + // from TextMetrics.cpp, where it definitely didn't need to be. + // Actually, this test ought not to be done at all, since the + // whole InsetBibitem business is a mess. But that is a different + // story. + if (cursor.inTexted()) { + int const moveCursor = cursor.paragraph().checkBiblio(buf); + if (moveCursor > 0) + cursor.posForward(); + else if (moveCursor < 0 && cursor.pos() >= -moveCursor) + cursor.posBackward(); + + if (moveCursor != 0 || dr.needBufferUpdate()) { + cursor.clearBufferUpdate(); + buf.updateBuffer(); + } + } + // BufferView::update() updates the ViewMetricsInfo and + // also initializes the position cache for all insets in + // (at least partially) visible top-level paragraphs. + // We will redraw the screen only if needed. + bv->processUpdateFlags(dr.screenUpdate()); + + // Do we have a selection? + theSelection().haveSelection(bv->cursor().selection()); + + // update gui + current_view_->restartCursor(); + } + if (dr.needMessageUpdate()) { + // Some messages may already be translated, so we cannot use _() + current_view_->message(makeDispatchMessage( + translateIfPossible(dr.message()), cmd)); + } +} + + +void GuiApplication::gotoBookmark(unsigned int idx, bool openFile, + bool switchToBuffer) +{ + if (!theSession().bookmarks().isValid(idx)) + return; + BookmarksSection::Bookmark const & bm = + theSession().bookmarks().bookmark(idx); + LASSERT(!bm.filename.empty(), /**/); + string const file = bm.filename.absFileName(); + // if the file is not opened, open it. + if (!theBufferList().exists(bm.filename)) { + if (openFile) + dispatch(FuncRequest(LFUN_FILE_OPEN, file)); + else + return; + } + // open may fail, so we need to test it again + if (!theBufferList().exists(bm.filename)) + return; + + // bm can be changed when saving + BookmarksSection::Bookmark tmp = bm; + + // Special case idx == 0 used for back-from-back jump navigation + if (idx == 0) + dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0")); + + // if the current buffer is not that one, switch to it. + BufferView * doc_bv = current_view_->documentBufferView(); + if (!doc_bv || doc_bv->buffer().fileName() != tmp.filename) { + if (switchToBuffer) { + dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file)); + doc_bv = current_view_->documentBufferView(); + } else + return; + } + + // moveToPosition try paragraph id first and then paragraph (pit, pos). + if (!doc_bv->moveToPosition( + tmp.bottom_pit, tmp.bottom_pos, tmp.top_id, tmp.top_pos)) + return; + + // bm changed + if (idx == 0) + return; + + // Cursor jump succeeded! + Cursor const & cur = doc_bv->cursor(); + pit_type new_pit = cur.pit(); + pos_type new_pos = cur.pos(); + int new_id = cur.paragraph().id(); + + // if bottom_pit, bottom_pos or top_id has been changed, update bookmark + // see http://www.lyx.org/trac/ticket/3092 + if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos + || bm.top_id != new_id) { + const_cast(bm).updatePos( + new_pit, new_pos, new_id); + } +} // This function runs "configure" and then rereads lyx.defaults to // reconfigure the automatic settings. -static void reconfigure(GuiView * lv, string const & option) +void GuiApplication::reconfigure(string const & option) { // emit message signal. - if (lv) - lv->message(_("Running configure...")); + if (current_view_) + current_view_->message(_("Running configure...")); // Run configure in user lyx directory PathChanger p(package().user_support()); @@ -893,8 +1222,8 @@ static void reconfigure(GuiView * lv, string const & option) int ret = one.startscript(Systemcall::Wait, configure_command); p.pop(); // emit message signal. - if (lv) - lv->message(_("Reloading configuration...")); + if (current_view_) + current_view_->message(_("Reloading configuration...")); lyxrc.read(libFileSearch(QString(), "lyxrc.defaults")); // Re-read packages.lst LaTeXFeatures::getAvailable(); @@ -906,7 +1235,6 @@ static void reconfigure(GuiView * lv, string const & option) "not be able to work properly.\n" "Please reconfigure again if needed.")); else - Alert::information(_("System reconfigured"), _("The system has been reconfigured.\n" "You need to restart LyX to make use of any\n" @@ -916,7 +1244,34 @@ static void reconfigure(GuiView * lv, string const & option) void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) { - switch (cmd.action) { + string const argument = to_utf8(cmd.argument()); + FuncCode const action = cmd.action(); + + LYXERR(Debug::ACTION, "cmd: " << cmd); + + // we have not done anything wrong yet. + dr.setError(false); + + FuncStatus const flag = getStatus(cmd); + if (!flag.enabled()) { + // We cannot use this function here + LYXERR(Debug::ACTION, "action " + << lyxaction.getActionName(action) + << " [" << action << "] is disabled at this location"); + if (current_view_) + current_view_->restartCursor(); + dr.setMessage(flag.message()); + dr.setError(true); + dr.dispatched(false); + dr.screenUpdate(Update::None); + dr.clearBufferUpdate(); + return; + }; + + // Assumes that the action will be dispatched. + dr.dispatched(true); + + switch (cmd.action()) { case LFUN_WINDOW_NEW: createView(toqstr(cmd.argument())); @@ -925,11 +1280,11 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_WINDOW_CLOSE: // update bookmark pit of the current buffer before window close for (size_t i = 0; i < theSession().bookmarks().size(); ++i) - theLyXFunc().gotoBookmark(i+1, false, false); + gotoBookmark(i+1, false, false); // clear the last opened list, because // maybe this will end the session theSession().lastOpened().clear(); - current_view_->close(); + current_view_->closeScheduled(); break; case LFUN_LYX_QUIT: @@ -949,10 +1304,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) // Set current_view_ to zero to forbid GuiWorkArea::redraw() // to skip the refresh. current_view_ = 0; - BufferList::iterator it = theBufferList().begin(); - BufferList::iterator const end = theBufferList().end(); - for (; it != end; ++it) - (*it)->changed(); + theBufferList().changed(false); // Restore current_view_ current_view_ = view; break; @@ -960,7 +1312,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_BUFFER_NEW: if (d->views_.empty() - || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) { + || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) { createView(QString(), false); // keep hidden current_view_->newDocument(to_utf8(cmd.argument()), false); current_view_->show(); @@ -972,7 +1324,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) case LFUN_BUFFER_NEW_TEMPLATE: if (d->views_.empty() - || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) { + || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) { createView(); current_view_->newDocument(to_utf8(cmd.argument()), true); if (!current_view_->documentBufferView()) @@ -982,11 +1334,11 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) } break; - case LFUN_FILE_OPEN: + case LFUN_FILE_OPEN: { // FIXME: create a new method shared with LFUN_HELP_OPEN. - if (d->views_.empty() - || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) { - string const fname = to_utf8(cmd.argument()); + string const fname = to_utf8(cmd.argument()); + if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs + && current_view_->documentBufferView() != 0)) { // 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; @@ -996,8 +1348,9 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) if (current_view_ && !current_view_->documentBufferView()) current_view_->close(); } else - current_view_->openDocument(to_utf8(cmd.argument())); + current_view_->openDocument(fname); break; + } case LFUN_HELP_OPEN: { // FIXME: create a new method shared with LFUN_FILE_OPEN. @@ -1009,22 +1362,24 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) break; } FileName fname = i18nLibFileSearch("doc", arg, "lyx"); - if (fname.empty()) + if (fname.empty()) fname = i18nLibFileSearch("examples", arg, "lyx"); if (fname.empty()) { lyxerr << "LyX: unable to find documentation file `" - << arg << "'. Bad installation?" << endl; + << arg << "'. Bad installation?" << endl; break; } current_view_->message(bformat(_("Opening help file %1$s..."), - makeDisplayPath(fname.absFilename()))); + makeDisplayPath(fname.absFileName()))); Buffer * buf = current_view_->loadDocument(fname, false); - if (buf) { - current_view_->setBuffer(buf); - buf->updateLabels(); - buf->errors("Parse"); - } + +#ifndef DEVEL_VERSION + if (buf) + buf->setReadonly(true); +#else + (void) buf; +#endif break; } @@ -1032,14 +1387,15 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) string lyx_name; string const x11_name = split(to_utf8(cmd.argument()), lyx_name, ' '); if (lyx_name.empty() || x11_name.empty()) { - current_view_->message( - _("Syntax: set-color ")); + if (current_view_) + current_view_->message( + _("Syntax: set-color ")); break; } string const graphicsbg = lcolor.getLyXName(Color_graphicsbg); - bool const graphicsbg_changed = lyx_name == graphicsbg - && x11_name != graphicsbg; + bool const graphicsbg_changed = + lyx_name == graphicsbg && x11_name != graphicsbg; if (graphicsbg_changed) { // FIXME: The graphics cache no longer has a changeDisplay method. #if 0 @@ -1049,10 +1405,10 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) if (!lcolor.setColor(lyx_name, x11_name)) { current_view_->message( - bformat(_("Set-color \"%1$s\" failed " - "- color is undefined or " - "may not be redefined"), - from_utf8(lyx_name))); + bformat(_("Set-color \"%1$s\" failed " + "- color is undefined or " + "may not be redefined"), + from_utf8(lyx_name))); break; } // Make sure we don't keep old colors in cache. @@ -1071,8 +1427,8 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) if (!success) { lyxerr << "Warning in LFUN_LYXRC_APPLY!\n" - << "Unable to read lyxrc data" - << endl; + << "Unable to read lyxrc data" + << endl; break; } @@ -1084,7 +1440,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) } case LFUN_COMMAND_PREFIX: - lyx::dispatch(FuncRequest(LFUN_MESSAGE, d->keyseq.printOptions(true))); + dispatch(FuncRequest(LFUN_MESSAGE, d->keyseq.printOptions(true))); break; case LFUN_CANCEL: { @@ -1093,7 +1449,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) GuiView * gv = currentView(); if (gv && gv->currentBufferView()) // cancel any selection - lyx::dispatch(FuncRequest(LFUN_MARK_OFF)); + processFuncRequest(FuncRequest(LFUN_MARK_OFF)); dr.setMessage(from_ascii(N_("Cancel"))); break; } @@ -1105,15 +1461,14 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) // --- Menus ----------------------------------------------- case LFUN_RECONFIGURE: // argument is any additional parameter to the configure.py command - reconfigure(currentView(), to_utf8(cmd.argument())); + reconfigure(to_utf8(cmd.argument())); break; // --- lyxserver commands ---------------------------- case LFUN_SERVER_GET_FILENAME: { - GuiView * lv = currentView(); - LASSERT(lv && lv->documentBufferView(), return); + LASSERT(current_view_ && current_view_->documentBufferView(), return); docstring const fname = from_utf8( - lv->documentBufferView()->buffer().absFileName()); + current_view_->documentBufferView()->buffer().absFileName()); dr.setMessage(fname); LYXERR(Debug::INFO, "FNAME[" << fname << ']'); break; @@ -1124,14 +1479,216 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) theServer().notifyClient(to_utf8(dispatch_buffer)); break; } - default: - // Notify the caller that the action has not been dispatched. - dr.dispatched(false); - return; + + case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE: + lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar; + break; + + case LFUN_REPEAT: { + // repeat command + string countstr; + string rest = split(argument, countstr, ' '); + istringstream is(countstr); + int count = 0; + is >> count; + //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl; + for (int i = 0; i < count; ++i) + dispatch(lyxaction.lookupFunc(rest)); + break; } - // The action has been dispatched. - dr.dispatched(true); + case LFUN_COMMAND_SEQUENCE: { + // argument contains ';'-terminated commands + string arg = argument; + // FIXME: this LFUN should also work without any view. + Buffer * buffer = (current_view_ && current_view_->documentBufferView()) + ? &(current_view_->documentBufferView()->buffer()) : 0; + if (buffer) + buffer->undo().beginUndoGroup(); + while (!arg.empty()) { + string first; + arg = split(arg, first, ';'); + FuncRequest func(lyxaction.lookupFunc(first)); + func.setOrigin(cmd.origin()); + dispatch(func); + } + // the buffer may have been closed by one action + if (theBufferList().isLoaded(buffer)) + buffer->undo().endUndoGroup(); + break; + } + + case LFUN_COMMAND_ALTERNATIVES: { + // argument contains ';'-terminated commands + string arg = argument; + while (!arg.empty()) { + string first; + arg = split(arg, first, ';'); + FuncRequest func(lyxaction.lookupFunc(first)); + func.setOrigin(cmd.origin()); + FuncStatus const stat = getStatus(func); + if (stat.enabled()) { + dispatch(func); + break; + } + } + break; + } + + case LFUN_CALL: { + FuncRequest func; + if (theTopLevelCmdDef().lock(argument, func)) { + func.setOrigin(cmd.origin()); + dispatch(func); + theTopLevelCmdDef().release(argument); + } else { + if (func.action() == LFUN_UNKNOWN_ACTION) { + // unknown command definition + lyxerr << "Warning: unknown command definition `" + << argument << "'" + << endl; + } else { + // recursion detected + lyxerr << "Warning: Recursion in the command definition `" + << argument << "' detected" + << endl; + } + } + break; + } + + case LFUN_PREFERENCES_SAVE: + lyxrc.write(support::makeAbsPath("preferences", + package().user_support().absFileName()), false); + break; + + case LFUN_BUFFER_SAVE_AS_DEFAULT: { + string const fname = addName(addPath(package().user_support().absFileName(), + "templates/"), "defaults.lyx"); + Buffer defaults(fname); + + istringstream ss(argument); + Lexer lex; + lex.setStream(ss); + int const unknown_tokens = defaults.readHeader(lex); + + if (unknown_tokens != 0) { + lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n" + << unknown_tokens << " unknown token" + << (unknown_tokens == 1 ? "" : "s") + << endl; + } + + if (defaults.writeFile(FileName(defaults.absFileName()))) + dr.setMessage(bformat(_("Document defaults saved in %1$s"), + makeDisplayPath(fname))); + else { + dr.setError(true); + dr.setMessage(from_ascii(N_("Unable to save document defaults"))); + } + break; + } + + case LFUN_BOOKMARK_GOTO: + // go to bookmark, open unopened file and switch to buffer if necessary + gotoBookmark(convert(to_utf8(cmd.argument())), true, true); + dr.screenUpdate(Update::Force | Update::FitCursor); + break; + + case LFUN_BOOKMARK_CLEAR: + theSession().bookmarks().clear(); + break; + + case LFUN_DEBUG_LEVEL_SET: + lyxerr.setLevel(Debug::value(to_utf8(cmd.argument()))); + break; + + default: + // Everything below is only for active window + if (current_view_ == 0) + break; + + // Let the current GuiView dispatch its own actions. + current_view_->dispatch(cmd, dr); + + if (dr.dispatched()) + break; + + BufferView * bv = current_view_->currentBufferView(); + LASSERT(bv, /**/); + + // Avoid a screen redraw in the middle of a dispatch operation just + // because a warning or an error was displayed. + current_view_->setBusy(true); + + // Let the current BufferView dispatch its own actions. + bv->dispatch(cmd, dr); + if (dr.dispatched()) + break; + + BufferView * doc_bv = current_view_->documentBufferView(); + // Try with the document BufferView dispatch if any. + if (doc_bv) { + doc_bv->dispatch(cmd, dr); + if (dr.dispatched()) + break; + } + + // OK, so try the current Buffer itself... + bv->buffer().dispatch(cmd, dr); + if (dr.dispatched()) + break; + + // and with the document Buffer. + if (doc_bv) { + doc_bv->buffer().dispatch(cmd, dr); + if (dr.dispatched()) + break; + } + + // Let the current Cursor dispatch its own actions. + Cursor old = bv->cursor(); + bv->cursor().dispatch(cmd); + + // notify insets we just left + // FIXME: move this code to Cursor::dispatch + if (bv->cursor() != old) { + old.beginUndoGroup(); + old.fixIfBroken(); + bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor()); + if (badcursor) { + bv->cursor().fixIfBroken(); + bv->fixInlineCompletionPos(); + } + old.endUndoGroup(); + } + + // update completion. We do it here and not in + // processKeySym to avoid another redraw just for a + // changed inline completion + if (cmd.origin() == FuncRequest::KEYBOARD) { + if (cmd.action() == LFUN_SELF_INSERT + || (cmd.action() == LFUN_ERT_INSERT && bv->cursor().inMathed())) + current_view_->updateCompletion(bv->cursor(), true, true); + else if (cmd.action() == LFUN_CHAR_DELETE_BACKWARD) + current_view_->updateCompletion(bv->cursor(), false, true); + else + current_view_->updateCompletion(bv->cursor(), false, false); + } + + dr = bv->cursor().result(); + + current_view_->setBusy(false); + } + + // if we executed a mutating lfun, mark the buffer as dirty + Buffer * doc_buffer = (current_view_ && current_view_->documentBufferView()) + ? &(current_view_->documentBufferView()->buffer()) : 0; + if (doc_buffer && theBufferList().isLoaded(doc_buffer) + && flag.enabled() + && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer) + && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly)) + current_view_->currentBufferView()->buffer().markDirty(); } @@ -1167,7 +1724,6 @@ void GuiApplication::handleKeyFunc(FuncCode action) d->keyseq.clear(); // copied verbatim from do_accent_char bv->cursor().resetAnchor(); - bv->processUpdateFlags(Update::FitCursor); } @@ -1175,19 +1731,18 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state) { LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName()); - GuiView * lv = currentView(); - // Do nothing if we have nothing (JMarc) if (!keysym.isOK()) { LYXERR(Debug::KEY, "Empty kbd action (probably composing)"); - lv->restartCursor(); + if (current_view_) + current_view_->restartCursor(); return; } if (keysym.isModifier()) { LYXERR(Debug::KEY, "isModifier true"); - if (lv) - lv->restartCursor(); + if (current_view_) + current_view_->restartCursor(); return; } @@ -1198,44 +1753,44 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state) d->cancel_meta_seq.reset(); FuncRequest func = d->cancel_meta_seq.addkey(keysym, state); - LYXERR(Debug::KEY, "action first set to [" << func.action << ']'); + LYXERR(Debug::KEY, "action first set to [" << func.action() << ']'); // When not cancel or meta-fake, do the normal lookup. // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards. // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5. - if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) { + if ((func.action() != LFUN_CANCEL) && (func.action() != LFUN_META_PREFIX)) { // remove Caps Lock and Mod2 as a modifiers func = d->keyseq.addkey(keysym, (state | d->meta_fake_bit)); - LYXERR(Debug::KEY, "action now set to [" << func.action << ']'); + LYXERR(Debug::KEY, "action now set to [" << func.action() << ']'); } // Dont remove this unless you know what you are doing. d->meta_fake_bit = NoModifier; // Can this happen now ? - if (func.action == LFUN_NOACTION) + if (func.action() == LFUN_NOACTION) func = FuncRequest(LFUN_COMMAND_PREFIX); - LYXERR(Debug::KEY, " Key [action=" << func.action << "][" + LYXERR(Debug::KEY, " Key [action=" << func.action() << "][" << d->keyseq.print(KeySequence::Portable) << ']'); // already here we know if it any point in going further // why not return already here if action == -1 and // num_bytes == 0? (Lgb) - if (d->keyseq.length() > 1) - lv->message(d->keyseq.print(KeySequence::ForGui)); + if (d->keyseq.length() > 1 && current_view_) + current_view_->message(d->keyseq.print(KeySequence::ForGui)); // Maybe user can only reach the key via holding down shift. // Let's see. But only if shift is the only modifier - if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) { + if (func.action() == LFUN_UNKNOWN_ACTION && state == ShiftModifier) { LYXERR(Debug::KEY, "Trying without shift"); func = d->keyseq.addkey(keysym, NoModifier); - LYXERR(Debug::KEY, "Action now " << func.action); + LYXERR(Debug::KEY, "Action now " << func.action()); } - if (func.action == LFUN_UNKNOWN_ACTION) { + if (func.action() == LFUN_UNKNOWN_ACTION) { // Hmm, we didn't match any of the keysequences. See // if it's normal insertable text not already covered // by a binding @@ -1245,31 +1800,57 @@ void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state) FuncRequest::KEYBOARD); } else { LYXERR(Debug::KEY, "Unknown, !isText() - giving up"); - lv->message(_("Unknown function.")); - lv->restartCursor(); + if (current_view_) { + current_view_->message(_("Unknown function.")); + current_view_->restartCursor(); + } return; } } - if (func.action == LFUN_SELF_INSERT) { + if (func.action() == LFUN_SELF_INSERT) { if (encoded_last_key != 0) { docstring const arg(1, encoded_last_key); - lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, arg, + processFuncRequest(FuncRequest(LFUN_SELF_INSERT, arg, FuncRequest::KEYBOARD)); LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']"); } - } else { - lyx::dispatch(func); - if (!lv) - return; + } else + processFuncRequest(func); +} + + +void GuiApplication::processFuncRequest(FuncRequest const & func) +{ + lyx::dispatch(func); +} + + +void GuiApplication::processFuncRequestAsync(FuncRequest const & func) +{ + addToFuncRequestQueue(func); + processFuncRequestQueueAsync(); +} + + +void GuiApplication::processFuncRequestQueue() +{ + while (!d->func_request_queue_.empty()) { + processFuncRequest(d->func_request_queue_.front()); + d->func_request_queue_.pop(); } } -void GuiApplication::dispatchDelayed(FuncRequest const & func) +void GuiApplication::processFuncRequestQueueAsync() +{ + QTimer::singleShot(0, this, SLOT(slotProcessFuncRequestQueue())); +} + + +void GuiApplication::addToFuncRequestQueue(FuncRequest const & func) { d->func_request_queue_.push(func); - QTimer::singleShot(0, this, SLOT(processFuncRequestQueue())); } @@ -1294,7 +1875,7 @@ void GuiApplication::resetGui() gv->resetDialogs(); } - lyx::dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE)); + processFuncRequest(FuncRequest(LFUN_SCREEN_FONT_UPDATE)); } @@ -1446,19 +2027,10 @@ void GuiApplication::setGuiLanguage() case QLocale::Hebrew : case QLocale::Persian : case QLocale::Urdu : - setLayoutDirection(Qt::RightToLeft); + setLayoutDirection(Qt::RightToLeft); break; default: - setLayoutDirection(Qt::LeftToRight); - } -} - - -void GuiApplication::processFuncRequestQueue() -{ - while (!d->func_request_queue_.empty()) { - lyx::dispatch(d->func_request_queue_.back()); - d->func_request_queue_.pop(); + setLayoutDirection(Qt::LeftToRight); } } @@ -1473,6 +2045,9 @@ void GuiApplication::execBatchCommands() return; #ifdef Q_WS_MACX +#if QT_VERSION > 0x040600 + setAttribute(Qt::AA_MacDontSwapCtrlAndMeta,lyxrc.mac_dontswap_ctrl_meta); +#endif // Create the global default menubar which is shown for the dialogs // and if no GuiView is visible. // This must be done after the session was recovered to know the "last files". @@ -1490,16 +2065,28 @@ QAbstractItemModel * GuiApplication::languageModel() return d->language_model_; QStandardItemModel * lang_model = new QStandardItemModel(this); - lang_model->insertColumns(0, 1); + lang_model->insertColumns(0, 3); int current_row; + QIcon speller(getPixmap("images/", "dialog-show_spellchecker", "png")); + QIcon saurus(getPixmap("images/", "thesaurus-entry", "png")); Languages::const_iterator it = lyx::languages.begin(); Languages::const_iterator end = lyx::languages.end(); for (; it != end; ++it) { current_row = lang_model->rowCount(); lang_model->insertRows(current_row, 1); - QModelIndex item = lang_model->index(current_row, 0); - lang_model->setData(item, qt_(it->second.display()), Qt::DisplayRole); - lang_model->setData(item, toqstr(it->second.lang()), Qt::UserRole); + QModelIndex pl_item = lang_model->index(current_row, 0); + QModelIndex sp_item = lang_model->index(current_row, 1); + QModelIndex th_item = lang_model->index(current_row, 2); + lang_model->setData(pl_item, qt_(it->second.display()), Qt::DisplayRole); + lang_model->setData(pl_item, toqstr(it->second.lang()), Qt::UserRole); + lang_model->setData(sp_item, qt_(it->second.display()), Qt::DisplayRole); + lang_model->setData(sp_item, toqstr(it->second.lang()), Qt::UserRole); + if (theSpellChecker() && theSpellChecker()->hasDictionary(&it->second)) + lang_model->setData(sp_item, speller, Qt::DecorationRole); + lang_model->setData(th_item, qt_(it->second.display()), Qt::DisplayRole); + lang_model->setData(th_item, toqstr(it->second.lang()), Qt::UserRole); + if (thesaurus.thesaurusInstalled(from_ascii(it->second.code()))) + lang_model->setData(th_item, saurus, Qt::DecorationRole); } d->language_model_ = new QSortFilterProxyModel(this); d->language_model_->setSourceModel(lang_model); @@ -1528,7 +2115,7 @@ void GuiApplication::restoreGuiSession() if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) { boost::crc_32_type crc; - string const & fname = file_name.absFilename(); + string const & fname = file_name.absFileName(); crc = for_each(fname.begin(), fname.end(), crc); createView(crc.checksum()); } @@ -1597,7 +2184,8 @@ bool GuiApplication::event(QEvent * e) // commands are not executed here yet and the gui is not ready // therefore. QFileOpenEvent * foe = static_cast(e); - dispatchDelayed(FuncRequest(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file()))); + FuncRequest const fr(LFUN_FILE_OPEN, qstring_to_ucs4(foe->file())); + processFuncRequestAsync(fr); e->accept(); return true; } @@ -1712,8 +2300,13 @@ void GuiApplication::commitData(QSessionManager & sm) /// interaction. /// We are changing that to close all wiew one by one. /// FIXME: verify if the default implementation is enough now. - if (sm.allowsInteraction() && !closeAllViews()) - sm.cancel(); + #ifdef QT_NO_SESSIONMANAGER + #warning Qt is compiled without session manager + (void) sm; + #else + if (sm.allowsInteraction() && !closeAllViews()) + sm.cancel(); + #endif } @@ -1737,7 +2330,7 @@ bool GuiApplication::closeAllViews() QList views = d->views_.values(); foreach (GuiView * view, views) { - if (!view->close()) + if (!view->closeScheduled()) return false; } @@ -1819,7 +2412,7 @@ bool GuiApplication::readUIFile(QString const & name, bool include) // Ensure that a file is read only once (prevents include loops) static QStringList uifiles; - QString const uifile = toqstr(ui_path.absFilename()); + QString const uifile = toqstr(ui_path.absFileName()); if (uifiles.contains(uifile)) { if (!include) { // We are reading again the top uifile so reset the safeguard: @@ -1973,7 +2566,7 @@ bool GuiApplication::x11EventFilter(XEvent * xev) void hideDialogs(std::string const & name, Inset * inset) { if (theApp()) - theApp()->hideDialogs(name, inset); + frontend::guiApp->hideDialogs(name, inset); }