X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiApplication.cpp;h=4656e63a69f5855ce8593e043a099075055a19ab;hb=28896d1bddcfcd358c863603334c0a013fb601ea;hp=172ac0bce479fd1ca07dee58dfd48eb2cdb15e94;hpb=20d5148dca2f706a453b83fb413d99016e809496;p=lyx.git diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp index 172ac0bce4..4656e63a69 100644 --- a/src/frontends/qt4/GuiApplication.cpp +++ b/src/frontends/qt4/GuiApplication.cpp @@ -33,31 +33,42 @@ #include "Buffer.h" #include "BufferList.h" #include "BufferView.h" +#include "CmdDef.h" #include "Color.h" #include "Font.h" #include "FuncRequest.h" #include "FuncStatus.h" +#include "Intl.h" +#include "KeyMap.h" #include "Language.h" +#include "LaTeXFeatures.h" #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" +#include "support/filetools.h" #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" #include "support/os.h" #include "support/Package.h" +#include "support/Path.h" +#include "support/Systemcall.h" #ifdef Q_WS_MACX #include "support/linkback/LinkBackProxy.h" @@ -93,6 +104,9 @@ #include #include #include +#if QT_VERSION >= 0x040400 +#include +#endif #include #ifdef Q_WS_X11 @@ -110,10 +124,11 @@ #include #endif // Q_WS_WIN -#include +#include "support/bind.h" #include #include +#include #include using namespace std; @@ -147,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 { @@ -217,6 +236,7 @@ PngMap sorted_png_map[] = { { "Cap", "cap2" }, { "Cup", "cup2" }, { "Delta", "delta2" }, + { "Diamond", "diamond2" }, { "Downarrow", "downarrow2" }, { "Gamma", "gamma2" }, { "Lambda", "lambda2" }, @@ -247,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); @@ -297,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/"; @@ -318,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()) { @@ -334,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); @@ -359,15 +398,39 @@ 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) + if (unknown) { + fname = libFileSearch(QString("images/"), "unknown", "png"); + if (fname.exists()) + return toqstr(fname.absFileName()); return QString(":/images/unknown.png"); + } return QString(); } +QPixmap getPixmap(QString const & path, QString const & name, QString const & ext) +{ + QPixmap pixmap; + FileName fname = libFileSearch(path, name, ext); + QString path1 = toqstr(fname.absFileName()); + QString path2 = ":/" + path + name + "." + ext; + + if (pixmap.load(path1)) { + return pixmap; + } + else if (pixmap.load(path2)) { + return pixmap; + } + + LYXERR0("Cannot load pixmap \"" + << path << name << '.' << ext + << "\", please verify resource system!"); + + return QPixmap(); +} QIcon getIcon(FuncRequest const & f, bool unknown) { @@ -446,7 +509,7 @@ public: QKeyEvent * ke = static_cast(e); KeySymbol sym; setKeySymbol(&sym, ke); - theLyXFunc().processKeySym(sym, q_key_state(ke->modifiers())); + guiApp->processKeySym(sym, q_key_state(ke->modifiers())); e->accept(); return true; } @@ -619,7 +682,21 @@ public: struct GuiApplication::Private { - Private(): language_model_(0), global_menubar_(0) {} + Private(): language_model_(0), meta_fake_bit(NoModifier), + global_menubar_(0) + { + #ifdef Q_WS_WIN + /// WMF Mime handler for Windows clipboard. + wmf_mime_ = new QWindowsMimeMetafile(); + #endif + initKeySequences(&theTopLevelKeymap()); + } + + void initKeySequences(KeyMap * kb) + { + keyseq = KeySequence(kb, kb); + cancel_meta_seq = KeySequence(kb, kb); + } /// QSortFilterProxyModel * language_model_; @@ -649,6 +726,13 @@ struct GuiApplication::Private /// delayed FuncRequests std::queue func_request_queue_; + /// + KeySequence keyseq; + /// + KeySequence cancel_meta_seq; + /// + KeyModifier meta_fake_bit; + /// Multiple views container. /** * Warning: This must not be a smart pointer as the destruction of the @@ -667,8 +751,7 @@ struct GuiApplication::Private #ifdef Q_WS_WIN /// WMF Mime handler for Windows clipboard. - /// \warning: see comment in ~GuiApplication and in bug 4846. - QWindowsMimeMetafile wmf_mime_; + QWindowsMimeMetafile * wmf_mime_; #endif }; @@ -680,24 +763,18 @@ 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; } GuiApplication::GuiApplication(int & argc, char ** argv) - : QApplication(argc, argv), current_view_(0), d(new GuiApplication::Private) + : QApplication(argc, argv), current_view_(0), + d(new GuiApplication::Private) { QString app_name = "LyX"; QCoreApplication::setOrganizationName(app_name); QCoreApplication::setOrganizationDomain("lyx.org"); - QCoreApplication::setApplicationName(app_name + "-" + lyx_version); + QCoreApplication::setApplicationName(lyx_package); // Install translator for GUI elements. installTranslator(&d->qt_trans_); @@ -713,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. @@ -754,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 } @@ -776,12 +859,159 @@ docstring GuiApplication::iconName(FuncRequest const & f, bool unknown) } +FuncStatus GuiApplication::getStatus(FuncRequest const & cmd) const +{ + 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 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()) { - 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; + } + + 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; @@ -790,10 +1020,18 @@ bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const case LFUN_BUFFER_NEW: case LFUN_BUFFER_NEW_TEMPLATE: case LFUN_FILE_OPEN: + case LFUN_HELP_OPEN: case LFUN_SCREEN_FONT_UPDATE: case LFUN_SET_COLOR: case LFUN_WINDOW_NEW: case LFUN_LYX_QUIT: + case LFUN_LYXRC_APPLY: + case LFUN_COMMAND_PREFIX: + case LFUN_CANCEL: + case LFUN_META_PREFIX: + case LFUN_RECONFIGURE: + case LFUN_SERVER_GET_FILENAME: + case LFUN_SERVER_NOTIFY: enable = true; break; @@ -803,14 +1041,237 @@ bool GuiApplication::getStatus(FuncRequest const & cmd, FuncStatus & flag) const if (!enable) flag.setEnabled(false); - return true; } - -bool GuiApplication::dispatch(FuncRequest const & cmd) +/// 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) { - switch (cmd.action) { + 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. +void GuiApplication::reconfigure(string const & option) +{ + // emit message signal. + if (current_view_) + current_view_->message(_("Running configure...")); + + // Run configure in user lyx directory + PathChanger p(package().user_support()); + string configure_command = package().configure_command(); + configure_command += option; + Systemcall one; + int ret = one.startscript(Systemcall::Wait, configure_command); + p.pop(); + // emit message signal. + if (current_view_) + current_view_->message(_("Reloading configuration...")); + lyxrc.read(libFileSearch(QString(), "lyxrc.defaults")); + // Re-read packages.lst + LaTeXFeatures::getAvailable(); + + if (ret) + Alert::information(_("System reconfiguration failed"), + _("The system reconfiguration has failed.\n" + "Default textclass is used but LyX may " + "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" + "updated document class specifications.")); +} + + +void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) +{ + 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())); @@ -819,8 +1280,11 @@ bool GuiApplication::dispatch(FuncRequest const & cmd) 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); - current_view_->close(); + gotoBookmark(i+1, false, false); + // clear the last opened list, because + // maybe this will end the session + theSession().lastOpened().clear(); + current_view_->closeScheduled(); break; case LFUN_LYX_QUIT: @@ -840,10 +1304,7 @@ bool GuiApplication::dispatch(FuncRequest const & cmd) // 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; @@ -851,7 +1312,7 @@ bool GuiApplication::dispatch(FuncRequest const & cmd) case LFUN_BUFFER_NEW: if (d->views_.empty() - || (!lyxrc.open_buffers_in_tabs && current_view_->buffer() != 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(); @@ -863,44 +1324,78 @@ bool GuiApplication::dispatch(FuncRequest const & cmd) case LFUN_BUFFER_NEW_TEMPLATE: if (d->views_.empty() - || (!lyxrc.open_buffers_in_tabs && current_view_->buffer() != 0)) { + || (!lyxrc.open_buffers_in_tabs && current_view_->documentBufferView() != 0)) { createView(); current_view_->newDocument(to_utf8(cmd.argument()), true); - if (!current_view_->buffer()) + if (!current_view_->documentBufferView()) current_view_->close(); } else { current_view_->newDocument(to_utf8(cmd.argument()), true); } break; - case LFUN_FILE_OPEN: - if (d->views_.empty() - || (!lyxrc.open_buffers_in_tabs && current_view_->buffer() != 0)) { - string const fname = to_utf8(cmd.argument()); + case LFUN_FILE_OPEN: { + // FIXME: create a new method shared with LFUN_HELP_OPEN. + 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; crc = for_each(fname.begin(), fname.end(), crc); createView(crc.checksum()); current_view_->openDocument(fname); - if (current_view_ && !current_view_->buffer()) + 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. + if (current_view_ == 0) + createView(); + string const arg = to_utf8(cmd.argument()); + if (arg.empty()) { + current_view_->message(_("Missing argument")); + break; + } + FileName fname = i18nLibFileSearch("doc", arg, "lyx"); + if (fname.empty()) + fname = i18nLibFileSearch("examples", arg, "lyx"); + + if (fname.empty()) { + lyxerr << "LyX: unable to find documentation file `" + << arg << "'. Bad installation?" << endl; + break; + } + current_view_->message(bformat(_("Opening help file %1$s..."), + makeDisplayPath(fname.absFileName()))); + Buffer * buf = current_view_->loadDocument(fname, false); + +#ifndef DEVEL_VERSION + if (buf) + buf->setReadonly(true); +#else + (void) buf; +#endif + break; + } case LFUN_SET_COLOR: { 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 @@ -910,10 +1405,10 @@ bool GuiApplication::dispatch(FuncRequest const & cmd) 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. @@ -921,20 +1416,441 @@ bool GuiApplication::dispatch(FuncRequest const & cmd) break; } + case LFUN_LYXRC_APPLY: { + // reset active key sequences, since the bindings + // are updated (bug 6064) + d->keyseq.reset(); + LyXRC const lyxrc_orig = lyxrc; + + istringstream ss(to_utf8(cmd.argument())); + bool const success = lyxrc.read(ss) == 0; + + if (!success) { + lyxerr << "Warning in LFUN_LYXRC_APPLY!\n" + << "Unable to read lyxrc data" + << endl; + break; + } + + actOnUpdatedPrefs(lyxrc_orig, lyxrc); + setSpellChecker(); + resetGui(); + + break; + } + + case LFUN_COMMAND_PREFIX: + dispatch(FuncRequest(LFUN_MESSAGE, d->keyseq.printOptions(true))); + break; + + case LFUN_CANCEL: { + d->keyseq.reset(); + d->meta_fake_bit = NoModifier; + GuiView * gv = currentView(); + if (gv && gv->currentBufferView()) + // cancel any selection + processFuncRequest(FuncRequest(LFUN_MARK_OFF)); + dr.setMessage(from_ascii(N_("Cancel"))); + break; + } + case LFUN_META_PREFIX: + d->meta_fake_bit = AltModifier; + dr.setMessage(d->keyseq.print(KeySequence::ForGui)); + break; + + // --- Menus ----------------------------------------------- + case LFUN_RECONFIGURE: + // argument is any additional parameter to the configure.py command + reconfigure(to_utf8(cmd.argument())); + break; + + // --- lyxserver commands ---------------------------- + case LFUN_SERVER_GET_FILENAME: { + LASSERT(current_view_ && current_view_->documentBufferView(), return); + docstring const fname = from_utf8( + current_view_->documentBufferView()->buffer().absFileName()); + dr.setMessage(fname); + LYXERR(Debug::INFO, "FNAME[" << fname << ']'); + break; + } + case LFUN_SERVER_NOTIFY: { + docstring const dispatch_buffer = d->keyseq.print(KeySequence::Portable); + dr.setMessage(dispatch_buffer); + theServer().notifyClient(to_utf8(dispatch_buffer)); + break; + } + + 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; + } + + 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: - // Notify the caller that the action has not been dispatched. - return false; + // 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(); +} - // The action has been dispatched. - return true; + +docstring GuiApplication::viewStatusMessage() +{ + // When meta-fake key is pressed, show the key sequence so far + "M-". + if (d->meta_fake_bit != NoModifier) + return d->keyseq.print(KeySequence::ForGui) + "M-"; + + // Else, when a non-complete key sequence is pressed, + // show the available options. + if (d->keyseq.length() > 0 && !d->keyseq.deleted()) + return d->keyseq.printOptions(true); + + return docstring(); +} + + +void GuiApplication::handleKeyFunc(FuncCode action) +{ + char_type c = 0; + + if (d->keyseq.length()) + c = 0; + GuiView * gv = currentView(); + LASSERT(gv && gv->currentBufferView(), return); + BufferView * bv = gv->currentBufferView(); + bv->getIntl().getTransManager().deadkey( + c, get_accent(action).accent, bv->cursor().innerText(), + bv->cursor()); + // Need to clear, in case the minibuffer calls these + // actions + d->keyseq.clear(); + // copied verbatim from do_accent_char + bv->cursor().resetAnchor(); +} + + +void GuiApplication::processKeySym(KeySymbol const & keysym, KeyModifier state) +{ + LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName()); + + // Do nothing if we have nothing (JMarc) + if (!keysym.isOK()) { + LYXERR(Debug::KEY, "Empty kbd action (probably composing)"); + if (current_view_) + current_view_->restartCursor(); + return; + } + + if (keysym.isModifier()) { + LYXERR(Debug::KEY, "isModifier true"); + if (current_view_) + current_view_->restartCursor(); + return; + } + + char_type encoded_last_key = keysym.getUCSEncoded(); + + // Do a one-deep top-level lookup for + // cancel and meta-fake keys. RVDK_PATCH_5 + d->cancel_meta_seq.reset(); + + FuncRequest func = d->cancel_meta_seq.addkey(keysym, state); + 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)) { + // 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() << ']'); + } + + // Dont remove this unless you know what you are doing. + d->meta_fake_bit = NoModifier; + + // Can this happen now ? + if (func.action() == LFUN_NOACTION) + func = FuncRequest(LFUN_COMMAND_PREFIX); + + 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 && 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) { + LYXERR(Debug::KEY, "Trying without shift"); + func = d->keyseq.addkey(keysym, NoModifier); + LYXERR(Debug::KEY, "Action now " << func.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 + if (keysym.isText() && d->keyseq.length() == 1) { + LYXERR(Debug::KEY, "isText() is true, inserting."); + func = FuncRequest(LFUN_SELF_INSERT, + FuncRequest::KEYBOARD); + } else { + LYXERR(Debug::KEY, "Unknown, !isText() - giving up"); + if (current_view_) { + current_view_->message(_("Unknown function.")); + current_view_->restartCursor(); + } + return; + } + } + + if (func.action() == LFUN_SELF_INSERT) { + if (encoded_last_key != 0) { + docstring const arg(1, encoded_last_key); + processFuncRequest(FuncRequest(LFUN_SELF_INSERT, arg, + FuncRequest::KEYBOARD)); + LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']"); + } + } 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())); } @@ -954,11 +1870,12 @@ void GuiApplication::resetGui() QHash::iterator it; for (it = d->views_.begin(); it != d->views_.end(); ++it) { GuiView * gv = *it; + setCurrentView(gv); gv->setLayoutDirection(layoutDirection()); gv->resetDialogs(); } - dispatch(FuncRequest(LFUN_SCREEN_FONT_UPDATE)); + processFuncRequest(FuncRequest(LFUN_SCREEN_FONT_UPDATE)); } @@ -978,10 +1895,9 @@ void GuiApplication::createView(QString const & geometry_arg, bool autoShow, // create new view int id = view_id; - if (id == 0) { - while (d->views_.find(id) != d->views_.end()) - id++; - } + 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); // register view @@ -1086,7 +2002,7 @@ void GuiApplication::setGuiLanguage() setRcGuiLanguage(); QString const default_language = toqstr(Messages::defaultLanguage()); - LYXERR(Debug::LOCALE, "Tring to set default locale to: " << default_language); + LYXERR(Debug::LOCALE, "Trying to set default locale to: " << default_language); QLocale const default_locale(default_language); QLocale::setDefault(default_locale); @@ -1099,7 +2015,7 @@ void GuiApplication::setGuiLanguage() // c.f. http://doc.trolltech.com/4.1/qtranslator.html#load if (!d->qt_trans_.load(language_name, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { - LYXERR(Debug::LOCALE, "Could not find Qt translations for locale " + LYXERR(Debug::LOCALE, "Could not find Qt translations for locale " << language_name); } else { LYXERR(Debug::LOCALE, "Successfully installed Qt translations for locale " @@ -1111,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); } } @@ -1138,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". @@ -1155,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; - Languages::const_iterator it = languages.begin(); - Languages::const_iterator end = languages.end(); + 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); @@ -1181,12 +2103,32 @@ void GuiApplication::restoreGuiSession() return; Session & session = theSession(); - vector const & lastopened = session.lastOpened().getfiles(); + LastOpenedSection::LastOpened const & lastopened = + session.lastOpened().getfiles(); + + FileName active_file; // do not add to the lastfile list since these files are restored from // last session, and should be already there (regular files), or should // not be added at all (help files). - for_each(lastopened.begin(), lastopened.end(), - bind(&GuiView::loadDocument, current_view_, _1, false)); + for (size_t i = 0; i < lastopened.size(); ++i) { + FileName const & file_name = lastopened[i].file_name; + if (d->views_.empty() || (!lyxrc.open_buffers_in_tabs + && current_view_->documentBufferView() != 0)) { + boost::crc_32_type crc; + string const & fname = file_name.absFileName(); + crc = for_each(fname.begin(), fname.end(), crc); + createView(crc.checksum()); + } + current_view_->loadDocument(file_name, false); + + if (lastopened[i].active) + active_file = file_name; + } + + // Restore last active buffer + Buffer * buffer = theBufferList().getBuffer(active_file); + if (buffer) + current_view_->setBuffer(buffer); // clear this list to save a few bytes of RAM session.lastOpened().clear(); @@ -1242,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; } @@ -1272,9 +2215,11 @@ bool GuiApplication::notify(QObject * receiver, QEvent * event) this->exit(1); case BufferException: { - Buffer * buf = current_view_->buffer(); + if (!current_view_->documentBufferView()) + return false; + Buffer * buf = ¤t_view_->documentBufferView()->buffer(); docstring details = e.details_ + '\n'; - details += theBufferList().emergencyWrite(buf); + details += buf->emergencyWrite(); theBufferList().release(buf); details += "\n" + _("The current document was closed."); Alert::error(e.title_, details); @@ -1355,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 } @@ -1364,10 +2314,8 @@ void GuiApplication::unregisterView(GuiView * gv) { LASSERT(d->views_[gv->id()] == gv, /**/); d->views_.remove(gv->id()); - if (current_view_ == gv) { + if (current_view_ == gv) current_view_ = 0; - theLyXFunc().setLyXView(0); - } } @@ -1376,9 +2324,13 @@ bool GuiApplication::closeAllViews() if (d->views_.empty()) return true; + // When a view/window was closed before without quitting LyX, there + // are already entries in the lastOpened list. + theSession().lastOpened().clear(); + QList views = d->views_.values(); foreach (GuiView * view, views) { - if (!view->close()) + if (!view->closeScheduled()) return false; } @@ -1423,21 +2375,6 @@ bool GuiApplication::searchMenu(FuncRequest const & func, bool GuiApplication::readUIFile(QString const & name, bool include) { - enum { - ui_menuset = 1, - ui_toolbars, - ui_toolbarset, - ui_include, - ui_last - }; - - LexerKeyword uitags[] = { - { "include", ui_include }, - { "menuset", ui_menuset }, - { "toolbars", ui_toolbars }, - { "toolbarset", ui_toolbarset } - }; - LYXERR(Debug::INIT, "About to read " << name << "..."); FileName ui_path; @@ -1450,17 +2387,32 @@ bool GuiApplication::readUIFile(QString const & name, bool include) } if (ui_path.empty()) { + static const QString defaultUIFile = "default"; LYXERR(Debug::INIT, "Could not find " << name); + if (include) { + Alert::warning(_("Could not find UI definition file"), + bformat(_("Error while reading the included file\n%1$s\n" + "Please check your installation."), qstring_to_ucs4(name))); + return false; + } + if (name == defaultUIFile) { + LYXERR(Debug::INIT, "Could not find default UI file!!"); + Alert::warning(_("Could not find default UI file"), + _("LyX could not find the default UI file!\n" + "Please check your installation.")); + return false; + } 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; + bformat(_("Error while reading the configuration file\n%1$s\n" + "Falling back to default.\n" + "Please look under Tools>Preferences>User Interface and\n" + "check which User Interface file you are using."), qstring_to_ucs4(name))); + return readUIFile(defaultUIFile, false); } - // 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: @@ -1477,6 +2429,21 @@ bool GuiApplication::readUIFile(QString const & name, bool include) LYXERR(Debug::INIT, "Found " << name << " in " << ui_path); + enum { + ui_menuset = 1, + ui_toolbars, + ui_toolbarset, + ui_include, + ui_last + }; + + LexerKeyword uitags[] = { + { "include", ui_include }, + { "menuset", ui_menuset }, + { "toolbars", ui_toolbars }, + { "toolbarset", ui_toolbarset } + }; + Lexer lex(uitags); lex.setFile(ui_path); if (!lex.isOK()) { @@ -1487,6 +2454,9 @@ bool GuiApplication::readUIFile(QString const & name, bool include) if (lyxerr.debugging(Debug::PARSER)) lex.printTable(lyxerr); + // store which ui files define Toolbars + static QStringList toolbar_uifiles; + while (lex.isOK()) { switch (lex.lex()) { case ui_include: { @@ -1506,6 +2476,7 @@ bool GuiApplication::readUIFile(QString const & name, bool include) case ui_toolbars: d->toolbars_.readToolbarSettings(lex); + toolbar_uifiles.push_back(uifile); break; default: @@ -1526,9 +2497,12 @@ bool GuiApplication::readUIFile(QString const & name, bool include) QFileInfo fi(uifiles[i]); QDateTime const date_value = fi.lastModified(); QString const name_key = QString::number(i); - if (!settings.contains(name_key) + // if an ui file which defines Toolbars has changed, + // we have to reset the settings + if (toolbar_uifiles.contains(uifiles[i]) + && (!settings.contains(name_key) || settings.value(name_key).toString() != uifiles[i] - || settings.value(name_key + "/date").toDateTime() != date_value) { + || settings.value(name_key + "/date").toDateTime() != date_value)) { touched = true; settings.setValue(name_key, uifiles[i]); settings.setValue(name_key + "/date", date_value); @@ -1564,7 +2538,7 @@ bool GuiApplication::x11EventFilter(XEvent * xev) if (xev->xselectionrequest.selection != XA_PRIMARY) break; LYXERR(Debug::SELECTION, "X requested selection."); - BufferView * bv = current_view_->view(); + BufferView * bv = current_view_->currentBufferView(); if (bv) { docstring const sel = bv->requestSelection(); if (!sel.empty()) @@ -1576,7 +2550,7 @@ bool GuiApplication::x11EventFilter(XEvent * xev) if (xev->xselectionclear.selection != XA_PRIMARY) break; LYXERR(Debug::SELECTION, "Lost selection."); - BufferView * bv = current_view_->view(); + BufferView * bv = current_view_->currentBufferView(); if (bv) bv->clearSelection(); break; @@ -1592,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); }