]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/Menus.cpp
thrid attempt at changing the naming pattern of the intermediated 'mocced' files
[lyx.git] / src / frontends / qt4 / Menus.cpp
index ddb88862163804d0f3deaa9a624b0f02d25de725..43e339401da309d9bb12f70ed397a3970a6a1862 100644 (file)
@@ -5,9 +5,9 @@
  *
  * \author John Levon
  * \author Asger Alstrup
- * \author Lars Gullik Bjønnes
+ * \author Lars Gullik Bjønnes
  * \author Jean-Marc Lasgouttes
- * \author André Pönitz
+ * \author André Pönitz
  * \author Dekel Tsur
  * \author Martin Vermeer
  *
 #include "Action.h"
 #include "GuiApplication.h"
 #include "GuiView.h"
+#include "qt_helpers.h"
 
+#include "BiblioInfo.h"
 #include "BranchList.h"
 #include "Buffer.h"
 #include "BufferList.h"
 #include "BufferParams.h"
+#include "BufferView.h"
 #include "Converter.h"
 #include "CutAndPaste.h"
 #include "Floating.h"
 #include "LyX.h" // for lastfiles
 #include "LyXFunc.h"
 #include "Paragraph.h"
-#include "qt_helpers.h"
+#include "ParIterator.h"
 #include "Session.h"
 #include "TextClass.h"
 #include "TocBackend.h"
-#include "ToolbarBackend.h"
+#include "Toolbars.h"
 
+#include "insets/Inset.h"
+#include "insets/InsetCitation.h"
+#include "insets/InsetGraphics.h"
+
+#include "support/lassert.h"
 #include "support/convert.h"
 #include "support/debug.h"
+#include "support/docstring_list.h"
 #include "support/filetools.h"
 #include "support/gettext.h"
 #include "support/lstrings.h"
 
 #include <QCursor>
 #include <QHash>
+#include <QList>
 #include <QMenuBar>
 #include <QString>
 
 #include <boost/shared_ptr.hpp>
 
 #include <algorithm>
-#include <ostream>
 #include <vector>
 
 using namespace std;
@@ -73,7 +82,7 @@ namespace {
 
 // MacOSX specific stuff is at the end.
 
-class Menu;
+class MenuDefinition;
 
 ///
 class MenuItem {
@@ -129,27 +138,44 @@ public:
                /** toolbars */
                Toolbars,
                /** Available branches in document */
-               Branches
+               Branches,
+               /** Available citation styles for a given citation */
+               CiteStyles,
+               /** Available graphics groups */
+               GraphicsGroups
        };
 
-       explicit MenuItem(Kind kind);
+       explicit MenuItem(Kind kind) : kind_(kind), optional_(false) {}
 
        MenuItem(Kind kind,
                 QString const & label,
                 QString const & submenu = QString(),
-                bool optional = false);
+                bool optional = false)
+               : kind_(kind), label_(label), submenuname_(submenu), optional_(optional)
+       {
+               LASSERT(kind == Submenu, /**/);
+       }
 
        MenuItem(Kind kind,
                 QString const & label,
                 FuncRequest const & func,
-                bool optional = false);
+                bool optional = false)
+               : kind_(kind), label_(label), func_(func), optional_(optional)
+       {
+               func_.origin = FuncRequest::MENU;
+       }
+
+       // boost::shared_ptr<MenuDefinition> needs this apprently...
+       ~MenuItem() {}
 
-       /// This one is just to please boost::shared_ptr<>
-       ~MenuItem();
        /// The label of a given menuitem
-       QString label() const;
+       QString label() const { return label_.split('|')[0]; }
+
        /// The keyboard shortcut (usually underlined in the entry)
-       QString shortcut() const;
+       QString shortcut() const
+       {
+               return label_.contains('|') ? label_.split('|')[1] : QString();
+       }
        /// The complete label, with label and shortcut separated by a '|'
        QString fulllabel() const { return label_;}
        /// The kind of entry
@@ -164,16 +190,39 @@ public:
        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;
+       QString 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();
+       }
+
        /// 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(); }
+       bool hasSubmenu() const { return !submenu_.isEmpty(); }
        ///
-       void setSubmenu(Menu * menu);
+       MenuDefinition const & submenu() const { return submenu_.at(0); }
+       MenuDefinition & submenu() { return submenu_[0]; }
+       ///
+       void setSubmenu(MenuDefinition const & menu)
+       {
+               submenu_.clear();
+               submenu_.append(menu);
+       }
 
 private:
        ///
@@ -188,20 +237,19 @@ private:
        bool optional_;
        ///
        FuncStatus status_;
-       ///
-       boost::shared_ptr<Menu> submenu_;
+       /// contains 0 or 1 item.
+       QList<MenuDefinition> submenu_;
 };
 
 ///
-class Menu {
+class MenuDefinition {
 public:
        ///
        typedef std::vector<MenuItem> ItemList;
        ///
        typedef ItemList::const_iterator const_iterator;
-
        ///
-       explicit Menu(QString const & name = QString()) : name_(name) {}
+       explicit MenuDefinition(QString const & name = QString()) : name_(name) {}
 
        ///
        void read(Lexer &);
@@ -222,11 +270,8 @@ public:
        
        // search for func in this menu iteratively, and put menu
        // names in a stack.
-       bool searchMenu(FuncRequest const & func, std::vector<docstring> & names)
+       bool searchMenu(FuncRequest const & func, docstring_list & names)
                const;
-
-private:
-       friend class Menus;
        ///
        bool hasFunc(FuncRequest const &) const;
        /// Add the menu item unconditionally
@@ -243,625 +288,242 @@ private:
        void expandFormats(MenuItem::Kind kind, Buffer const * buf);
        void expandFloatListInsert(Buffer const * buf);
        void expandFloatInsert(Buffer const * buf);
-       void expandFlexInsert(Buffer const * buf, std::string s);
+       void expandFlexInsert(Buffer const * buf, InsetLayout::InsetLyXType type);
        void expandToc2(Toc const & toc_list, size_t from, size_t to, int depth);
        void expandToc(Buffer const * buf);
-       void expandPasteRecent();
+       void expandPasteRecent(Buffer const * buf);
        void expandToolbars();
        void expandBranches(Buffer const * buf);
-
-       ItemList items_;
-       ///
-       QString name_;
-};
-
-/// a submenu
-class GuiPopupMenu : public QMenu
-{
-public:
+       void expandCiteStyles(BufferView const *);
+       void expandGraphicsGroups(BufferView const *);
        ///
-       GuiPopupMenu(GuiView * owner, MenuItem const & mi, bool top_level)
-               : QMenu(owner), owner_(owner), top_level_(top_level)
-       {
-               name_ = mi.submenuname();
-               setTitle(label(mi));
-       }
-
-       /// populates the menu or one of its submenu
-       /// This is used as a recursive function
-       void populate(QMenu * qMenu, Menu * menu);
-
-       /// Get a Menu item label from the menu backend
-       QString label(MenuItem const & mi) const;
-
-       void showEvent(QShowEvent * ev)
-       {
-               if (top_level_)
-                       guiApp->menus().updateMenu(name_);
-               QMenu::showEvent(ev);
-       }
-
-       bool const top_level_;
-
+       ItemList items_;
        ///
-       Menu topLevelMenu_;
-
-       /// our owning view
-       GuiView * owner_;
-       /// the name of this menu
        QString name_;
 };
 
 
+/// Helper for std::find_if
 class MenuNamesEqual
 {
 public:
        MenuNamesEqual(QString const & name) : name_(name) {}
-       bool operator()(Menu const & menu) const { return menu.name() == name_; }
+       bool operator()(MenuDefinition const & menu) const { return menu.name() == name_; }
 private:
        QString name_;
 };
 
+
 ///
-typedef std::vector<Menu> MenuList;
+typedef std::vector<MenuDefinition> MenuList;
 ///
 typedef MenuList::const_iterator const_iterator;
 ///
 typedef MenuList::iterator iterator;
 
+/////////////////////////////////////////////////////////////////////
+// MenuDefinition implementation
+/////////////////////////////////////////////////////////////////////
 
-void GuiPopupMenu::populate(QMenu * qMenu, Menu * menu)
+void MenuDefinition::addWithStatusCheck(MenuItem const & i)
 {
-       LYXERR(Debug::GUI, "populating menu " << fromqstr(menu->name()));
-       if (menu->size() == 0) {
-               LYXERR(Debug::GUI, "\tERROR: empty menu " << fromqstr(menu->name()));
-               return;
+       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;
        }
-       LYXERR(Debug::GUI, " *****  menu entries " << menu->size());
 
-       Menu::const_iterator m = menu->begin();
-       Menu::const_iterator end = menu->end();
+       case MenuItem::Submenu: {
+               if (i.hasSubmenu()) {
+                       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().setEnabled(enabled);
+                       }
+               }
+               else
+                       items_.push_back(i);
+               break;
+       }
 
-       for (; m != end; ++m) {
+       case MenuItem::Separator:
+               if (!items_.empty() && items_.back().kind() != MenuItem::Separator)
+                       items_.push_back(i);
+               break;
 
-               if (m->kind() == MenuItem::Separator) {
+       default:
+               items_.push_back(i);
+       }
+}
 
-                       qMenu->addSeparator();
-                       LYXERR(Debug::GUI, "adding Menubar Separator");
 
-               } else if (m->kind() == MenuItem::Submenu) {
+void MenuDefinition::read(Lexer & lex)
+{
+       enum {
+               md_item = 1,
+               md_branches,
+               md_citestyles,
+               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_graphicsgroups
+       };
 
-                       LYXERR(Debug::GUI, "** creating New Sub-Menu "
-                               << fromqstr(label(*m)));
-                       QMenu * subMenu = qMenu->addMenu(label(*m));
-                       populate(subMenu, m->submenu());
+       LexerKeyword menutags[] = {
+               { "bookmarks", md_bookmarks },
+               { "branches", md_branches },
+               { "charstyles", md_charstyles },
+               { "citestyles", md_citestyles },
+               { "custom", md_custom },
+               { "documents", md_documents },
+               { "elements", md_elements },
+               { "end", md_endmenu },
+               { "exportformats", md_exportformats },
+               { "floatinsert", md_floatinsert },
+               { "floatlistinsert", md_floatlistinsert },
+               { "graphicsgroups", md_graphicsgroups },
+               { "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 }
+       };
 
-               } else { // we have a MenuItem::Command
+       lex.pushTable(menutags);
+       lex.setContext("MenuDefinition::read: ");
 
-                       LYXERR(Debug::GUI, "creating Menu Item "
-                               << fromqstr(m->label()));
+       bool quit = false;
+       bool optional = false;
 
-                       Action * action = new Action(*owner_,
-                               QIcon(), label(*m), m->func(), QString());
-                       qMenu->addAction(action);
+       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;
 
-QString GuiPopupMenu::label(MenuItem const & mi) const
-{
-       QString label = mi.label();
-       label.replace("&", "&&");
+               case md_lastfiles:
+                       add(MenuItem(MenuItem::Lastfiles));
+                       break;
 
-       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, "&");
-       }
+               case md_charstyles:
+                       add(MenuItem(MenuItem::CharStyles));
+                       break;
 
-       QString const binding = mi.binding();
-       if (!binding.isEmpty())
-               label += '\t' + binding;
+               case md_custom:
+                       add(MenuItem(MenuItem::Custom));
+                       break;
 
-       return label;
-}
+               case md_elements:
+                       add(MenuItem(MenuItem::Elements));
+                       break;
 
-} // namespace anon
+               case md_documents:
+                       add(MenuItem(MenuItem::Documents));
+                       break;
 
-struct Menus::Impl {
-       ///
-       void add(Menu const &);
-       ///
-       bool hasMenu(QString const &) const;
-       ///
-       Menu & getMenu(QString const &);
-       ///
-       Menu const & getMenu(QString const &) 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 setSpecialMenu(Menu const & menu) { specialmenu_ = menu; }
-       ///
-       Menu const & specialMenu() { return specialmenu_; }
+               case md_bookmarks:
+                       add(MenuItem(MenuItem::Bookmarks));
+                       break;
 
-       /// 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(); }
+               case md_toc:
+                       add(MenuItem(MenuItem::Toc));
+                       break;
 
-       ///
-       MenuList menulist_;
-       ///
-       Menu menubar_;
-       ///
-       Menu specialmenu_;
+               case md_viewformats:
+                       add(MenuItem(MenuItem::ViewFormats));
+                       break;
 
-       /// Initialize specific MACOS X menubar
-       void macxMenuBarInit(GuiView * view);
+               case md_updateformats:
+                       add(MenuItem(MenuItem::UpdateFormats));
+                       break;
 
-       typedef QHash<QString, GuiPopupMenu *> NameMap;
+               case md_exportformats:
+                       add(MenuItem(MenuItem::ExportFormats));
+                       break;
 
-       /// name to menu for \c menu() method.
-       NameMap name_map_;
-};
+               case md_importformats:
+                       add(MenuItem(MenuItem::ImportFormats));
+                       break;
 
+               case md_floatlistinsert:
+                       add(MenuItem(MenuItem::FloatListInsert));
+                       break;
 
-Menus::Menus(): d(new Impl) {}
+               case md_floatinsert:
+                       add(MenuItem(MenuItem::FloatInsert));
+                       break;
 
+               case md_pasterecent:
+                       add(MenuItem(MenuItem::PasteRecent));
+                       break;
 
-bool Menus::searchMenu(FuncRequest const & func,
-       vector<docstring> & names) const
-{
-       return d->menubar_.searchMenu(func, names);
-}
+               case md_toolbars:
+                       add(MenuItem(MenuItem::Toolbars));
+                       break;
 
+               case md_branches:
+                       add(MenuItem(MenuItem::Branches));
+                       break;
 
-void Menus::fillMenuBar(GuiView * view)
-{
-       // Clear all menubar contents before filling it.
-       view->menuBar()->clear();
-       
-#ifdef Q_WS_MACX
-       // setup special mac specific menu item
-       macxMenuBarInit(view);
-#endif
+               case md_citestyles:
+                       add(MenuItem(MenuItem::CiteStyles));
+                       break;
 
-       LYXERR(Debug::GUI, "populating menu bar" << fromqstr(d->menubar_.name()));
-
-       if (d->menubar_.size() == 0) {
-               LYXERR(Debug::GUI, "\tERROR: empty menu bar"
-                       << fromqstr(d->menubar_.name()));
-               return;
-       }
-       else {
-               LYXERR(Debug::GUI, "menu bar entries "
-                       << d->menubar_.size());
-       }
-
-       Menu menu;
-       d->expand(d->menubar_, menu, view->buffer());
-
-       Menu::const_iterator m = menu.begin();
-       Menu::const_iterator end = menu.end();
-
-       for (; m != end; ++m) {
-
-               if (m->kind() != MenuItem::Submenu) {
-                       LYXERR(Debug::GUI, "\tERROR: not a submenu " << fromqstr(m->label()));
-                       continue;
-               }
-
-               LYXERR(Debug::GUI, "menu bar item " << fromqstr(m->label())
-                       << " is a submenu named " << fromqstr(m->submenuname()));
-
-               QString name = m->submenuname();
-               if (!d->hasMenu(name)) {
-                       LYXERR(Debug::GUI, "\tERROR: " << fromqstr(name)
-                               << " submenu has no menu!");
-                       continue;
-               }
-
-               GuiPopupMenu * qmenu = new GuiPopupMenu(view, *m, true);
-               view->menuBar()->addMenu(qmenu);
-
-               d->name_map_[name] = qmenu;
-       }
-}
-
-
-void Menus::updateMenu(QString const & name)
-{
-       GuiPopupMenu * qmenu = d->name_map_[name];
-       LYXERR(Debug::GUI, "GuiPopupMenu::updateView()"
-               << "\tTriggered menu: " << fromqstr(qmenu->name_));
-       qmenu->clear();
-
-       if (qmenu->name_.isEmpty())
-               return;
-
-       // Here, We make sure that theLyXFunc points to the correct LyXView.
-       theLyXFunc().setLyXView(qmenu->owner_);
-
-       Menu const & fromLyxMenu = d->getMenu(qmenu->name_);
-       d->expand(fromLyxMenu, qmenu->topLevelMenu_, qmenu->owner_->buffer());
-
-       if (!d->hasMenu(qmenu->topLevelMenu_.name())) {
-               LYXERR(Debug::GUI, "\tWARNING: menu seems empty"
-                       << fromqstr(qmenu->topLevelMenu_.name()));
-       }
-       qmenu->populate(qmenu, &qmenu->topLevelMenu_);
-}
-
-
-QMenu * Menus::menu(QString const & name)
-{
-       LYXERR(Debug::GUI, "Context menu requested: " << fromqstr(name));
-       GuiPopupMenu * menu = d->name_map_.value(name, 0);
-       if (!menu)
-               LYXERR0("resquested context menu not found: " << fromqstr(name));
-       return menu;
-}
-
-
-/// Some special Qt/Mac support hacks
-
-/*
-  Here is what the Qt documentation says about how a menubar is chosen:
-
-     1) If the window has a QMenuBar then it is used. 2) If the window
-     is a modal then its menubar is used. If no menubar is specified
-     then a default menubar is used (as documented below) 3) If the
-     window has no parent then the default menubar is used (as
-     documented below).
-
-     The above 3 steps are applied all the way up the parent window
-     chain until one of the above are satisifed. If all else fails a
-     default menubar will be created, the default menubar on Qt/Mac is
-     an empty menubar, however you can create a different default
-     menubar by creating a parentless QMenuBar, the first one created
-     will thus be designated the default menubar, and will be used
-     whenever a default menubar is needed.
-
-  Thus, for Qt/Mac, we add the menus to a free standing menubar, so
-  that this menubar will be used also when one of LyX' dialogs has
-  focus. (JMarc)
-*/
-
-void Menus::Impl::macxMenuBarInit(GuiView * view)
-{
-       // The Mac menubar initialisation must be done only once!
-       static bool done = false;
-       if (done)
-               return;
-       done = true;
-
-       /* Since Qt 4.2, the qt/mac menu code has special code for
-          specifying the role of a menu entry. However, it does not
-          work very well with our scheme of creating menus on demand,
-          and therefore we need to put these entries in a special
-          invisible menu. (JMarc)
-       */
-
-       /* The entries of our special mac menu. If we add support for
-        * 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
-        * the hassle, though. (JMarc)
-        */
-       struct MacMenuEntry {
-               kb_action action;
-               char const * arg;
-               char const * label;
-               QAction::MenuRole role;
-       };
-
-       MacMenuEntry entries[] = {
-               {LFUN_DIALOG_SHOW, "aboutlyx", "About LyX",
-                QAction::AboutRole},
-               {LFUN_DIALOG_SHOW, "prefs", "Preferences",
-                QAction::PreferencesRole},
-               {LFUN_RECONFIGURE, "", "Reconfigure",
-                QAction::ApplicationSpecificRole},
-               {LFUN_LYX_QUIT, "", "Quit LyX", QAction::QuitRole}
-       };
-       const size_t num_entries = sizeof(entries) / sizeof(entries[0]);
-
-       // 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, entries[i].label, func));
-       }
-       setSpecialMenu(special);
-
-       // add the entries to a QMenu that will eventually be empty
-       // and therefore invisible.
-       QMenu * qMenu = view->menuBar()->addMenu("special");
-
-       // we do not use 'special' because it is a temporary variable,
-       // 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(), cit->label(),
-                                            cit->func(), QString());
-               action->setMenuRole(entries[i].role);
-               qMenu->addAction(action);
-       }
-}
-
-
-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::setSubmenu(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();
-}
-
-
-void 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);
-       }
-}
-
-
-void 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_graphicsgroups:
+                       add(MenuItem(MenuItem::GraphicsGroups));
+                       break;
 
                case md_optsubmenu:
                        optional = true;
@@ -882,8 +544,7 @@ void Menu::read(Lexer & lex)
                        break;
 
                default:
-                       lex.printError("Menu::read: "
-                                      "Unknown menu tag: `$$Token'");
+                       lex.printError("Unknown menu tag");
                        break;
                }
        }
@@ -891,13 +552,13 @@ void Menu::read(Lexer & lex)
 }
 
 
-MenuItem const & Menu::operator[](size_type i) const
+MenuItem const & MenuDefinition::operator[](size_type i) const
 {
        return items_[i];
 }
 
 
-bool Menu::hasFunc(FuncRequest const & func) const
+bool MenuDefinition::hasFunc(FuncRequest const & func) const
 {
        for (const_iterator it = begin(), et = end(); it != et; ++it)
                if (it->func() == func)
@@ -906,7 +567,7 @@ bool Menu::hasFunc(FuncRequest const & func) const
 }
 
 
-void Menu::checkShortcuts() const
+void MenuDefinition::checkShortcuts() const
 {
        // This is a quadratic algorithm, but we do not care because
        // menus are short enough
@@ -915,24 +576,23 @@ void Menu::checkShortcuts() const
                if (shortcut.isEmpty())
                        continue;
                if (!it1->label().contains(shortcut))
-                       lyxerr << "Menu warning: menu entry \""
-                              << fromqstr(it1->label())
+                       LYXERR0("Menu warning: menu entry \""
+                              << it1->label()
                               << "\" does not contain shortcut `"
-                              << fromqstr(shortcut) << "'." << endl;
+                              << shortcut << "'.");
                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;
+                               LYXERR0("Menu warning: menu entries "
+                                      << '"' << it1->fulllabel()
+                                      << "\" and \"" << it2->fulllabel()
+                                      << "\" share the same shortcut.");
                        }
                }
        }
 }
 
 
-bool Menu::searchMenu(FuncRequest const & func, vector<docstring> & names) const
+bool MenuDefinition::searchMenu(FuncRequest const & func, docstring_list & names) const
 {
        const_iterator m = begin();
        const_iterator m_end = end();
@@ -943,8 +603,13 @@ bool Menu::searchMenu(FuncRequest const & func, vector<docstring> & names) const
                }
                if (m->kind() == MenuItem::Submenu) {
                        names.push_back(qstring_to_ucs4(m->label()));
-                       Menu const & submenu = *m->submenu();
-                       if (submenu.searchMenu(func, names))
+                       if (!m->hasSubmenu()) {
+                               LYXERR(Debug::GUI, "Warning: non existing sub menu label="
+                                       << m->label() << " name=" << m->submenuname());
+                               names.pop_back();
+                               continue;
+                       }
+                       if (m->submenu().searchMenu(func, names))
                                return true;
                        names.pop_back();
                }
@@ -953,8 +618,6 @@ bool Menu::searchMenu(FuncRequest const & func, vector<docstring> & names) const
 }
 
 
-namespace {
-
 bool compareFormat(Format const * p1, Format const * p2)
 {
        return *p1 < *p2;
@@ -971,12 +634,29 @@ QString limitStringLength(docstring const & str)
        return toqstr(str);
 }
 
-} // namespace anon
 
+void MenuDefinition::expandGraphicsGroups(BufferView const * bv)
+{
+       if (!bv)
+               return;
+       set<string> grp;
+       graphics::getGraphicsGroups(bv->buffer(), grp);
+       if (grp.empty())
+               return;
 
-void Menu::expandLastfiles()
+       set<string>::const_iterator it = grp.begin();
+       set<string>::const_iterator end = grp.end();
+       add(MenuItem(MenuItem::Command, qt_("No Group"), 
+                    FuncRequest(LFUN_SET_GRAPHICS_GROUP)));
+       for (; it != end; it++) {
+               addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(*it),
+                               FuncRequest(LFUN_SET_GRAPHICS_GROUP, *it)));
+       }
+}
+
+void MenuDefinition::expandLastfiles()
 {
-       LastFilesSection::LastFiles const & lf = LyX::cref().session().lastFiles().lastFiles();
+       LastFilesSection::LastFiles const & lf = theSession().lastFiles().lastFiles();
        LastFilesSection::LastFiles::const_iterator lfit = lf.begin();
 
        int ii = 1;
@@ -990,7 +670,7 @@ void Menu::expandLastfiles()
 }
 
 
-void Menu::expandDocuments()
+void MenuDefinition::expandDocuments()
 {
        Buffer * first = theBufferList().first();
        if (first) {
@@ -1017,9 +697,9 @@ void Menu::expandDocuments()
 }
 
 
-void Menu::expandBookmarks()
+void MenuDefinition::expandBookmarks()
 {
-       lyx::BookmarksSection const & bm = LyX::cref().session().bookmarks();
+       lyx::BookmarksSection const & bm = theSession().bookmarks();
 
        for (size_t i = 1; i <= bm.size(); ++i) {
                if (bm.isValid(i)) {
@@ -1033,7 +713,7 @@ void Menu::expandBookmarks()
 }
 
 
-void Menu::expandFormats(MenuItem::Kind kind, Buffer const * buf)
+void MenuDefinition::expandFormats(MenuItem::Kind kind, Buffer const * buf)
 {
        if (!buf && kind != MenuItem::ImportFormats) {
                add(MenuItem(MenuItem::Command,
@@ -1044,7 +724,7 @@ void Menu::expandFormats(MenuItem::Kind kind, Buffer const * buf)
 
        typedef vector<Format const *> Formats;
        Formats formats;
-       kb_action action;
+       FuncCode action;
 
        switch (kind) {
        case MenuItem::ImportFormats:
@@ -1070,17 +750,24 @@ void Menu::expandFormats(MenuItem::Kind kind, Buffer const * buf)
        for (; fit != end ; ++fit) {
                if ((*fit)->dummy())
                        continue;
-               QString label = toqstr((*fit)->prettyname());
-               QString const shortcut = toqstr((*fit)->shortcut());
+
+               docstring lab = from_utf8((*fit)->prettyname());
+               docstring scut = from_utf8((*fit)->shortcut());
+               docstring const tmplab = lab;
+               if (!scut.empty())
+                       lab += char_type('|') + scut;
+               docstring lab_i18n = translateIfPossible(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));
 
                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:
@@ -1090,15 +777,10 @@ void Menu::expandFormats(MenuItem::Kind kind, Buffer const * buf)
                                continue;
                        break;
                default:
-                       BOOST_ASSERT(false);
+                       LASSERT(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))
+               if (!shortcut.isEmpty())
                        label += '|' + shortcut;
 
                if (buf)
@@ -1111,7 +793,7 @@ void Menu::expandFormats(MenuItem::Kind kind, Buffer const * buf)
 }
 
 
-void Menu::expandFloatListInsert(Buffer const * buf)
+void MenuDefinition::expandFloatListInsert(Buffer const * buf)
 {
        if (!buf) {
                add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
@@ -1125,13 +807,13 @@ void Menu::expandFloatListInsert(Buffer const * buf)
        for (; cit != end; ++cit) {
                addWithStatusCheck(MenuItem(MenuItem::Command,
                                    qt_(cit->second.listName()),
-                                   FuncRequest(LFUN_FLOAT_LIST,
+                                   FuncRequest(LFUN_FLOAT_LIST_INSERT,
                                                cit->second.type())));
        }
 }
 
 
-void Menu::expandFloatInsert(Buffer const * buf)
+void MenuDefinition::expandFloatInsert(Buffer const * buf)
 {
        if (!buf) {
                add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
@@ -1152,7 +834,8 @@ void Menu::expandFloatInsert(Buffer const * buf)
 }
 
 
-void Menu::expandFlexInsert(Buffer const * buf, string s)
+void MenuDefinition::expandFlexInsert(
+               Buffer const * buf, InsetLayout::InsetLyXType type)
 {
        if (!buf) {
                add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
@@ -1165,17 +848,22 @@ void Menu::expandFlexInsert(Buffer const * buf, string s)
        TextClass::InsetLayouts::const_iterator end = insetLayouts.end();
        for (; cit != end; ++cit) {
                docstring const label = cit->first;
-               if (cit->second.lyxtype() == s)
+               if (cit->second.lyxtype() == type)
                        addWithStatusCheck(MenuItem(MenuItem::Command, 
-                               toqstr(label), FuncRequest(LFUN_FLEX_INSERT,
-                                               label)));
+                               toqstr(translateIfPossible(label)),
+                               FuncRequest(LFUN_FLEX_INSERT, label)));
        }
+       // FIXME This is a little clunky.
+       if (items_.empty() && type == InsetLayout::CUSTOM)
+               add(MenuItem(MenuItem::Command,
+                                   qt_("No custom insets defined!"),
+                                   FuncRequest(LFUN_NOACTION)));
 }
 
 
 size_t const max_number_of_items = 25;
 
-void Menu::expandToc2(Toc const & toc_list,
+void MenuDefinition::expandToc2(Toc const & toc_list,
                size_t from, size_t to, int depth)
 {
        int shortcut_count = 0;
@@ -1203,8 +891,7 @@ void Menu::expandToc2(Toc const & toc_list,
                size_t pos = from;
                while (pos < to) {
                        size_t new_pos = pos + 1;
-                       while (new_pos < to &&
-                              toc_list[new_pos].depth() > depth)
+                       while (new_pos < to && toc_list[new_pos].depth() > depth)
                                ++new_pos;
 
                        QString label(4 * max(0, toc_list[pos].depth() - depth), ' ');
@@ -1218,9 +905,10 @@ void Menu::expandToc2(Toc const & toc_list,
                                add(MenuItem(MenuItem::Command,
                                                    label, FuncRequest(toc_list[pos].action())));
                        } else {
+                               MenuDefinition sub;
+                               sub.expandToc2(toc_list, pos, new_pos, depth + 1);
                                MenuItem item(MenuItem::Submenu, label);
-                               item.setSubmenu(new Menu);
-                               item.submenu()->expandToc2(toc_list, pos, new_pos, depth + 1);
+                               item.setSubmenu(sub);
                                add(item);
                        }
                        pos = new_pos;
@@ -1229,7 +917,7 @@ void Menu::expandToc2(Toc const & toc_list,
 }
 
 
-void Menu::expandToc(Buffer const * buf)
+void MenuDefinition::expandToc(Buffer const * buf)
 {
        // To make things very cleanly, we would have to pass buf to
        // all MenuItem constructors and to expandToc2. However, we
@@ -1242,10 +930,6 @@ void Menu::expandToc(Buffer const * buf)
                return;
        }
 
-       Buffer* cbuf = const_cast<Buffer*>(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) {
@@ -1255,6 +939,8 @@ void Menu::expandToc(Buffer const * buf)
                add(MenuItem(MenuItem::Command, qt_("Master Document"), f));
        }
 
+       MenuDefinition other_lists;
+       
        FloatList const & floatlist = buf->params().documentClass().floats();
        TocList const & toc_list = buf->tocBackend().tocs();
        TocList::const_iterator cit = toc_list.begin();
@@ -1264,43 +950,32 @@ void Menu::expandToc(Buffer const * buf)
                if (cit->first == "tableofcontents")
                        continue;
 
-               // All the rest is for floats
-               auto_ptr<Menu> 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())));
+               MenuDefinition submenu;
+               if (cit->second.size() >= 30) {
+                       FuncRequest f(LFUN_DIALOG_SHOW, "toc " + cit->first);
+                       submenu.add(MenuItem(MenuItem::Command, qt_("Open Navigator..."), f));
+               } else {
+                       TocIterator ccit = cit->second.begin();
+                       TocIterator eend = cit->second.end();
+                       for (; ccit != eend; ++ccit) {
+                               submenu.add(MenuItem(MenuItem::Command,
+                                       limitStringLength(ccit->str()),
+                                       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.setSubmenu(menu.release());
+
+               MenuItem item(MenuItem::Submenu, guiName(cit->first, buf->params()));
+               item.setSubmenu(submenu);
+               if (floatlist.typeExist(cit->first) || cit->first == "child") {
+                       // Those two types deserve to be in the main menu.
+                       item.setSubmenu(submenu);
+                       add(item);
+               } else
+                       other_lists.add(item);
+       }
+       if (!other_lists.empty()) {
+               MenuItem item(MenuItem::Submenu, qt_("Other Lists"));
+               item.setSubmenu(other_lists);
                add(item);
        }
 
@@ -1310,18 +985,17 @@ void Menu::expandToc(Buffer const * buf)
                addWithStatusCheck(MenuItem(MenuItem::Command,
                                    qt_("No Table of contents"),
                                    FuncRequest()));
-       } else {
+       } else
                expandToc2(cit->second, 0, cit->second.size(), 0);
-       }
 }
 
 
-void Menu::expandPasteRecent()
+void MenuDefinition::expandPasteRecent(Buffer const * buf)
 {
-       vector<docstring> const sel = cap::availableSelections();
+       docstring_list const sel = cap::availableSelections(buf);
 
-       vector<docstring>::const_iterator cit = sel.begin();
-       vector<docstring>::const_iterator end = sel.end();
+       docstring_list::const_iterator cit = sel.begin();
+       docstring_list::const_iterator end = sel.end();
 
        for (unsigned int index = 0; cit != end; ++cit, ++index) {
                add(MenuItem(MenuItem::Command, toqstr(*cit),
@@ -1330,34 +1004,30 @@ void Menu::expandPasteRecent()
 }
 
 
-void Menu::expandToolbars()
+void MenuDefinition::expandToolbars()
 {
-       //
+       MenuDefinition other_lists;
        // extracts the toolbars from the backend
-       ToolbarBackend::Toolbars::const_iterator cit = toolbarbackend.begin();
-       ToolbarBackend::Toolbars::const_iterator end = toolbarbackend.end();
-
+       Toolbars::Infos::const_iterator cit = guiApp->toolbars().begin();
+       Toolbars::Infos::const_iterator end = guiApp->toolbars().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)");
-               add(MenuItem(MenuItem::Command, label,
-                                   FuncRequest(LFUN_TOOLBAR_TOGGLE, cit->name + " allowauto")));
+               MenuItem const item(MenuItem::Command, toqstr(cit->gui_name),
+                               FuncRequest(LFUN_TOOLBAR_TOGGLE, cit->name));
+               if (guiApp->toolbars().isMainToolbar(cit->name))
+                       add(item);
+               else
+                       other_lists.add(item);
+       }
+
+       if (!other_lists.empty()) {
+               MenuItem item(MenuItem::Submenu, qt_("Other Toolbars"));
+               item.setSubmenu(other_lists);
+               add(item);
        }
 }
 
 
-void Menu::expandBranches(Buffer const * buf)
+void MenuDefinition::expandBranches(Buffer const * buf)
 {
        if (!buf) {
                add(MenuItem(MenuItem::Command,
@@ -1378,24 +1048,296 @@ void Menu::expandBranches(Buffer const * buf)
        BranchList::const_iterator end = params.branchlist().end();
 
        for (int ii = 1; cit != end; ++cit, ++ii) {
-               docstring label = cit->getBranch();
-               if (ii < 10)
-                       label = convert<docstring>(ii) + ". " + label + char_type('|') + convert<docstring>(ii);
+               docstring label = cit->branch();
+               if (ii < 10) {
+                       label = convert<docstring>(ii) + ". " + label
+                               + char_type('|') + convert<docstring>(ii);
+               }
                addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
                                    FuncRequest(LFUN_BRANCH_INSERT,
-                                               cit->getBranch())));
+                                               cit->branch())));
+       }
+}
+
+
+void MenuDefinition::expandCiteStyles(BufferView const * bv)
+{
+       if (!bv) {
+               add(MenuItem(MenuItem::Command,
+                                   qt_("No Document Open!"),
+                                   FuncRequest(LFUN_NOACTION)));
+               return;
+       }
+
+       Inset const * inset = bv->cursor().nextInset();
+       if (!inset || inset->lyxCode() != CITE_CODE) {
+               add(MenuItem(MenuItem::Command,
+                                   qt_("No Citation in Scope!"),
+                                   FuncRequest(LFUN_NOACTION)));
+               return;
+       }
+       InsetCommand const * citinset =
+                               static_cast<InsetCommand const *>(inset);
+       
+       Buffer const * buf = &bv->buffer();
+       docstring key = citinset->getParam("key");
+       // we can only handle one key currently
+       if (contains(key, ','))
+               key = qstring_to_ucs4(toqstr(key).split(',')[0]);
+
+       vector<CiteStyle> citeStyleList = citeStyles(buf->params().citeEngine());
+       docstring_list citeStrings =
+               buf->masterBibInfo().getCiteStrings(key, bv->buffer());
+
+       docstring_list::const_iterator cit = citeStrings.begin();
+       docstring_list::const_iterator end = citeStrings.end();
+
+       for (int ii = 1; cit != end; ++cit, ++ii) {
+               docstring label = *cit;
+               CitationStyle cs;
+               CiteStyle cst = citeStyleList[ii - 1];
+               cs.style = cst;
+               addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
+                                   FuncRequest(LFUN_NEXT_INSET_MODIFY,
+                                               "changetype " + from_utf8(citationStyleToString(cs)))));
+       }
+}
+
+} // namespace anon
+
+
+/////////////////////////////////////////////////////////////////////
+// Menu::Impl definition and implementation
+/////////////////////////////////////////////////////////////////////
+
+struct Menu::Impl
+{
+       /// populates the menu or one of its submenu
+       /// This is used as a recursive function
+       void populate(QMenu & qMenu, MenuDefinition const & menu);
+
+       /// Only needed for top level menus.
+       MenuDefinition * top_level_menu;
+       /// our owning view
+       GuiView * view;
+       /// the name of this menu
+       QString name;
+};
+
+
+
+/// Get a MenuDefinition item label from the menu backend
+static QString label(MenuItem const & mi)
+{
+       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, "&");
+       }
+
+       QString const binding = mi.binding();
+       if (!binding.isEmpty())
+               label += '\t' + binding;
+
+       return label;
+}
+
+void Menu::Impl::populate(QMenu & qMenu, MenuDefinition const & menu)
+{
+       LYXERR(Debug::GUI, "populating menu " << menu.name());
+       if (menu.size() == 0) {
+               LYXERR(Debug::GUI, "\tERROR: empty menu " << menu.name());
+               return;
+       }
+       LYXERR(Debug::GUI, " *****  menu entries " << menu.size());
+       MenuDefinition::const_iterator m = menu.begin();
+       MenuDefinition::const_iterator end = menu.end();
+       for (; m != end; ++m) {
+               if (m->kind() == MenuItem::Separator)
+                       qMenu.addSeparator();
+               else if (m->kind() == MenuItem::Submenu) {
+                       QMenu * subMenu = qMenu.addMenu(label(*m));
+                       populate(*subMenu, m->submenu());
+               } else {
+                       // we have a MenuItem::Command
+                       qMenu.addAction(new Action(view, QIcon(), label(*m), 
+                               m->func(), QString(), &qMenu));
+               }
+       }
+}
+
+/////////////////////////////////////////////////////////////////////
+// Menu implementation
+/////////////////////////////////////////////////////////////////////
+
+Menu::Menu(GuiView * gv, QString const & name, bool top_level)
+: QMenu(gv), d(new Menu::Impl)
+{
+       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()));
+}
+
+
+Menu::~Menu()
+{
+       delete d->top_level_menu;
+       delete d;
+}
+
+
+void Menu::updateView()
+{
+       guiApp->menus().updateMenu(this);
+}
+
+
+/////////////////////////////////////////////////////////////////////
+// Menus::Impl definition and implementation
+/////////////////////////////////////////////////////////////////////
+
+struct Menus::Impl {
+       ///
+       bool hasMenu(QString const &) const;
+       ///
+       MenuDefinition & getMenu(QString const &);
+       ///
+       MenuDefinition const & getMenu(QString const &) const;
+
+       /// 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(MenuDefinition const & frommenu, MenuDefinition & tomenu,
+               BufferView const *) const;
+
+       /// Initialize specific MACOS X menubar
+       void macxMenuBarInit(GuiView * view, QMenuBar * qmb);
+
+       /// Mac special menu.
+       /** 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.
+
+           NOTE: Qt does not remove the menu items when clearing a QMenuBar,
+           such that the items will keep accessing the FuncRequests in
+           the MenuDefinition. While Menus::Impl might be recreated,
+           we keep mac_special_menu_ in memory by making it static.
+       */
+       static MenuDefinition mac_special_menu_;
+
+       ///
+       MenuList menulist_;
+       ///
+       MenuDefinition menubar_;
+
+       typedef QMap<GuiView *, QHash<QString, Menu*> > NameMap;
+
+       /// name to menu for \c menu() method.
+       NameMap name_map_;
+};
+
+
+MenuDefinition Menus::Impl::mac_special_menu_;
+
+
+/*
+  Here is what the Qt documentation says about how a menubar is chosen:
+
+     1) If the window has a QMenuBar then it is used. 2) If the window
+     is a modal then its menubar is used. If no menubar is specified
+     then a default menubar is used (as documented below) 3) If the
+     window has no parent then the default menubar is used (as
+     documented below).
+
+     The above 3 steps are applied all the way up the parent window
+     chain until one of the above are satisifed. If all else fails a
+     default menubar will be created, the default menubar on Qt/Mac is
+     an empty menubar, however you can create a different default
+     menubar by creating a parentless QMenuBar, the first one created
+     will thus be designated the default menubar, and will be used
+     whenever a default menubar is needed.
+
+  Thus, for Qt/Mac, we add the menus to a free standing menubar, so
+  that this menubar will be used also when one of LyX' dialogs has
+  focus. (JMarc)
+*/
+void Menus::Impl::macxMenuBarInit(GuiView * view, QMenuBar * qmb)
+{
+       /* Since Qt 4.2, the qt/mac menu code has special code for
+          specifying the role of a menu entry. However, it does not
+          work very well with our scheme of creating menus on demand,
+          and therefore we need to put these entries in a special
+          invisible menu. (JMarc)
+       */
+
+       /* The entries of our special mac menu. If we add support for
+        * 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
+        * the hassle, though. (JMarc)
+        */
+       struct MacMenuEntry {
+               FuncCode action;
+               char const * arg;
+               char const * label;
+               QAction::MenuRole role;
+       };
+
+       MacMenuEntry entries[] = {
+               {LFUN_DIALOG_SHOW, "aboutlyx", "About LyX",
+                QAction::AboutRole},
+               {LFUN_DIALOG_SHOW, "prefs", "Preferences",
+                QAction::PreferencesRole},
+               {LFUN_RECONFIGURE, "", "Reconfigure",
+                QAction::ApplicationSpecificRole},
+               {LFUN_LYX_QUIT, "", "Quit LyX", QAction::QuitRole}
+       };
+       const size_t num_entries = sizeof(entries) / sizeof(entries[0]);
+
+       // the special menu for Menus. Fill it up only once.
+       if (mac_special_menu_.size() == 0) {
+               for (size_t i = 0 ; i < num_entries ; ++i) {
+                       FuncRequest const func(entries[i].action,
+                               from_utf8(entries[i].arg));
+                       mac_special_menu_.add(MenuItem(MenuItem::Command,
+                               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) {
+               Action * action = new Action(view, QIcon(), cit->label(),
+                       cit->func(), QString(), qMenu);
+               action->setMenuRole(entries[i].role);
+               qMenu->addAction(action);
        }
 }
 
 
-void Menus::Impl::expand(Menu const & frommenu, Menu & tomenu,
-                        Buffer const * buf) const
+void Menus::Impl::expand(MenuDefinition const & frommenu,
+       MenuDefinition & tomenu, BufferView const * bv) const
 {
        if (!tomenu.empty())
                tomenu.clear();
 
-       for (Menu::const_iterator cit = frommenu.begin();
+       for (MenuDefinition::const_iterator cit = frommenu.begin();
             cit != frommenu.end() ; ++cit) {
+               Buffer const * buf = bv ? &bv->buffer() : 0;
                switch (cit->kind()) {
                case MenuItem::Lastfiles:
                        tomenu.expandLastfiles();
@@ -1417,15 +1359,15 @@ void Menus::Impl::expand(Menu const & frommenu, Menu & tomenu,
                        break;
 
                case MenuItem::CharStyles:
-                       tomenu.expandFlexInsert(buf, "charstyle");
+                       tomenu.expandFlexInsert(buf, InsetLayout::CHARSTYLE);
                        break;
 
                case MenuItem::Custom:
-                       tomenu.expandFlexInsert(buf, "custom");
+                       tomenu.expandFlexInsert(buf, InsetLayout::CUSTOM);
                        break;
 
                case MenuItem::Elements:
-                       tomenu.expandFlexInsert(buf, "element");
+                       tomenu.expandFlexInsert(buf, InsetLayout::ELEMENT);
                        break;
 
                case MenuItem::FloatListInsert:
@@ -1437,7 +1379,7 @@ void Menus::Impl::expand(Menu const & frommenu, Menu & tomenu,
                        break;
 
                case MenuItem::PasteRecent:
-                       tomenu.expandPasteRecent();
+                       tomenu.expandPasteRecent(buf);
                        break;
 
                case MenuItem::Toolbars:
@@ -1448,15 +1390,22 @@ void Menus::Impl::expand(Menu const & frommenu, Menu & tomenu,
                        tomenu.expandBranches(buf);
                        break;
 
+               case MenuItem::CiteStyles:
+                       tomenu.expandCiteStyles(bv);
+                       break;
+
                case MenuItem::Toc:
                        tomenu.expandToc(buf);
                        break;
 
+               case MenuItem::GraphicsGroups:
+                       tomenu.expandGraphicsGroups(bv);
+                       break;
+
                case MenuItem::Submenu: {
                        MenuItem item(*cit);
-                       item.setSubmenu(new Menu(cit->submenuname()));
-                       expand(getMenu(cit->submenuname()),
-                              *item.submenu(), buf);
+                       item.setSubmenu(MenuDefinition(cit->submenuname()));
+                       expand(getMenu(cit->submenuname()), item.submenu(), bv);
                        tomenu.addWithStatusCheck(item);
                }
                break;
@@ -1466,14 +1415,13 @@ void Menus::Impl::expand(Menu const & frommenu, Menu & tomenu,
                        break;
 
                case MenuItem::Command:
-                       if (!specialmenu_.hasFunc(cit->func()))
+                       if (!mac_special_menu_.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)
+       if (!tomenu.empty() && tomenu.items_.back().kind() == MenuItem::Separator)
                tomenu.items_.pop_back();
 
        // Check whether the shortcuts are unique
@@ -1481,30 +1429,77 @@ void Menus::Impl::expand(Menu const & frommenu, Menu & tomenu,
 }
 
 
+bool Menus::Impl::hasMenu(QString const & name) const
+{
+       return find_if(menulist_.begin(), menulist_.end(),
+               MenuNamesEqual(name)) != menulist_.end();
+}
+
+
+MenuDefinition const & Menus::Impl::getMenu(QString const & name) const
+{
+       const_iterator cit = find_if(menulist_.begin(), menulist_.end(),
+               MenuNamesEqual(name));
+       if (cit == menulist_.end())
+               LYXERR0("No submenu named " << name);
+       LASSERT(cit != menulist_.end(), /**/);
+       return (*cit);
+}
+
+
+MenuDefinition & Menus::Impl::getMenu(QString const & name)
+{
+       iterator it = find_if(menulist_.begin(), menulist_.end(),
+               MenuNamesEqual(name));
+       if (it == menulist_.end())
+               LYXERR0("No submenu named " << name);
+       LASSERT(it != menulist_.end(), /**/);
+       return (*it);
+}
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Menus 
+//
+/////////////////////////////////////////////////////////////////////
+
+Menus::Menus() : d(new Impl) {}
+
+
+Menus::~Menus()
+{
+  delete d;
+}
+
+
+void Menus::reset()
+{
+       delete d;
+       d = new Impl;
+}
+
+
 void Menus::read(Lexer & lex)
 {
-       enum Menutags {
-               md_menu = 1,
+       enum {
+               md_menu,
                md_menubar,
                md_endmenuset,
-               md_last
        };
 
-       struct keyword_item menutags[md_last - 1] = {
+       LexerKeyword menutags[] = {
                { "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;
-       }
+       // consistency check
+       if (compare_ascii_no_case(lex.getString(), "menuset"))
+               LYXERR0("Menus::read: ERROR wrong token: `" << lex.getString() << '\'');
 
-       lex.pushTable(menutags, md_last - 1);
-       if (lyxerr.debugging(Debug::PARSER))
-               lex.printTable(lyxerr);
+       lex.pushTable(menutags);
+       lex.setContext("Menus::read");
 
        bool quit = false;
 
@@ -1516,12 +1511,12 @@ void Menus::read(Lexer & lex)
                case md_menu: {
                        lex.next(true);
                        QString const name = toqstr(lex.getDocString());
-                       if (d->hasMenu(name)) {
+                       if (d->hasMenu(name))
                                d->getMenu(name).read(lex);
-                       else {
-                               Menu menu(name);
+                       else {
+                               MenuDefinition menu(name);
                                menu.read(lex);
-                               d->add(menu);
+                               d->menulist_.push_back(menu);
                        }
                        break;
                }
@@ -1529,8 +1524,7 @@ void Menus::read(Lexer & lex)
                        quit = true;
                        break;
                default:
-                       lex.printError("menubackend::read: "
-                                      "Unknown menu tag: `$$Token'");
+                       lex.printError("Unknown menu tag");
                        break;
                }
        }
@@ -1538,39 +1532,117 @@ void Menus::read(Lexer & lex)
 }
 
 
-void Menus::Impl::add(Menu const & menu)
+bool Menus::searchMenu(FuncRequest const & func,
+       docstring_list & names) const
 {
-       menulist_.push_back(menu);
+       MenuDefinition menu;
+       d->expand(d->menubar_, menu, 0);
+       return menu.searchMenu(func, names);
 }
 
 
-bool Menus::Impl::hasMenu(QString const & name) const
+void Menus::fillMenuBar(QMenuBar * qmb, GuiView * view, bool initial)
 {
-       return find_if(begin(), end(), MenuNamesEqual(name)) != end();
+       if (initial) {
+#ifdef Q_WS_MACX
+               // setup special mac specific menu items, but only do this
+               // the first time a QMenuBar is created. Otherwise Qt will
+               // create duplicate items in the application menu. It seems
+               // that Qt does not remove them when the QMenubar is cleared.
+               LYXERR(Debug::GUI, "Creating Mac OS X special menu bar");
+               d->macxMenuBarInit(view, qmb);
+#endif
+       } else {
+               // Clear all menubar contents before filling it.
+               qmb->clear();
+       }
+
+       LYXERR(Debug::GUI, "populating menu bar" << d->menubar_.name());
+
+       if (d->menubar_.size() == 0) {
+               LYXERR(Debug::GUI, "\tERROR: empty menu bar"
+                       << d->menubar_.name());
+               return;
+       }
+       LYXERR(Debug::GUI, "menu bar entries " << d->menubar_.size());
+
+       MenuDefinition menu;
+       BufferView * bv = 0;
+       if (view)
+               bv = view->view();
+       d->expand(d->menubar_, menu, bv);
+
+       MenuDefinition::const_iterator m = menu.begin();
+       MenuDefinition::const_iterator end = menu.end();
+
+       for (; m != end; ++m) {
+
+               if (m->kind() != MenuItem::Submenu) {
+                       LYXERR(Debug::GUI, "\tERROR: not a submenu " << m->label());
+                       continue;
+               }
+
+               LYXERR(Debug::GUI, "menu bar item " << m->label()
+                       << " is a submenu named " << m->submenuname());
+
+               QString name = m->submenuname();
+               if (!d->hasMenu(name)) {
+                       LYXERR(Debug::GUI, "\tERROR: " << name
+                               << " submenu has no menu!");
+                       continue;
+               }
+
+               Menu * menu = new Menu(view, m->submenuname(), true);
+               menu->setTitle(label(*m));
+               qmb->addMenu(menu);
+
+               d->name_map_[view][name] = menu;
+       }
 }
 
 
-Menu const & Menus::Impl::getMenu(QString const & name) const
+void Menus::updateMenu(Menu * qmenu)
 {
-       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);
+       LYXERR(Debug::GUI, "Triggered menu: " << qmenu->d->name);
+       qmenu->clear();
+
+       if (qmenu->d->name.isEmpty())
+               return;
+
+       // Here, We make sure that theLyXFunc points to the correct LyXView.
+       theLyXFunc().setLyXView(qmenu->d->view);
+
+       if (!d->hasMenu(qmenu->d->name)) {
+               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->view();
+       d->expand(fromLyxMenu, *qmenu->d->top_level_menu, bv);
+       qmenu->d->populate(*qmenu, *qmenu->d->top_level_menu);
 }
 
 
-Menu & Menus::Impl::getMenu(QString const & name)
+Menu * Menus::menu(QString const & name, GuiView & view)
 {
-       iterator it = find_if(begin(), end(), MenuNamesEqual(name));
-       if (it == end())
-               lyxerr << "No submenu named " << fromqstr(name) << endl;
-       BOOST_ASSERT(it != end());
-       return (*it);
-}
+       LYXERR(Debug::GUI, "Context menu requested: " << name);
+       Menu * menu = d->name_map_[&view].value(name, 0);
+       if (!menu && !name.startsWith("context-")) {
+               LYXERR0("requested context menu not found: " << name);
+               return 0;
+       }
 
+       menu = new Menu(&view, name, true);
+       d->name_map_[&view][name] = menu;
+       return menu;
+}
 
 } // namespace frontend
 } // namespace lyx
 
-#include "Menus_moc.cpp"
+#include "moc_Menus.cpp"