X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FMenus.cpp;h=8689b32f05b834e1fd6c6be55807743daa0c2092;hb=569815e79dbb76a99033560d811fb5f1fb2f292e;hp=4137f33f4616d1aa803d4f8389965d40a5399f63;hpb=dc5934737d63035227a3dc899b784f2de4bacd6e;p=lyx.git diff --git a/src/frontends/qt4/Menus.cpp b/src/frontends/qt4/Menus.cpp index 4137f33f46..8689b32f05 100644 --- a/src/frontends/qt4/Menus.cpp +++ b/src/frontends/qt4/Menus.cpp @@ -21,6 +21,7 @@ #include "Action.h" #include "GuiApplication.h" #include "GuiView.h" +#include "GuiWorkArea.h" #include "qt_helpers.h" #include "BiblioInfo.h" @@ -43,6 +44,7 @@ #include "LyXAction.h" #include "LyX.h" #include "LyXRC.h" +#include "lyxfind.h" #include "Paragraph.h" #include "ParIterator.h" #include "Session.h" @@ -69,6 +71,9 @@ #include #include #include +#if QT_VERSION >= 0x040600 +#include +#endif #include "support/shared_ptr.h" @@ -128,7 +133,7 @@ public: typically for the File->Export menu. */ ExportFormats, /** This is a list of importable formats - typically for the File->Export menu. */ + typically for the File->Import menu. */ ImportFormats, /** This is the list of elements available * for insertion into document. */ @@ -175,8 +180,10 @@ public: MenuItem(Kind kind, QString const & label, QString const & submenu = QString(), + QString const & tooltip = QString(), bool optional = false) - : kind_(kind), label_(label), submenuname_(submenu), optional_(optional) + : kind_(kind), label_(label), submenuname_(submenu), + tooltip_(tooltip), optional_(optional) { LASSERT(kind == Submenu || kind == Help || kind == Info, /**/); } @@ -184,9 +191,11 @@ public: MenuItem(Kind kind, QString const & label, FuncRequest const & func, + QString const & tooltip = QString(), bool optional = false, FuncRequest::Origin origin = FuncRequest::MENU) - : kind_(kind), label_(label), func_(func), optional_(optional) + : kind_(kind), label_(label), func_(func), + tooltip_(tooltip), optional_(optional) { func_.setOrigin(origin); } @@ -208,11 +217,13 @@ public: return index == -1 ? QString() : label_.mid(index + 1); } /// The complete label, with label and shortcut separated by a '|' - QString fulllabel() const { return label_;} + QString fulllabel() const { return label_; } /// The kind of entry Kind kind() const { return kind_; } /// the action (if relevant) FuncRequest const & func() const { return func_; } + /// the tooltip + QString const & tooltip() const { return tooltip_; } /// returns true if the entry should be omitted when disabled bool optional() const { return optional_; } /// returns the status of the lfun associated with this entry @@ -265,6 +276,8 @@ private: /// QString submenuname_; /// + QString tooltip_; + /// bool optional_; /// FuncStatus status_; @@ -298,6 +311,10 @@ public: const_iterator begin() const { return items_.begin(); } /// const_iterator end() const { return items_.end(); } + /// + void cat(MenuDefinition const & other); + /// + void catSub(docstring const & name); // search for func in this menu iteratively, and put menu // names in a stack. @@ -316,7 +333,7 @@ public: void expandLastfiles(); void expandDocuments(); void expandBookmarks(); - void expandFormats(MenuItem::Kind kind, Buffer const * buf); + void expandFormats(MenuItem::Kind const kind, Buffer const * buf); void expandFloatListInsert(Buffer const * buf); void expandFloatInsert(Buffer const * buf); void expandFlexInsert(Buffer const * buf, InsetLayout::InsetLyXType type); @@ -494,7 +511,7 @@ void MenuDefinition::read(Lexer & lex) FuncRequest::Origin origin = FuncRequest::MENU; if (name_.startsWith("context-toc-")) origin = FuncRequest::TOC; - add(MenuItem(MenuItem::Command, toqstr(name), func, optional, origin)); + add(MenuItem(MenuItem::Command, toqstr(name), func, QString(), optional, origin)); optional = false; break; } @@ -608,7 +625,7 @@ void MenuDefinition::read(Lexer & lex) lex.next(true); docstring const mname = lex.getDocString(); add(MenuItem(MenuItem::Submenu, - toqstr(mlabel), toqstr(mname), optional)); + toqstr(mlabel), toqstr(mname), QString(), optional)); optional = false; break; } @@ -641,6 +658,20 @@ bool MenuDefinition::hasFunc(FuncRequest const & func) const } +void MenuDefinition::catSub(docstring const & name) +{ + add(MenuItem(MenuItem::Submenu, + qt_("More...|M"), toqstr(name), QString(), false)); +} + +void MenuDefinition::cat(MenuDefinition const & other) +{ + const_iterator et = other.end(); + for (const_iterator it = other.begin(); it != et; ++it) + add(*it); +} + + void MenuDefinition::checkShortcuts() const { // This is a quadratic algorithm, but we do not care because @@ -733,38 +764,47 @@ void MenuDefinition::expandSpellingSuggestions(BufferView const * bv) { if (!bv) return; + Cursor const & cur = bv->cursor(); + if (!cur.inTexted()) + return; WordLangTuple wl; docstring_list suggestions; - pos_type from = bv->cursor().pos(); + Paragraph const & par = cur.paragraph(); + pos_type from = cur.pos(); pos_type to = from; - Paragraph const & par = bv->cursor().paragraph(); SpellChecker::Result res = par.spellCheck(from, to, wl, suggestions, true, true); switch (res) { case SpellChecker::UNKNOWN_WORD: if (lyxrc.spellcheck_continuously) { LYXERR(Debug::GUI, "Misspelled Word! Suggested Words = "); - size_t i = 0; - MenuItem item(MenuItem::Submenu, qt_("More Spelling Suggestions")); - item.setSubmenu(MenuDefinition(qt_("More Spelling Suggestions"))); - for (; i != suggestions.size(); ++i) { - docstring const & suggestion = suggestions[i]; - LYXERR(Debug::GUI, suggestion); - MenuItem w(MenuItem::Command, toqstr(suggestion), - FuncRequest(LFUN_WORD_REPLACE, suggestion)); - if (i < 10) - add(w); - else - item.submenu().add(w); + docstring const & selection = cur.selectionAsString(false); + if (!cur.selection() || selection == wl.word()) { + size_t i = 0; + size_t m = 10; // first submenu index + MenuItem item(MenuItem::Submenu, qt_("More Spelling Suggestions")); + item.setSubmenu(MenuDefinition(qt_("More Spelling Suggestions"))); + for (; i != suggestions.size(); ++i) { + docstring const & suggestion = suggestions[i]; + LYXERR(Debug::GUI, suggestion); + MenuItem w(MenuItem::Command, toqstr(suggestion), + FuncRequest(LFUN_WORD_REPLACE, + replace2string(suggestion, selection, + true, true, false, true, false))); + if (i < m) + add(w); + else + item.submenu().add(w); + } + if (i > m) + add(item); + if (i > 0) + add(MenuItem(MenuItem::Separator)); + docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang()); + add(MenuItem(MenuItem::Command, qt_("Add to personal dictionary|n"), + FuncRequest(LFUN_SPELLING_ADD, arg))); + add(MenuItem(MenuItem::Command, qt_("Ignore all|I"), + FuncRequest(LFUN_SPELLING_IGNORE, arg))); } - if (i >= 10) - add(item); - if (i > 0) - add(MenuItem(MenuItem::Separator)); - docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang()); - add(MenuItem(MenuItem::Command, qt_("Add to personal dictionary|n"), - FuncRequest(LFUN_SPELLING_ADD, arg))); - add(MenuItem(MenuItem::Command, qt_("Ignore all|I"), - FuncRequest(LFUN_SPELLING_IGNORE, arg))); } break; case SpellChecker::LEARNED_WORD: { @@ -865,14 +905,15 @@ void MenuDefinition::expandLastfiles() for (; lfit != lf.end() && ii <= lyxrc.num_lastfiles; ++lfit, ++ii) { string const file = lfit->absFileName(); + QString const short_path = toqstr(makeDisplayPath(file, 30)); + QString const long_path = toqstr(makeDisplayPath(file)); QString label; if (ii < 10) - label = QString("%1. %2|%3").arg(ii) - .arg(toqstr(makeDisplayPath(file, 30))).arg(ii); + label = QString("%1. %2|%3").arg(ii).arg(short_path).arg(ii); else - label = QString("%1. %2").arg(ii) - .arg(toqstr(makeDisplayPath(file, 30))); - add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_FILE_OPEN, file))); + label = QString("%1. %2").arg(ii).arg(short_path); + add(MenuItem(MenuItem::Command, label, + FuncRequest(LFUN_FILE_OPEN, file), long_path)); } } @@ -883,36 +924,51 @@ void MenuDefinition::expandDocuments() item.setSubmenu(MenuDefinition(qt_("Hidden|H"))); Buffer * first = theBufferList().first(); - if (first) { - Buffer * b = first; - int vis = 1; - int invis = 1; - - // We cannot use a for loop as the buffer list cycles. - do { + if (!first) { + add(MenuItem(MenuItem::Info, qt_(""))); + return; + } + + int i = 0; + while (true) { + if (!guiApp->currentView()) + break; + GuiWorkArea * wa = guiApp->currentView()->workArea(i); + if (!wa) + break; + Buffer const & b = wa->bufferView().buffer(); + QString label = toqstr(b.fileName().displayName(20)); + if (!b.isClean()) + label += "*"; + if (i < 10) + label = QString::number(i) + ". " + label + '|' + QString::number(i); + add(MenuItem(MenuItem::Command, label, + FuncRequest(LFUN_BUFFER_SWITCH, b.absFileName()))); + ++i; + } + + + i = 0; + Buffer * b = first; + // We cannot use a for loop as the buffer list cycles. + do { + bool const shown = guiApp->currentView() + ? guiApp->currentView()->workArea(*b) : false; + if (!shown) { QString label = toqstr(b->fileName().displayName(20)); if (!b->isClean()) label += "*"; - bool const shown = guiApp->currentView() - ? guiApp->currentView()->workArea(*b) : false; - int ii = shown ? vis : invis; - if (ii < 10) - label = QString::number(ii) + ". " + label + '|' + QString::number(ii); - if (shown) { - add(MenuItem(MenuItem::Command, label, - FuncRequest(LFUN_BUFFER_SWITCH, b->absFileName()))); - ++vis; - } else { - item.submenu().add(MenuItem(MenuItem::Command, label, - FuncRequest(LFUN_BUFFER_SWITCH, b->absFileName()))); - ++invis; - } - b = theBufferList().next(b); - } while (b != first); - if (!item.submenu().empty()) - add(item); - } else - add(MenuItem(MenuItem::Info, qt_(""))); + if (i < 10) + label = QString::number(i) + ". " + label + '|' + QString::number(i); + item.submenu().add(MenuItem(MenuItem::Command, label, + FuncRequest(LFUN_BUFFER_SWITCH, b->absFileName()))); + ++i; + } + b = theBufferList().next(b); + } while (b != first); + + if (!item.submenu().empty()) + add(item); } @@ -936,14 +992,14 @@ void MenuDefinition::expandBookmarks() } -void MenuDefinition::expandFormats(MenuItem::Kind kind, Buffer const * buf) +void MenuDefinition::expandFormats(MenuItem::Kind const kind, Buffer const * buf) { if (!buf && kind != MenuItem::ImportFormats) return; typedef vector Formats; Formats formats; - FuncCode action; + FuncCode action = LFUN_NOACTION; switch (kind) { case MenuItem::ImportFormats: @@ -951,17 +1007,22 @@ void MenuDefinition::expandFormats(MenuItem::Kind kind, Buffer const * buf) action = LFUN_BUFFER_IMPORT; break; case MenuItem::ViewFormats: - formats = buf->exportableFormats(true); + formats = buf->params().exportableFormats(true); action = LFUN_BUFFER_VIEW; break; case MenuItem::UpdateFormats: - formats = buf->exportableFormats(true); + formats = buf->params().exportableFormats(true); action = LFUN_BUFFER_UPDATE; break; - default: - formats = buf->exportableFormats(false); + case MenuItem::ExportFormats: + formats = buf->params().exportableFormats(false); action = LFUN_BUFFER_EXPORT; + break; + default: + LASSERT(false, /* */); + return; } + sort(formats.begin(), formats.end(), &compareFormat); bool const view_update = (kind == MenuItem::ViewFormats @@ -969,8 +1030,8 @@ void MenuDefinition::expandFormats(MenuItem::Kind kind, Buffer const * buf) QString smenue; if (view_update) - smenue = (kind == MenuItem::ViewFormats ? - qt_("View (Other Formats)|F") + smenue = (kind == MenuItem::ViewFormats + ? qt_("View (Other Formats)|F") : qt_("Update (Other Formats)|p")); MenuItem item(MenuItem::Submenu, smenue); item.setSubmenu(MenuDefinition(smenue)); @@ -982,59 +1043,55 @@ void MenuDefinition::expandFormats(MenuItem::Kind kind, Buffer const * buf) continue; docstring lab = from_utf8((*fit)->prettyname()); - docstring scut = from_utf8((*fit)->shortcut()); + docstring const scut = from_utf8((*fit)->shortcut()); docstring const tmplab = lab; if (!scut.empty()) lab += char_type('|') + scut; - docstring lab_i18n = translateIfPossible(lab); + docstring const lab_i18n = translateIfPossible(lab); + docstring const shortcut = split(lab_i18n, lab, '|'); + bool const untranslated = (lab == lab_i18n); - QString const shortcut = toqstr(split(lab_i18n, lab, '|')); - QString label = toqstr(lab); - if (untranslated) - // this might happen if the shortcut - // has been redefined - label = toqstr(translateIfPossible(tmplab)); + docstring label = untranslated ? translateIfPossible(tmplab) : lab; switch (kind) { case MenuItem::ImportFormats: - label += "..."; + label += from_ascii("..."); break; case MenuItem::ViewFormats: case MenuItem::UpdateFormats: - if ((*fit)->name() == buf->getDefaultOutputFormat()) { - docstring lbl = (kind == MenuItem::ViewFormats ? - bformat(_("View [%1$s]|V"), qstring_to_ucs4(label)) - : bformat(_("Update [%1$s]|U"), qstring_to_ucs4(label))); - MenuItem w(MenuItem::Command, toqstr(lbl), - FuncRequest(action, (*fit)->name())); - add(w); + if ((*fit)->name() == buf->params().getDefaultOutputFormat()) { + docstring lbl = (kind == MenuItem::ViewFormats + ? bformat(_("View [%1$s]|V"), label) + : bformat(_("Update [%1$s]|U"), label)); + add(MenuItem(MenuItem::Command, toqstr(lbl), FuncRequest(action))); continue; } + // fall through case MenuItem::ExportFormats: - if (!(*fit)->documentFormat()) + if (!(*fit)->inExportMenu()) continue; break; default: - LASSERT(false, /**/); - break; + LASSERT(false, /* */); + continue; } - if (!shortcut.isEmpty()) + if (!shortcut.empty()) label += '|' + shortcut; if (view_update) { if (buf) - item.submenu().addWithStatusCheck(MenuItem(MenuItem::Command, label, - FuncRequest(action, (*fit)->name()))); + item.submenu().addWithStatusCheck(MenuItem(MenuItem::Command, + toqstr(label), FuncRequest(action, (*fit)->name()))); else - item.submenu().add(MenuItem(MenuItem::Command, label, + item.submenu().add(MenuItem(MenuItem::Command, toqstr(label), FuncRequest(action, (*fit)->name()))); } else { if (buf) - addWithStatusCheck(MenuItem(MenuItem::Command, label, + addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label), FuncRequest(action, (*fit)->name()))); else - add(MenuItem(MenuItem::Command, label, + add(MenuItem(MenuItem::Command, toqstr(label), FuncRequest(action, (*fit)->name()))); } } @@ -1051,11 +1108,26 @@ void MenuDefinition::expandFloatListInsert(Buffer const * buf) FloatList const & floats = buf->params().documentClass().floats(); FloatList::const_iterator cit = floats.begin(); FloatList::const_iterator end = floats.end(); + set seen; for (; cit != end; ++cit) { - addWithStatusCheck(MenuItem(MenuItem::Command, - qt_(cit->second.listName()), - FuncRequest(LFUN_FLOAT_LIST_INSERT, - cit->second.floattype()))); + if (!cit->second.usesFloatPkg()) { + // Different floats could declare the same ListCommand. We only + // want it on the list once, though. + string const & list_cmd = cit->second.listCommand(); + if (list_cmd.empty()) + // we do not know how to generate such a list + continue; + // This form of insert returns an iterator pointing to the newly + // inserted element OR the existing element with that value, and + // a bool indicating whether we inserted a new element. So we can + // see if one is there and insert it if not all at once. + pair::iterator, bool> ret = seen.insert(list_cmd); + if (!ret.second) + continue; + } + string const & list_name = cit->second.listName(); + addWithStatusCheck(MenuItem(MenuItem::Command, qt_(list_name), + FuncRequest(LFUN_FLOAT_LIST_INSERT, cit->second.floattype()))); } } @@ -1506,24 +1578,43 @@ void Menu::Impl::populate(QMenu & qMenu, MenuDefinition const & menu) } else { // we have a MenuItem::Command qMenu.addAction(new Action(view, QIcon(), label(*m), - m->func(), QString(), &qMenu)); + m->func(), m->tooltip(), &qMenu)); } } } +#if defined(Q_WS_WIN) && (QT_VERSION >= 0x040600) +class AlwaysMnemonicStyle : public QProxyStyle { +public: + int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0, + QStyleHintReturn *returnData = 0) const + { + if (hint == QStyle::SH_UnderlineShortcut) + return 1; + return QProxyStyle::styleHint(hint, opt, widget, returnData); + } +}; +#endif + ///////////////////////////////////////////////////////////////////// // Menu implementation ///////////////////////////////////////////////////////////////////// -Menu::Menu(GuiView * gv, QString const & name, bool top_level) +Menu::Menu(GuiView * gv, QString const & name, bool top_level, bool keyboard) : QMenu(gv), d(new Menu::Impl) { +#if defined(Q_WS_WIN) && (QT_VERSION >= 0x040600) + if (keyboard) + setStyle(new AlwaysMnemonicStyle); +#else + (void) keyboard; +#endif d->top_level_menu = top_level? new MenuDefinition : 0; d->view = gv; d->name = name; setTitle(name); if (d->top_level_menu) - connect(this, SIGNAL(aboutToShow()), this, SLOT(updateView())); + connect(this, SIGNAL(aboutToShow()), this, SLOT(updateView())); } @@ -1540,6 +1631,17 @@ void Menu::updateView() } +void Menu::clear() +{ + QList items = actions(); + for (int i = 0; i != items.size(); ++i) { + // QAction::menu() returns 0 if there's no submenu. + delete items.at(i)->menu(); + } + QMenu::clear(); +} + + ///////////////////////////////////////////////////////////////////// // Menus::Impl definition and implementation ///////////////////////////////////////////////////////////////////// @@ -1644,9 +1746,10 @@ void Menus::Impl::macxMenuBarInit(GuiView * view, QMenuBar * qmb) {LFUN_LYX_QUIT, "", "Quit LyX", QAction::QuitRole} }; const size_t num_entries = sizeof(entries) / sizeof(entries[0]); + const bool first_call = mac_special_menu_.size() == 0; // the special menu for Menus. Fill it up only once. - if (mac_special_menu_.size() == 0) { + if (first_call) { for (size_t i = 0 ; i < num_entries ; ++i) { FuncRequest const func(entries[i].action, from_utf8(entries[i].arg)); @@ -1654,17 +1757,26 @@ void Menus::Impl::macxMenuBarInit(GuiView * view, QMenuBar * qmb) entries[i].label, func)); } } - + // add the entries to a QMenu that will eventually be empty // and therefore invisible. QMenu * qMenu = qmb->addMenu("special"); MenuDefinition::const_iterator cit = mac_special_menu_.begin(); MenuDefinition::const_iterator end = mac_special_menu_.end(); for (size_t i = 0 ; cit != end ; ++cit, ++i) { +#if defined(QT_MAC_USE_COCOA) && (QT_MAC_USE_COCOA > 0) + if (first_call || entries[i].role != QAction::ApplicationSpecificRole) { + Action * action = new Action(view, QIcon(), cit->label(), + cit->func(), QString(), qMenu); + action->setMenuRole(entries[i].role); + qMenu->addAction(action); + } +#else Action * action = new Action(view, QIcon(), cit->label(), cit->func(), QString(), qMenu); action->setMenuRole(entries[i].role); qMenu->addAction(action); +#endif } } @@ -1851,7 +1963,7 @@ void Menus::read(Lexer & lex) enum { md_menu, md_menubar, - md_endmenuset, + md_endmenuset }; LexerKeyword menutags[] = { @@ -1975,14 +2087,35 @@ void Menus::updateMenu(Menu * qmenu) if (qmenu->d->name.isEmpty()) return; - if (!d->hasMenu(qmenu->d->name)) { + docstring identifier = qstring_to_ucs4(qmenu->d->name); + MenuDefinition fromLyxMenu(qmenu->d->name); + while (!identifier.empty()) { + docstring menu_name; + identifier = split(identifier, menu_name, ';'); + + if (!d->hasMenu(toqstr(menu_name))) { + LYXERR(Debug::GUI, "\tWARNING: non existing menu: " + << menu_name); + continue; + } + + MenuDefinition cat_menu = d->getMenu(toqstr(menu_name)); + //FIXME: 50 is a wild guess. We should take into account here + //the expansion of menu items, disabled optional items etc. + bool const in_sub_menu = fromLyxMenu.size() > 0 + && fromLyxMenu.size() + cat_menu.size() > 50 ; + if (in_sub_menu) + fromLyxMenu.catSub(menu_name); + else + fromLyxMenu.cat(cat_menu); + fromLyxMenu.add(MenuItem(MenuItem::Separator)); + } + + if (fromLyxMenu.empty()) { qmenu->addAction(qt_("No Action Defined!")); - LYXERR(Debug::GUI, "\tWARNING: non existing menu: " - << qmenu->d->name); return; } - MenuDefinition const & fromLyxMenu = d->getMenu(qmenu->d->name); BufferView * bv = 0; if (qmenu->d->view) bv = qmenu->d->view->currentBufferView(); @@ -1991,7 +2124,7 @@ void Menus::updateMenu(Menu * qmenu) } -Menu * Menus::menu(QString const & name, GuiView & view) +Menu * Menus::menu(QString const & name, GuiView & view, bool keyboard) { LYXERR(Debug::GUI, "Context menu requested: " << name); Menu * menu = d->name_map_[&view].value(name, 0); @@ -2000,7 +2133,7 @@ Menu * Menus::menu(QString const & name, GuiView & view) return 0; } - menu = new Menu(&view, name, true); + menu = new Menu(&view, name, true, keyboard); d->name_map_[&view][name] = menu; return menu; }