]> git.lyx.org Git - lyx.git/blob - src/MenuBackend.cpp
Some more cleanup of LyXView:
[lyx.git] / src / MenuBackend.cpp
1 /**
2  * \file MenuBackend.cpp
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 "Converter.h"
25 #include "CutAndPaste.h"
26 #include "debug.h"
27 #include "Floating.h"
28 #include "FloatList.h"
29 #include "Format.h"
30 #include "gettext.h"
31 #include "KeyMap.h"
32 #include "Session.h"
33 #include "LyXAction.h"
34 #include "LyX.h" // for lastfiles
35 #include "LyXFunc.h"
36 #include "Lexer.h"
37 #include "Paragraph.h"
38 #include "TextClass.h"
39 #include "TocBackend.h"
40 #include "ToolbarBackend.h"
41
42 #include "support/filetools.h"
43 #include "support/lstrings.h"
44 #include "support/convert.h"
45
46 #include <boost/bind.hpp>
47
48 #include <algorithm>
49
50
51 namespace lyx {
52
53 using support::compare_ascii_no_case;
54 using support::contains;
55 using support::makeDisplayPath;
56 using support::token;
57
58 using boost::bind;
59
60 using std::auto_ptr;
61 using std::endl;
62 using std::equal_to;
63 using std::find_if;
64 using std::max;
65 using std::sort;
66 using std::string;
67 using std::vector;
68 using std::stack;
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                 return menu.name() == name_;
78         }
79 private:
80         docstring name_;
81 };
82
83 } // namespace anon
84
85
86 // This is the global menu definition
87 MenuBackend menubackend;
88
89
90 MenuItem::MenuItem(Kind kind)
91         : kind_(kind), optional_(false)
92 {}
93
94
95 MenuItem::MenuItem(Kind kind, docstring const & label,
96                    docstring const & submenu, bool optional)
97         : kind_(kind), label_(label),
98           submenuname_(submenu), optional_(optional)
99 {
100         BOOST_ASSERT(kind == Submenu);
101 }
102
103
104 MenuItem::MenuItem(Kind kind, docstring const & label,
105                    FuncRequest const & func, bool optional)
106         : kind_(kind), label_(label), func_(func), optional_(optional)
107 {
108         func_.origin = FuncRequest::MENU;
109 }
110
111
112 MenuItem::~MenuItem()
113 {}
114
115
116 void MenuItem::submenu(Menu * menu)
117 {
118         submenu_.reset(menu);
119 }
120
121
122 docstring const MenuItem::label() const
123 {
124         return token(label_, char_type('|'), 0);
125 }
126
127
128 docstring const MenuItem::shortcut() const
129 {
130         return token(label_, char_type('|'), 1);
131 }
132
133
134 docstring const MenuItem::binding(bool forgui) const
135 {
136         if (kind_ != Command)
137                 return docstring();
138
139         // Get the keys bound to this action, but keep only the
140         // first one later
141         KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(func_);
142
143         if (bindings.size())
144                 return bindings.begin()->print(KeySequence::ForGui);
145
146         LYXERR(Debug::KBMAP, "No binding for "
147                 << lyxaction.getActionName(func_.action)
148                 << '(' << to_utf8(func_.argument()) << ')');
149         return docstring();
150 }
151
152
153 Menu & Menu::add(MenuItem const & i)
154 {
155         items_.push_back(i);
156         return *this;
157 }
158
159
160 Menu & Menu::addWithStatusCheck(MenuItem const & i)
161 {
162         switch (i.kind()) {
163
164         case MenuItem::Command: {
165                 FuncStatus status = lyx::getStatus(i.func());
166                 if (status.unknown() || (!status.enabled() && i.optional()))
167                         break;
168                 items_.push_back(i);
169                 items_.back().status(status);
170                 break;
171         }
172
173         case MenuItem::Submenu: {
174                 if (i.submenu()) {
175                         bool enabled = false;
176                         for (const_iterator cit = i.submenu()->begin();
177                              cit != i.submenu()->end(); ++cit) {
178                                 if ((cit->kind() == MenuItem::Command
179                                      || cit->kind() == MenuItem::Submenu)
180                                     && cit->status().enabled()) {
181                                         enabled = true;
182                                         break;
183                                 }
184                         }
185                         if (enabled || !i.optional()) {
186                                 items_.push_back(i);
187                                 items_.back().status().enabled(enabled);
188                         }
189                 }
190                 else
191                         items_.push_back(i);
192                 break;
193         }
194
195         case MenuItem::Separator:
196                 if (!items_.empty()
197                     && items_.back().kind() != MenuItem::Separator)
198                         items_.push_back(i);
199                 break;
200
201         default:
202                 items_.push_back(i);
203         }
204
205         return *this;
206 }
207
208
209 Menu & Menu::read(Lexer & lex)
210 {
211         enum Menutags {
212                 md_item = 1,
213                 md_branches,
214                 md_documents,
215                 md_bookmarks,
216                 md_charstyles,
217                 md_custom,
218                 md_elements,
219                 md_endmenu,
220                 md_exportformats,
221                 md_importformats,
222                 md_lastfiles,
223                 md_optitem,
224                 md_optsubmenu,
225                 md_separator,
226                 md_submenu,
227                 md_toc,
228                 md_updateformats,
229                 md_viewformats,
230                 md_floatlistinsert,
231                 md_floatinsert,
232                 md_pasterecent,
233                 md_toolbars,
234                 md_last
235         };
236
237         struct keyword_item menutags[md_last - 1] = {
238                 { "bookmarks", md_bookmarks },
239                 { "branches", md_branches },
240                 { "charstyles", md_charstyles },
241                 { "custom", md_custom },
242                 { "documents", md_documents },
243                 { "elements", md_elements },
244                 { "end", md_endmenu },
245                 { "exportformats", md_exportformats },
246                 { "floatinsert", md_floatinsert },
247                 { "floatlistinsert", md_floatlistinsert },
248                 { "importformats", md_importformats },
249                 { "item", md_item },
250                 { "lastfiles", md_lastfiles },
251                 { "optitem", md_optitem },
252                 { "optsubmenu", md_optsubmenu },
253                 { "pasterecent", md_pasterecent },
254                 { "separator", md_separator },
255                 { "submenu", md_submenu },
256                 { "toc", md_toc },
257                 { "toolbars", md_toolbars },
258                 { "updateformats", md_updateformats },
259                 { "viewformats", md_viewformats }
260         };
261
262         lex.pushTable(menutags, md_last - 1);
263         if (lyxerr.debugging(Debug::PARSER))
264                 lex.printTable(lyxerr);
265
266         bool quit = false;
267         bool optional = false;
268
269         while (lex.isOK() && !quit) {
270                 switch (lex.lex()) {
271                 case md_optitem:
272                         optional = true;
273                         // fallback to md_item
274                 case md_item: {
275                         lex.next(true);
276                         docstring const name = translateIfPossible(lex.getDocString());
277                         lex.next(true);
278                         string const command = lex.getString();
279                         FuncRequest func = lyxaction.lookupFunc(command);
280                         add(MenuItem(MenuItem::Command, name, func, optional));
281                         optional = false;
282                         break;
283                 }
284
285                 case md_separator:
286                         add(MenuItem(MenuItem::Separator));
287                         break;
288
289                 case md_lastfiles:
290                         add(MenuItem(MenuItem::Lastfiles));
291                         break;
292
293                 case md_charstyles:
294                         add(MenuItem(MenuItem::CharStyles));
295                         break;
296
297                 case md_custom:
298                         add(MenuItem(MenuItem::Custom));
299                         break;
300
301                 case md_elements:
302                         add(MenuItem(MenuItem::Elements));
303                         break;
304
305                 case md_documents:
306                         add(MenuItem(MenuItem::Documents));
307                         break;
308
309                 case md_bookmarks:
310                         add(MenuItem(MenuItem::Bookmarks));
311                         break;
312
313                 case md_toc:
314                         add(MenuItem(MenuItem::Toc));
315                         break;
316
317                 case md_viewformats:
318                         add(MenuItem(MenuItem::ViewFormats));
319                         break;
320
321                 case md_updateformats:
322                         add(MenuItem(MenuItem::UpdateFormats));
323                         break;
324
325                 case md_exportformats:
326                         add(MenuItem(MenuItem::ExportFormats));
327                         break;
328
329                 case md_importformats:
330                         add(MenuItem(MenuItem::ImportFormats));
331                         break;
332
333                 case md_floatlistinsert:
334                         add(MenuItem(MenuItem::FloatListInsert));
335                         break;
336
337                 case md_floatinsert:
338                         add(MenuItem(MenuItem::FloatInsert));
339                         break;
340
341                 case md_pasterecent:
342                         add(MenuItem(MenuItem::PasteRecent));
343                         break;
344
345                 case md_toolbars:
346                         add(MenuItem(MenuItem::Toolbars));
347                         break;
348
349                 case md_branches:
350                         add(MenuItem(MenuItem::Branches));
351                         break;
352
353                 case md_optsubmenu:
354                         optional = true;
355                         // fallback to md_submenu
356                 case md_submenu: {
357                         lex.next(true);
358                         docstring const mlabel = translateIfPossible(lex.getDocString());
359                         lex.next(true);
360                         docstring const mname = lex.getDocString();
361                         add(MenuItem(MenuItem::Submenu, mlabel, mname,
362                                      optional));
363                         optional = false;
364                         break;
365                 }
366
367                 case md_endmenu:
368                         quit = true;
369                         break;
370
371                 default:
372                         lex.printError("Menu::read: "
373                                        "Unknown menu tag: `$$Token'");
374                         break;
375                 }
376         }
377         lex.popTable();
378         return *this;
379 }
380
381
382 MenuItem const & Menu::operator[](size_type i) const
383 {
384         return items_[i];
385 }
386
387
388 bool Menu::hasFunc(FuncRequest const & func) const
389 {
390         return find_if(begin(), end(),
391                        bind(std::equal_to<FuncRequest>(),
392                             bind(&MenuItem::func, _1),
393                             func)) != end();
394 }
395
396 void Menu::checkShortcuts() const
397 {
398         // This is a quadratic algorithm, but we do not care because
399         // menus are short enough
400         for (const_iterator it1 = begin(); it1 != end(); ++it1) {
401                 docstring shortcut = it1->shortcut();
402                 if (shortcut.empty())
403                         continue;
404                 if (!contains(it1->label(), shortcut))
405                         lyxerr << "Menu warning: menu entry \""
406                                << to_utf8(it1->label())
407                                << "\" does not contain shortcut `"
408                                << to_utf8(shortcut) << "'." << endl;
409                 for (const_iterator it2 = begin(); it2 != it1 ; ++it2) {
410                         if (!compare_ascii_no_case(it2->shortcut(), shortcut)) {
411                                 lyxerr << "Menu warning: menu entries "
412                                        << '"' << to_utf8(it1->fulllabel())
413                                        << "\" and \"" << to_utf8(it2->fulllabel())
414                                        << "\" share the same shortcut."
415                                        << endl;
416                         }
417                 }
418         }
419 }
420
421
422 bool Menu::searchFunc(FuncRequest & func, stack<docstring> & names)
423 {
424         const_iterator m = begin();
425         const_iterator m_end = end();
426         for (; m != m_end; ++m) {
427                 if (m->kind() == MenuItem::Command && m->func() == func) {
428                         names.push(m->label());
429                         return true;
430                 } else if (m->kind() == MenuItem::Submenu) {
431                         names.push(m->label());
432                         Menu submenu = menubackend.getMenu(m->submenuname());
433                         if (submenu.searchFunc(func, names))
434                                 return true;
435                         else
436                                 names.pop();
437                 }
438         }
439         return false;
440 }
441
442
443 void MenuBackend::specialMenu(Menu const & menu)
444 {
445         specialmenu_ = menu;
446 }
447
448
449 namespace {
450
451 class compare_format {
452 public:
453         bool operator()(Format const * p1, Format const * p2) {
454                 return *p1 < *p2;
455         }
456 };
457
458 docstring const limit_string_length(docstring const & str)
459 {
460         docstring::size_type const max_item_length = 45;
461
462         if (str.size() > max_item_length)
463                 return str.substr(0, max_item_length - 3) + "...";
464         else
465                 return str;
466 }
467
468
469 void expandLastfiles(Menu & tomenu)
470 {
471         lyx::LastFilesSection::LastFiles const & lf = LyX::cref().session().lastFiles().lastFiles();
472         lyx::LastFilesSection::LastFiles::const_iterator lfit = lf.begin();
473
474         int ii = 1;
475
476         for (; lfit != lf.end() && ii < 10; ++lfit, ++ii) {
477                 string const file = lfit->absFilename();
478                 docstring const label = convert<docstring>(ii) + ". "
479                         + makeDisplayPath(file, 30)
480                         + char_type('|') + convert<docstring>(ii);
481                 tomenu.add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_FILE_OPEN, file)));
482         }
483 }
484
485
486 void expandDocuments(Menu & tomenu)
487 {
488         Buffer * first = theBufferList().first();
489         if (first) {
490                 Buffer * b = first;
491                 int ii = 1;
492                 
493                 // We cannot use a for loop as the buffer list cycles.
494                 do {
495                         docstring label = b->fileName().displayName(20);
496                         if (!b->isClean())
497                                 label = label + "*";
498                         if (ii < 10)
499                                 label = convert<docstring>(ii) + ". " + label + '|' + convert<docstring>(ii);
500                         tomenu.add(MenuItem(MenuItem::Command, label,
501                                 FuncRequest(LFUN_BUFFER_SWITCH, b->absFileName())));
502                         
503                         b = theBufferList().next(b);
504                         ++ii;
505                 } while (b != first); 
506         } else {
507                 tomenu.add(MenuItem(MenuItem::Command, _("No Documents Open!"),
508                            FuncRequest(LFUN_NOACTION)));
509         }
510 }
511
512
513 void expandBookmarks(Menu & tomenu)
514 {
515         lyx::BookmarksSection const & bm = LyX::cref().session().bookmarks();
516
517         for (size_t i = 1; i <= bm.size(); ++i) {
518                 if (bm.isValid(i)) {
519                         docstring const label = convert<docstring>(i) + ". "
520                                 + makeDisplayPath(bm.bookmark(i).filename.absFilename(), 20)
521                                 + char_type('|') + convert<docstring>(i);
522                         tomenu.add(MenuItem(MenuItem::Command, label, FuncRequest(LFUN_BOOKMARK_GOTO,
523                                 convert<docstring>(i))));
524                 }
525         }
526 }
527
528
529 void expandFormats(MenuItem::Kind kind, Menu & tomenu, Buffer const * buf)
530 {
531         if (!buf && kind != MenuItem::ImportFormats) {
532                 tomenu.add(MenuItem(MenuItem::Command,
533                                     _("No Document Open!"),
534                                     FuncRequest(LFUN_NOACTION)));
535                 return;
536         }
537
538         typedef vector<Format const *> Formats;
539         Formats formats;
540         kb_action action;
541
542         switch (kind) {
543         case MenuItem::ImportFormats:
544                 formats = theConverters().importableFormats();
545                 action = LFUN_BUFFER_IMPORT;
546                 break;
547         case MenuItem::ViewFormats:
548                 formats = buf->exportableFormats(true);
549                 action = LFUN_BUFFER_VIEW;
550                 break;
551         case MenuItem::UpdateFormats:
552                 formats = buf->exportableFormats(true);
553                 action = LFUN_BUFFER_UPDATE;
554                 break;
555         default:
556                 formats = buf->exportableFormats(false);
557                 action = LFUN_BUFFER_EXPORT;
558         }
559         sort(formats.begin(), formats.end(), compare_format());
560
561         Formats::const_iterator fit = formats.begin();
562         Formats::const_iterator end = formats.end();
563         for (; fit != end ; ++fit) {
564                 if ((*fit)->dummy())
565                         continue;
566                 docstring label = from_utf8((*fit)->prettyname());
567
568                 switch (kind) {
569                 case MenuItem::ImportFormats:
570                         // FIXME: This is a hack, we should rather solve
571                         // FIXME: bug 2488 instead.
572                         if ((*fit)->name() == "text")
573                                 label = _("Plain Text");
574                         else if ((*fit)->name() == "textparagraph")
575                                 label = _("Plain Text, Join Lines");
576                         label += "...";
577                         break;
578                 case MenuItem::ViewFormats:
579                 case MenuItem::ExportFormats:
580                 case MenuItem::UpdateFormats:
581                         if (!(*fit)->documentFormat())
582                                 continue;
583                         break;
584                 default:
585                         BOOST_ASSERT(false);
586                         break;
587                 }
588                 if (!(*fit)->shortcut().empty())
589                         label += char_type('|') + from_utf8((*fit)->shortcut());
590
591                 if (buf)
592                         tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label,
593                                 FuncRequest(action, (*fit)->name())));
594                 else
595                         tomenu.add(MenuItem(MenuItem::Command, label,
596                                 FuncRequest(action, (*fit)->name())));
597         }
598 }
599
600
601 void expandFloatListInsert(Menu & tomenu, Buffer const * buf)
602 {
603         if (!buf) {
604                 tomenu.add(MenuItem(MenuItem::Command,
605                                     _("No Document Open!"),
606                                     FuncRequest(LFUN_NOACTION)));
607                 return;
608         }
609
610         FloatList const & floats =
611                 buf->params().getTextClass().floats();
612         FloatList::const_iterator cit = floats.begin();
613         FloatList::const_iterator end = floats.end();
614         for (; cit != end; ++cit) {
615                 tomenu.addWithStatusCheck(MenuItem(MenuItem::Command,
616                                     _(cit->second.listName()),
617                                     FuncRequest(LFUN_FLOAT_LIST,
618                                                 cit->second.type())));
619         }
620 }
621
622
623 void expandFloatInsert(Menu & tomenu, Buffer const * buf)
624 {
625         if (!buf) {
626                 tomenu.add(MenuItem(MenuItem::Command,
627                                     _("No Document Open!"),
628                                     FuncRequest(LFUN_NOACTION)));
629                 return;
630         }
631
632         FloatList const & floats =
633                 buf->params().getTextClass().floats();
634         FloatList::const_iterator cit = floats.begin();
635         FloatList::const_iterator end = floats.end();
636         for (; cit != end; ++cit) {
637                 // normal float
638                 docstring const label = _(cit->second.name());
639                 tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label,
640                                     FuncRequest(LFUN_FLOAT_INSERT,
641                                                 cit->second.type())));
642         }
643 }
644
645
646 void expandFlexInsert(Menu & tomenu, Buffer const * buf, std::string s)
647 {
648         if (!buf) {
649                 tomenu.add(MenuItem(MenuItem::Command,
650                                     _("No Document Open!"),
651                                     FuncRequest(LFUN_NOACTION)));
652                 return;
653         }
654         InsetLayouts const & insetlayouts =
655                 buf->params().getTextClass().insetlayouts();
656         InsetLayouts::const_iterator cit = insetlayouts.begin();
657         InsetLayouts::const_iterator end = insetlayouts.end();
658         for (; cit != end; ++cit) {
659                 docstring const label = cit->first;
660                 if (cit->second.lyxtype == s)
661                         tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, 
662                                 label, FuncRequest(LFUN_FLEX_INSERT,
663                                                 label)));
664         }
665 }
666
667
668 Menu::size_type const max_number_of_items = 25;
669
670 void expandToc2(Menu & tomenu,
671                 Toc const & toc_list,
672                 Toc::size_type from,
673                 Toc::size_type to, int depth)
674 {
675         int shortcut_count = 0;
676
677         // check whether depth is smaller than the smallest depth in toc.
678         int min_depth = 1000;
679         for (Toc::size_type i = from; i < to; ++i)
680                 min_depth = std::min(min_depth, toc_list[i].depth());
681         if (min_depth > depth)
682                 depth = min_depth;
683
684
685         if (to - from <= max_number_of_items) {
686                 for (Toc::size_type i = from; i < to; ++i) {
687                         docstring label(4 * max(0, toc_list[i].depth() - depth), char_type(' '));
688                         label += limit_string_length(toc_list[i].str());
689                         if (toc_list[i].depth() == depth
690                             && shortcut_count < 9) {
691                                 if (label.find(convert<docstring>(shortcut_count + 1)) != docstring::npos)
692                                         label += char_type('|') + convert<docstring>(++shortcut_count);
693                         }
694                         tomenu.add(MenuItem(MenuItem::Command, label,
695                                             FuncRequest(toc_list[i].action())));
696                 }
697         } else {
698                 Toc::size_type pos = from;
699                 while (pos < to) {
700                         Toc::size_type new_pos = pos + 1;
701                         while (new_pos < to &&
702                                toc_list[new_pos].depth() > depth)
703                                 ++new_pos;
704
705                         docstring label(4 * max(0, toc_list[pos].depth() - depth), ' ');
706                         label += limit_string_length(toc_list[pos].str());
707                         if (toc_list[pos].depth() == depth &&
708                             shortcut_count < 9) {
709                                 if (label.find(convert<docstring>(shortcut_count + 1)) != docstring::npos)
710                                         label += char_type('|') + convert<docstring>(++shortcut_count);
711                         }
712                         if (new_pos == pos + 1) {
713                                 tomenu.add(MenuItem(MenuItem::Command,
714                                                     label, FuncRequest(toc_list[pos].action())));
715                         } else {
716                                 MenuItem item(MenuItem::Submenu, label);
717                                 item.submenu(new Menu);
718                                 expandToc2(*item.submenu(),
719                                            toc_list, pos, new_pos, depth + 1);
720                                 tomenu.add(item);
721                         }
722                         pos = new_pos;
723                 }
724         }
725 }
726
727
728 void expandToc(Menu & tomenu, Buffer const * buf)
729 {
730         // To make things very cleanly, we would have to pass buf to
731         // all MenuItem constructors and to expandToc2. However, we
732         // know that all the entries in a TOC will be have status_ ==
733         // OK, so we avoid this unnecessary overhead (JMarc)
734
735         if (!buf) {
736                 tomenu.add(MenuItem(MenuItem::Command,
737                                     _("No Document Open!"),
738                                     FuncRequest(LFUN_NOACTION)));
739                 return;
740         }
741
742         Buffer* cbuf = const_cast<Buffer*>(buf);
743         cbuf->tocBackend().update();
744         cbuf->structureChanged();
745
746         // Add an entry for the master doc if this is a child doc
747         Buffer const * const master = buf->masterBuffer();
748         if (buf != master) {
749                 ParIterator const pit = par_iterator_begin(master->inset());
750                 string const arg = convert<string>(pit->id());
751                 FuncRequest f(LFUN_PARAGRAPH_GOTO, arg);
752                 tomenu.add(MenuItem(MenuItem::Command, _("Master Document"), f));
753         }
754
755         FloatList const & floatlist = buf->params().getTextClass().floats();
756         TocList const & toc_list = buf->tocBackend().tocs();
757         TocList::const_iterator cit = toc_list.begin();
758         TocList::const_iterator end = toc_list.end();
759         for (; cit != end; ++cit) {
760                 // Handle this later
761                 if (cit->first == "tableofcontents")
762                         continue;
763
764                 // All the rest is for floats
765                 auto_ptr<Menu> menu(new Menu);
766                 TocIterator ccit = cit->second.begin();
767                 TocIterator eend = cit->second.end();
768                 for (; ccit != eend; ++ccit) {
769                         docstring const label = limit_string_length(ccit->str());
770                         menu->add(MenuItem(MenuItem::Command,
771                                            label,
772                                            FuncRequest(ccit->action())));
773                 }
774                 string const & floatName = floatlist.getType(cit->first).listName();
775                 docstring label;
776                 if (!floatName.empty())
777                         label = _(floatName);
778                 // BUG3633: listings is not a proper float so its name
779                 // is not shown in floatlist.
780                 else if (cit->first == "listing")
781                         label = _("List of listings");
782                 // this should not happen now, but if something else like
783                 // listings is added later, this can avoid an empty menu name.
784                 else
785                         label = _("Other floats");
786                 MenuItem item(MenuItem::Submenu, label);
787                 item.submenu(menu.release());
788                 tomenu.add(item);
789         }
790
791         // Handle normal TOC
792         cit = toc_list.find("tableofcontents");
793         if (cit == end) {
794                 tomenu.addWithStatusCheck(MenuItem(MenuItem::Command,
795                                     _("No Table of contents"),
796                                     FuncRequest()));
797         } else {
798                 expandToc2(tomenu, cit->second, 0, cit->second.size(), 0);
799         }
800 }
801
802
803 void expandPasteRecent(Menu & tomenu, Buffer const * buf)
804 {
805         if (!buf)
806                 return;
807
808         vector<docstring> const sel =
809                 cap::availableSelections(*buf);
810
811         vector<docstring>::const_iterator cit = sel.begin();
812         vector<docstring>::const_iterator end = sel.end();
813
814         for (unsigned int index = 0; cit != end; ++cit, ++index) {
815                 tomenu.add(MenuItem(MenuItem::Command, *cit,
816                                     FuncRequest(LFUN_PASTE, convert<string>(index))));
817         }
818 }
819
820
821 void expandToolbars(Menu & tomenu)
822 {
823         //
824         // extracts the toolbars from the backend
825         ToolbarBackend::Toolbars::const_iterator cit = toolbarbackend.begin();
826         ToolbarBackend::Toolbars::const_iterator end = toolbarbackend.end();
827
828         for (; cit != end; ++cit) {
829                 docstring label = _(cit->gui_name);
830                 // frontends are not supposed to turn on/off toolbars,
831                 // if they cannot update ToolbarBackend::flags. That
832                 // is to say, ToolbarsBackend::flags should reflect
833                 // the true state of toolbars.
834                 //
835                 // menu is displayed as
836                 //       on/off review
837                 // and
838                 //              review (auto)
839                 // in the case of auto.
840                 if (cit->flags & ToolbarInfo::AUTO)
841                         label += _(" (auto)");
842                 tomenu.add(MenuItem(MenuItem::Command, label,
843                                     FuncRequest(LFUN_TOOLBAR_TOGGLE, cit->name + " allowauto")));
844         }
845 }
846
847
848 void expandBranches(Menu & tomenu, Buffer const * buf)
849 {
850         if (!buf) {
851                 tomenu.add(MenuItem(MenuItem::Command,
852                                     _("No Document Open!"),
853                                     FuncRequest(LFUN_NOACTION)));
854                 return;
855         }
856
857         BufferParams const & params = buf->masterBuffer()->params();
858         if (params.branchlist().empty()) {
859                 tomenu.add(MenuItem(MenuItem::Command,
860                                     _("No Branch in Document!"),
861                                     FuncRequest(LFUN_NOACTION)));
862                 return;
863         }
864
865         BranchList::const_iterator cit = params.branchlist().begin();
866         BranchList::const_iterator end = params.branchlist().end();
867
868         for (int ii = 1; cit != end; ++cit, ++ii) {
869                 docstring label = cit->getBranch();
870                 if (ii < 10)
871                         label = convert<docstring>(ii) + ". " + label + char_type('|') + convert<docstring>(ii);
872                 tomenu.addWithStatusCheck(MenuItem(MenuItem::Command, label,
873                                     FuncRequest(LFUN_BRANCH_INSERT,
874                                                 cit->getBranch())));
875         }
876 }
877
878
879 } // namespace anon
880
881
882 void MenuBackend::expand(Menu const & frommenu, Menu & tomenu,
883                          Buffer const * buf) const
884 {
885         if (!tomenu.empty())
886                 tomenu.clear();
887
888         for (Menu::const_iterator cit = frommenu.begin();
889              cit != frommenu.end() ; ++cit) {
890                 switch (cit->kind()) {
891                 case MenuItem::Lastfiles:
892                         expandLastfiles(tomenu);
893                         break;
894
895                 case MenuItem::Documents:
896                         expandDocuments(tomenu);
897                         break;
898
899                 case MenuItem::Bookmarks:
900                         expandBookmarks(tomenu);
901                         break;
902
903                 case MenuItem::ImportFormats:
904                 case MenuItem::ViewFormats:
905                 case MenuItem::UpdateFormats:
906                 case MenuItem::ExportFormats:
907                         expandFormats(cit->kind(), tomenu, buf);
908                         break;
909
910                 case MenuItem::CharStyles:
911                         expandFlexInsert(tomenu, buf, "charstyle");
912                         break;
913
914                 case MenuItem::Custom:
915                         expandFlexInsert(tomenu, buf, "custom");
916                         break;
917
918                 case MenuItem::Elements:
919                         expandFlexInsert(tomenu, buf, "element");
920                         break;
921
922                 case MenuItem::FloatListInsert:
923                         expandFloatListInsert(tomenu, buf);
924                         break;
925
926                 case MenuItem::FloatInsert:
927                         expandFloatInsert(tomenu, buf);
928                         break;
929
930                 case MenuItem::PasteRecent:
931                         expandPasteRecent(tomenu, buf);
932                         break;
933
934                 case MenuItem::Toolbars:
935                         expandToolbars(tomenu);
936                         break;
937
938                 case MenuItem::Branches:
939                         expandBranches(tomenu, buf);
940                         break;
941
942                 case MenuItem::Toc:
943                         expandToc(tomenu, buf);
944                         break;
945
946                 case MenuItem::Submenu: {
947                         MenuItem item(*cit);
948                         item.submenu(new Menu(cit->submenuname()));
949                         expand(getMenu(cit->submenuname()),
950                                *item.submenu(), buf);
951                         tomenu.addWithStatusCheck(item);
952                 }
953                 break;
954
955                 case MenuItem::Separator:
956                         tomenu.addWithStatusCheck(*cit);
957                         break;
958
959                 case MenuItem::Command:
960                         if (!specialmenu_.hasFunc(cit->func()))
961                                 tomenu.addWithStatusCheck(*cit);
962                 }
963         }
964
965         // we do not want the menu to end with a separator
966         if (!tomenu.empty()
967             && tomenu.items_.back().kind() == MenuItem::Separator)
968                 tomenu.items_.pop_back();
969
970         // Check whether the shortcuts are unique
971         tomenu.checkShortcuts();
972 }
973
974
975 void MenuBackend::read(Lexer & lex)
976 {
977         enum Menutags {
978                 md_menu = 1,
979                 md_menubar,
980                 md_endmenuset,
981                 md_last
982         };
983
984         struct keyword_item menutags[md_last - 1] = {
985                 { "end", md_endmenuset },
986                 { "menu", md_menu },
987                 { "menubar", md_menubar }
988         };
989
990         //consistency check
991         if (compare_ascii_no_case(lex.getString(), "menuset")) {
992                 lyxerr << "Menubackend::read: ERROR wrong token:`"
993                        << lex.getString() << '\'' << endl;
994         }
995
996         lex.pushTable(menutags, md_last - 1);
997         if (lyxerr.debugging(Debug::PARSER))
998                 lex.printTable(lyxerr);
999
1000         bool quit = false;
1001
1002         while (lex.isOK() && !quit) {
1003                 switch (lex.lex()) {
1004                 case md_menubar:
1005                         menubar_.read(lex);
1006                         break;
1007                 case md_menu: {
1008                         lex.next(true);
1009                         docstring const name = lex.getDocString();
1010                         if (hasMenu(name)) {
1011                                 getMenu(name).read(lex);
1012                         } else {
1013                                 Menu menu(name);
1014                                 menu.read(lex);
1015                                 add(menu);
1016                         }
1017                         break;
1018                 }
1019                 case md_endmenuset:
1020                         quit = true;
1021                         break;
1022                 default:
1023                         lex.printError("menubackend::read: "
1024                                        "Unknown menu tag: `$$Token'");
1025                         break;
1026                 }
1027         }
1028         lex.popTable();
1029 }
1030
1031
1032 void MenuBackend::add(Menu const & menu)
1033 {
1034         menulist_.push_back(menu);
1035 }
1036
1037
1038 bool MenuBackend::hasMenu(docstring const & name) const
1039 {
1040         return find_if(begin(), end(), MenuNamesEqual(name)) != end();
1041 }
1042
1043
1044 Menu const & MenuBackend::getMenu(docstring const & name) const
1045 {
1046         const_iterator cit = find_if(begin(), end(), MenuNamesEqual(name));
1047         if (cit == end())
1048                 lyxerr << "No submenu named " << to_utf8(name) << endl;
1049         BOOST_ASSERT(cit != end());
1050         return (*cit);
1051 }
1052
1053
1054 Menu & MenuBackend::getMenu(docstring const & name)
1055 {
1056         iterator it = find_if(begin(), end(), MenuNamesEqual(name));
1057         if (it == end())
1058                 lyxerr << "No submenu named " << to_utf8(name) << endl;
1059         BOOST_ASSERT(it != end());
1060         return (*it);
1061 }
1062
1063
1064 Menu const & MenuBackend::getMenubar() const
1065 {
1066         return menubar_;
1067 }
1068
1069
1070 } // namespace lyx