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