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