]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/Menus.cpp
merge MenuBackend into frontend/Menus
[lyx.git] / src / frontends / qt4 / Menus.cpp
1 /**
2  * \file qt4/Menus.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Asger Alstrup
8  * \author Lars Gullik Bjønnes
9  * \author Jean-Marc Lasgouttes
10  * \author André Pönitz
11  * \author Dekel Tsur
12  * \author Martin Vermeer
13  *
14  * Full author contact details are available in file CREDITS.
15  */
16
17 #include <config.h>
18
19 #include "Menus.h"
20
21 #include "Action.h"
22 #include "GuiApplication.h"
23 #include "GuiPopupMenu.h"
24 #include "GuiView.h"
25
26 #include "BranchList.h"
27 #include "Buffer.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "Converter.h"
31 #include "CutAndPaste.h"
32 #include "Floating.h"
33 #include "FloatList.h"
34 #include "Format.h"
35 #include "KeyMap.h"
36 #include "Session.h"
37 #include "LyXAction.h"
38 #include "LyX.h" // for lastfiles
39 #include "LyXFunc.h"
40 #include "Lexer.h"
41 #include "Paragraph.h"
42 #include "TextClass.h"
43 #include "TocBackend.h"
44 #include "ToolbarBackend.h"
45
46 #include "frontends/Application.h"
47
48 #include "support/convert.h"
49 #include "support/debug.h"
50 #include "support/filetools.h"
51 #include "support/gettext.h"
52 #include "support/lstrings.h"
53
54 #include <boost/bind.hpp>
55
56 #include "qt_helpers.h"
57
58 #include <QCursor>
59 #include <QMenuBar>
60
61 #include <algorithm>
62 #include <ostream>
63
64 using namespace std;
65 using boost::bind;
66 using namespace lyx::support;
67
68
69 namespace lyx {
70 namespace frontend {
71
72 namespace {
73
74 class MenuNamesEqual
75 {
76 public:
77         MenuNamesEqual(QString const & name) : name_(name) {}
78         bool operator()(Menu const & menu) const { return menu.name() == name_; }
79 private:
80         QString name_;
81 };
82
83 } // namespace anon
84
85 // MacOSX specific stuff is at the end.
86
87 void Menus::fillMenuBar(GuiView * view)
88 {
89         // Clear all menubar contents before filling it.
90         view->menuBar()->clear();
91         
92 #ifdef Q_WS_MACX
93         // setup special mac specific menu item
94         macxMenuBarInit(view);
95 #endif
96
97         LYXERR(Debug::GUI, "populating menu bar" << fromqstr(getMenubar().name()));
98
99         if (getMenubar().size() == 0) {
100                 LYXERR(Debug::GUI, "\tERROR: empty menu bar"
101                         << fromqstr(getMenubar().name()));
102                 return;
103         }
104         else {
105                 LYXERR(Debug::GUI, "menu bar entries "
106                         << getMenubar().size());
107         }
108
109         Menu menu;
110         expand(getMenubar(), menu, view->buffer());
111
112         Menu::const_iterator m = menu.begin();
113         Menu::const_iterator end = menu.end();
114
115         for (; m != end; ++m) {
116
117                 if (m->kind() != MenuItem::Submenu) {
118                         LYXERR(Debug::GUI, "\tERROR: not a submenu " << fromqstr(m->label()));
119                         continue;
120                 }
121
122                 LYXERR(Debug::GUI, "menu bar item " << fromqstr(m->label())
123                         << " is a submenu named " << fromqstr(m->submenuname()));
124
125                 QString name = m->submenuname();
126                 if (!hasMenu(name)) {
127                         LYXERR(Debug::GUI, "\tERROR: " << fromqstr(name)
128                                 << " submenu has no menu!");
129                         continue;
130                 }
131
132                 GuiPopupMenu * qMenu = new GuiPopupMenu(view, *m, true);
133                 view->menuBar()->addMenu(qMenu);
134
135                 name_map_[name] = qMenu;
136         }
137 }
138
139
140 QMenu * Menus::menu(QString const & name)
141 {
142         LYXERR(Debug::GUI, "Context menu requested: " << fromqstr(name));
143         GuiPopupMenu * menu = name_map_.value(name, 0);
144         if (!menu)
145                 LYXERR0("resquested context menu not found: " << fromqstr(name));
146         return menu;
147 }
148
149
150 /// Some special Qt/Mac support hacks
151
152 /*
153   Here is what the Qt documentation says about how a menubar is chosen:
154
155      1) If the window has a QMenuBar then it is used. 2) If the window
156      is a modal then its menubar is used. If no menubar is specified
157      then a default menubar is used (as documented below) 3) If the
158      window has no parent then the default menubar is used (as
159      documented below).
160
161      The above 3 steps are applied all the way up the parent window
162      chain until one of the above are satisifed. If all else fails a
163      default menubar will be created, the default menubar on Qt/Mac is
164      an empty menubar, however you can create a different default
165      menubar by creating a parentless QMenuBar, the first one created
166      will thus be designated the default menubar, and will be used
167      whenever a default menubar is needed.
168
169   Thus, for Qt/Mac, we add the menus to a free standing menubar, so
170   that this menubar will be used also when one of LyX' dialogs has
171   focus. (JMarc)
172 */
173
174 void Menus::macxMenuBarInit(GuiView * view)
175 {
176         // The Mac menubar initialisation must be done only once!
177         static bool done = false;
178         if (done)
179                 return;
180         done = true;
181
182         /* Since Qt 4.2, the qt/mac menu code has special code for
183            specifying the role of a menu entry. However, it does not
184            work very well with our scheme of creating menus on demand,
185            and therefore we need to put these entries in a special
186            invisible menu. (JMarc)
187         */
188
189         /* The entries of our special mac menu. If we add support for
190          * special entries in Menus, we could imagine something
191          * like
192          *    SpecialItem About " "About LyX" "dialog-show aboutlyx"
193          * and therefore avoid hardcoding. I am not sure it is worth
194          * the hassle, though. (JMarc)
195          */
196         struct MacMenuEntry {
197                 kb_action action;
198                 char const * arg;
199                 char const * label;
200                 QAction::MenuRole role;
201         };
202
203         MacMenuEntry entries[] = {
204                 {LFUN_DIALOG_SHOW, "aboutlyx", "About LyX",
205                  QAction::AboutRole},
206                 {LFUN_DIALOG_SHOW, "prefs", "Preferences",
207                  QAction::PreferencesRole},
208                 {LFUN_RECONFIGURE, "", "Reconfigure",
209                  QAction::ApplicationSpecificRole},
210                 {LFUN_LYX_QUIT, "", "Quit LyX", QAction::QuitRole}
211         };
212         const size_t num_entries = sizeof(entries) / sizeof(MacMenuEntry);
213
214         // the special menu for Menus.
215         Menu special;
216         for (size_t i = 0 ; i < num_entries ; ++i) {
217                 FuncRequest const func(entries[i].action,
218                                        from_utf8(entries[i].arg));
219                 special.add(MenuItem(MenuItem::Command, entries[i].label, func));
220         }
221         specialMenu(special);
222
223         // add the entries to a QMenu that will eventually be empty
224         // and therefore invisible.
225         QMenu * qMenu = view->menuBar()->addMenu("special");
226
227         // we do not use 'special' because it is a temporary variable,
228         // whereas Menus::specialMenu points to a persistent
229         // copy.
230         Menu::const_iterator cit = specialMenu().begin();
231         Menu::const_iterator end = specialMenu().end();
232         for (size_t i = 0 ; cit != end ; ++cit, ++i) {
233                 Action * action = new Action(*view, QIcon(), cit->label(),
234                                              cit->func(), QString());
235                 action->setMenuRole(entries[i].role);
236                 qMenu->addAction(action);
237
238         }
239 }
240
241
242 MenuItem::MenuItem(Kind kind)
243         : kind_(kind), optional_(false)
244 {}
245
246
247 MenuItem::MenuItem(Kind kind, QString const & label,
248                    QString const & submenu, bool optional)
249         : kind_(kind), label_(label),
250           submenuname_(submenu), optional_(optional)
251 {
252         BOOST_ASSERT(kind == Submenu);
253 }
254
255
256 MenuItem::MenuItem(Kind kind, QString const & label,
257                    FuncRequest const & func, bool optional)
258         : kind_(kind), label_(label), func_(func), optional_(optional)
259 {
260         func_.origin = FuncRequest::MENU;
261 }
262
263
264 MenuItem::~MenuItem()
265 {}
266
267
268 void MenuItem::submenu(Menu * menu)
269 {
270         submenu_.reset(menu);
271 }
272
273
274 QString MenuItem::label() const
275 {
276         return label_.split('|')[0];
277 }
278
279
280 QString MenuItem::shortcut() const
281 {
282         return label_.contains('|') ? label_.split('|')[1] : QString();
283 }
284
285
286 QString MenuItem::binding() const
287 {
288         if (kind_ != Command)
289                 return QString();
290
291         // Get the keys bound to this action, but keep only the
292         // first one later
293         KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(func_);
294
295         if (bindings.size())
296                 return toqstr(bindings.begin()->print(KeySequence::ForGui));
297
298         LYXERR(Debug::KBMAP, "No binding for "
299                 << lyxaction.getActionName(func_.action)
300                 << '(' << func_.argument() << ')');
301         return QString();
302 }
303
304
305 Menu & Menu::add(MenuItem const & i)
306 {
307         items_.push_back(i);
308         return *this;
309 }
310
311
312 Menu & Menu::addWithStatusCheck(MenuItem const & i)
313 {
314         switch (i.kind()) {
315
316         case MenuItem::Command: {
317                 FuncStatus status = lyx::getStatus(i.func());
318                 if (status.unknown() || (!status.enabled() && i.optional()))
319                         break;
320                 items_.push_back(i);
321                 items_.back().status(status);
322                 break;
323         }
324
325         case MenuItem::Submenu: {
326                 if (i.submenu()) {
327                         bool enabled = false;
328                         for (const_iterator cit = i.submenu()->begin();
329                              cit != i.submenu()->end(); ++cit) {
330                                 if ((cit->kind() == MenuItem::Command
331                                      || cit->kind() == MenuItem::Submenu)
332                                     && cit->status().enabled()) {
333                                         enabled = true;
334                                         break;
335                                 }
336                         }
337                         if (enabled || !i.optional()) {
338                                 items_.push_back(i);
339                                 items_.back().status().enabled(enabled);
340                         }
341                 }
342                 else
343                         items_.push_back(i);
344                 break;
345         }
346
347         case MenuItem::Separator:
348                 if (!items_.empty() && items_.back().kind() != MenuItem::Separator)
349                         items_.push_back(i);
350                 break;
351
352         default:
353                 items_.push_back(i);
354         }
355
356         return *this;
357 }
358
359
360 Menu & Menu::read(Lexer & lex)
361 {
362         enum Menutags {
363                 md_item = 1,
364                 md_branches,
365                 md_documents,
366                 md_bookmarks,
367                 md_charstyles,
368                 md_custom,
369                 md_elements,
370                 md_endmenu,
371                 md_exportformats,
372                 md_importformats,
373                 md_lastfiles,
374                 md_optitem,
375                 md_optsubmenu,
376                 md_separator,
377                 md_submenu,
378                 md_toc,
379                 md_updateformats,
380                 md_viewformats,
381                 md_floatlistinsert,
382                 md_floatinsert,
383                 md_pasterecent,
384                 md_toolbars,
385                 md_last
386         };
387
388         struct keyword_item menutags[md_last - 1] = {
389                 { "bookmarks", md_bookmarks },
390                 { "branches", md_branches },
391                 { "charstyles", md_charstyles },
392                 { "custom", md_custom },
393                 { "documents", md_documents },
394                 { "elements", md_elements },
395                 { "end", md_endmenu },
396                 { "exportformats", md_exportformats },
397                 { "floatinsert", md_floatinsert },
398                 { "floatlistinsert", md_floatlistinsert },
399                 { "importformats", md_importformats },
400                 { "item", md_item },
401                 { "lastfiles", md_lastfiles },
402                 { "optitem", md_optitem },
403                 { "optsubmenu", md_optsubmenu },
404                 { "pasterecent", md_pasterecent },
405                 { "separator", md_separator },
406                 { "submenu", md_submenu },
407                 { "toc", md_toc },
408                 { "toolbars", md_toolbars },
409                 { "updateformats", md_updateformats },
410                 { "viewformats", md_viewformats }
411         };
412
413         lex.pushTable(menutags, md_last - 1);
414         if (lyxerr.debugging(Debug::PARSER))
415                 lex.printTable(lyxerr);
416
417         bool quit = false;
418         bool optional = false;
419
420         while (lex.isOK() && !quit) {
421                 switch (lex.lex()) {
422                 case md_optitem:
423                         optional = true;
424                         // fallback to md_item
425                 case md_item: {
426                         lex.next(true);
427                         docstring const name = translateIfPossible(lex.getDocString());
428                         lex.next(true);
429                         string const command = lex.getString();
430                         FuncRequest func = lyxaction.lookupFunc(command);
431                         add(MenuItem(MenuItem::Command, toqstr(name), func, optional));
432                         optional = false;
433                         break;
434                 }
435
436                 case md_separator:
437                         add(MenuItem(MenuItem::Separator));
438                         break;
439
440                 case md_lastfiles:
441                         add(MenuItem(MenuItem::Lastfiles));
442                         break;
443
444                 case md_charstyles:
445                         add(MenuItem(MenuItem::CharStyles));
446                         break;
447
448                 case md_custom:
449                         add(MenuItem(MenuItem::Custom));
450                         break;
451
452                 case md_elements:
453                         add(MenuItem(MenuItem::Elements));
454                         break;
455
456                 case md_documents:
457                         add(MenuItem(MenuItem::Documents));
458                         break;
459
460                 case md_bookmarks:
461                         add(MenuItem(MenuItem::Bookmarks));
462                         break;
463
464                 case md_toc:
465                         add(MenuItem(MenuItem::Toc));
466                         break;
467
468                 case md_viewformats:
469                         add(MenuItem(MenuItem::ViewFormats));
470                         break;
471
472                 case md_updateformats:
473                         add(MenuItem(MenuItem::UpdateFormats));
474                         break;
475
476                 case md_exportformats:
477                         add(MenuItem(MenuItem::ExportFormats));
478                         break;
479
480                 case md_importformats:
481                         add(MenuItem(MenuItem::ImportFormats));
482                         break;
483
484                 case md_floatlistinsert:
485                         add(MenuItem(MenuItem::FloatListInsert));
486                         break;
487
488                 case md_floatinsert:
489                         add(MenuItem(MenuItem::FloatInsert));
490                         break;
491
492                 case md_pasterecent:
493                         add(MenuItem(MenuItem::PasteRecent));
494                         break;
495
496                 case md_toolbars:
497                         add(MenuItem(MenuItem::Toolbars));
498                         break;
499
500                 case md_branches:
501                         add(MenuItem(MenuItem::Branches));
502                         break;
503
504                 case md_optsubmenu:
505                         optional = true;
506                         // fallback to md_submenu
507                 case md_submenu: {
508                         lex.next(true);
509                         docstring const mlabel = translateIfPossible(lex.getDocString());
510                         lex.next(true);
511                         docstring const mname = lex.getDocString();
512                         add(MenuItem(MenuItem::Submenu,
513                                 toqstr(mlabel), toqstr(mname), optional));
514                         optional = false;
515                         break;
516                 }
517
518                 case md_endmenu:
519                         quit = true;
520                         break;
521
522                 default:
523                         lex.printError("Menu::read: "
524                                        "Unknown menu tag: `$$Token'");
525                         break;
526                 }
527         }
528         lex.popTable();
529         return *this;
530 }
531
532
533 MenuItem const & Menu::operator[](size_type i) const
534 {
535         return items_[i];
536 }
537
538
539 bool Menu::hasFunc(FuncRequest const & func) const
540 {
541         return find_if(begin(), end(),
542                        bind(equal_to<FuncRequest>(),
543                             bind(&MenuItem::func, _1),
544                             func)) != end();
545 }
546
547 void Menu::checkShortcuts() const
548 {
549         // This is a quadratic algorithm, but we do not care because
550         // menus are short enough
551         for (const_iterator it1 = begin(); it1 != end(); ++it1) {
552                 QString shortcut = it1->shortcut();
553                 if (shortcut.isEmpty())
554                         continue;
555                 if (!it1->label().contains(shortcut))
556                         lyxerr << "Menu warning: menu entry \""
557                                << fromqstr(it1->label())
558                                << "\" does not contain shortcut `"
559                                << fromqstr(shortcut) << "'." << endl;
560                 for (const_iterator it2 = begin(); it2 != it1 ; ++it2) {
561                         if (!it2->shortcut().compare(shortcut, Qt::CaseInsensitive)) {
562                                 lyxerr << "Menu warning: menu entries "
563                                        << '"' << fromqstr(it1->fulllabel())
564                                        << "\" and \"" << fromqstr(it2->fulllabel())
565                                        << "\" share the same shortcut."
566                                        << endl;
567                         }
568                 }
569         }
570 }
571
572
573 bool Menu::searchMenu(FuncRequest const & func, vector<docstring> & names) const
574 {
575         const_iterator m = begin();
576         const_iterator m_end = end();
577         for (; m != m_end; ++m) {
578                 if (m->kind() == MenuItem::Command && m->func() == func) {
579                         names.push_back(qstring_to_ucs4(m->label()));
580                         return true;
581                 }
582                 if (m->kind() == MenuItem::Submenu) {
583                         names.push_back(qstring_to_ucs4(m->label()));
584                         Menu const & submenu = *m->submenu();
585                         if (submenu.searchMenu(func, names))
586                                 return true;
587                         names.pop_back();
588                 }
589         }
590         return false;
591 }
592
593
594 void Menus::specialMenu(Menu const & menu)
595 {
596         specialmenu_ = menu;
597 }
598
599
600 namespace {
601
602 bool compareFormat(Format const * p1, Format const * p2)
603 {
604         return *p1 < *p2;
605 }
606
607
608 QString limitStringLength(docstring const & str)
609 {
610         size_t const max_item_length = 45;
611
612         if (str.size() > max_item_length)
613                 return toqstr(str.substr(0, max_item_length - 3) + "...");
614
615         return toqstr(str);
616 }
617
618
619 void expandLastfiles(Menu & tomenu)
620 {
621         LastFilesSection::LastFiles const & lf = LyX::cref().session().lastFiles().lastFiles();
622         LastFilesSection::LastFiles::const_iterator lfit = lf.begin();
623
624         int ii = 1;
625
626         for (; lfit != lf.end() && ii < 10; ++lfit, ++ii) {
627                 string const file = lfit->absFilename();
628                 QString const label = QString("%1. %2|%3").arg(ii)
629                         .arg(toqstr(makeDisplayPath(file, 30))).arg(ii);
630                 tomenu.add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_FILE_OPEN, file)));
631         }
632 }
633
634
635 void expandDocuments(Menu & tomenu)
636 {
637         Buffer * first = theBufferList().first();
638         if (first) {
639                 Buffer * b = first;
640                 int ii = 1;
641                 
642                 // We cannot use a for loop as the buffer list cycles.
643                 do {
644                         QString label = toqstr(b->fileName().displayName(20));
645                         if (!b->isClean())
646                                 label += "*";
647                         if (ii < 10)
648                                 label = QString::number(ii) + ". " + label + '|' + QString::number(ii);
649                         tomenu.add(MenuItem(MenuItem::Command, label,
650                                 FuncRequest(LFUN_BUFFER_SWITCH, b->absFileName())));
651                         
652                         b = theBufferList().next(b);
653                         ++ii;
654                 } while (b != first); 
655         } else {
656                 tomenu.add(MenuItem(MenuItem::Command, qt_("No Documents Open!"),
657                            FuncRequest(LFUN_NOACTION)));
658         }
659 }
660
661
662 void expandBookmarks(Menu & tomenu)
663 {
664         lyx::BookmarksSection const & bm = LyX::cref().session().bookmarks();
665
666         for (size_t i = 1; i <= bm.size(); ++i) {
667                 if (bm.isValid(i)) {
668                         string const file = bm.bookmark(i).filename.absFilename();
669                         QString const label = QString("%1. %2|%3").arg(i)
670                                 .arg(toqstr(makeDisplayPath(file, 20))).arg(i);
671                         tomenu.add(MenuItem(MenuItem::Command, label,
672                                 FuncRequest(LFUN_BOOKMARK_GOTO, convert<docstring>(i))));
673                 }
674         }
675 }
676
677
678 void expandFormats(MenuItem::Kind kind, Menu & tomenu, Buffer const * buf)
679 {
680         if (!buf && kind != MenuItem::ImportFormats) {
681                 tomenu.add(MenuItem(MenuItem::Command,
682                                     qt_("No Document Open!"),
683                                     FuncRequest(LFUN_NOACTION)));
684                 return;
685         }
686
687         typedef vector<Format const *> Formats;
688         Formats formats;
689         kb_action action;
690
691         switch (kind) {
692         case MenuItem::ImportFormats:
693                 formats = theConverters().importableFormats();
694                 action = LFUN_BUFFER_IMPORT;
695                 break;
696         case MenuItem::ViewFormats:
697                 formats = buf->exportableFormats(true);
698                 action = LFUN_BUFFER_VIEW;
699                 break;
700         case MenuItem::UpdateFormats:
701                 formats = buf->exportableFormats(true);
702                 action = LFUN_BUFFER_UPDATE;
703                 break;
704         default:
705                 formats = buf->exportableFormats(false);
706                 action = LFUN_BUFFER_EXPORT;
707         }
708         sort(formats.begin(), formats.end(), &compareFormat);
709
710         Formats::const_iterator fit = formats.begin();
711         Formats::const_iterator end = formats.end();
712         for (; fit != end ; ++fit) {
713                 if ((*fit)->dummy())
714                         continue;
715                 QString label = toqstr((*fit)->prettyname());
716                 QString const shortcut = toqstr((*fit)->shortcut());
717
718                 switch (kind) {
719                 case MenuItem::ImportFormats:
720                         // FIXME: This is a hack, we should rather solve
721                         // FIXME: bug 2488 instead.
722                         if ((*fit)->name() == "text")
723                                 label = qt_("Plain Text");
724                         else if ((*fit)->name() == "textparagraph")
725                                 label = qt_("Plain Text, Join Lines");
726                         label += "...";
727                         break;
728                 case MenuItem::ViewFormats:
729                 case MenuItem::ExportFormats:
730                 case MenuItem::UpdateFormats:
731                         if (!(*fit)->documentFormat())
732                                 continue;
733                         break;
734                 default:
735                         BOOST_ASSERT(false);
736                         break;
737                 }
738                 // FIXME: if we had proper support for translating the
739                 // format names defined in configure.py, there would
740                 // not be a need to check whether the shortcut is
741                 // correct. If we add it uncondiitonally, it would
742                 // create useless warnings on bad shortcuts
743                 if (!shortcut.isEmpty() && label.contains(shortcut))
744                         label += '|' + shortcut;
745
746                 if (buf)
747                         tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label,
748                                 FuncRequest(action, (*fit)->name())));
749                 else
750                         tomenu.add(MenuItem(MenuItem::Command, label,
751                                 FuncRequest(action, (*fit)->name())));
752         }
753 }
754
755
756 void expandFloatListInsert(Menu & tomenu, Buffer const * buf)
757 {
758         if (!buf) {
759                 tomenu.add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
760                                     FuncRequest(LFUN_NOACTION)));
761                 return;
762         }
763
764         FloatList const & floats = buf->params().documentClass().floats();
765         FloatList::const_iterator cit = floats.begin();
766         FloatList::const_iterator end = floats.end();
767         for (; cit != end; ++cit) {
768                 tomenu.addWithStatusCheck(MenuItem(MenuItem::Command,
769                                     qt_(cit->second.listName()),
770                                     FuncRequest(LFUN_FLOAT_LIST,
771                                                 cit->second.type())));
772         }
773 }
774
775
776 void expandFloatInsert(Menu & tomenu, Buffer const * buf)
777 {
778         if (!buf) {
779                 tomenu.add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
780                                     FuncRequest(LFUN_NOACTION)));
781                 return;
782         }
783
784         FloatList const & floats = buf->params().documentClass().floats();
785         FloatList::const_iterator cit = floats.begin();
786         FloatList::const_iterator end = floats.end();
787         for (; cit != end; ++cit) {
788                 // normal float
789                 QString const label = qt_(cit->second.name());
790                 tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label,
791                                     FuncRequest(LFUN_FLOAT_INSERT,
792                                                 cit->second.type())));
793         }
794 }
795
796
797 void expandFlexInsert(Menu & tomenu, Buffer const * buf, string s)
798 {
799         if (!buf) {
800                 tomenu.add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
801                                     FuncRequest(LFUN_NOACTION)));
802                 return;
803         }
804         TextClass::InsetLayouts const & insetLayouts =
805                 buf->params().documentClass().insetLayouts();
806         TextClass::InsetLayouts::const_iterator cit = insetLayouts.begin();
807         TextClass::InsetLayouts::const_iterator end = insetLayouts.end();
808         for (; cit != end; ++cit) {
809                 docstring const label = cit->first;
810                 if (cit->second.lyxtype() == s)
811                         tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, 
812                                 toqstr(label), FuncRequest(LFUN_FLEX_INSERT,
813                                                 label)));
814         }
815 }
816
817
818 size_t const max_number_of_items = 25;
819
820 void expandToc2(Menu & tomenu, Toc const & toc_list,
821                 size_t from, size_t to, int depth)
822 {
823         int shortcut_count = 0;
824
825         // check whether depth is smaller than the smallest depth in toc.
826         int min_depth = 1000;
827         for (size_t i = from; i < to; ++i)
828                 min_depth = min(min_depth, toc_list[i].depth());
829         if (min_depth > depth)
830                 depth = min_depth;
831
832         if (to - from <= max_number_of_items) {
833                 for (size_t i = from; i < to; ++i) {
834                         QString label(4 * max(0, toc_list[i].depth() - depth), ' ');
835                         label += limitStringLength(toc_list[i].str());
836                         if (toc_list[i].depth() == depth
837                             && shortcut_count < 9) {
838                                 if (label.contains(QString::number(shortcut_count + 1)))
839                                         label += '|' + QString::number(++shortcut_count);
840                         }
841                         tomenu.add(MenuItem(MenuItem::Command, label,
842                                             FuncRequest(toc_list[i].action())));
843                 }
844         } else {
845                 size_t pos = from;
846                 while (pos < to) {
847                         size_t new_pos = pos + 1;
848                         while (new_pos < to &&
849                                toc_list[new_pos].depth() > depth)
850                                 ++new_pos;
851
852                         QString label(4 * max(0, toc_list[pos].depth() - depth), ' ');
853                         label += limitStringLength(toc_list[pos].str());
854                         if (toc_list[pos].depth() == depth &&
855                             shortcut_count < 9) {
856                                 if (label.contains(QString::number(shortcut_count + 1)))
857                                         label += '|' + QString::number(++shortcut_count);
858                         }
859                         if (new_pos == pos + 1) {
860                                 tomenu.add(MenuItem(MenuItem::Command,
861                                                     label, FuncRequest(toc_list[pos].action())));
862                         } else {
863                                 MenuItem item(MenuItem::Submenu, label);
864                                 item.submenu(new Menu);
865                                 expandToc2(*item.submenu(),
866                                            toc_list, pos, new_pos, depth + 1);
867                                 tomenu.add(item);
868                         }
869                         pos = new_pos;
870                 }
871         }
872 }
873
874
875 void expandToc(Menu & tomenu, Buffer const * buf)
876 {
877         // To make things very cleanly, we would have to pass buf to
878         // all MenuItem constructors and to expandToc2. However, we
879         // know that all the entries in a TOC will be have status_ ==
880         // OK, so we avoid this unnecessary overhead (JMarc)
881
882         if (!buf) {
883                 tomenu.add(MenuItem(MenuItem::Command, qt_("No Document Open!"),
884                                     FuncRequest(LFUN_NOACTION)));
885                 return;
886         }
887
888         Buffer* cbuf = const_cast<Buffer*>(buf);
889         cbuf->tocBackend().update();
890         cbuf->structureChanged();
891
892         // Add an entry for the master doc if this is a child doc
893         Buffer const * const master = buf->masterBuffer();
894         if (buf != master) {
895                 ParIterator const pit = par_iterator_begin(master->inset());
896                 string const arg = convert<string>(pit->id());
897                 FuncRequest f(LFUN_PARAGRAPH_GOTO, arg);
898                 tomenu.add(MenuItem(MenuItem::Command, qt_("Master Document"), f));
899         }
900
901         FloatList const & floatlist = buf->params().documentClass().floats();
902         TocList const & toc_list = buf->tocBackend().tocs();
903         TocList::const_iterator cit = toc_list.begin();
904         TocList::const_iterator end = toc_list.end();
905         for (; cit != end; ++cit) {
906                 // Handle this later
907                 if (cit->first == "tableofcontents")
908                         continue;
909
910                 // All the rest is for floats
911                 auto_ptr<Menu> menu(new Menu);
912                 TocIterator ccit = cit->second.begin();
913                 TocIterator eend = cit->second.end();
914                 for (; ccit != eend; ++ccit) {
915                         QString const label = limitStringLength(ccit->str());
916                         menu->add(MenuItem(MenuItem::Command, label,
917                                            FuncRequest(ccit->action())));
918                 }
919                 string const & floatName = floatlist.getType(cit->first).listName();
920                 QString label;
921                 if (!floatName.empty())
922                         label = qt_(floatName);
923                 // BUG3633: listings is not a proper float so its name
924                 // is not shown in floatlist.
925                 else if (cit->first == "equation")
926                         label = qt_("List of Equations");
927                 else if (cit->first == "index")
928                         label = qt_("List of Indexes");
929                 else if (cit->first == "listing")
930                         label = qt_("List of Listings");
931                 else if (cit->first == "marginalnote")
932                         label = qt_("List of Marginal notes");
933                 else if (cit->first == "note")
934                         label = qt_("List of Notes");
935                 else if (cit->first == "footnote")
936                         label = qt_("List of Foot notes");
937                 else if (cit->first == "label")
938                         label = qt_("Labels and References");
939                 else if (cit->first == "citation")
940                         label = qt_("List of Citations");
941                 // this should not happen now, but if something else like
942                 // listings is added later, this can avoid an empty menu name.
943                 else
944                         label = qt_("Other floats");
945                 MenuItem item(MenuItem::Submenu, label);
946                 item.submenu(menu.release());
947                 tomenu.add(item);
948         }
949
950         // Handle normal TOC
951         cit = toc_list.find("tableofcontents");
952         if (cit == end) {
953                 tomenu.addWithStatusCheck(MenuItem(MenuItem::Command,
954                                     qt_("No Table of contents"),
955                                     FuncRequest()));
956         } else {
957                 expandToc2(tomenu, cit->second, 0, cit->second.size(), 0);
958         }
959 }
960
961
962 void expandPasteRecent(Menu & tomenu)
963 {
964         vector<docstring> const sel = cap::availableSelections();
965
966         vector<docstring>::const_iterator cit = sel.begin();
967         vector<docstring>::const_iterator end = sel.end();
968
969         for (unsigned int index = 0; cit != end; ++cit, ++index) {
970                 tomenu.add(MenuItem(MenuItem::Command, toqstr(*cit),
971                                     FuncRequest(LFUN_PASTE, convert<string>(index))));
972         }
973 }
974
975
976 void expandToolbars(Menu & tomenu)
977 {
978         //
979         // extracts the toolbars from the backend
980         ToolbarBackend::Toolbars::const_iterator cit = toolbarbackend.begin();
981         ToolbarBackend::Toolbars::const_iterator end = toolbarbackend.end();
982
983         for (; cit != end; ++cit) {
984                 QString label = qt_(cit->gui_name);
985                 // frontends are not supposed to turn on/off toolbars,
986                 // if they cannot update ToolbarBackend::flags. That
987                 // is to say, ToolbarsBackend::flags should reflect
988                 // the true state of toolbars.
989                 //
990                 // menu is displayed as
991                 //       on/off review
992                 // and
993                 //              review (auto)
994                 // in the case of auto.
995                 if (cit->flags & ToolbarInfo::AUTO)
996                         label += qt_(" (auto)");
997                 tomenu.add(MenuItem(MenuItem::Command, label,
998                                     FuncRequest(LFUN_TOOLBAR_TOGGLE, cit->name + " allowauto")));
999         }
1000 }
1001
1002
1003 void expandBranches(Menu & tomenu, Buffer const * buf)
1004 {
1005         if (!buf) {
1006                 tomenu.add(MenuItem(MenuItem::Command,
1007                                     qt_("No Document Open!"),
1008                                     FuncRequest(LFUN_NOACTION)));
1009                 return;
1010         }
1011
1012         BufferParams const & params = buf->masterBuffer()->params();
1013         if (params.branchlist().empty()) {
1014                 tomenu.add(MenuItem(MenuItem::Command,
1015                                     qt_("No Branch in Document!"),
1016                                     FuncRequest(LFUN_NOACTION)));
1017                 return;
1018         }
1019
1020         BranchList::const_iterator cit = params.branchlist().begin();
1021         BranchList::const_iterator end = params.branchlist().end();
1022
1023         for (int ii = 1; cit != end; ++cit, ++ii) {
1024                 docstring label = cit->getBranch();
1025                 if (ii < 10)
1026                         label = convert<docstring>(ii) + ". " + label + char_type('|') + convert<docstring>(ii);
1027                 tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
1028                                     FuncRequest(LFUN_BRANCH_INSERT,
1029                                                 cit->getBranch())));
1030         }
1031 }
1032
1033
1034 } // namespace anon
1035
1036
1037 void Menus::expand(Menu const & frommenu, Menu & tomenu,
1038                          Buffer const * buf) const
1039 {
1040         if (!tomenu.empty())
1041                 tomenu.clear();
1042
1043         for (Menu::const_iterator cit = frommenu.begin();
1044              cit != frommenu.end() ; ++cit) {
1045                 switch (cit->kind()) {
1046                 case MenuItem::Lastfiles:
1047                         expandLastfiles(tomenu);
1048                         break;
1049
1050                 case MenuItem::Documents:
1051                         expandDocuments(tomenu);
1052                         break;
1053
1054                 case MenuItem::Bookmarks:
1055                         expandBookmarks(tomenu);
1056                         break;
1057
1058                 case MenuItem::ImportFormats:
1059                 case MenuItem::ViewFormats:
1060                 case MenuItem::UpdateFormats:
1061                 case MenuItem::ExportFormats:
1062                         expandFormats(cit->kind(), tomenu, buf);
1063                         break;
1064
1065                 case MenuItem::CharStyles:
1066                         expandFlexInsert(tomenu, buf, "charstyle");
1067                         break;
1068
1069                 case MenuItem::Custom:
1070                         expandFlexInsert(tomenu, buf, "custom");
1071                         break;
1072
1073                 case MenuItem::Elements:
1074                         expandFlexInsert(tomenu, buf, "element");
1075                         break;
1076
1077                 case MenuItem::FloatListInsert:
1078                         expandFloatListInsert(tomenu, buf);
1079                         break;
1080
1081                 case MenuItem::FloatInsert:
1082                         expandFloatInsert(tomenu, buf);
1083                         break;
1084
1085                 case MenuItem::PasteRecent:
1086                         expandPasteRecent(tomenu);
1087                         break;
1088
1089                 case MenuItem::Toolbars:
1090                         expandToolbars(tomenu);
1091                         break;
1092
1093                 case MenuItem::Branches:
1094                         expandBranches(tomenu, buf);
1095                         break;
1096
1097                 case MenuItem::Toc:
1098                         expandToc(tomenu, buf);
1099                         break;
1100
1101                 case MenuItem::Submenu: {
1102                         MenuItem item(*cit);
1103                         item.submenu(new Menu(cit->submenuname()));
1104                         expand(getMenu(cit->submenuname()),
1105                                *item.submenu(), buf);
1106                         tomenu.addWithStatusCheck(item);
1107                 }
1108                 break;
1109
1110                 case MenuItem::Separator:
1111                         tomenu.addWithStatusCheck(*cit);
1112                         break;
1113
1114                 case MenuItem::Command:
1115                         if (!specialmenu_.hasFunc(cit->func()))
1116                                 tomenu.addWithStatusCheck(*cit);
1117                 }
1118         }
1119
1120         // we do not want the menu to end with a separator
1121         if (!tomenu.empty()
1122             && tomenu.items_.back().kind() == MenuItem::Separator)
1123                 tomenu.items_.pop_back();
1124
1125         // Check whether the shortcuts are unique
1126         tomenu.checkShortcuts();
1127 }
1128
1129
1130 void Menus::read(Lexer & lex)
1131 {
1132         enum Menutags {
1133                 md_menu = 1,
1134                 md_menubar,
1135                 md_endmenuset,
1136                 md_last
1137         };
1138
1139         struct keyword_item menutags[md_last - 1] = {
1140                 { "end", md_endmenuset },
1141                 { "menu", md_menu },
1142                 { "menubar", md_menubar }
1143         };
1144
1145         //consistency check
1146         if (compare_ascii_no_case(lex.getString(), "menuset")) {
1147                 lyxerr << "Menubackend::read: ERROR wrong token:`"
1148                        << lex.getString() << '\'' << endl;
1149         }
1150
1151         lex.pushTable(menutags, md_last - 1);
1152         if (lyxerr.debugging(Debug::PARSER))
1153                 lex.printTable(lyxerr);
1154
1155         bool quit = false;
1156
1157         while (lex.isOK() && !quit) {
1158                 switch (lex.lex()) {
1159                 case md_menubar:
1160                         menubar_.read(lex);
1161                         break;
1162                 case md_menu: {
1163                         lex.next(true);
1164                         QString const name = toqstr(lex.getDocString());
1165                         if (hasMenu(name)) {
1166                                 getMenu(name).read(lex);
1167                         } else {
1168                                 Menu menu(name);
1169                                 menu.read(lex);
1170                                 add(menu);
1171                         }
1172                         break;
1173                 }
1174                 case md_endmenuset:
1175                         quit = true;
1176                         break;
1177                 default:
1178                         lex.printError("menubackend::read: "
1179                                        "Unknown menu tag: `$$Token'");
1180                         break;
1181                 }
1182         }
1183         lex.popTable();
1184 }
1185
1186
1187 void Menus::add(Menu const & menu)
1188 {
1189         menulist_.push_back(menu);
1190 }
1191
1192
1193 bool Menus::hasMenu(QString const & name) const
1194 {
1195         return find_if(begin(), end(), MenuNamesEqual(name)) != end();
1196 }
1197
1198
1199 Menu const & Menus::getMenu(QString const & name) const
1200 {
1201         const_iterator cit = find_if(begin(), end(), MenuNamesEqual(name));
1202         if (cit == end())
1203                 lyxerr << "No submenu named " << fromqstr(name) << endl;
1204         BOOST_ASSERT(cit != end());
1205         return (*cit);
1206 }
1207
1208
1209 Menu & Menus::getMenu(QString const & name)
1210 {
1211         iterator it = find_if(begin(), end(), MenuNamesEqual(name));
1212         if (it == end())
1213                 lyxerr << "No submenu named " << fromqstr(name) << endl;
1214         BOOST_ASSERT(it != end());
1215         return (*it);
1216 }
1217
1218
1219 Menu const & Menus::getMenubar() const
1220 {
1221         return menubar_;
1222 }
1223
1224
1225 } // namespace frontend
1226 } // namespace lyx
1227
1228 #include "Menus_moc.cpp"