From: André Pönitz Date: Fri, 7 Mar 2008 00:21:23 +0000 (+0000) Subject: merge MenuBackend into frontend/Menus X-Git-Tag: 1.6.10~5821 X-Git-Url: https://git.lyx.org/gitweb/?a=commitdiff_plain;h=4b127828549eaf5624f31278689c06eea3f441b8;p=features.git merge MenuBackend into frontend/Menus git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@23527 a592a061-630c-0410-9148-cb99ea01b6c8 --- diff --git a/src/Makefile.am b/src/Makefile.am index 36d8cdd478..cf22a0b3b9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -63,8 +63,6 @@ lyx_SOURCES = \ $(ASPELL) $(PSPELL) $(ISPELL) SpellBase.cpp \ Box.cpp \ Box.h \ - MenuBackend.cpp \ - MenuBackend.h \ Dimension.cpp \ Dimension.h \ PrinterParams.cpp \ diff --git a/src/MenuBackend.cpp b/src/MenuBackend.cpp deleted file mode 100644 index 4409135c4d..0000000000 --- a/src/MenuBackend.cpp +++ /dev/null @@ -1,1069 +0,0 @@ -/** - * \file MenuBackend.cpp - * This file is part of LyX, the document processor. - * Licence details can be found in the file COPYING. - * - * \author Asger Alstrup - * \author Lars Gullik Bjønnes - * \author Jean-Marc Lasgouttes - * \author André Pönitz - * \author Dekel Tsur - * \author Martin Vermeer - * - * Full author contact details are available in file CREDITS. - */ - -#include - -#include "MenuBackend.h" - -#include "BranchList.h" -#include "Buffer.h" -#include "BufferList.h" -#include "BufferParams.h" -#include "Converter.h" -#include "CutAndPaste.h" -#include "Floating.h" -#include "FloatList.h" -#include "Format.h" -#include "KeyMap.h" -#include "Session.h" -#include "LyXAction.h" -#include "LyX.h" // for lastfiles -#include "LyXFunc.h" -#include "Lexer.h" -#include "Paragraph.h" -#include "TextClass.h" -#include "TocBackend.h" -#include "ToolbarBackend.h" - -#include "frontends/Application.h" - -#include "support/convert.h" -#include "support/debug.h" -#include "support/filetools.h" -#include "support/gettext.h" -#include "support/lstrings.h" - -#include - -#include -#include - -using namespace std; -using boost::bind; -using namespace lyx::support; - -namespace lyx { - -namespace { - -class MenuNamesEqual : public unary_function { -public: - MenuNamesEqual(docstring const & name) - : name_(name) {} - bool operator()(Menu const & menu) const { - return menu.name() == name_; - } -private: - docstring name_; -}; - -} // namespace anon - - -MenuItem::MenuItem(Kind kind) - : kind_(kind), optional_(false) -{} - - -MenuItem::MenuItem(Kind kind, docstring const & label, - docstring const & submenu, bool optional) - : kind_(kind), label_(label), - submenuname_(submenu), optional_(optional) -{ - BOOST_ASSERT(kind == Submenu); -} - - -MenuItem::MenuItem(Kind kind, docstring const & label, - FuncRequest const & func, bool optional) - : kind_(kind), label_(label), func_(func), optional_(optional) -{ - func_.origin = FuncRequest::MENU; -} - - -MenuItem::~MenuItem() -{} - - -void MenuItem::submenu(Menu * menu) -{ - submenu_.reset(menu); -} - - -docstring const MenuItem::label() const -{ - return token(label_, char_type('|'), 0); -} - - -docstring const MenuItem::shortcut() const -{ - return token(label_, char_type('|'), 1); -} - - -docstring const MenuItem::binding() const -{ - if (kind_ != Command) - return docstring(); - - // Get the keys bound to this action, but keep only the - // first one later - KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(func_); - - if (bindings.size()) - return bindings.begin()->print(KeySequence::ForGui); - - LYXERR(Debug::KBMAP, "No binding for " - << lyxaction.getActionName(func_.action) - << '(' << to_utf8(func_.argument()) << ')'); - return docstring(); -} - - -Menu & Menu::add(MenuItem const & i) -{ - items_.push_back(i); - return *this; -} - - -Menu & Menu::addWithStatusCheck(MenuItem const & i) -{ - switch (i.kind()) { - - case MenuItem::Command: { - FuncStatus status = lyx::getStatus(i.func()); - if (status.unknown() || (!status.enabled() && i.optional())) - break; - items_.push_back(i); - items_.back().status(status); - break; - } - - case MenuItem::Submenu: { - if (i.submenu()) { - bool enabled = false; - for (const_iterator cit = i.submenu()->begin(); - cit != i.submenu()->end(); ++cit) { - if ((cit->kind() == MenuItem::Command - || cit->kind() == MenuItem::Submenu) - && cit->status().enabled()) { - enabled = true; - break; - } - } - if (enabled || !i.optional()) { - items_.push_back(i); - items_.back().status().enabled(enabled); - } - } - else - items_.push_back(i); - break; - } - - case MenuItem::Separator: - if (!items_.empty() - && items_.back().kind() != MenuItem::Separator) - items_.push_back(i); - break; - - default: - items_.push_back(i); - } - - return *this; -} - - -Menu & Menu::read(Lexer & lex) -{ - enum Menutags { - md_item = 1, - md_branches, - md_documents, - md_bookmarks, - md_charstyles, - md_custom, - md_elements, - md_endmenu, - md_exportformats, - md_importformats, - md_lastfiles, - md_optitem, - md_optsubmenu, - md_separator, - md_submenu, - md_toc, - md_updateformats, - md_viewformats, - md_floatlistinsert, - md_floatinsert, - md_pasterecent, - md_toolbars, - md_last - }; - - struct keyword_item menutags[md_last - 1] = { - { "bookmarks", md_bookmarks }, - { "branches", md_branches }, - { "charstyles", md_charstyles }, - { "custom", md_custom }, - { "documents", md_documents }, - { "elements", md_elements }, - { "end", md_endmenu }, - { "exportformats", md_exportformats }, - { "floatinsert", md_floatinsert }, - { "floatlistinsert", md_floatlistinsert }, - { "importformats", md_importformats }, - { "item", md_item }, - { "lastfiles", md_lastfiles }, - { "optitem", md_optitem }, - { "optsubmenu", md_optsubmenu }, - { "pasterecent", md_pasterecent }, - { "separator", md_separator }, - { "submenu", md_submenu }, - { "toc", md_toc }, - { "toolbars", md_toolbars }, - { "updateformats", md_updateformats }, - { "viewformats", md_viewformats } - }; - - lex.pushTable(menutags, md_last - 1); - if (lyxerr.debugging(Debug::PARSER)) - lex.printTable(lyxerr); - - bool quit = false; - bool optional = false; - - while (lex.isOK() && !quit) { - switch (lex.lex()) { - case md_optitem: - optional = true; - // fallback to md_item - case md_item: { - lex.next(true); - docstring const name = translateIfPossible(lex.getDocString()); - lex.next(true); - string const command = lex.getString(); - FuncRequest func = lyxaction.lookupFunc(command); - add(MenuItem(MenuItem::Command, name, func, optional)); - optional = false; - break; - } - - case md_separator: - add(MenuItem(MenuItem::Separator)); - break; - - case md_lastfiles: - add(MenuItem(MenuItem::Lastfiles)); - break; - - case md_charstyles: - add(MenuItem(MenuItem::CharStyles)); - break; - - case md_custom: - add(MenuItem(MenuItem::Custom)); - break; - - case md_elements: - add(MenuItem(MenuItem::Elements)); - break; - - case md_documents: - add(MenuItem(MenuItem::Documents)); - break; - - case md_bookmarks: - add(MenuItem(MenuItem::Bookmarks)); - break; - - case md_toc: - add(MenuItem(MenuItem::Toc)); - break; - - case md_viewformats: - add(MenuItem(MenuItem::ViewFormats)); - break; - - case md_updateformats: - add(MenuItem(MenuItem::UpdateFormats)); - break; - - case md_exportformats: - add(MenuItem(MenuItem::ExportFormats)); - break; - - case md_importformats: - add(MenuItem(MenuItem::ImportFormats)); - break; - - case md_floatlistinsert: - add(MenuItem(MenuItem::FloatListInsert)); - break; - - case md_floatinsert: - add(MenuItem(MenuItem::FloatInsert)); - break; - - case md_pasterecent: - add(MenuItem(MenuItem::PasteRecent)); - break; - - case md_toolbars: - add(MenuItem(MenuItem::Toolbars)); - break; - - case md_branches: - add(MenuItem(MenuItem::Branches)); - break; - - case md_optsubmenu: - optional = true; - // fallback to md_submenu - case md_submenu: { - lex.next(true); - docstring const mlabel = translateIfPossible(lex.getDocString()); - lex.next(true); - docstring const mname = lex.getDocString(); - add(MenuItem(MenuItem::Submenu, mlabel, mname, - optional)); - optional = false; - break; - } - - case md_endmenu: - quit = true; - break; - - default: - lex.printError("Menu::read: " - "Unknown menu tag: `$$Token'"); - break; - } - } - lex.popTable(); - return *this; -} - - -MenuItem const & Menu::operator[](size_type i) const -{ - return items_[i]; -} - - -bool Menu::hasFunc(FuncRequest const & func) const -{ - return find_if(begin(), end(), - bind(equal_to(), - bind(&MenuItem::func, _1), - func)) != end(); -} - -void Menu::checkShortcuts() const -{ - // This is a quadratic algorithm, but we do not care because - // menus are short enough - for (const_iterator it1 = begin(); it1 != end(); ++it1) { - docstring shortcut = it1->shortcut(); - if (shortcut.empty()) - continue; - if (!contains(it1->label(), shortcut)) - lyxerr << "Menu warning: menu entry \"" - << to_utf8(it1->label()) - << "\" does not contain shortcut `" - << to_utf8(shortcut) << "'." << endl; - for (const_iterator it2 = begin(); it2 != it1 ; ++it2) { - if (!compare_ascii_no_case(it2->shortcut(), shortcut)) { - lyxerr << "Menu warning: menu entries " - << '"' << to_utf8(it1->fulllabel()) - << "\" and \"" << to_utf8(it2->fulllabel()) - << "\" share the same shortcut." - << endl; - } - } - } -} - - -bool Menu::searchMenu(FuncRequest const & func, vector & names) const -{ - const_iterator m = begin(); - const_iterator m_end = end(); - for (; m != m_end; ++m) { - if (m->kind() == MenuItem::Command && m->func() == func) { - names.push_back(m->label()); - return true; - } - if (m->kind() == MenuItem::Submenu) { - names.push_back(m->label()); - Menu submenu = theApp()->menuBackend().getMenu(m->submenuname()); - if (submenu.searchMenu(func, names)) - return true; - names.pop_back(); - } - } - return false; -} - - -void MenuBackend::specialMenu(Menu const & menu) -{ - specialmenu_ = menu; -} - - -namespace { - -class compare_format { -public: - bool operator()(Format const * p1, Format const * p2) { - return *p1 < *p2; - } -}; - -docstring const limit_string_length(docstring const & str) -{ - docstring::size_type const max_item_length = 45; - - if (str.size() > max_item_length) - return str.substr(0, max_item_length - 3) + "..."; - else - return str; -} - - -void expandLastfiles(Menu & tomenu) -{ - lyx::LastFilesSection::LastFiles const & lf = LyX::cref().session().lastFiles().lastFiles(); - lyx::LastFilesSection::LastFiles::const_iterator lfit = lf.begin(); - - int ii = 1; - - for (; lfit != lf.end() && ii < 10; ++lfit, ++ii) { - string const file = lfit->absFilename(); - docstring const label = convert(ii) + ". " - + makeDisplayPath(file, 30) - + char_type('|') + convert(ii); - tomenu.add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_FILE_OPEN, file))); - } -} - - -void expandDocuments(Menu & tomenu) -{ - Buffer * first = theBufferList().first(); - if (first) { - Buffer * b = first; - int ii = 1; - - // We cannot use a for loop as the buffer list cycles. - do { - docstring label = b->fileName().displayName(20); - if (!b->isClean()) - label = label + "*"; - if (ii < 10) - label = convert(ii) + ". " + label + '|' + convert(ii); - tomenu.add(MenuItem(MenuItem::Command, label, - FuncRequest(LFUN_BUFFER_SWITCH, b->absFileName()))); - - b = theBufferList().next(b); - ++ii; - } while (b != first); - } else { - tomenu.add(MenuItem(MenuItem::Command, _("No Documents Open!"), - FuncRequest(LFUN_NOACTION))); - } -} - - -void expandBookmarks(Menu & tomenu) -{ - lyx::BookmarksSection const & bm = LyX::cref().session().bookmarks(); - - for (size_t i = 1; i <= bm.size(); ++i) { - if (bm.isValid(i)) { - docstring const label = convert(i) + ". " - + makeDisplayPath(bm.bookmark(i).filename.absFilename(), 20) - + char_type('|') + convert(i); - tomenu.add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_BOOKMARK_GOTO, - convert(i)))); - } - } -} - - -void expandFormats(MenuItem::Kind kind, Menu & tomenu, Buffer const * buf) -{ - if (!buf && kind != MenuItem::ImportFormats) { - tomenu.add(MenuItem(MenuItem::Command, - _("No Document Open!"), - FuncRequest(LFUN_NOACTION))); - return; - } - - typedef vector Formats; - Formats formats; - kb_action action; - - switch (kind) { - case MenuItem::ImportFormats: - formats = theConverters().importableFormats(); - action = LFUN_BUFFER_IMPORT; - break; - case MenuItem::ViewFormats: - formats = buf->exportableFormats(true); - action = LFUN_BUFFER_VIEW; - break; - case MenuItem::UpdateFormats: - formats = buf->exportableFormats(true); - action = LFUN_BUFFER_UPDATE; - break; - default: - formats = buf->exportableFormats(false); - action = LFUN_BUFFER_EXPORT; - } - sort(formats.begin(), formats.end(), compare_format()); - - Formats::const_iterator fit = formats.begin(); - Formats::const_iterator end = formats.end(); - for (; fit != end ; ++fit) { - if ((*fit)->dummy()) - continue; - docstring label = from_utf8((*fit)->prettyname()); - docstring const shortcut = from_utf8((*fit)->shortcut()); - - switch (kind) { - case MenuItem::ImportFormats: - // FIXME: This is a hack, we should rather solve - // FIXME: bug 2488 instead. - if ((*fit)->name() == "text") - label = _("Plain Text"); - else if ((*fit)->name() == "textparagraph") - label = _("Plain Text, Join Lines"); - label += "..."; - break; - case MenuItem::ViewFormats: - case MenuItem::ExportFormats: - case MenuItem::UpdateFormats: - if (!(*fit)->documentFormat()) - continue; - break; - default: - BOOST_ASSERT(false); - break; - } - // FIXME: if we had proper support for translating the - // format names defined in configure.py, there would - // not be a need to check whether the shortcut is - // correct. If we add it uncondiitonally, it would - // create useless warnings on bad shortcuts - if (!shortcut.empty() && contains(label, shortcut)) - label += char_type('|') + shortcut; - - if (buf) - tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label, - FuncRequest(action, (*fit)->name()))); - else - tomenu.add(MenuItem(MenuItem::Command, label, - FuncRequest(action, (*fit)->name()))); - } -} - - -void expandFloatListInsert(Menu & tomenu, Buffer const * buf) -{ - if (!buf) { - tomenu.add(MenuItem(MenuItem::Command, - _("No Document Open!"), - FuncRequest(LFUN_NOACTION))); - return; - } - - FloatList const & floats = buf->params().documentClass().floats(); - FloatList::const_iterator cit = floats.begin(); - FloatList::const_iterator end = floats.end(); - for (; cit != end; ++cit) { - tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, - _(cit->second.listName()), - FuncRequest(LFUN_FLOAT_LIST, - cit->second.type()))); - } -} - - -void expandFloatInsert(Menu & tomenu, Buffer const * buf) -{ - if (!buf) { - tomenu.add(MenuItem(MenuItem::Command, - _("No Document Open!"), - FuncRequest(LFUN_NOACTION))); - return; - } - - FloatList const & floats = buf->params().documentClass().floats(); - FloatList::const_iterator cit = floats.begin(); - FloatList::const_iterator end = floats.end(); - for (; cit != end; ++cit) { - // normal float - docstring const label = _(cit->second.name()); - tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label, - FuncRequest(LFUN_FLOAT_INSERT, - cit->second.type()))); - } -} - - -void expandFlexInsert(Menu & tomenu, Buffer const * buf, string s) -{ - if (!buf) { - tomenu.add(MenuItem(MenuItem::Command, - _("No Document Open!"), - FuncRequest(LFUN_NOACTION))); - return; - } - TextClass::InsetLayouts const & insetLayouts = - buf->params().documentClass().insetLayouts(); - TextClass::InsetLayouts::const_iterator cit = insetLayouts.begin(); - TextClass::InsetLayouts::const_iterator end = insetLayouts.end(); - for (; cit != end; ++cit) { - docstring const label = cit->first; - if (cit->second.lyxtype() == s) - tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, - label, FuncRequest(LFUN_FLEX_INSERT, - label))); - } -} - - -Menu::size_type const max_number_of_items = 25; - -void expandToc2(Menu & tomenu, - Toc const & toc_list, - Toc::size_type from, - Toc::size_type to, int depth) -{ - int shortcut_count = 0; - - // check whether depth is smaller than the smallest depth in toc. - int min_depth = 1000; - for (Toc::size_type i = from; i < to; ++i) - min_depth = min(min_depth, toc_list[i].depth()); - if (min_depth > depth) - depth = min_depth; - - - if (to - from <= max_number_of_items) { - for (Toc::size_type i = from; i < to; ++i) { - docstring label(4 * max(0, toc_list[i].depth() - depth), char_type(' ')); - label += limit_string_length(toc_list[i].str()); - if (toc_list[i].depth() == depth - && shortcut_count < 9) { - if (label.find(convert(shortcut_count + 1)) != docstring::npos) - label += char_type('|') + convert(++shortcut_count); - } - tomenu.add(MenuItem(MenuItem::Command, label, - FuncRequest(toc_list[i].action()))); - } - } else { - Toc::size_type pos = from; - while (pos < to) { - Toc::size_type new_pos = pos + 1; - while (new_pos < to && - toc_list[new_pos].depth() > depth) - ++new_pos; - - docstring label(4 * max(0, toc_list[pos].depth() - depth), ' '); - label += limit_string_length(toc_list[pos].str()); - if (toc_list[pos].depth() == depth && - shortcut_count < 9) { - if (label.find(convert(shortcut_count + 1)) != docstring::npos) - label += char_type('|') + convert(++shortcut_count); - } - if (new_pos == pos + 1) { - tomenu.add(MenuItem(MenuItem::Command, - label, FuncRequest(toc_list[pos].action()))); - } else { - MenuItem item(MenuItem::Submenu, label); - item.submenu(new Menu); - expandToc2(*item.submenu(), - toc_list, pos, new_pos, depth + 1); - tomenu.add(item); - } - pos = new_pos; - } - } -} - - -void expandToc(Menu & tomenu, Buffer const * buf) -{ - // To make things very cleanly, we would have to pass buf to - // all MenuItem constructors and to expandToc2. However, we - // know that all the entries in a TOC will be have status_ == - // OK, so we avoid this unnecessary overhead (JMarc) - - if (!buf) { - tomenu.add(MenuItem(MenuItem::Command, - _("No Document Open!"), - FuncRequest(LFUN_NOACTION))); - return; - } - - Buffer* cbuf = const_cast(buf); - cbuf->tocBackend().update(); - cbuf->structureChanged(); - - // Add an entry for the master doc if this is a child doc - Buffer const * const master = buf->masterBuffer(); - if (buf != master) { - ParIterator const pit = par_iterator_begin(master->inset()); - string const arg = convert(pit->id()); - FuncRequest f(LFUN_PARAGRAPH_GOTO, arg); - tomenu.add(MenuItem(MenuItem::Command, _("Master Document"), f)); - } - - FloatList const & floatlist = buf->params().documentClass().floats(); - TocList const & toc_list = buf->tocBackend().tocs(); - TocList::const_iterator cit = toc_list.begin(); - TocList::const_iterator end = toc_list.end(); - for (; cit != end; ++cit) { - // Handle this later - if (cit->first == "tableofcontents") - continue; - - // All the rest is for floats - auto_ptr menu(new Menu); - TocIterator ccit = cit->second.begin(); - TocIterator eend = cit->second.end(); - for (; ccit != eend; ++ccit) { - docstring const label = limit_string_length(ccit->str()); - menu->add(MenuItem(MenuItem::Command, - label, - FuncRequest(ccit->action()))); - } - string const & floatName = floatlist.getType(cit->first).listName(); - docstring label; - if (!floatName.empty()) - label = _(floatName); - // BUG3633: listings is not a proper float so its name - // is not shown in floatlist. - else if (cit->first == "equation") - label = _("List of Equations"); - else if (cit->first == "index") - label = _("List of Indexes"); - else if (cit->first == "listing") - label = _("List of Listings"); - else if (cit->first == "marginalnote") - label = _("List of Marginal notes"); - else if (cit->first == "note") - label = _("List of Notes"); - else if (cit->first == "footnote") - label = _("List of Foot notes"); - else if (cit->first == "label") - label = _("Labels and References"); - else if (cit->first == "citation") - label = _("List of Citations"); - // this should not happen now, but if something else like - // listings is added later, this can avoid an empty menu name. - else - label = _("Other floats"); - MenuItem item(MenuItem::Submenu, label); - item.submenu(menu.release()); - tomenu.add(item); - } - - // Handle normal TOC - cit = toc_list.find("tableofcontents"); - if (cit == end) { - tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, - _("No Table of contents"), - FuncRequest())); - } else { - expandToc2(tomenu, cit->second, 0, cit->second.size(), 0); - } -} - - -void expandPasteRecent(Menu & tomenu) -{ - vector const sel = cap::availableSelections(); - - vector::const_iterator cit = sel.begin(); - vector::const_iterator end = sel.end(); - - for (unsigned int index = 0; cit != end; ++cit, ++index) { - tomenu.add(MenuItem(MenuItem::Command, *cit, - FuncRequest(LFUN_PASTE, convert(index)))); - } -} - - -void expandToolbars(Menu & tomenu) -{ - // - // extracts the toolbars from the backend - ToolbarBackend::Toolbars::const_iterator cit = toolbarbackend.begin(); - ToolbarBackend::Toolbars::const_iterator end = toolbarbackend.end(); - - for (; cit != end; ++cit) { - docstring label = _(cit->gui_name); - // frontends are not supposed to turn on/off toolbars, - // if they cannot update ToolbarBackend::flags. That - // is to say, ToolbarsBackend::flags should reflect - // the true state of toolbars. - // - // menu is displayed as - // on/off review - // and - // review (auto) - // in the case of auto. - if (cit->flags & ToolbarInfo::AUTO) - label += _(" (auto)"); - tomenu.add(MenuItem(MenuItem::Command, label, - FuncRequest(LFUN_TOOLBAR_TOGGLE, cit->name + " allowauto"))); - } -} - - -void expandBranches(Menu & tomenu, Buffer const * buf) -{ - if (!buf) { - tomenu.add(MenuItem(MenuItem::Command, - _("No Document Open!"), - FuncRequest(LFUN_NOACTION))); - return; - } - - BufferParams const & params = buf->masterBuffer()->params(); - if (params.branchlist().empty()) { - tomenu.add(MenuItem(MenuItem::Command, - _("No Branch in Document!"), - FuncRequest(LFUN_NOACTION))); - return; - } - - BranchList::const_iterator cit = params.branchlist().begin(); - BranchList::const_iterator end = params.branchlist().end(); - - for (int ii = 1; cit != end; ++cit, ++ii) { - docstring label = cit->getBranch(); - if (ii < 10) - label = convert(ii) + ". " + label + char_type('|') + convert(ii); - tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label, - FuncRequest(LFUN_BRANCH_INSERT, - cit->getBranch()))); - } -} - - -} // namespace anon - - -void MenuBackend::expand(Menu const & frommenu, Menu & tomenu, - Buffer const * buf) const -{ - if (!tomenu.empty()) - tomenu.clear(); - - for (Menu::const_iterator cit = frommenu.begin(); - cit != frommenu.end() ; ++cit) { - switch (cit->kind()) { - case MenuItem::Lastfiles: - expandLastfiles(tomenu); - break; - - case MenuItem::Documents: - expandDocuments(tomenu); - break; - - case MenuItem::Bookmarks: - expandBookmarks(tomenu); - break; - - case MenuItem::ImportFormats: - case MenuItem::ViewFormats: - case MenuItem::UpdateFormats: - case MenuItem::ExportFormats: - expandFormats(cit->kind(), tomenu, buf); - break; - - case MenuItem::CharStyles: - expandFlexInsert(tomenu, buf, "charstyle"); - break; - - case MenuItem::Custom: - expandFlexInsert(tomenu, buf, "custom"); - break; - - case MenuItem::Elements: - expandFlexInsert(tomenu, buf, "element"); - break; - - case MenuItem::FloatListInsert: - expandFloatListInsert(tomenu, buf); - break; - - case MenuItem::FloatInsert: - expandFloatInsert(tomenu, buf); - break; - - case MenuItem::PasteRecent: - expandPasteRecent(tomenu); - break; - - case MenuItem::Toolbars: - expandToolbars(tomenu); - break; - - case MenuItem::Branches: - expandBranches(tomenu, buf); - break; - - case MenuItem::Toc: - expandToc(tomenu, buf); - break; - - case MenuItem::Submenu: { - MenuItem item(*cit); - item.submenu(new Menu(cit->submenuname())); - expand(getMenu(cit->submenuname()), - *item.submenu(), buf); - tomenu.addWithStatusCheck(item); - } - break; - - case MenuItem::Separator: - tomenu.addWithStatusCheck(*cit); - break; - - case MenuItem::Command: - if (!specialmenu_.hasFunc(cit->func())) - tomenu.addWithStatusCheck(*cit); - } - } - - // we do not want the menu to end with a separator - if (!tomenu.empty() - && tomenu.items_.back().kind() == MenuItem::Separator) - tomenu.items_.pop_back(); - - // Check whether the shortcuts are unique - tomenu.checkShortcuts(); -} - - -void MenuBackend::read(Lexer & lex) -{ - enum Menutags { - md_menu = 1, - md_menubar, - md_endmenuset, - md_last - }; - - struct keyword_item menutags[md_last - 1] = { - { "end", md_endmenuset }, - { "menu", md_menu }, - { "menubar", md_menubar } - }; - - //consistency check - if (compare_ascii_no_case(lex.getString(), "menuset")) { - lyxerr << "Menubackend::read: ERROR wrong token:`" - << lex.getString() << '\'' << endl; - } - - lex.pushTable(menutags, md_last - 1); - if (lyxerr.debugging(Debug::PARSER)) - lex.printTable(lyxerr); - - bool quit = false; - - while (lex.isOK() && !quit) { - switch (lex.lex()) { - case md_menubar: - menubar_.read(lex); - break; - case md_menu: { - lex.next(true); - docstring const name = lex.getDocString(); - if (hasMenu(name)) { - getMenu(name).read(lex); - } else { - Menu menu(name); - menu.read(lex); - add(menu); - } - break; - } - case md_endmenuset: - quit = true; - break; - default: - lex.printError("menubackend::read: " - "Unknown menu tag: `$$Token'"); - break; - } - } - lex.popTable(); -} - - -void MenuBackend::add(Menu const & menu) -{ - menulist_.push_back(menu); -} - - -bool MenuBackend::hasMenu(docstring const & name) const -{ - return find_if(begin(), end(), MenuNamesEqual(name)) != end(); -} - - -Menu const & MenuBackend::getMenu(docstring const & name) const -{ - const_iterator cit = find_if(begin(), end(), MenuNamesEqual(name)); - if (cit == end()) - lyxerr << "No submenu named " << to_utf8(name) << endl; - BOOST_ASSERT(cit != end()); - return (*cit); -} - - -Menu & MenuBackend::getMenu(docstring const & name) -{ - iterator it = find_if(begin(), end(), MenuNamesEqual(name)); - if (it == end()) - lyxerr << "No submenu named " << to_utf8(name) << endl; - BOOST_ASSERT(it != end()); - return (*it); -} - - -Menu const & MenuBackend::getMenubar() const -{ - return menubar_; -} - - -} // namespace lyx diff --git a/src/MenuBackend.h b/src/MenuBackend.h deleted file mode 100644 index 65c2d97398..0000000000 --- a/src/MenuBackend.h +++ /dev/null @@ -1,261 +0,0 @@ -// -*- C++ -*- -/** - * \file MenuBackend.h - * This file is part of LyX, the document processor. - * Licence details can be found in the file COPYING. - * - * \author Lars Gullik Bjønnes - * \author Jean-Marc Lasgouttes - * - * Full author contact details are available in file CREDITS. - */ - -#ifndef MENUBACKEND_H -#define MENUBACKEND_H - -#include "FuncStatus.h" -#include "FuncRequest.h" - -#include - -#include - - -namespace lyx { - -class Lexer; -class Buffer; -class Menu; - -/// -class MenuItem { -public: - /// The type of elements that can be in a menu - enum Kind { - /// - Command, - /// - Submenu, - /// - Separator, - /** This is the list of last opened file, - typically for the File menu. */ - Lastfiles, - /** This is the list of opened Documents, - typically for the Documents menu. */ - Documents, - /** This is the bookmarks */ - Bookmarks, - /// - Toc, - /** This is a list of viewable formats - typically for the File->View menu. */ - ViewFormats, - /** This is a list of updatable formats - typically for the File->Update menu. */ - UpdateFormats, - /** This is a list of exportable formats - typically for the File->Export menu. */ - ExportFormats, - /** This is a list of importable formats - typically for the File->Export menu. */ - ImportFormats, - /** This is the list of elements available - * for insertion into document. */ - CharStyles, - /** This is the list of user-configurable - insets to insert into document */ - Custom, - /** This is the list of XML elements to - insert into the document */ - Elements, - /** This is the list of floats that we can - insert a list for. */ - FloatListInsert, - /** This is the list of floats that we can - insert. */ - FloatInsert, - /** This is the list of selections that can - be pasted. */ - PasteRecent, - /** toolbars */ - Toolbars, - /** Available branches in document */ - Branches - }; - - explicit MenuItem(Kind kind); - - MenuItem(Kind kind, - docstring const & label, - docstring const & submenu = docstring(), - bool optional = false); - - MenuItem(Kind kind, - docstring const & label, - FuncRequest const & func, - bool optional = false); - - /// This one is just to please boost::shared_ptr<> - ~MenuItem(); - /// The label of a given menuitem - docstring const label() const; - /// The keyboard shortcut (usually underlined in the entry) - docstring const shortcut() const; - /// The complete label, with label and shortcut separated by a '|' - docstring const fulllabel() const { return label_;} - /// The kind of entry - Kind kind() const { return kind_; } - /// the action (if relevant) - FuncRequest const & func() const { return func_; } - /// returns true if the entry should be ommited when disabled - bool optional() const { return optional_; } - /// returns the status of the lfun associated with this entry - FuncStatus const & status() const { return status_; } - /// returns the status of the lfun associated with this entry - FuncStatus & status() { return status_; } - /// returns the status of the lfun associated with this entry - void status(FuncStatus const & status) { status_ = status; } - ///returns the binding associated to this action. - docstring const binding() const; - /// the description of the submenu (if relevant) - docstring const & submenuname() const { return submenuname_; } - /// set the description of the submenu - void submenuname(docstring const & name) { submenuname_ = name; } - /// - Menu * submenu() const { return submenu_.get(); } - /// - void submenu(Menu * menu); - -private: - //friend class MenuBackend; - /// - Kind kind_; - /// - docstring label_; - /// - FuncRequest func_; - /// - docstring submenuname_; - /// - bool optional_; - /// - FuncStatus status_; - /// - boost::shared_ptr submenu_; -}; - - -/// -class Menu { -public: - /// - typedef std::vector ItemList; - /// - typedef ItemList::const_iterator const_iterator; - /// - typedef ItemList::size_type size_type; - /// - explicit Menu(docstring const & name = docstring()) : name_(name) {} - /// Add the menu item unconditionally - Menu & add(MenuItem const &); - /// Checks the associated FuncRequest status before adding the - /// menu item. - Menu & addWithStatusCheck(MenuItem const &); - /// - Menu & read(Lexer &); - /// - docstring const & name() const { return name_; } - /// - bool empty() const { return items_.empty(); } - /// Clear the menu content. - void clear() { items_.clear(); } - /// - ItemList::size_type size() const { return items_.size(); } - /// - MenuItem const & operator[](size_type) const; - /// - bool hasFunc(FuncRequest const &) const; - /// - const_iterator begin() const { return items_.begin(); } - /// - const_iterator end() const { return items_.end(); } - - // Check whether the menu shortcuts are unique - void checkShortcuts() const; - - // search for func in this menu iteratively, and put menu - // names in a stack. - bool searchMenu(FuncRequest const & func, std::vector & names) - const; - -private: - friend class MenuBackend; - /// - ItemList items_; - /// - docstring name_; -}; - - -/// -class MenuBackend { -public: - /// - typedef std::vector MenuList; - /// - typedef MenuList::const_iterator const_iterator; - /// - typedef MenuList::iterator iterator; - /// - MenuBackend() {} - /// - void read(Lexer &); - /// - void add(Menu const &); - /// - bool hasMenu(docstring const &) const; - /// - Menu & getMenu(docstring const &); - /// - Menu const & getMenu(docstring const &) const; - /// - Menu const & getMenubar() const; - /// - bool empty() const { return menulist_.empty(); } - /** This defines a menu whose entries list the FuncRequests - that will be removed by expand() in other menus. This is - used by the Qt/Mac code - */ - void specialMenu(Menu const &); - /// - Menu const & specialMenu() { return specialmenu_; } - - /// Expands some special entries of the menu - /** The entries with the following kind are expanded to a - sequence of Command MenuItems: Lastfiles, Documents, - ViewFormats, ExportFormats, UpdateFormats, Branches - */ - void expand(Menu const & frommenu, Menu & tomenu, - Buffer const *) const; - /// - const_iterator begin() const { return menulist_.begin(); } - /// - iterator begin() { return menulist_.begin(); } - /// - const_iterator end() const { return menulist_.end(); } - /// - iterator end() { return menulist_.end(); } - -private: - /// - MenuList menulist_; - /// - Menu menubar_; - /// - Menu specialmenu_; -}; - -} // namespace lyx - -#endif // MENUBACKEND_H diff --git a/src/frontends/Application.h b/src/frontends/Application.h index ebb98e9aff..0cc2ed00f6 100644 --- a/src/frontends/Application.h +++ b/src/frontends/Application.h @@ -29,7 +29,6 @@ class FuncRequest; class FuncStatus; class Inset; class Lexer; -class MenuBackend; struct RGBColor; namespace frontend { @@ -233,10 +232,6 @@ public: */ virtual void unregisterSocketCallback(int fd) = 0; - /// - virtual MenuBackend const & menuBackend() const = 0; - virtual MenuBackend & menuBackend() = 0; - virtual bool searchMenu(FuncRequest const & func, std::vector & names) const = 0; }; diff --git a/src/frontends/qt4/GuiApplication.cpp b/src/frontends/qt4/GuiApplication.cpp index 0beb0f7c24..5c9d04894c 100644 --- a/src/frontends/qt4/GuiApplication.cpp +++ b/src/frontends/qt4/GuiApplication.cpp @@ -651,14 +651,14 @@ Buffer const * GuiApplication::updateInset(Inset const * inset) const void GuiApplication::readMenus(Lexer & lex) { - menuBackend().read(lex); + menus().read(lex); } bool GuiApplication::searchMenu(FuncRequest const & func, vector & names) const { - return menuBackend().getMenubar().searchMenu(func, names); + return menus().getMenubar().searchMenu(func, names); } diff --git a/src/frontends/qt4/GuiApplication.h b/src/frontends/qt4/GuiApplication.h index 5460de3bc8..f9a770a3ab 100644 --- a/src/frontends/qt4/GuiApplication.h +++ b/src/frontends/qt4/GuiApplication.h @@ -65,8 +65,6 @@ public: virtual Clipboard & clipboard(); virtual Selection & selection(); virtual FontLoader & fontLoader() { return font_loader_; } - MenuBackend const & menuBackend() const { return menus_; } - MenuBackend & menuBackend() { return menus_; } virtual int exec(); virtual void exit(int status); virtual bool event(QEvent * e); diff --git a/src/frontends/qt4/GuiPopupMenu.cpp b/src/frontends/qt4/GuiPopupMenu.cpp index cdbcf34bfa..163be877fd 100644 --- a/src/frontends/qt4/GuiPopupMenu.cpp +++ b/src/frontends/qt4/GuiPopupMenu.cpp @@ -11,18 +11,16 @@ #include -#include "GuiView.h" +#include "GuiPopupMenu.h" #include "Action.h" #include "GuiApplication.h" -#include "GuiPopupMenu.h" +#include "GuiView.h" #include "qt_helpers.h" #include "LyXFunc.h" -#include "MenuBackend.h" #include "support/debug.h" -#include "support/lstrings.h" namespace lyx { namespace frontend { @@ -31,7 +29,7 @@ GuiPopupMenu::GuiPopupMenu(GuiView * owner, MenuItem const & mi, bool topLevelMenu) : QMenu(owner), owner_(owner) { - name_ = toqstr(mi.submenuname()); + name_ = mi.submenuname(); setTitle(label(mi)); @@ -53,13 +51,13 @@ void GuiPopupMenu::updateView() // Here, We make sure that theLyXFunc points to the correct LyXView. theLyXFunc().setLyXView(owner_); - MenuBackend const & menubackend = guiApp->menuBackend(); - Menu const & fromLyxMenu = menubackend.getMenu(qstring_to_ucs4(name_)); - menubackend.expand(fromLyxMenu, topLevelMenu_, owner_->buffer()); + Menus const & menus = guiApp->menus(); + Menu const & fromLyxMenu = menus.getMenu(name_); + menus.expand(fromLyxMenu, topLevelMenu_, owner_->buffer()); - if (!menubackend.hasMenu(topLevelMenu_.name())) { + if (!menus.hasMenu(topLevelMenu_.name())) { LYXERR(Debug::GUI, "\tWARNING: menu seems empty" - << to_utf8(topLevelMenu_.name())); + << fromqstr(topLevelMenu_.name())); } populate(this, &topLevelMenu_); } @@ -67,9 +65,9 @@ void GuiPopupMenu::updateView() void GuiPopupMenu::populate(QMenu * qMenu, Menu * menu) { - LYXERR(Debug::GUI, "populating menu " << to_utf8(menu->name())); + LYXERR(Debug::GUI, "populating menu " << fromqstr(menu->name())); if (menu->size() == 0) { - LYXERR(Debug::GUI, "\tERROR: empty menu " << to_utf8(menu->name())); + LYXERR(Debug::GUI, "\tERROR: empty menu " << fromqstr(menu->name())); return; } LYXERR(Debug::GUI, " ***** menu entries " << menu->size()); @@ -94,9 +92,9 @@ void GuiPopupMenu::populate(QMenu * qMenu, Menu * menu) } else { // we have a MenuItem::Command LYXERR(Debug::GUI, "creating Menu Item " - << to_utf8(m->label())); + << fromqstr(m->label())); - Action * action = new Action(*(owner_), + Action * action = new Action(*owner_, QIcon(), label(*m), m->func(), QString()); qMenu->addAction(action); } @@ -106,21 +104,22 @@ void GuiPopupMenu::populate(QMenu * qMenu, Menu * menu) QString GuiPopupMenu::label(MenuItem const & mi) const { - docstring label = support::subst(mi.label(), - from_ascii("&"), from_ascii("&&")); - - docstring const shortcut = mi.shortcut(); - if (!shortcut.empty()) { - size_t pos = label.find(shortcut); - if (pos != docstring::npos) - label.insert(pos, 1, char_type('&')); + QString label = mi.label(); + label.replace("&", "&&"); + + QString shortcut = mi.shortcut(); + if (!shortcut.isEmpty()) { + int pos = label.indexOf(shortcut); + if (pos != -1) + //label.insert(pos, 1, char_type('&')); + label.replace(pos, 0, "&"); } - docstring const binding = mi.binding(); - if (!binding.empty()) + QString const binding = mi.binding(); + if (!binding.isEmpty()) label += '\t' + binding; - return toqstr(label); + return label; } diff --git a/src/frontends/qt4/GuiPopupMenu.h b/src/frontends/qt4/GuiPopupMenu.h index cf50f3b71c..e17fd632a6 100644 --- a/src/frontends/qt4/GuiPopupMenu.h +++ b/src/frontends/qt4/GuiPopupMenu.h @@ -12,7 +12,7 @@ #ifndef GUIPOPUPMENU_H #define GUIPOPUPMENU_H -#include "MenuBackend.h" +#include "Menus.h" #include diff --git a/src/frontends/qt4/GuiWorkArea.cpp b/src/frontends/qt4/GuiWorkArea.cpp index 60a2de6265..81aad47b29 100644 --- a/src/frontends/qt4/GuiWorkArea.cpp +++ b/src/frontends/qt4/GuiWorkArea.cpp @@ -23,7 +23,6 @@ #include "GuiApplication.h" #include "GuiKeySymbol.h" #include "GuiPainter.h" -#include "GuiPopupMenu.h" #include "GuiView.h" #include "KeySymbol.h" #include "Language.h" @@ -51,6 +50,7 @@ #include #endif #include +#include #include #include #include diff --git a/src/frontends/qt4/Menus.cpp b/src/frontends/qt4/Menus.cpp index fd4884f54b..b3d3b872ee 100644 --- a/src/frontends/qt4/Menus.cpp +++ b/src/frontends/qt4/Menus.cpp @@ -4,6 +4,12 @@ * Licence details can be found in the file COPYING. * * \author John Levon + * \author Asger Alstrup + * \author Lars Gullik Bjønnes + * \author Jean-Marc Lasgouttes + * \author André Pönitz + * \author Dekel Tsur + * \author Martin Vermeer * * Full author contact details are available in file CREDITS. */ @@ -17,17 +23,65 @@ #include "GuiPopupMenu.h" #include "GuiView.h" -#include "qt_helpers.h" +#include "BranchList.h" +#include "Buffer.h" +#include "BufferList.h" +#include "BufferParams.h" +#include "Converter.h" +#include "CutAndPaste.h" +#include "Floating.h" +#include "FloatList.h" +#include "Format.h" +#include "KeyMap.h" +#include "Session.h" +#include "LyXAction.h" +#include "LyX.h" // for lastfiles +#include "LyXFunc.h" +#include "Lexer.h" +#include "Paragraph.h" +#include "TextClass.h" +#include "TocBackend.h" +#include "ToolbarBackend.h" + +#include "frontends/Application.h" +#include "support/convert.h" #include "support/debug.h" +#include "support/filetools.h" +#include "support/gettext.h" +#include "support/lstrings.h" + +#include + +#include "qt_helpers.h" #include #include +#include +#include + +using namespace std; +using boost::bind; +using namespace lyx::support; + namespace lyx { namespace frontend { +namespace { + +class MenuNamesEqual +{ +public: + MenuNamesEqual(QString const & name) : name_(name) {} + bool operator()(Menu const & menu) const { return menu.name() == name_; } +private: + QString name_; +}; + +} // namespace anon + // MacOSX specific stuff is at the end. void Menus::fillMenuBar(GuiView * view) @@ -40,11 +94,11 @@ void Menus::fillMenuBar(GuiView * view) macxMenuBarInit(view); #endif - LYXERR(Debug::GUI, "populating menu bar" << to_utf8(getMenubar().name())); + LYXERR(Debug::GUI, "populating menu bar" << fromqstr(getMenubar().name())); if (getMenubar().size() == 0) { LYXERR(Debug::GUI, "\tERROR: empty menu bar" - << to_utf8(getMenubar().name())); + << fromqstr(getMenubar().name())); return; } else { @@ -61,16 +115,16 @@ void Menus::fillMenuBar(GuiView * view) for (; m != end; ++m) { if (m->kind() != MenuItem::Submenu) { - LYXERR(Debug::GUI, "\tERROR: not a submenu " << to_utf8(m->label())); + LYXERR(Debug::GUI, "\tERROR: not a submenu " << fromqstr(m->label())); continue; } - LYXERR(Debug::GUI, "menu bar item " << to_utf8(m->label()) - << " is a submenu named " << to_utf8(m->submenuname())); + LYXERR(Debug::GUI, "menu bar item " << fromqstr(m->label()) + << " is a submenu named " << fromqstr(m->submenuname())); - docstring name = m->submenuname(); + QString name = m->submenuname(); if (!hasMenu(name)) { - LYXERR(Debug::GUI, "\tERROR: " << to_utf8(name) + LYXERR(Debug::GUI, "\tERROR: " << fromqstr(name) << " submenu has no menu!"); continue; } @@ -78,19 +132,17 @@ void Menus::fillMenuBar(GuiView * view) GuiPopupMenu * qMenu = new GuiPopupMenu(view, *m, true); view->menuBar()->addMenu(qMenu); - name_map_[toqstr(name)] = qMenu; + name_map_[name] = qMenu; } } QMenu * Menus::menu(QString const & name) { - LYXERR(Debug::GUI, "Context menu requested: " - << qstring_to_ucs4(name)); + LYXERR(Debug::GUI, "Context menu requested: " << fromqstr(name)); GuiPopupMenu * menu = name_map_.value(name, 0); if (!menu) - LYXERR0("resquested context menu not found: " - << qstring_to_ucs4(name)); + LYXERR0("resquested context menu not found: " << fromqstr(name)); return menu; } @@ -135,7 +187,7 @@ void Menus::macxMenuBarInit(GuiView * view) */ /* The entries of our special mac menu. If we add support for - * special entries in MenuBackend, we could imagine something + * special entries in Menus, we could imagine something * like * SpecialItem About " "About LyX" "dialog-show aboutlyx" * and therefore avoid hardcoding. I am not sure it is worth @@ -159,14 +211,12 @@ void Menus::macxMenuBarInit(GuiView * view) }; const size_t num_entries = sizeof(entries) / sizeof(MacMenuEntry); - // the special menu for MenuBackend. + // the special menu for Menus. Menu special; for (size_t i = 0 ; i < num_entries ; ++i) { FuncRequest const func(entries[i].action, from_utf8(entries[i].arg)); - special.add(MenuItem(MenuItem::Command, - from_utf8(entries[i].label), - func)); + special.add(MenuItem(MenuItem::Command, entries[i].label, func)); } specialMenu(special); @@ -175,13 +225,12 @@ void Menus::macxMenuBarInit(GuiView * view) QMenu * qMenu = view->menuBar()->addMenu("special"); // we do not use 'special' because it is a temporary variable, - // whereas MenuBackend::specialMenu points to a persistent + // whereas Menus::specialMenu points to a persistent // copy. Menu::const_iterator cit = specialMenu().begin(); Menu::const_iterator end = specialMenu().end(); for (size_t i = 0 ; cit != end ; ++cit, ++i) { - Action * action = new Action(*view, QIcon(), - toqstr(cit->label()), + Action * action = new Action(*view, QIcon(), cit->label(), cit->func(), QString()); action->setMenuRole(entries[i].role); qMenu->addAction(action); @@ -189,6 +238,990 @@ void Menus::macxMenuBarInit(GuiView * view) } } + +MenuItem::MenuItem(Kind kind) + : kind_(kind), optional_(false) +{} + + +MenuItem::MenuItem(Kind kind, QString const & label, + QString const & submenu, bool optional) + : kind_(kind), label_(label), + submenuname_(submenu), optional_(optional) +{ + BOOST_ASSERT(kind == Submenu); +} + + +MenuItem::MenuItem(Kind kind, QString const & label, + FuncRequest const & func, bool optional) + : kind_(kind), label_(label), func_(func), optional_(optional) +{ + func_.origin = FuncRequest::MENU; +} + + +MenuItem::~MenuItem() +{} + + +void MenuItem::submenu(Menu * menu) +{ + submenu_.reset(menu); +} + + +QString MenuItem::label() const +{ + return label_.split('|')[0]; +} + + +QString MenuItem::shortcut() const +{ + return label_.contains('|') ? label_.split('|')[1] : QString(); +} + + +QString MenuItem::binding() const +{ + if (kind_ != Command) + return QString(); + + // Get the keys bound to this action, but keep only the + // first one later + KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(func_); + + if (bindings.size()) + return toqstr(bindings.begin()->print(KeySequence::ForGui)); + + LYXERR(Debug::KBMAP, "No binding for " + << lyxaction.getActionName(func_.action) + << '(' << func_.argument() << ')'); + return QString(); +} + + +Menu & Menu::add(MenuItem const & i) +{ + items_.push_back(i); + return *this; +} + + +Menu & Menu::addWithStatusCheck(MenuItem const & i) +{ + switch (i.kind()) { + + case MenuItem::Command: { + FuncStatus status = lyx::getStatus(i.func()); + if (status.unknown() || (!status.enabled() && i.optional())) + break; + items_.push_back(i); + items_.back().status(status); + break; + } + + case MenuItem::Submenu: { + if (i.submenu()) { + bool enabled = false; + for (const_iterator cit = i.submenu()->begin(); + cit != i.submenu()->end(); ++cit) { + if ((cit->kind() == MenuItem::Command + || cit->kind() == MenuItem::Submenu) + && cit->status().enabled()) { + enabled = true; + break; + } + } + if (enabled || !i.optional()) { + items_.push_back(i); + items_.back().status().enabled(enabled); + } + } + else + items_.push_back(i); + break; + } + + case MenuItem::Separator: + if (!items_.empty() && items_.back().kind() != MenuItem::Separator) + items_.push_back(i); + break; + + default: + items_.push_back(i); + } + + return *this; +} + + +Menu & Menu::read(Lexer & lex) +{ + enum Menutags { + md_item = 1, + md_branches, + md_documents, + md_bookmarks, + md_charstyles, + md_custom, + md_elements, + md_endmenu, + md_exportformats, + md_importformats, + md_lastfiles, + md_optitem, + md_optsubmenu, + md_separator, + md_submenu, + md_toc, + md_updateformats, + md_viewformats, + md_floatlistinsert, + md_floatinsert, + md_pasterecent, + md_toolbars, + md_last + }; + + struct keyword_item menutags[md_last - 1] = { + { "bookmarks", md_bookmarks }, + { "branches", md_branches }, + { "charstyles", md_charstyles }, + { "custom", md_custom }, + { "documents", md_documents }, + { "elements", md_elements }, + { "end", md_endmenu }, + { "exportformats", md_exportformats }, + { "floatinsert", md_floatinsert }, + { "floatlistinsert", md_floatlistinsert }, + { "importformats", md_importformats }, + { "item", md_item }, + { "lastfiles", md_lastfiles }, + { "optitem", md_optitem }, + { "optsubmenu", md_optsubmenu }, + { "pasterecent", md_pasterecent }, + { "separator", md_separator }, + { "submenu", md_submenu }, + { "toc", md_toc }, + { "toolbars", md_toolbars }, + { "updateformats", md_updateformats }, + { "viewformats", md_viewformats } + }; + + lex.pushTable(menutags, md_last - 1); + if (lyxerr.debugging(Debug::PARSER)) + lex.printTable(lyxerr); + + bool quit = false; + bool optional = false; + + while (lex.isOK() && !quit) { + switch (lex.lex()) { + case md_optitem: + optional = true; + // fallback to md_item + case md_item: { + lex.next(true); + docstring const name = translateIfPossible(lex.getDocString()); + lex.next(true); + string const command = lex.getString(); + FuncRequest func = lyxaction.lookupFunc(command); + add(MenuItem(MenuItem::Command, toqstr(name), func, optional)); + optional = false; + break; + } + + case md_separator: + add(MenuItem(MenuItem::Separator)); + break; + + case md_lastfiles: + add(MenuItem(MenuItem::Lastfiles)); + break; + + case md_charstyles: + add(MenuItem(MenuItem::CharStyles)); + break; + + case md_custom: + add(MenuItem(MenuItem::Custom)); + break; + + case md_elements: + add(MenuItem(MenuItem::Elements)); + break; + + case md_documents: + add(MenuItem(MenuItem::Documents)); + break; + + case md_bookmarks: + add(MenuItem(MenuItem::Bookmarks)); + break; + + case md_toc: + add(MenuItem(MenuItem::Toc)); + break; + + case md_viewformats: + add(MenuItem(MenuItem::ViewFormats)); + break; + + case md_updateformats: + add(MenuItem(MenuItem::UpdateFormats)); + break; + + case md_exportformats: + add(MenuItem(MenuItem::ExportFormats)); + break; + + case md_importformats: + add(MenuItem(MenuItem::ImportFormats)); + break; + + case md_floatlistinsert: + add(MenuItem(MenuItem::FloatListInsert)); + break; + + case md_floatinsert: + add(MenuItem(MenuItem::FloatInsert)); + break; + + case md_pasterecent: + add(MenuItem(MenuItem::PasteRecent)); + break; + + case md_toolbars: + add(MenuItem(MenuItem::Toolbars)); + break; + + case md_branches: + add(MenuItem(MenuItem::Branches)); + break; + + case md_optsubmenu: + optional = true; + // fallback to md_submenu + case md_submenu: { + lex.next(true); + docstring const mlabel = translateIfPossible(lex.getDocString()); + lex.next(true); + docstring const mname = lex.getDocString(); + add(MenuItem(MenuItem::Submenu, + toqstr(mlabel), toqstr(mname), optional)); + optional = false; + break; + } + + case md_endmenu: + quit = true; + break; + + default: + lex.printError("Menu::read: " + "Unknown menu tag: `$$Token'"); + break; + } + } + lex.popTable(); + return *this; +} + + +MenuItem const & Menu::operator[](size_type i) const +{ + return items_[i]; +} + + +bool Menu::hasFunc(FuncRequest const & func) const +{ + return find_if(begin(), end(), + bind(equal_to(), + bind(&MenuItem::func, _1), + func)) != end(); +} + +void Menu::checkShortcuts() const +{ + // This is a quadratic algorithm, but we do not care because + // menus are short enough + for (const_iterator it1 = begin(); it1 != end(); ++it1) { + QString shortcut = it1->shortcut(); + if (shortcut.isEmpty()) + continue; + if (!it1->label().contains(shortcut)) + lyxerr << "Menu warning: menu entry \"" + << fromqstr(it1->label()) + << "\" does not contain shortcut `" + << fromqstr(shortcut) << "'." << endl; + for (const_iterator it2 = begin(); it2 != it1 ; ++it2) { + if (!it2->shortcut().compare(shortcut, Qt::CaseInsensitive)) { + lyxerr << "Menu warning: menu entries " + << '"' << fromqstr(it1->fulllabel()) + << "\" and \"" << fromqstr(it2->fulllabel()) + << "\" share the same shortcut." + << endl; + } + } + } +} + + +bool Menu::searchMenu(FuncRequest const & func, vector & names) const +{ + const_iterator m = begin(); + const_iterator m_end = end(); + for (; m != m_end; ++m) { + if (m->kind() == MenuItem::Command && m->func() == func) { + names.push_back(qstring_to_ucs4(m->label())); + return true; + } + if (m->kind() == MenuItem::Submenu) { + names.push_back(qstring_to_ucs4(m->label())); + Menu const & submenu = *m->submenu(); + if (submenu.searchMenu(func, names)) + return true; + names.pop_back(); + } + } + return false; +} + + +void Menus::specialMenu(Menu const & menu) +{ + specialmenu_ = menu; +} + + +namespace { + +bool compareFormat(Format const * p1, Format const * p2) +{ + return *p1 < *p2; +} + + +QString limitStringLength(docstring const & str) +{ + size_t const max_item_length = 45; + + if (str.size() > max_item_length) + return toqstr(str.substr(0, max_item_length - 3) + "..."); + + return toqstr(str); +} + + +void expandLastfiles(Menu & tomenu) +{ + LastFilesSection::LastFiles const & lf = LyX::cref().session().lastFiles().lastFiles(); + LastFilesSection::LastFiles::const_iterator lfit = lf.begin(); + + int ii = 1; + + for (; lfit != lf.end() && ii < 10; ++lfit, ++ii) { + string const file = lfit->absFilename(); + QString const label = QString("%1. %2|%3").arg(ii) + .arg(toqstr(makeDisplayPath(file, 30))).arg(ii); + tomenu.add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_FILE_OPEN, file))); + } +} + + +void expandDocuments(Menu & tomenu) +{ + Buffer * first = theBufferList().first(); + if (first) { + Buffer * b = first; + int ii = 1; + + // We cannot use a for loop as the buffer list cycles. + do { + QString label = toqstr(b->fileName().displayName(20)); + if (!b->isClean()) + label += "*"; + if (ii < 10) + label = QString::number(ii) + ". " + label + '|' + QString::number(ii); + tomenu.add(MenuItem(MenuItem::Command, label, + FuncRequest(LFUN_BUFFER_SWITCH, b->absFileName()))); + + b = theBufferList().next(b); + ++ii; + } while (b != first); + } else { + tomenu.add(MenuItem(MenuItem::Command, qt_("No Documents Open!"), + FuncRequest(LFUN_NOACTION))); + } +} + + +void expandBookmarks(Menu & tomenu) +{ + lyx::BookmarksSection const & bm = LyX::cref().session().bookmarks(); + + for (size_t i = 1; i <= bm.size(); ++i) { + if (bm.isValid(i)) { + string const file = bm.bookmark(i).filename.absFilename(); + QString const label = QString("%1. %2|%3").arg(i) + .arg(toqstr(makeDisplayPath(file, 20))).arg(i); + tomenu.add(MenuItem(MenuItem::Command, label, + FuncRequest(LFUN_BOOKMARK_GOTO, convert(i)))); + } + } +} + + +void expandFormats(MenuItem::Kind kind, Menu & tomenu, Buffer const * buf) +{ + if (!buf && kind != MenuItem::ImportFormats) { + tomenu.add(MenuItem(MenuItem::Command, + qt_("No Document Open!"), + FuncRequest(LFUN_NOACTION))); + return; + } + + typedef vector Formats; + Formats formats; + kb_action action; + + switch (kind) { + case MenuItem::ImportFormats: + formats = theConverters().importableFormats(); + action = LFUN_BUFFER_IMPORT; + break; + case MenuItem::ViewFormats: + formats = buf->exportableFormats(true); + action = LFUN_BUFFER_VIEW; + break; + case MenuItem::UpdateFormats: + formats = buf->exportableFormats(true); + action = LFUN_BUFFER_UPDATE; + break; + default: + formats = buf->exportableFormats(false); + action = LFUN_BUFFER_EXPORT; + } + sort(formats.begin(), formats.end(), &compareFormat); + + Formats::const_iterator fit = formats.begin(); + Formats::const_iterator end = formats.end(); + for (; fit != end ; ++fit) { + if ((*fit)->dummy()) + continue; + QString label = toqstr((*fit)->prettyname()); + QString const shortcut = toqstr((*fit)->shortcut()); + + switch (kind) { + case MenuItem::ImportFormats: + // FIXME: This is a hack, we should rather solve + // FIXME: bug 2488 instead. + if ((*fit)->name() == "text") + label = qt_("Plain Text"); + else if ((*fit)->name() == "textparagraph") + label = qt_("Plain Text, Join Lines"); + label += "..."; + break; + case MenuItem::ViewFormats: + case MenuItem::ExportFormats: + case MenuItem::UpdateFormats: + if (!(*fit)->documentFormat()) + continue; + break; + default: + BOOST_ASSERT(false); + break; + } + // FIXME: if we had proper support for translating the + // format names defined in configure.py, there would + // not be a need to check whether the shortcut is + // correct. If we add it uncondiitonally, it would + // create useless warnings on bad shortcuts + if (!shortcut.isEmpty() && label.contains(shortcut)) + label += '|' + shortcut; + + if (buf) + tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label, + FuncRequest(action, (*fit)->name()))); + else + tomenu.add(MenuItem(MenuItem::Command, label, + FuncRequest(action, (*fit)->name()))); + } +} + + +void expandFloatListInsert(Menu & tomenu, Buffer const * buf) +{ + if (!buf) { + tomenu.add(MenuItem(MenuItem::Command, qt_("No Document Open!"), + FuncRequest(LFUN_NOACTION))); + return; + } + + FloatList const & floats = buf->params().documentClass().floats(); + FloatList::const_iterator cit = floats.begin(); + FloatList::const_iterator end = floats.end(); + for (; cit != end; ++cit) { + tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, + qt_(cit->second.listName()), + FuncRequest(LFUN_FLOAT_LIST, + cit->second.type()))); + } +} + + +void expandFloatInsert(Menu & tomenu, Buffer const * buf) +{ + if (!buf) { + tomenu.add(MenuItem(MenuItem::Command, qt_("No Document Open!"), + FuncRequest(LFUN_NOACTION))); + return; + } + + FloatList const & floats = buf->params().documentClass().floats(); + FloatList::const_iterator cit = floats.begin(); + FloatList::const_iterator end = floats.end(); + for (; cit != end; ++cit) { + // normal float + QString const label = qt_(cit->second.name()); + tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label, + FuncRequest(LFUN_FLOAT_INSERT, + cit->second.type()))); + } +} + + +void expandFlexInsert(Menu & tomenu, Buffer const * buf, string s) +{ + if (!buf) { + tomenu.add(MenuItem(MenuItem::Command, qt_("No Document Open!"), + FuncRequest(LFUN_NOACTION))); + return; + } + TextClass::InsetLayouts const & insetLayouts = + buf->params().documentClass().insetLayouts(); + TextClass::InsetLayouts::const_iterator cit = insetLayouts.begin(); + TextClass::InsetLayouts::const_iterator end = insetLayouts.end(); + for (; cit != end; ++cit) { + docstring const label = cit->first; + if (cit->second.lyxtype() == s) + tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, + toqstr(label), FuncRequest(LFUN_FLEX_INSERT, + label))); + } +} + + +size_t const max_number_of_items = 25; + +void expandToc2(Menu & tomenu, Toc const & toc_list, + size_t from, size_t to, int depth) +{ + int shortcut_count = 0; + + // check whether depth is smaller than the smallest depth in toc. + int min_depth = 1000; + for (size_t i = from; i < to; ++i) + min_depth = min(min_depth, toc_list[i].depth()); + if (min_depth > depth) + depth = min_depth; + + if (to - from <= max_number_of_items) { + for (size_t i = from; i < to; ++i) { + QString label(4 * max(0, toc_list[i].depth() - depth), ' '); + label += limitStringLength(toc_list[i].str()); + if (toc_list[i].depth() == depth + && shortcut_count < 9) { + if (label.contains(QString::number(shortcut_count + 1))) + label += '|' + QString::number(++shortcut_count); + } + tomenu.add(MenuItem(MenuItem::Command, label, + FuncRequest(toc_list[i].action()))); + } + } else { + size_t pos = from; + while (pos < to) { + size_t new_pos = pos + 1; + while (new_pos < to && + toc_list[new_pos].depth() > depth) + ++new_pos; + + QString label(4 * max(0, toc_list[pos].depth() - depth), ' '); + label += limitStringLength(toc_list[pos].str()); + if (toc_list[pos].depth() == depth && + shortcut_count < 9) { + if (label.contains(QString::number(shortcut_count + 1))) + label += '|' + QString::number(++shortcut_count); + } + if (new_pos == pos + 1) { + tomenu.add(MenuItem(MenuItem::Command, + label, FuncRequest(toc_list[pos].action()))); + } else { + MenuItem item(MenuItem::Submenu, label); + item.submenu(new Menu); + expandToc2(*item.submenu(), + toc_list, pos, new_pos, depth + 1); + tomenu.add(item); + } + pos = new_pos; + } + } +} + + +void expandToc(Menu & tomenu, Buffer const * buf) +{ + // To make things very cleanly, we would have to pass buf to + // all MenuItem constructors and to expandToc2. However, we + // know that all the entries in a TOC will be have status_ == + // OK, so we avoid this unnecessary overhead (JMarc) + + if (!buf) { + tomenu.add(MenuItem(MenuItem::Command, qt_("No Document Open!"), + FuncRequest(LFUN_NOACTION))); + return; + } + + Buffer* cbuf = const_cast(buf); + cbuf->tocBackend().update(); + cbuf->structureChanged(); + + // Add an entry for the master doc if this is a child doc + Buffer const * const master = buf->masterBuffer(); + if (buf != master) { + ParIterator const pit = par_iterator_begin(master->inset()); + string const arg = convert(pit->id()); + FuncRequest f(LFUN_PARAGRAPH_GOTO, arg); + tomenu.add(MenuItem(MenuItem::Command, qt_("Master Document"), f)); + } + + FloatList const & floatlist = buf->params().documentClass().floats(); + TocList const & toc_list = buf->tocBackend().tocs(); + TocList::const_iterator cit = toc_list.begin(); + TocList::const_iterator end = toc_list.end(); + for (; cit != end; ++cit) { + // Handle this later + if (cit->first == "tableofcontents") + continue; + + // All the rest is for floats + auto_ptr menu(new Menu); + TocIterator ccit = cit->second.begin(); + TocIterator eend = cit->second.end(); + for (; ccit != eend; ++ccit) { + QString const label = limitStringLength(ccit->str()); + menu->add(MenuItem(MenuItem::Command, label, + FuncRequest(ccit->action()))); + } + string const & floatName = floatlist.getType(cit->first).listName(); + QString label; + if (!floatName.empty()) + label = qt_(floatName); + // BUG3633: listings is not a proper float so its name + // is not shown in floatlist. + else if (cit->first == "equation") + label = qt_("List of Equations"); + else if (cit->first == "index") + label = qt_("List of Indexes"); + else if (cit->first == "listing") + label = qt_("List of Listings"); + else if (cit->first == "marginalnote") + label = qt_("List of Marginal notes"); + else if (cit->first == "note") + label = qt_("List of Notes"); + else if (cit->first == "footnote") + label = qt_("List of Foot notes"); + else if (cit->first == "label") + label = qt_("Labels and References"); + else if (cit->first == "citation") + label = qt_("List of Citations"); + // this should not happen now, but if something else like + // listings is added later, this can avoid an empty menu name. + else + label = qt_("Other floats"); + MenuItem item(MenuItem::Submenu, label); + item.submenu(menu.release()); + tomenu.add(item); + } + + // Handle normal TOC + cit = toc_list.find("tableofcontents"); + if (cit == end) { + tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, + qt_("No Table of contents"), + FuncRequest())); + } else { + expandToc2(tomenu, cit->second, 0, cit->second.size(), 0); + } +} + + +void expandPasteRecent(Menu & tomenu) +{ + vector const sel = cap::availableSelections(); + + vector::const_iterator cit = sel.begin(); + vector::const_iterator end = sel.end(); + + for (unsigned int index = 0; cit != end; ++cit, ++index) { + tomenu.add(MenuItem(MenuItem::Command, toqstr(*cit), + FuncRequest(LFUN_PASTE, convert(index)))); + } +} + + +void expandToolbars(Menu & tomenu) +{ + // + // extracts the toolbars from the backend + ToolbarBackend::Toolbars::const_iterator cit = toolbarbackend.begin(); + ToolbarBackend::Toolbars::const_iterator end = toolbarbackend.end(); + + for (; cit != end; ++cit) { + QString label = qt_(cit->gui_name); + // frontends are not supposed to turn on/off toolbars, + // if they cannot update ToolbarBackend::flags. That + // is to say, ToolbarsBackend::flags should reflect + // the true state of toolbars. + // + // menu is displayed as + // on/off review + // and + // review (auto) + // in the case of auto. + if (cit->flags & ToolbarInfo::AUTO) + label += qt_(" (auto)"); + tomenu.add(MenuItem(MenuItem::Command, label, + FuncRequest(LFUN_TOOLBAR_TOGGLE, cit->name + " allowauto"))); + } +} + + +void expandBranches(Menu & tomenu, Buffer const * buf) +{ + if (!buf) { + tomenu.add(MenuItem(MenuItem::Command, + qt_("No Document Open!"), + FuncRequest(LFUN_NOACTION))); + return; + } + + BufferParams const & params = buf->masterBuffer()->params(); + if (params.branchlist().empty()) { + tomenu.add(MenuItem(MenuItem::Command, + qt_("No Branch in Document!"), + FuncRequest(LFUN_NOACTION))); + return; + } + + BranchList::const_iterator cit = params.branchlist().begin(); + BranchList::const_iterator end = params.branchlist().end(); + + for (int ii = 1; cit != end; ++cit, ++ii) { + docstring label = cit->getBranch(); + if (ii < 10) + label = convert(ii) + ". " + label + char_type('|') + convert(ii); + tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label), + FuncRequest(LFUN_BRANCH_INSERT, + cit->getBranch()))); + } +} + + +} // namespace anon + + +void Menus::expand(Menu const & frommenu, Menu & tomenu, + Buffer const * buf) const +{ + if (!tomenu.empty()) + tomenu.clear(); + + for (Menu::const_iterator cit = frommenu.begin(); + cit != frommenu.end() ; ++cit) { + switch (cit->kind()) { + case MenuItem::Lastfiles: + expandLastfiles(tomenu); + break; + + case MenuItem::Documents: + expandDocuments(tomenu); + break; + + case MenuItem::Bookmarks: + expandBookmarks(tomenu); + break; + + case MenuItem::ImportFormats: + case MenuItem::ViewFormats: + case MenuItem::UpdateFormats: + case MenuItem::ExportFormats: + expandFormats(cit->kind(), tomenu, buf); + break; + + case MenuItem::CharStyles: + expandFlexInsert(tomenu, buf, "charstyle"); + break; + + case MenuItem::Custom: + expandFlexInsert(tomenu, buf, "custom"); + break; + + case MenuItem::Elements: + expandFlexInsert(tomenu, buf, "element"); + break; + + case MenuItem::FloatListInsert: + expandFloatListInsert(tomenu, buf); + break; + + case MenuItem::FloatInsert: + expandFloatInsert(tomenu, buf); + break; + + case MenuItem::PasteRecent: + expandPasteRecent(tomenu); + break; + + case MenuItem::Toolbars: + expandToolbars(tomenu); + break; + + case MenuItem::Branches: + expandBranches(tomenu, buf); + break; + + case MenuItem::Toc: + expandToc(tomenu, buf); + break; + + case MenuItem::Submenu: { + MenuItem item(*cit); + item.submenu(new Menu(cit->submenuname())); + expand(getMenu(cit->submenuname()), + *item.submenu(), buf); + tomenu.addWithStatusCheck(item); + } + break; + + case MenuItem::Separator: + tomenu.addWithStatusCheck(*cit); + break; + + case MenuItem::Command: + if (!specialmenu_.hasFunc(cit->func())) + tomenu.addWithStatusCheck(*cit); + } + } + + // we do not want the menu to end with a separator + if (!tomenu.empty() + && tomenu.items_.back().kind() == MenuItem::Separator) + tomenu.items_.pop_back(); + + // Check whether the shortcuts are unique + tomenu.checkShortcuts(); +} + + +void Menus::read(Lexer & lex) +{ + enum Menutags { + md_menu = 1, + md_menubar, + md_endmenuset, + md_last + }; + + struct keyword_item menutags[md_last - 1] = { + { "end", md_endmenuset }, + { "menu", md_menu }, + { "menubar", md_menubar } + }; + + //consistency check + if (compare_ascii_no_case(lex.getString(), "menuset")) { + lyxerr << "Menubackend::read: ERROR wrong token:`" + << lex.getString() << '\'' << endl; + } + + lex.pushTable(menutags, md_last - 1); + if (lyxerr.debugging(Debug::PARSER)) + lex.printTable(lyxerr); + + bool quit = false; + + while (lex.isOK() && !quit) { + switch (lex.lex()) { + case md_menubar: + menubar_.read(lex); + break; + case md_menu: { + lex.next(true); + QString const name = toqstr(lex.getDocString()); + if (hasMenu(name)) { + getMenu(name).read(lex); + } else { + Menu menu(name); + menu.read(lex); + add(menu); + } + break; + } + case md_endmenuset: + quit = true; + break; + default: + lex.printError("menubackend::read: " + "Unknown menu tag: `$$Token'"); + break; + } + } + lex.popTable(); +} + + +void Menus::add(Menu const & menu) +{ + menulist_.push_back(menu); +} + + +bool Menus::hasMenu(QString const & name) const +{ + return find_if(begin(), end(), MenuNamesEqual(name)) != end(); +} + + +Menu const & Menus::getMenu(QString const & name) const +{ + const_iterator cit = find_if(begin(), end(), MenuNamesEqual(name)); + if (cit == end()) + lyxerr << "No submenu named " << fromqstr(name) << endl; + BOOST_ASSERT(cit != end()); + return (*cit); +} + + +Menu & Menus::getMenu(QString const & name) +{ + iterator it = find_if(begin(), end(), MenuNamesEqual(name)); + if (it == end()) + lyxerr << "No submenu named " << fromqstr(name) << endl; + BOOST_ASSERT(it != end()); + return (*it); +} + + +Menu const & Menus::getMenubar() const +{ + return menubar_; +} + + } // namespace frontend } // namespace lyx diff --git a/src/frontends/qt4/Menus.h b/src/frontends/qt4/Menus.h index 77da32d57f..c20eccbd82 100644 --- a/src/frontends/qt4/Menus.h +++ b/src/frontends/qt4/Menus.h @@ -13,25 +13,211 @@ #ifndef MENUS_H #define MENUS_H -#include "MenuBackend.h" +#include "FuncStatus.h" +#include "FuncRequest.h" #include #include -class QMenu; +#include + +#include +class QMenu; namespace lyx { + +class Lexer; +class Buffer; + namespace frontend { +class Menu; class GuiView; class GuiPopupMenu; class GuiView; -class Menus : public QObject, public MenuBackend +/// +class MenuItem { +public: + /// The type of elements that can be in a menu + enum Kind { + /// + Command, + /// + Submenu, + /// + Separator, + /** This is the list of last opened file, + typically for the File menu. */ + Lastfiles, + /** This is the list of opened Documents, + typically for the Documents menu. */ + Documents, + /** This is the bookmarks */ + Bookmarks, + /// + Toc, + /** This is a list of viewable formats + typically for the File->View menu. */ + ViewFormats, + /** This is a list of updatable formats + typically for the File->Update menu. */ + UpdateFormats, + /** This is a list of exportable formats + typically for the File->Export menu. */ + ExportFormats, + /** This is a list of importable formats + typically for the File->Export menu. */ + ImportFormats, + /** This is the list of elements available + * for insertion into document. */ + CharStyles, + /** This is the list of user-configurable + insets to insert into document */ + Custom, + /** This is the list of XML elements to + insert into the document */ + Elements, + /** This is the list of floats that we can + insert a list for. */ + FloatListInsert, + /** This is the list of floats that we can + insert. */ + FloatInsert, + /** This is the list of selections that can + be pasted. */ + PasteRecent, + /** toolbars */ + Toolbars, + /** Available branches in document */ + Branches + }; + + explicit MenuItem(Kind kind); + + MenuItem(Kind kind, + QString const & label, + QString const & submenu = QString(), + bool optional = false); + + MenuItem(Kind kind, + QString const & label, + FuncRequest const & func, + bool optional = false); + + /// This one is just to please boost::shared_ptr<> + ~MenuItem(); + /// The label of a given menuitem + QString label() const; + /// The keyboard shortcut (usually underlined in the entry) + QString shortcut() const; + /// The complete label, with label and shortcut separated by a '|' + QString fulllabel() const { return label_;} + /// The kind of entry + Kind kind() const { return kind_; } + /// the action (if relevant) + FuncRequest const & func() const { return func_; } + /// returns true if the entry should be ommited when disabled + bool optional() const { return optional_; } + /// returns the status of the lfun associated with this entry + FuncStatus const & status() const { return status_; } + /// returns the status of the lfun associated with this entry + FuncStatus & status() { return status_; } + /// returns the status of the lfun associated with this entry + void status(FuncStatus const & status) { status_ = status; } + ///returns the binding associated to this action. + QString binding() const; + /// the description of the submenu (if relevant) + QString const & submenuname() const { return submenuname_; } + /// set the description of the submenu + void submenuname(QString const & name) { submenuname_ = name; } + /// + Menu * submenu() const { return submenu_.get(); } + /// + void submenu(Menu * menu); + +private: + /// + Kind kind_; + /// + QString label_; + /// + FuncRequest func_; + /// + QString submenuname_; + /// + bool optional_; + /// + FuncStatus status_; + /// + boost::shared_ptr submenu_; +}; + + +/// +class Menu { +public: + /// + typedef std::vector ItemList; + /// + typedef ItemList::const_iterator const_iterator; + + /// + explicit Menu(QString const & name = QString()) : name_(name) {} + + /// Add the menu item unconditionally + Menu & add(MenuItem const &); + /// Checks the associated FuncRequest status before adding the + /// menu item. + Menu & addWithStatusCheck(MenuItem const &); + /// + Menu & read(Lexer &); + /// + QString const & name() const { return name_; } + /// + bool empty() const { return items_.empty(); } + /// Clear the menu content. + void clear() { items_.clear(); } + /// + size_t size() const { return items_.size(); } + /// + MenuItem const & operator[](size_t) const; + /// + bool hasFunc(FuncRequest const &) const; + /// + const_iterator begin() const { return items_.begin(); } + /// + const_iterator end() const { return items_.end(); } + + // Check whether the menu shortcuts are unique + void checkShortcuts() const; + + // search for func in this menu iteratively, and put menu + // names in a stack. + bool searchMenu(FuncRequest const & func, std::vector & names) + const; + +private: + friend class Menus; + /// + ItemList items_; + /// + QString name_; +}; + + +class Menus { - Q_OBJECT public: + /// + typedef std::vector MenuList; + /// + typedef MenuList::const_iterator const_iterator; + /// + typedef MenuList::iterator iterator; + + Menus() {} /// @@ -42,6 +228,51 @@ public: /// update the state of the menuitems - not needed void updateView(); + /// + void read(Lexer &); + /// + void add(Menu const &); + /// + bool hasMenu(QString const &) const; + /// + Menu & getMenu(QString const &); + /// + Menu const & getMenu(QString const &) const; + /// + Menu const & getMenubar() const; + /// + bool empty() const { return menulist_.empty(); } + /** This defines a menu whose entries list the FuncRequests + that will be removed by expand() in other menus. This is + used by the Qt/Mac code + */ + void specialMenu(Menu const &); + /// + Menu const & specialMenu() { return specialmenu_; } + + /// Expands some special entries of the menu + /** The entries with the following kind are expanded to a + sequence of Command MenuItems: Lastfiles, Documents, + ViewFormats, ExportFormats, UpdateFormats, Branches + */ + void expand(Menu const & frommenu, Menu & tomenu, + Buffer const *) const; + /// + const_iterator begin() const { return menulist_.begin(); } + /// + iterator begin() { return menulist_.begin(); } + /// + const_iterator end() const { return menulist_.end(); } + /// + iterator end() { return menulist_.end(); } + +private: + /// + MenuList menulist_; + /// + Menu menubar_; + /// + Menu specialmenu_; private: /// Initialize specific MACOS X menubar