]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/Menus.cpp
Fix bug http://bugzilla.lyx.org/show_bug.cgi?id=4621
[lyx.git] / src / frontends / qt4 / Menus.cpp
index 4d83752d2d12bd168c878fe9d52a649ab90273f8..9202893871154a9bbc61cd37760adebf117eb081 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
  *
@@ -20,8 +20,8 @@
 
 #include "Action.h"
 #include "GuiApplication.h"
-#include "GuiPopupMenu.h"
 #include "GuiView.h"
+#include "qt_helpers.h"
 
 #include "BranchList.h"
 #include "Buffer.h"
 #include "Floating.h"
 #include "FloatList.h"
 #include "Format.h"
+#include "FuncRequest.h"
+#include "FuncStatus.h"
 #include "KeyMap.h"
-#include "Session.h"
+#include "Lexer.h"
 #include "LyXAction.h"
 #include "LyX.h" // for lastfiles
 #include "LyXFunc.h"
-#include "Lexer.h"
 #include "Paragraph.h"
+#include "Session.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 "qt_helpers.h"
-
 #include <QCursor>
+#include <QHash>
 #include <QMenuBar>
+#include <QString>
+
+#include <boost/shared_ptr.hpp>
 
 #include <algorithm>
 #include <ostream>
+#include <vector>
 
 using namespace std;
 using namespace lyx::support;
@@ -68,236 +71,239 @@ 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)
-{
-       // Clear all menubar contents before filling it.
-       view->menuBar()->clear();
-       
-#ifdef Q_WS_MACX
-       // setup special mac specific menu item
-       macxMenuBarInit(view);
-#endif
-
-       LYXERR(Debug::GUI, "populating menu bar" << fromqstr(getMenubar().name()));
-
-       if (getMenubar().size() == 0) {
-               LYXERR(Debug::GUI, "\tERROR: empty menu bar"
-                       << fromqstr(getMenubar().name()));
-               return;
-       }
-       else {
-               LYXERR(Debug::GUI, "menu bar entries "
-                       << getMenubar().size());
-       }
-
-       Menu menu;
-       expand(getMenubar(), 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 (!hasMenu(name)) {
-                       LYXERR(Debug::GUI, "\tERROR: " << fromqstr(name)
-                               << " submenu has no menu!");
-                       continue;
-               }
-
-               GuiPopupMenu * qMenu = new GuiPopupMenu(view, *m, true);
-               view->menuBar()->addMenu(qMenu);
-
-               name_map_[name] = qMenu;
-       }
-}
-
-
-QMenu * Menus::menu(QString const & name)
-{
-       LYXERR(Debug::GUI, "Context menu requested: " << fromqstr(name));
-       GuiPopupMenu * menu = name_map_.value(name, 0);
-       if (!menu)
-               LYXERR0("resquested context menu not found: " << fromqstr(name));
-       return menu;
-}
-
+class MenuDefinition;
 
-/// 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::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;
+///
+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
        };
 
-       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]);
+       explicit MenuItem(Kind kind) : kind_(kind), optional_(false) {}
 
-       // 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));
+       MenuItem(Kind kind,
+                QString const & label,
+                QString const & submenu = QString(),
+                bool optional = false)
+               : kind_(kind), label_(label), submenuname_(submenu), optional_(optional)
+       {
+               BOOST_ASSERT(kind == Submenu);
        }
-       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(Kind kind,
+                QString const & label,
+                FuncRequest const & func,
+                bool optional = false)
+               : kind_(kind), label_(label), func_(func), optional_(optional)
+       {
+               func_.origin = FuncRequest::MENU;
        }
-}
 
+       // boost::shared_ptr<MenuDefinition> needs this apprently...
+       ~MenuItem() {}
 
-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);
-}
+       /// The label of a given menuitem
+       QString label() const { return label_.split('|')[0]; }
 
+       /// The keyboard shortcut (usually underlined in the entry)
+       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
+       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
+       {
+               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();
+       }
 
-QString MenuItem::label() const
-{
-       return label_.split('|')[0];
-}
+       /// 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; }
+       ///
+       MenuDefinition * submenu() const { return submenu_.get(); }
+       ///
+       void setSubmenu(MenuDefinition * menu) { submenu_.reset(menu); }
 
+private:
+       ///
+       Kind kind_;
+       ///
+       QString label_;
+       ///
+       FuncRequest func_;
+       ///
+       QString submenuname_;
+       ///
+       bool optional_;
+       ///
+       FuncStatus status_;
+       ///
+       boost::shared_ptr<MenuDefinition> submenu_;
+};
 
-QString MenuItem::shortcut() const
-{
-       return label_.contains('|') ? label_.split('|')[1] : QString();
-}
+///
+class MenuDefinition {
+public:
+       ///
+       typedef std::vector<MenuItem> ItemList;
+       ///
+       typedef ItemList::const_iterator const_iterator;
+       ///
+       explicit MenuDefinition(QString const & name = QString()) : name_(name) {}
+
+       ///
+       void 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;
+       ///
+       const_iterator begin() const { return items_.begin(); }
+       ///
+       const_iterator end() const { return items_.end(); }
+       
+       // search for func in this menu iteratively, and put menu
+       // names in a stack.
+       bool searchMenu(FuncRequest const & func, std::vector<docstring> & names)
+               const;
+       ///
+       bool hasFunc(FuncRequest const &) const;
+       /// Add the menu item unconditionally
+       void add(MenuItem const & item) { items_.push_back(item); }
+       /// Checks the associated FuncRequest status before adding the
+       /// menu item.
+       void addWithStatusCheck(MenuItem const &);
+       // Check whether the menu shortcuts are unique
+       void checkShortcuts() const;
+       ///
+       void expandLastfiles();
+       void expandDocuments();
+       void expandBookmarks();
+       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 expandToc2(Toc const & toc_list, size_t from, size_t to, int depth);
+       void expandToc(Buffer const * buf);
+       void expandPasteRecent();
+       void expandToolbars();
+       void expandBranches(Buffer const * buf);
+       ///
+       ItemList items_;
+       ///
+       QString name_;
+};
 
 
-QString MenuItem::binding() const
+/// Helper for std::find_if
+class MenuNamesEqual
 {
-       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_);
+public:
+       MenuNamesEqual(QString const & name) : name_(name) {}
+       bool operator()(MenuDefinition const & menu) const { return menu.name() == name_; }
+private:
+       QString name_;
+};
 
-       if (bindings.size())
-               return toqstr(bindings.begin()->print(KeySequence::ForGui));
 
-       LYXERR(Debug::KBMAP, "No binding for "
-               << lyxaction.getActionName(func_.action)
-               << '(' << func_.argument() << ')');
-       return QString();
-}
+///
+typedef std::vector<MenuDefinition> MenuList;
+///
+typedef MenuList::const_iterator const_iterator;
+///
+typedef MenuList::iterator iterator;
 
+/////////////////////////////////////////////////////////////////////
+// MenuDefinition implementation
+/////////////////////////////////////////////////////////////////////
 
-void Menu::addWithStatusCheck(MenuItem const & i)
+void MenuDefinition::addWithStatusCheck(MenuItem const & i)
 {
        switch (i.kind()) {
 
@@ -343,7 +349,7 @@ void Menu::addWithStatusCheck(MenuItem const & i)
 }
 
 
-void Menu::read(Lexer & lex)
+void MenuDefinition::read(Lexer & lex)
 {
        enum Menutags {
                md_item = 1,
@@ -506,7 +512,7 @@ void Menu::read(Lexer & lex)
                        break;
 
                default:
-                       lex.printError("Menu::read: "
+                       lex.printError("MenuDefinition::read: "
                                       "Unknown menu tag: `$$Token'");
                        break;
                }
@@ -515,13 +521,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)
@@ -530,7 +536,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
@@ -556,7 +562,7 @@ void Menu::checkShortcuts() const
 }
 
 
-bool Menu::searchMenu(FuncRequest const & func, vector<docstring> & names) const
+bool MenuDefinition::searchMenu(FuncRequest const & func, vector<docstring> & names) const
 {
        const_iterator m = begin();
        const_iterator m_end = end();
@@ -567,8 +573,15 @@ 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))
+                       MenuDefinition const * submenu = m->submenu();
+                       if (!submenu) {
+                               LYXERR(Debug::GUI, "Warning: non existing sub menu label="
+                                       << fromqstr(m->label())
+                                       << " name=" << fromqstr(m->submenuname()));
+                               names.pop_back();
+                               continue;
+                       }
+                       if (submenu->searchMenu(func, names))
                                return true;
                        names.pop_back();
                }
@@ -577,8 +590,6 @@ bool Menu::searchMenu(FuncRequest const & func, vector<docstring> & names) const
 }
 
 
-namespace {
-
 bool compareFormat(Format const * p1, Format const * p2)
 {
        return *p1 < *p2;
@@ -595,10 +606,8 @@ QString limitStringLength(docstring const & str)
        return toqstr(str);
 }
 
-} // namespace anon
-
 
-void Menu::expandLastfiles()
+void MenuDefinition::expandLastfiles()
 {
        LastFilesSection::LastFiles const & lf = LyX::cref().session().lastFiles().lastFiles();
        LastFilesSection::LastFiles::const_iterator lfit = lf.begin();
@@ -614,7 +623,7 @@ void Menu::expandLastfiles()
 }
 
 
-void Menu::expandDocuments()
+void MenuDefinition::expandDocuments()
 {
        Buffer * first = theBufferList().first();
        if (first) {
@@ -641,7 +650,7 @@ void Menu::expandDocuments()
 }
 
 
-void Menu::expandBookmarks()
+void MenuDefinition::expandBookmarks()
 {
        lyx::BookmarksSection const & bm = LyX::cref().session().bookmarks();
 
@@ -657,7 +666,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,
@@ -735,7 +744,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!"),
@@ -755,7 +764,7 @@ void Menu::expandFloatListInsert(Buffer const * buf)
 }
 
 
-void Menu::expandFloatInsert(Buffer const * buf)
+void MenuDefinition::expandFloatInsert(Buffer const * buf)
 {
        if (!buf) {
                add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
@@ -776,7 +785,7 @@ void Menu::expandFloatInsert(Buffer const * buf)
 }
 
 
-void Menu::expandFlexInsert(Buffer const * buf, string s)
+void MenuDefinition::expandFlexInsert(Buffer const * buf, string s)
 {
        if (!buf) {
                add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
@@ -799,7 +808,7 @@ void Menu::expandFlexInsert(Buffer const * buf, string s)
 
 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;
@@ -843,7 +852,7 @@ void Menu::expandToc2(Toc const & toc_list,
                                                    label, FuncRequest(toc_list[pos].action())));
                        } else {
                                MenuItem item(MenuItem::Submenu, label);
-                               item.setSubmenu(new Menu);
+                               item.setSubmenu(new MenuDefinition);
                                item.submenu()->expandToc2(toc_list, pos, new_pos, depth + 1);
                                add(item);
                        }
@@ -853,7 +862,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
@@ -889,12 +898,12 @@ void Menu::expandToc(Buffer const * buf)
                        continue;
 
                // All the rest is for floats
-               auto_ptr<Menu> menu(new Menu);
+               MenuDefinition * submenu = new MenuDefinition;
                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,
+                       submenu->add(MenuItem(MenuItem::Command, label,
                                           FuncRequest(ccit->action())));
                }
                string const & floatName = floatlist.getType(cit->first).listName();
@@ -924,7 +933,7 @@ void Menu::expandToc(Buffer const * buf)
                else
                        label = qt_("Other floats");
                MenuItem item(MenuItem::Submenu, label);
-               item.setSubmenu(menu.release());
+               item.setSubmenu(submenu);
                add(item);
        }
 
@@ -940,7 +949,7 @@ void Menu::expandToc(Buffer const * buf)
 }
 
 
-void Menu::expandPasteRecent()
+void MenuDefinition::expandPasteRecent()
 {
        vector<docstring> const sel = cap::availableSelections();
 
@@ -954,7 +963,7 @@ void Menu::expandPasteRecent()
 }
 
 
-void Menu::expandToolbars()
+void MenuDefinition::expandToolbars()
 {
        //
        // extracts the toolbars from the backend
@@ -981,7 +990,7 @@ void Menu::expandToolbars()
 }
 
 
-void Menu::expandBranches(Buffer const * buf)
+void MenuDefinition::expandBranches(Buffer const * buf)
 {
        if (!buf) {
                add(MenuItem(MenuItem::Command,
@@ -1011,14 +1020,232 @@ void Menu::expandBranches(Buffer const * buf)
        }
 }
 
+} // 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 * 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 * menu)
+{
+       LYXERR(Debug::GUI, "populating menu " << fromqstr(menu->name()));
+       if (menu->size() == 0) {
+               LYXERR(Debug::GUI, "\tERROR: empty menu " << fromqstr(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()));
+               }
+       }
+}
+
+/////////////////////////////////////////////////////////////////////
+// Menu implementation
+/////////////////////////////////////////////////////////////////////
+
+Menu::Menu(GuiView * gv, QString const & name, bool top_level)
+: 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(d->name);
+}
+
+/////////////////////////////////////////////////////////////////////
+// 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,
+                   Buffer const *) const;
+
+       /// Initialize specific MACOS X menubar
+       void macxMenuBarInit(GuiView * view);
+
+       /// 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
+       */
+       MenuDefinition specialmenu_;
+
+       ///
+       MenuList menulist_;
+       ///
+       MenuDefinition menubar_;
+
+       typedef QHash<QString, Menu *> NameMap;
+
+       /// name to menu for \c menu() method.
+       NameMap name_map_;
+};
+
+/*
+  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;
 
-void Menus::expand(Menu const & frommenu, Menu & tomenu,
+       /* 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.
+       for (size_t i = 0 ; i < num_entries ; ++i) {
+               FuncRequest const func(entries[i].action,
+                                      from_utf8(entries[i].arg));
+               specialmenu_.add(MenuItem(MenuItem::Command, entries[i].label, func));
+       }
+
+       // add the entries to a QMenu that will eventually be empty
+       // and therefore invisible.
+       QMenu * qMenu = view->menuBar()->addMenu("special");
+       MenuDefinition::const_iterator cit = specialmenu_.begin();
+       MenuDefinition::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);
+       }
+}
+
+
+void Menus::Impl::expand(MenuDefinition const & frommenu, MenuDefinition & tomenu,
                         Buffer const * buf) const
 {
        if (!tomenu.empty())
                tomenu.clear();
 
-       for (Menu::const_iterator cit = frommenu.begin();
+       for (MenuDefinition::const_iterator cit = frommenu.begin();
             cit != frommenu.end() ; ++cit) {
                switch (cit->kind()) {
                case MenuItem::Lastfiles:
@@ -1078,9 +1305,8 @@ void Menus::expand(Menu const & frommenu, Menu & tomenu,
 
                case MenuItem::Submenu: {
                        MenuItem item(*cit);
-                       item.setSubmenu(new Menu(cit->submenuname()));
-                       expand(getMenu(cit->submenuname()),
-                              *item.submenu(), buf);
+                       item.setSubmenu(new MenuDefinition(cit->submenuname()));
+                       expand(getMenu(cit->submenuname()), *item.submenu(), buf);
                        tomenu.addWithStatusCheck(item);
                }
                break;
@@ -1096,8 +1322,7 @@ void Menus::expand(Menu const & frommenu, Menu & tomenu,
        }
 
        // 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
@@ -1105,6 +1330,41 @@ void Menus::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())
+               lyxerr << "No submenu named " << fromqstr(name) << endl;
+       BOOST_ASSERT(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())
+               lyxerr << "No submenu named " << fromqstr(name) << endl;
+       BOOST_ASSERT(it != menulist_.end());
+       return (*it);
+}
+
+/////////////////////////////////////////////////////////////////////
+// Menus implementation
+/////////////////////////////////////////////////////////////////////
+
+Menus::Menus(): d(new Impl) {}
+
+
 void Menus::read(Lexer & lex)
 {
        enum Menutags {
@@ -1135,17 +1395,17 @@ void Menus::read(Lexer & lex)
        while (lex.isOK() && !quit) {
                switch (lex.lex()) {
                case md_menubar:
-                       menubar_.read(lex);
+                       d->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);
+                       if (d->hasMenu(name))
+                               d->getMenu(name).read(lex);
+                       else {
+                               MenuDefinition menu(name);
                                menu.read(lex);
-                               add(menu);
+                               d->menulist_.push_back(menu);
                        }
                        break;
                }
@@ -1162,43 +1422,107 @@ void Menus::read(Lexer & lex)
 }
 
 
-void Menus::add(Menu const & menu)
+bool Menus::searchMenu(FuncRequest const & func,
+       vector<docstring> & names) const
 {
-       menulist_.push_back(menu);
+       MenuDefinition menu;
+       d->expand(d->menubar_, menu, 0);
+       return menu.searchMenu(func, names);
 }
 
 
-bool Menus::hasMenu(QString const & name) const
+void Menus::fillMenuBar(GuiView * view)
 {
-       return find_if(begin(), end(), MenuNamesEqual(name)) != end();
-}
+       // Clear all menubar contents before filling it.
+       view->menuBar()->clear();
+       
+#ifdef Q_WS_MACX
+       // setup special mac specific menu item
+       d->macxMenuBarInit(view);
+#endif
 
+       LYXERR(Debug::GUI, "populating menu bar" << fromqstr(d->menubar_.name()));
 
-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);
+       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());
+       }
+
+       MenuDefinition menu;
+       d->expand(d->menubar_, menu, view->buffer());
+
+       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 " << 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;
+               }
+
+               Menu * menu = new Menu(view, m->submenuname(), true);
+               menu->setTitle(label(*m));
+               view->menuBar()->addMenu(menu);
+
+               d->name_map_[name] = menu;
+       }
 }
 
 
-Menu & Menus::getMenu(QString const & name)
+void Menus::updateMenu(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 * qmenu = d->name_map_[name];
+       LYXERR(Debug::GUI, "Triggered menu: " << fromqstr(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(name)) {
+               qmenu->addAction(qt_("No action defined!"));
+               LYXERR(Debug::GUI, "\tWARNING: non existing menu: "
+                       << fromqstr(qmenu->d->name));
+               return;
+       }
+
+       MenuDefinition const & fromLyxMenu = d->getMenu(name);
+       d->expand(fromLyxMenu, *qmenu->d->top_level_menu, qmenu->d->view->buffer());
+       qmenu->d->populate(qmenu, qmenu->d->top_level_menu);
 }
 
 
-Menu const & Menus::getMenubar() const
+Menu * Menus::menu(QString const & name, GuiView & view)
 {
-       return menubar_;
-}
+       LYXERR(Debug::GUI, "Context menu requested: " << fromqstr(name));
+       Menu * menu = d->name_map_.value(name, 0);
+       if (!menu && !name.startsWith("context-")) {
+               LYXERR0("resquested context menu not found: " << fromqstr(name));
+               return 0;
+       }
 
+       menu = new Menu(&view, name, true);
+       d->name_map_[name] = menu;
+       return menu;
+}
 
 } // namespace frontend
 } // namespace lyx