]> git.lyx.org Git - lyx.git/blob - src/MenuBackend.C
7eb3166240c89da740ae6fb440557141c30e2617
[lyx.git] / src / MenuBackend.C
1 /**
2  * \file MenuBackend.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author André Pönitz
10  * \author Dekel Tsur
11  * \author Martin Vermeer
12  *
13  * Full author contact details are available in file CREDITS.
14  */
15
16 #include <config.h>
17
18 #include "MenuBackend.h"
19
20 #include "BranchList.h"
21 #include "buffer.h"
22 #include "bufferlist.h"
23 #include "bufferparams.h"
24 #include "CutAndPaste.h"
25 #include "debug.h"
26 #include "exporter.h"
27 #include "Floating.h"
28 #include "FloatList.h"
29 #include "format.h"
30 #include "gettext.h"
31 #include "importer.h"
32 #include "kbmap.h"
33 #include "lastfiles.h"
34 #include "LyXAction.h"
35 #include "lyx_main.h" // for lastfiles
36 #include "lyxfunc.h"
37 #include "lyxlex.h"
38 #include "toc.h"
39
40 #include "frontends/LyXView.h"
41
42 #include "support/filetools.h"
43 #include "support/lstrings.h"
44 #include "support/tostr.h"
45
46 #include <boost/bind.hpp>
47
48 #include <algorithm>
49
50 using lyx::support::compare_ascii_no_case;
51 using lyx::support::contains;
52 using lyx::support::MakeDisplayPath;
53 using lyx::support::token;
54
55 using boost::bind;
56
57 using std::auto_ptr;
58 using std::endl;
59 using std::equal_to;
60 using std::find_if;
61 using std::max;
62 using std::sort;
63 using std::string;
64 using std::vector;
65
66
67 extern BufferList bufferlist;
68 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
69
70 namespace {
71
72 class MenuNamesEqual : public std::unary_function<Menu, bool> {
73 public:
74         MenuNamesEqual(string const & name)
75                 : name_(name) {}
76         bool operator()(Menu const & menu) const
77         {
78                 return menu.name() == name_;
79         }
80 private:
81         string name_;
82 };
83
84 } // namespace anon
85
86
87 // This is the global menu definition
88 MenuBackend menubackend;
89
90
91 MenuItem::MenuItem(Kind kind)
92         : kind_(kind), optional_(false)
93 {}
94
95
96 MenuItem::MenuItem(Kind kind, string const & label,
97                    string const & submenu, bool optional)
98         : kind_(kind), label_(label),
99           submenuname_(submenu), optional_(optional)
100 {
101         BOOST_ASSERT(kind == Submenu);
102 }
103
104
105 MenuItem::MenuItem(Kind kind, string const & label,
106                    FuncRequest const & func, bool optional)
107         : kind_(kind), label_(label), func_(func), optional_(optional)
108 {}
109
110
111 MenuItem::~MenuItem()
112 {}
113
114
115 void MenuItem::submenu(Menu * menu)
116 {
117         submenu_.reset(menu);
118 }
119
120
121 string const MenuItem::label() const
122 {
123         return token(label_, '|', 0);
124 }
125
126
127 string const MenuItem::shortcut() const
128 {
129         return token(label_, '|', 1);
130 }
131
132
133 string const MenuItem::binding() const
134 {
135         if (kind_ != Command)
136                 return string();
137
138         // Get the keys bound to this action, but keep only the
139         // first one later
140         string bindings = toplevel_keymap->findbinding(func_);
141
142         if (!bindings.empty()) {
143                 return bindings.substr(1, bindings.find(']') - 1);
144         } else {
145                 lyxerr[Debug::KBMAP]
146                         << "No bindings for "
147                         << lyxaction.getActionName(func_.action)
148                         << '(' << func_.argument << ')' << endl;
149                 return string();
150         }
151
152 }
153
154
155 Menu & Menu::add(MenuItem const & i, LyXView const * view)
156 {
157         if (!view) {
158                 items_.push_back(i);
159                 return *this;
160         }
161
162         switch (i.kind()) {
163
164         case MenuItem::Command: {
165                 FuncStatus status =
166                         view->getLyXFunc().getStatus(i.func());
167                 if (status.unknown()
168                     || (!status.enabled() && i.optional()))
169                         break;
170                 items_.push_back(i);
171                 items_.back().status(status);
172                 break;
173         }
174
175         case MenuItem::Submenu: {
176                 if (i.submenu()) {
177                         bool enabled = false;
178                         for (const_iterator cit = i.submenu()->begin();
179                              cit != i.submenu()->end(); ++cit) {
180                                 if ((cit->kind() == MenuItem::Command
181                                      || cit->kind() == MenuItem::Submenu)
182                                     && cit->status().enabled()) {
183                                         enabled = true;
184                                         break;
185                                 }
186                         }
187                         if (enabled || !i.optional()) {
188                                 items_.push_back(i);
189                                 items_.back().status().enabled(enabled);
190                         }
191                 }
192                 else
193                         items_.push_back(i);
194                 break;
195         }
196
197         case MenuItem::Separator:
198                 if (!items_.empty()
199                     && items_.back().kind() != MenuItem::Separator)
200                         items_.push_back(i);
201                 break;
202
203         default:
204                 items_.push_back(i);
205         }
206
207         return *this;
208 }
209
210
211 Menu & Menu::read(LyXLex & lex)
212 {
213         enum Menutags {
214                 md_item = 1,
215                 md_branches,
216                 md_documents,
217                 md_charstyles,
218                 md_endmenu,
219                 md_exportformats,
220                 md_importformats,
221                 md_lastfiles,
222                 md_optitem,
223                 md_optsubmenu,
224                 md_separator,
225                 md_submenu,
226                 md_toc,
227                 md_updateformats,
228                 md_viewformats,
229                 md_floatlistinsert,
230                 md_floatinsert,
231                 md_pasterecent,
232                 md_last
233         };
234
235         struct keyword_item menutags[md_last - 1] = {
236                 { "branches", md_branches },
237                 { "charstyles", md_charstyles },
238                 { "documents", md_documents },
239                 { "end", md_endmenu },
240                 { "exportformats", md_exportformats },
241                 { "floatinsert", md_floatinsert },
242                 { "floatlistinsert", md_floatlistinsert },
243                 { "importformats", md_importformats },
244                 { "item", md_item },
245                 { "lastfiles", md_lastfiles },
246                 { "optitem", md_optitem },
247                 { "optsubmenu", md_optsubmenu },
248                 { "pasterecent", md_pasterecent },
249                 { "separator", md_separator },
250                 { "submenu", md_submenu },
251                 { "toc", md_toc },
252                 { "updateformats", md_updateformats },
253                 { "viewformats", md_viewformats }
254         };
255
256         lex.pushTable(menutags, md_last - 1);
257         if (lyxerr.debugging(Debug::PARSER))
258                 lex.printTable(lyxerr);
259
260         bool quit = false;
261         bool optional = false;
262
263         while (lex.isOK() && !quit) {
264                 switch (lex.lex()) {
265                 case md_optitem:
266                         optional = true;
267                         // fallback to md_item
268                 case md_item: {
269                         lex.next(true);
270                         string const name = _(lex.getString());
271                         lex.next(true);
272                         string const command = lex.getString();
273                         FuncRequest func = lyxaction.lookupFunc(command);
274                         add(MenuItem(MenuItem::Command, name, func, optional));
275                         optional = false;
276                         break;
277                 }
278
279                 case md_separator:
280                         add(MenuItem(MenuItem::Separator));
281                         break;
282
283                 case md_lastfiles:
284                         add(MenuItem(MenuItem::Lastfiles));
285                         break;
286
287                 case md_charstyles:
288                         add(MenuItem(MenuItem::CharStyles));
289                         break;
290
291                 case md_documents:
292                         add(MenuItem(MenuItem::Documents));
293                         break;
294
295                 case md_toc:
296                         add(MenuItem(MenuItem::Toc));
297                         break;
298
299                 case md_viewformats:
300                         add(MenuItem(MenuItem::ViewFormats));
301                         break;
302
303                 case md_updateformats:
304                         add(MenuItem(MenuItem::UpdateFormats));
305                         break;
306
307                 case md_exportformats:
308                         add(MenuItem(MenuItem::ExportFormats));
309                         break;
310
311                 case md_importformats:
312                         add(MenuItem(MenuItem::ImportFormats));
313                         break;
314
315                 case md_floatlistinsert:
316                         add(MenuItem(MenuItem::FloatListInsert));
317                         break;
318
319                 case md_floatinsert:
320                         add(MenuItem(MenuItem::FloatInsert));
321                         break;
322
323                 case md_pasterecent:
324                         add(MenuItem(MenuItem::PasteRecent));
325                         break;
326
327                 case md_branches:
328                         add(MenuItem(MenuItem::Branches));
329                         break;
330
331                 case md_optsubmenu:
332                         optional = true;
333                         // fallback to md_submenu
334                 case md_submenu: {
335                         lex.next(true);
336                         string const mlabel = _(lex.getString());
337                         lex.next(true);
338                         string const mname = lex.getString();
339                         add(MenuItem(MenuItem::Submenu, mlabel, mname,
340                                      optional));
341                         optional = false;
342                         break;
343                 }
344
345                 case md_endmenu:
346                         quit = true;
347                         break;
348
349                 default:
350                         lex.printError("Menu::read: "
351                                        "Unknown menu tag: `$$Token'");
352                         break;
353                 }
354         }
355         lex.popTable();
356         return *this;
357 }
358
359
360 void Menu::checkShortcuts() const
361 {
362         // This is a quadratic algorithm, but we do not care because
363         // menus are short enough
364         for (const_iterator it1 = begin(); it1 != end(); ++it1) {
365                 string shortcut = it1->shortcut();
366                 if (shortcut.empty())
367                         continue;
368                 if (!contains(it1->label(), shortcut))
369                         lyxerr << "Menu warning: menu entry \""
370                                << it1->label()
371                                << "\" does not contain shortcut `"
372                                << shortcut << "'." << endl;
373                 for (const_iterator it2 = begin(); it2 != it1 ; ++it2) {
374                         if (!compare_ascii_no_case(it2->shortcut(), shortcut)) {
375                                 lyxerr << "Menu warning: menu entries "
376                                        << '"' << it1->fulllabel()
377                                        << "\" and \"" << it2->fulllabel()
378                                        << "\" share the same shortcut."
379                                        << endl;
380                         }
381                 }
382         }
383 }
384
385
386 namespace {
387
388 class compare_format {
389 public:
390         bool operator()(Format const * p1, Format const * p2) {
391                 return *p1 < *p2;
392         }
393 };
394
395 string const limit_string_length(string const & str)
396 {
397         string::size_type const max_item_length = 45;
398
399         if (str.size() > max_item_length)
400                 return str.substr(0, max_item_length - 3) + "...";
401         else
402                 return str;
403 }
404
405
406 void expandLastfiles(Menu & tomenu, LyXView const * view)
407 {
408         LastFiles const & lastfiles = LyX::cref().lastfiles();
409
410         int ii = 1;
411         LastFiles::const_iterator lfit = lastfiles.begin();
412         LastFiles::const_iterator end = lastfiles.end();
413
414         for (; lfit != end && ii < 10; ++lfit, ++ii) {
415                 string const label = tostr(ii) + ". "
416                         + MakeDisplayPath((*lfit), 30)
417                         + '|' + tostr(ii);
418                 tomenu.add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_FILE_OPEN, (*lfit))), view);
419         }
420 }
421
422 void expandDocuments(Menu & tomenu, LyXView const * view)
423 {
424         typedef vector<string> Strings;
425         Strings const names = bufferlist.getFileNames();
426
427         if (names.empty()) {
428                 tomenu.add(MenuItem(MenuItem::Command, _("No Documents Open!"),
429                                     FuncRequest(LFUN_NOACTION)), view);
430                 return;
431         }
432
433         int ii = 1;
434         Strings::const_iterator docit = names.begin();
435         Strings::const_iterator end = names.end();
436         for (; docit != end; ++docit, ++ii) {
437                 string label = MakeDisplayPath(*docit, 20);
438                 if (ii < 10)
439                         label = tostr(ii) + ". " + label + '|' + tostr(ii);
440                 tomenu.add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_SWITCHBUFFER, *docit)), view);
441         }
442 }
443
444
445 void expandFormats(MenuItem::Kind kind, Menu & tomenu, LyXView const * view)
446 {
447         if (!view->buffer() && kind != MenuItem::ImportFormats) {
448                 tomenu.add(MenuItem(MenuItem::Command,
449                                     _("No Documents Open!"),
450                                     FuncRequest(LFUN_NOACTION)),
451                                     view);
452                 return;
453         }
454
455         typedef vector<Format const *> Formats;
456         Formats formats;
457         kb_action action;
458
459         switch (kind) {
460         case MenuItem::ImportFormats:
461                 formats = Importer::GetImportableFormats();
462                 action = LFUN_IMPORT;
463                 break;
464         case MenuItem::ViewFormats:
465                 formats = Exporter::GetExportableFormats(*view->buffer(), true);
466                 action = LFUN_PREVIEW;
467                 break;
468         case MenuItem::UpdateFormats:
469                 formats = Exporter::GetExportableFormats(*view->buffer(), true);
470                 action = LFUN_UPDATE;
471                 break;
472         default:
473                 formats = Exporter::GetExportableFormats(*view->buffer(), false);
474                 action = LFUN_EXPORT;
475         }
476         sort(formats.begin(), formats.end(), compare_format());
477
478         Formats::const_iterator fit = formats.begin();
479         Formats::const_iterator end = formats.end();
480         for (; fit != end ; ++fit) {
481                 if ((*fit)->dummy())
482                         continue;
483                 string label = (*fit)->prettyname();
484                 // we need to hide the default graphic export formats
485                 // from the external menu, because we need them only
486                 // for the internal lyx-view and external latex run
487                 if (label == "EPS" || label == "XPM" || label == "PNG")
488                         continue;
489
490                 if (kind == MenuItem::ImportFormats) {
491                         if ((*fit)->name() == "text")
492                                 label = _("ASCII text as lines");
493                         else if ((*fit)->name() == "textparagraph")
494                                 label = _("ASCII text as paragraphs");
495                         label += "...";
496                 }
497                 if (!(*fit)->shortcut().empty())
498                         label += '|' + (*fit)->shortcut();
499
500                 tomenu.add(MenuItem(MenuItem::Command, label,
501                                     FuncRequest(action, (*fit)->name())),
502                            view);
503         }
504 }
505
506
507 void expandFloatListInsert(Menu & tomenu, LyXView const * view)
508 {
509         if (!view->buffer()) {
510                 tomenu.add(MenuItem(MenuItem::Command,
511                                     _("No Documents Open!"),
512                                     FuncRequest(LFUN_NOACTION)),
513                            view);
514                 return;
515         }
516
517         FloatList const & floats =
518                 view->buffer()->params().getLyXTextClass().floats();
519         FloatList::const_iterator cit = floats.begin();
520         FloatList::const_iterator end = floats.end();
521         for (; cit != end; ++cit) {
522                 tomenu.add(MenuItem(MenuItem::Command,
523                                     _(cit->second.listName()),
524                                     FuncRequest(LFUN_FLOAT_LIST,
525                                                 cit->second.type())),
526                            view);
527         }
528 }
529
530
531 void expandFloatInsert(Menu & tomenu, LyXView const * view)
532 {
533         if (!view->buffer()) {
534                 tomenu.add(MenuItem(MenuItem::Command,
535                                     _("No Documents Open!"),
536                                     FuncRequest(LFUN_NOACTION)),
537                            view);
538                 return;
539         }
540
541         FloatList const & floats =
542                 view->buffer()->params().getLyXTextClass().floats();
543         FloatList::const_iterator cit = floats.begin();
544         FloatList::const_iterator end = floats.end();
545         for (; cit != end; ++cit) {
546                 // normal float
547                 string const label = _(cit->second.name());
548                 tomenu.add(MenuItem(MenuItem::Command, label,
549                                     FuncRequest(LFUN_INSET_FLOAT,
550                                                 cit->second.type())),
551                            view);
552         }
553 }
554
555
556 void expandCharStyleInsert(Menu & tomenu, LyXView const * view)
557 {
558         if (!view->buffer()) {
559                 tomenu.add(MenuItem(MenuItem::Command,
560                                     _("No Documents Open!"),
561                                     FuncRequest(LFUN_NOACTION)),
562                            view);
563                 return;
564         }
565         CharStyles & charstyles =
566                 view->buffer()->params().getLyXTextClass().charstyles();
567         CharStyles::iterator cit = charstyles.begin();
568         CharStyles::iterator end = charstyles.end();
569         for (; cit != end; ++cit) {
570                 string const label = cit->name;
571                 tomenu.add(MenuItem(MenuItem::Command, label,
572                                     FuncRequest(LFUN_INSERT_CHARSTYLE,
573                                                 cit->name)), view);
574         }
575 }
576
577
578 Menu::size_type const max_number_of_items = 25;
579
580 void expandToc2(Menu & tomenu,
581                 lyx::toc::Toc const & toc_list,
582                 lyx::toc::Toc::size_type from,
583                 lyx::toc::Toc::size_type to, int depth)
584 {
585         int shortcut_count = 0;
586         if (to - from <= max_number_of_items) {
587                 for (lyx::toc::Toc::size_type i = from; i < to; ++i) {
588                         string label(4 * max(0, toc_list[i].depth - depth),' ');
589                         label += limit_string_length(toc_list[i].str);
590                         if (toc_list[i].depth == depth
591                             && shortcut_count < 9) {
592                                 if (label.find(tostr(shortcut_count + 1)) != string::npos)
593                                         label += '|' + tostr(++shortcut_count);
594                         }
595                         tomenu.add(MenuItem(MenuItem::Command, label,
596                                             FuncRequest(toc_list[i].action())));
597                 }
598         } else {
599                 lyx::toc::Toc::size_type pos = from;
600                 while (pos < to) {
601                         lyx::toc::Toc::size_type new_pos = pos + 1;
602                         while (new_pos < to &&
603                                toc_list[new_pos].depth > depth)
604                                 ++new_pos;
605
606                         string label(4 * max(0, toc_list[pos].depth - depth), ' ');
607                         label += limit_string_length(toc_list[pos].str);
608                         if (toc_list[pos].depth == depth &&
609                             shortcut_count < 9) {
610                                 if (label.find(tostr(shortcut_count + 1)) != string::npos)
611                                         label += '|' + tostr(++shortcut_count);
612                                 }
613                         if (new_pos == pos + 1) {
614                                 tomenu.add(MenuItem(MenuItem::Command,
615                                                     label, FuncRequest(toc_list[pos].action())));
616                         } else {
617                                 MenuItem item(MenuItem::Submenu, label);
618                                 item.submenu(new Menu);
619                                 expandToc2(*item.submenu(),
620                                            toc_list, pos, new_pos, depth + 1);
621                                 tomenu.add(item);
622                         }
623                         pos = new_pos;
624                 }
625         }
626 }
627
628
629 void expandToc(Menu & tomenu, LyXView const * view)
630 {
631         // To make things very cleanly, we would have to pass view to
632         // all MenuItem constructors and to expandToc2. However, we
633         // know that all the entries in a TOC will be have status_ ==
634         // OK, so we avoid this unnecessary overhead (JMarc)
635
636         if (!view->buffer()) {
637                 tomenu.add(MenuItem(MenuItem::Command,
638                                     _("No Documents Open!"),
639                                     FuncRequest(LFUN_NOACTION)),
640                            view);
641                 return;
642         }
643
644         lyx::toc::TocList toc_list = lyx::toc::getTocList(*view->buffer());
645         lyx::toc::TocList::const_iterator cit = toc_list.begin();
646         lyx::toc::TocList::const_iterator end = toc_list.end();
647         for (; cit != end; ++cit) {
648                 // Handle this later
649                 if (cit->first == "TOC")
650                         continue;
651
652                 // All the rest is for floats
653                 auto_ptr<Menu> menu(new Menu);
654                 lyx::toc::Toc::const_iterator ccit = cit->second.begin();
655                 lyx::toc::Toc::const_iterator eend = cit->second.end();
656                 for (; ccit != eend; ++ccit) {
657                         string const label = limit_string_length(ccit->str);
658                         menu->add(MenuItem(MenuItem::Command,
659                                            label,
660                                            FuncRequest(ccit->action())));
661                 }
662                 string const & floatName = cit->first;
663                 // Is the _(...) really needed here? (Lgb)
664                 MenuItem item(MenuItem::Submenu, _(floatName));
665                 item.submenu(menu.release());
666                 tomenu.add(item);
667         }
668
669         // Handle normal TOC
670         cit = toc_list.find("TOC");
671         if (cit == end) {
672                 tomenu.add(MenuItem(MenuItem::Command,
673                                     _("No Table of contents"),
674                                     FuncRequest()),
675                            view);
676         } else {
677                 expandToc2(tomenu, cit->second, 0, cit->second.size(), 0);
678         }
679 }
680
681
682 void expandPasteRecent(Menu & tomenu, LyXView const * view)
683 {
684         if (!view || !view->buffer())
685                 return;
686
687         vector<string> const sel =
688                 lyx::cap::availableSelections(*view->buffer());
689
690         vector<string>::const_iterator cit = sel.begin();
691         vector<string>::const_iterator end = sel.end();
692
693         for (unsigned int index = 0; cit != end; ++cit, ++index) {
694                 tomenu.add(MenuItem(MenuItem::Command, *cit,
695                                     FuncRequest(LFUN_PASTE, tostr(index))));
696         }
697 }
698
699
700 void expandBranches(Menu & tomenu, LyXView const * view)
701 {
702         if (!view || !view->buffer())
703                 return;
704
705         BufferParams const & params = view->buffer()->params();
706
707         std::list<Branch>::const_iterator cit = params.branchlist().begin();
708         std::list<Branch>::const_iterator end = params.branchlist().end();
709
710         for (int ii = 1; cit != end; ++cit, ++ii) {
711                 string label = cit->getBranch();
712                 if (ii < 10)
713                         label = tostr(ii) + ". " + label + "|" + tostr(ii);
714                 tomenu.add(MenuItem(MenuItem::Command, label,
715                                     FuncRequest(LFUN_INSERT_BRANCH,
716                                                 cit->getBranch())), view);
717         }
718 }
719
720
721 } // namespace anon
722
723
724 void MenuBackend::expand(Menu const & frommenu, Menu & tomenu,
725                          LyXView const * view) const
726 {
727         for (Menu::const_iterator cit = frommenu.begin();
728              cit != frommenu.end() ; ++cit) {
729                 switch (cit->kind()) {
730                 case MenuItem::Lastfiles:
731                         expandLastfiles(tomenu, view);
732                         break;
733
734                 case MenuItem::Documents:
735                         expandDocuments(tomenu, view);
736                         break;
737
738                 case MenuItem::ImportFormats:
739                 case MenuItem::ViewFormats:
740                 case MenuItem::UpdateFormats:
741                 case MenuItem::ExportFormats:
742                         expandFormats(cit->kind(), tomenu, view);
743                         break;
744
745                 case MenuItem::CharStyles:
746                         expandCharStyleInsert(tomenu, view);
747                         break;
748
749                 case MenuItem::FloatListInsert:
750                         expandFloatListInsert(tomenu, view);
751                         break;
752
753                 case MenuItem::FloatInsert:
754                         expandFloatInsert(tomenu, view);
755                         break;
756
757                 case MenuItem::PasteRecent:
758                         expandPasteRecent(tomenu, view);
759                         break;
760
761                 case MenuItem::Branches:
762                         expandBranches(tomenu, view);
763                         break;
764
765                 case MenuItem::Toc:
766                         expandToc(tomenu, view);
767                         break;
768
769                 case MenuItem::Submenu: {
770                         MenuItem item(*cit);
771                         item.submenu(new Menu(cit->submenuname()));
772                         expand(getMenu(cit->submenuname()),
773                                *item.submenu(), view);
774                         tomenu.add(item, view);
775                 }
776                 break;
777
778                 default:
779                         tomenu.add(*cit, view);
780                 }
781         }
782
783         // we do not want the menu to end with a separator
784         if (!tomenu.empty()
785             && tomenu.items_.back().kind() == MenuItem::Separator)
786                 tomenu.items_.pop_back();
787
788         // Check whether the shortcuts are unique
789         tomenu.checkShortcuts();
790 }
791
792
793 bool Menu::hasSubmenu(string const & name) const
794 {
795 #if 1
796         return find_if(begin(), end(),
797                        bind(std::equal_to<string>(),
798                             bind(&MenuItem::submenuname, _1),
799                             name)) != end();
800 #else
801         // I would have prefered this, but I am not sure if it
802         // makes a difference. (Lgb)
803         return find_if(
804                 make_transform_iterator(begin(),
805                                         bind(&MenuItem::submenuname, _1)),
806                 make_transform_iterator(end(),
807                                         bind(&MenuItem::submenuname, _1)),
808                 name
809                 ).base() != end();
810 #endif
811 }
812
813
814 void MenuBackend::read(LyXLex & lex)
815 {
816         enum Menutags {
817                 md_menu = 1,
818                 md_menubar,
819                 md_endmenuset,
820                 md_last
821         };
822
823         struct keyword_item menutags[md_last - 1] = {
824                 { "end", md_endmenuset },
825                 { "menu", md_menu },
826                 { "menubar", md_menubar }
827         };
828
829         //consistency check
830         if (compare_ascii_no_case(lex.getString(), "menuset")) {
831                 lyxerr << "Menubackend::read: ERROR wrong token:`"
832                        << lex.getString() << '\'' << endl;
833         }
834
835         lex.pushTable(menutags, md_last - 1);
836         if (lyxerr.debugging(Debug::PARSER))
837                 lex.printTable(lyxerr);
838
839         bool quit = false;
840
841         while (lex.isOK() && !quit) {
842                 switch (lex.lex()) {
843                 case md_menubar:
844                         menubar_.read(lex);
845                         break;
846                 case md_menu: {
847                         lex.next(true);
848                         string const name = lex.getString();
849                         if (hasMenu(name)) {
850                                 getMenu(name).read(lex);
851                         } else {
852                                 Menu menu(name);
853                                 menu.read(lex);
854                                 add(menu);
855                         }
856                         break;
857                 }
858                 case md_endmenuset:
859                         quit = true;
860                         break;
861                 default:
862                         lex.printError("menubackend::read: "
863                                        "Unknown menu tag: `$$Token'");
864                         break;
865                 }
866         }
867         lex.popTable();
868 }
869
870
871 void MenuBackend::add(Menu const & menu)
872 {
873         menulist_.push_back(menu);
874 }
875
876
877 bool MenuBackend::hasMenu(string const & name) const
878 {
879         return find_if(begin(), end(), MenuNamesEqual(name)) != end();
880 }
881
882
883 Menu const & MenuBackend::getMenu(string const & name) const
884 {
885         const_iterator cit = find_if(begin(), end(), MenuNamesEqual(name));
886         if (cit == end())
887                 lyxerr << "No submenu named " << name << endl;
888         BOOST_ASSERT(cit != end());
889         return (*cit);
890 }
891
892
893 Menu & MenuBackend::getMenu(string const & name)
894 {
895         iterator it = find_if(begin(), end(), MenuNamesEqual(name));
896         if (it == end())
897                 lyxerr << "No submenu named " << name << endl;
898         BOOST_ASSERT(it != end());
899         return (*it);
900 }
901
902
903 Menu const & MenuBackend::getMenubar() const
904 {
905         return menubar_;
906 }