]> git.lyx.org Git - features.git/blob - src/MenuBackend.C
remove NO_COMPABILITY stuff
[features.git] / src / MenuBackend.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  *
10  * ====================================================== */
11
12 #include <config.h>
13
14 #ifdef __GNUG__
15 #pragma implementation
16 #endif
17
18 #include <algorithm>
19 #include "MenuBackend.h"
20 #include "lyxlex.h"
21 #include "LyXAction.h"
22 #include "debug.h"
23 #include "gettext.h"
24 #include "lastfiles.h"
25 #include "lyx_main.h" // for lastfiles
26 #include "bufferlist.h"
27 #include "converter.h"
28 #include "exporter.h"
29 #include "importer.h"
30 #include "FloatList.h"
31 #include "toc.h"
32 #include "support/LAssert.h"
33 #include "support/filetools.h"
34 #include "support/lyxfunctional.h"
35 #include "support/lstrings.h"
36
37 extern BufferList bufferlist;
38
39 using std::endl;
40 using std::vector;
41 using std::max;
42 using std::pair;
43 using std::find_if;
44 using std::sort;
45
46 // This is the global menu definition
47 MenuBackend menubackend;
48
49
50 MenuItem::MenuItem(Kind kind, string const & label,
51                    string const & command, bool optional)
52         : kind_(kind), label_(label), optional_(optional)
53 {
54         switch (kind) {
55         case Separator:
56         case Documents:
57         case Lastfiles:
58         case Toc:
59         case ViewFormats:
60         case UpdateFormats:
61         case ExportFormats:
62         case ImportFormats:
63         case FloatListInsert:
64         case FloatInsert:
65                 break;
66         case Command:
67                 action_ = lyxaction.LookupFunc(command);
68
69                 if (action_ == LFUN_UNKNOWN_ACTION) {
70                         lyxerr << "MenuItem(): LyX command `"
71                                << command << "' does not exist." << endl;
72                 }
73                 if (optional_)
74                         lyxerr[Debug::GUI] << "Optional item "
75                                            << command << endl;
76                 break;
77         case Submenu:
78                 submenuname_ = command;
79                 break;
80         }
81 }
82
83
84 MenuItem::MenuItem(Kind kind, string const & label, int action, bool optional)
85         : kind_(kind), label_(label), action_(action), submenuname_(),
86           optional_(optional)
87 {}
88
89
90 MenuItem::~MenuItem()
91 {}
92
93
94 void MenuItem::submenu(Menu * menu)
95 {
96         submenu_.reset(menu);
97 }
98
99
100 string const MenuItem::label() const
101 {
102         return token(label_, '|', 0);
103 }
104
105
106 string const MenuItem::shortcut() const
107 {
108         return token(label_, '|', 1);
109 }
110
111
112 Menu & Menu::add(MenuItem const & i)
113 {
114         items_.push_back(i);
115         return *this;
116 }
117
118
119 Menu & Menu::read(LyXLex & lex)
120 {
121         enum Menutags {
122                 md_item = 1,
123                 md_documents,
124                 md_endmenu,
125                 md_exportformats,
126                 md_importformats,
127                 md_lastfiles,
128                 md_optitem,
129                 md_separator,
130                 md_submenu,
131                 md_toc,
132                 md_updateformats,
133                 md_viewformats,
134                 md_floatlistinsert,
135                 md_floatinsert,
136                 md_last
137         };
138
139         struct keyword_item menutags[md_last - 1] = {
140                 { "documents", md_documents },
141                 { "end", md_endmenu },
142                 { "exportformats", md_exportformats },
143                 { "floatinsert", md_floatinsert },
144                 { "floatlistinsert", md_floatlistinsert },
145                 { "importformats", md_importformats },
146                 { "item", md_item },
147                 { "lastfiles", md_lastfiles },
148                 { "optitem", md_optitem },
149                 { "separator", md_separator },
150                 { "submenu", md_submenu },
151                 { "toc", md_toc },
152                 { "updateformats", md_updateformats },
153                 { "viewformats", md_viewformats }
154         };
155
156         lex.pushTable(menutags, md_last - 1);
157         if (lyxerr.debugging(Debug::PARSER))
158                 lex.printTable(lyxerr);
159
160         bool quit = false;
161         bool optional = false;
162
163         while (lex.isOK() && !quit) {
164                 switch (lex.lex()) {
165                 case md_optitem:
166                         optional = true;
167                         // fallback to md_item
168                 case md_item: {
169                         lex.next(true);
170                         string const name = _(lex.getString());
171                         lex.next(true);
172                         string const command = lex.getString();
173                         add(MenuItem(MenuItem::Command, name,
174                                      command, optional));
175                         optional = false;
176                         break;
177                 }
178
179                 case md_separator:
180                         add(MenuItem(MenuItem::Separator));
181                         break;
182
183                 case md_lastfiles:
184                         add(MenuItem(MenuItem::Lastfiles));
185                         break;
186
187                 case md_documents:
188                         add(MenuItem(MenuItem::Documents));
189                         break;
190
191                 case md_toc:
192                         add(MenuItem(MenuItem::Toc));
193                         break;
194
195                 case md_viewformats:
196                         add(MenuItem(MenuItem::ViewFormats));
197                         break;
198
199                 case md_updateformats:
200                         add(MenuItem(MenuItem::UpdateFormats));
201                         break;
202
203                 case md_exportformats:
204                         add(MenuItem(MenuItem::ExportFormats));
205                         break;
206
207                 case md_importformats:
208                         add(MenuItem(MenuItem::ImportFormats));
209                         break;
210
211                 case md_floatlistinsert:
212                         add(MenuItem(MenuItem::FloatListInsert));
213                         break;
214
215                 case md_floatinsert:
216                         add(MenuItem(MenuItem::FloatInsert));
217                         break;
218
219                 case md_submenu: {
220                         lex.next(true);
221                         string const mlabel = _(lex.getString());
222                         lex.next(true);
223                         string const mname = lex.getString();
224                         add(MenuItem(MenuItem::Submenu, mlabel, mname));
225                         break;
226                 }
227
228                 case md_endmenu:
229                         quit = true;
230                         break;
231
232                 default:
233                         lex.printError("Menu::read: "
234                                        "Unknown menu tag: `$$Token'");
235                         break;
236                 }
237         }
238         lex.popTable();
239         return *this;
240 }
241
242
243 void Menu::checkShortcuts() const
244 {
245         // This is a quadratic algorithm, but we do not care because
246         // it is used for debugging only.
247         for (const_iterator it1 = begin(); it1 != end(); ++it1) {
248                 string shortcut = it1->shortcut();
249                 if (shortcut.empty())
250                         continue;
251                 if (!contains(it1->label(), shortcut))
252                         lyxerr << "Menu warning: menu entry \""
253                                << it1->label()
254                                << "\" does not contain shortcut `"
255                                << shortcut << '\'' << endl;
256                 for (const_iterator it2 = begin(); it2 != it1 ; ++it2) {
257                         if (!compare_ascii_no_case(it2->shortcut(), shortcut)) {
258                                 lyxerr << "Menu warning: menu entries "
259                                        << '"' << it1->fulllabel()
260                                        << "\" and \"" << it2->fulllabel()
261                                        << "\" share the same shortcut."
262                                        << endl;
263                         }
264                 }
265         }
266 }
267
268
269 namespace {
270
271 class compare_format {
272 public:
273         bool operator()(Format const * p1, Format const * p2) {
274                 return *p1 < *p2;
275         }
276 };
277
278 string const limit_string_length(string const & str)
279 {
280         string::size_type const max_item_length = 45;
281
282         if (str.size() > max_item_length)
283                 return str.substr(0, max_item_length - 3) + "...";
284         else
285                 return str;
286 }
287
288
289 void expandLastfiles(Menu & tomenu)
290 {
291         int ii = 1;
292         LastFiles::const_iterator lfit = lastfiles->begin();
293         LastFiles::const_iterator end = lastfiles->end();
294
295         for (; lfit != end && ii < 10; ++lfit, ++ii) {
296                 string const label = tostr(ii) + ". "
297                         + MakeDisplayPath((*lfit), 30)
298                         + '|' + tostr(ii);
299                 int const action = lyxaction.
300                         getPseudoAction(LFUN_FILE_OPEN,
301                                         (*lfit));
302                 tomenu.add(MenuItem(MenuItem::Command,
303                                     label, action));
304         }
305 }
306
307 void expandDocuments(Menu & tomenu)
308 {
309         typedef vector<string> Strings;
310         Strings const names = bufferlist.getFileNames();
311
312         if (names.empty()) {
313                 tomenu.add(MenuItem(MenuItem::Command, _("No Documents Open!"),
314                                     LFUN_NOACTION));
315                 return;
316         }
317
318         int ii = 1;
319         Strings::const_iterator docit = names.begin();
320         Strings::const_iterator end = names.end();
321         for (; docit != end; ++docit, ++ii) {
322                 int const action =
323                         lyxaction.getPseudoAction(LFUN_SWITCHBUFFER, *docit);
324                 string label = MakeDisplayPath(*docit, 30);
325                 if (ii < 10)
326                         label = tostr(ii) + ". " + label + '|' + tostr(ii);
327                 tomenu.add(MenuItem(MenuItem::Command, label, action));
328         }
329 }
330
331
332 void expandFormats(MenuItem::Kind kind, Menu & tomenu, Buffer const * buf)
333 {
334         if (!buf && kind != MenuItem::ImportFormats) {
335                 tomenu.add(MenuItem(MenuItem::Command,
336                                     _("No Documents Open!"), LFUN_NOACTION));
337                 return;
338         }
339
340         typedef vector<Format const *> Formats;
341         Formats formats;
342         kb_action action;
343
344         switch (kind) {
345         case MenuItem::ImportFormats:
346                 formats = Importer::GetImportableFormats();
347                 action = LFUN_IMPORT;
348                 break;
349         case MenuItem::ViewFormats:
350                 formats = Exporter::GetExportableFormats(buf, true);
351                 action = LFUN_PREVIEW;
352                 break;
353         case MenuItem::UpdateFormats:
354                 formats = Exporter::GetExportableFormats(buf, true);
355                 action = LFUN_UPDATE;
356                 break;
357         default:
358                 formats = Exporter::GetExportableFormats(buf, false);
359                 action = LFUN_EXPORT;
360         }
361         sort(formats.begin(), formats.end(), compare_format());
362
363         Formats::const_iterator fit = formats.begin();
364         Formats::const_iterator end = formats.end();
365         for (; fit != end ; ++fit) {
366                 if ((*fit)->dummy())
367                         continue;
368                 string label = (*fit)->prettyname();
369                 // we need to hide the default graphic export formats
370                 // from the external menu, because we need them only
371                 // for the internal lyx-view and external latex run
372                 if (label == "EPS" || label == "XPM" || label == "PNG")
373                         continue;
374
375                 if (kind == MenuItem::ImportFormats)
376                         if ((*fit)->name() == "text")
377                                 label = _("Ascii text as lines");
378                         else if ((*fit)->name() == "textparagraph")
379                                 label = _("Ascii text as paragraphs");
380                 if (!(*fit)->shortcut().empty())
381                         label += "|" + (*fit)->shortcut();
382                 int const action2 = lyxaction.
383                         getPseudoAction(action, (*fit)->name());
384                 tomenu.add(MenuItem(MenuItem::Command, label, action2));
385         }
386 }
387
388
389 void expandFloatListInsert(Menu & tomenu)
390 {
391         FloatList::const_iterator cit = floatList.begin();
392         FloatList::const_iterator end = floatList.end();
393         for (; cit != end; ++cit) {
394                 int const action =  lyxaction
395                         .getPseudoAction(LFUN_FLOAT_LIST, cit->second.type());
396                 tomenu.add(MenuItem(MenuItem::Command,
397                                     _(cit->second.listName()),
398                                     action));
399         }
400 }
401
402
403 void expandFloatInsert(Menu & tomenu)
404 {
405         FloatList::const_iterator cit = floatList.begin();
406         FloatList::const_iterator end = floatList.end();
407         for (; cit != end; ++cit) {
408                 // normal float
409                 int const action =
410                         lyxaction.getPseudoAction(LFUN_INSET_FLOAT,
411                                                   cit->second.type());
412                 string const label = _(cit->second.name());
413                 tomenu.add(MenuItem(MenuItem::Command, label, action));
414         }
415 }
416
417
418 Menu::size_type const max_number_of_items = 25;
419
420 void expandToc2(Menu & tomenu, toc::Toc const & toc_list,
421                 toc::Toc::size_type from, toc::Toc::size_type to, int depth)
422 {
423         int shortcut_count = 0;
424         if (to - from <= max_number_of_items) {
425                 for (toc::Toc::size_type i = from; i < to; ++i) {
426                         int const action = toc_list[i].action();
427                         string label(4 * max(0, toc_list[i].depth - depth),' ');
428                         label += limit_string_length(toc_list[i].str);
429                         if (toc_list[i].depth == depth
430                             && ++shortcut_count <= 9) {
431                                 label += "|" + tostr(shortcut_count);
432                         }
433                         tomenu.add(MenuItem(MenuItem::Command, label, action));
434                 }
435         } else {
436                 toc::Toc::size_type pos = from;
437                 while (pos < to) {
438                         toc::Toc::size_type new_pos = pos + 1;
439                         while (new_pos < to &&
440                                toc_list[new_pos].depth > depth)
441                                 ++new_pos;
442
443                         int const action = toc_list[pos].action();
444                         string label(4 * max(0, toc_list[pos].depth - depth), ' ');
445                         label += limit_string_length(toc_list[pos].str);
446                         if (toc_list[pos].depth == depth &&
447                             ++shortcut_count <= 9)
448                                 label += '|' + tostr(shortcut_count);
449
450                         if (new_pos == pos + 1) {
451                                 tomenu.add(MenuItem(MenuItem::Command,
452                                                     label, action));
453                         } else {
454                                 MenuItem item(MenuItem::Submenu, label);
455                                 item.submenu(new Menu);
456                                 expandToc2(*item.submenu(),
457                                            toc_list, pos, new_pos, depth + 1);
458                                 tomenu.add(item);
459                         }
460                         pos = new_pos;
461                 }
462         }
463 }
464
465
466 void expandToc(Menu & tomenu, Buffer const * buf)
467 {
468         toc::TocList toc_list = toc::getTocList(buf);
469         toc::TocList::const_iterator cit = toc_list.begin();
470         toc::TocList::const_iterator end = toc_list.end();
471         for (; cit != end; ++cit) {
472                 // Handle this later
473                 if (cit->first == "TOC") continue;
474
475                 // All the rest is for floats
476                 Menu * menu = new Menu;
477                 toc::Toc::const_iterator ccit = cit->second.begin();
478                 toc::Toc::const_iterator eend = cit->second.end();
479                 for (; ccit != eend; ++ccit) {
480                         string const label = limit_string_length(ccit->str);
481                         menu->add(MenuItem(MenuItem::Command,
482                                            label, ccit->action()));
483                 }
484                 MenuItem item(MenuItem::Submenu,
485                               floatList[cit->first]->second.name());
486                 item.submenu(menu);
487                 tomenu.add(item);
488         }
489
490         // Handle normal TOC
491         cit = toc_list.find("TOC");
492         if (cit == end) {
493                 tomenu.add(MenuItem(MenuItem::Command,
494                                     _("No Table of contents")));
495         } else {
496                 expandToc2(tomenu, cit->second, 0, cit->second.size(), 0);
497         }
498 }
499
500
501 } // namespace anon
502
503
504 void MenuBackend::expand(Menu const & frommenu, Menu & tomenu,
505                          Buffer const * buf) const
506 {
507         for (Menu::const_iterator cit = frommenu.begin();
508              cit != frommenu.end() ; ++cit) {
509                 switch (cit->kind()) {
510                 case MenuItem::Lastfiles:
511                         expandLastfiles(tomenu);
512                         break;
513
514                 case MenuItem::Documents:
515                         expandDocuments(tomenu);
516                         break;
517
518                 case MenuItem::ImportFormats:
519                 case MenuItem::ViewFormats:
520                 case MenuItem::UpdateFormats:
521                 case MenuItem::ExportFormats:
522                         expandFormats(cit->kind(), tomenu, buf);
523                         break;
524
525                 case MenuItem::FloatListInsert:
526                         expandFloatListInsert(tomenu);
527                         break;
528
529                 case MenuItem::FloatInsert:
530                         expandFloatInsert(tomenu);
531                         break;
532
533                 case MenuItem::Toc:
534                         expandToc(tomenu, buf);
535                         break;
536
537                 case MenuItem::Submenu: {
538                         MenuItem item(*cit);
539                         item.submenu(new Menu(cit->submenuname()));
540                         expand(getMenu(cit->submenuname()),
541                                *item.submenu(), buf);
542                         tomenu.add(item);
543                 }
544                 break;
545
546                 default:
547                         tomenu.add(*cit);
548                 }
549         }
550
551         // Check whether the shortcuts are unique
552         if (lyxerr.debugging(Debug::GUI))
553                 tomenu.checkShortcuts();
554 }
555
556
557 bool Menu::hasSubmenu(string const & name) const
558 {
559         return find_if(begin(), end(),
560                        lyx::compare_memfun(&MenuItem::submenuname,
561                                            name)) != end();
562 }
563
564
565 void MenuBackend::read(LyXLex & lex)
566 {
567         enum Menutags {
568                 md_menu = 1,
569                 md_menubar,
570                 md_endmenuset,
571                 md_last
572         };
573
574         struct keyword_item menutags[md_last - 1] = {
575                 { "end", md_endmenuset },
576                 { "menu", md_menu },
577                 { "menubar", md_menubar }
578         };
579
580         //consistency check
581         if (compare_ascii_no_case(lex.getString(), "menuset")) {
582                 lyxerr << "Menubackend::read: ERROR wrong token:`"
583                        << lex.getString() << '\'' << endl;
584         }
585
586         lex.pushTable(menutags, md_last - 1);
587         if (lyxerr.debugging(Debug::PARSER))
588                 lex.printTable(lyxerr);
589
590         bool quit = false;
591
592         while (lex.isOK() && !quit) {
593                 switch (lex.lex()) {
594                 case md_menubar:
595                         menubar_.read(lex);
596                         break;
597                 case md_menu: {
598                         lex.next(true);
599                         string const name = lex.getString();
600                         if (hasMenu(name)) {
601                                 getMenu(name).read(lex);
602                         } else {
603                                 Menu menu(name);
604                                 menu.read(lex);
605                                 add(menu);
606                         }
607                         break;
608                 }
609                 case md_endmenuset:
610                         quit = true;
611                         break;
612                 default:
613                         lex.printError("menubackend::read: "
614                                        "Unknown menu tag: `$$Token'");
615                         break;
616                 }
617         }
618         lex.popTable();
619 }
620
621
622 void MenuBackend::defaults()
623 {
624         menulist_.clear();
625
626         lyxerr[Debug::GUI] << "MenuBackend::defaults: using default values"
627                            << endl;
628
629         Menu file("file");
630         file
631                 .add(MenuItem(MenuItem::Command, _("New...|N"), "buffer-new"))
632                 .add(MenuItem(MenuItem::Command, _("Open...|O"), "file-open"))
633                 .add(MenuItem(MenuItem::Submenu, _("Import|I"), "import"))
634                 .add(MenuItem(MenuItem::Command, _("Quit|Q"), "lyx-quit"))
635                 .add(MenuItem(MenuItem::Separator))
636                 .add(MenuItem(MenuItem::Lastfiles));
637         add(file);
638
639         Menu import("import");
640         import
641                 .add(MenuItem(MenuItem::Command,
642                               _("LaTeX...|L"), "buffer-import latex"))
643                 .add(MenuItem(MenuItem::Command,
644                               _("LinuxDoc...|L"), "buffer-import linuxdoc"));
645         add(import);
646
647         Menu edit("edit");
648         edit
649                 .add(MenuItem(MenuItem::Command, _("Cut"), "cut"))
650                 .add(MenuItem(MenuItem::Command, _("Copy"), "copy"))
651                 .add(MenuItem(MenuItem::Command, _("Paste"), "paste"))
652                 .add(MenuItem(MenuItem::Command, _("Emphasize"), "font-emph"));
653         add(edit);
654
655         Menu documents("documents");
656         documents.add(MenuItem(MenuItem::Documents));
657         add(documents);
658
659         menubar_.add(MenuItem(MenuItem::Submenu, _("File|F"), "file"))
660                 .add(MenuItem(MenuItem::Submenu, _("Edit|E"), "edit"))
661                 .add(MenuItem(MenuItem::Submenu,
662                               _("Documents|D"), "documents"));
663
664 }
665
666
667 void MenuBackend::add(Menu const & menu)
668 {
669         menulist_.push_back(menu);
670 }
671
672
673 bool MenuBackend::hasMenu(string const & name) const
674 {
675         return find_if(begin(), end(),
676                        lyx::compare_memfun(&Menu::name, name)) != end();
677 }
678
679
680 Menu const & MenuBackend::getMenu(string const & name) const
681 {
682         const_iterator cit = find_if(begin(), end(),
683                                      lyx::compare_memfun(&Menu::name, name));
684         lyx::Assert(cit != end());
685         return (*cit);
686 }
687
688
689 Menu & MenuBackend::getMenu(string const & name)
690 {
691         MenuList::iterator it =
692                 find_if(menulist_.begin(), menulist_.end(),
693                         lyx::compare_memfun(&Menu::name, name));
694         lyx::Assert(it != menulist_.end());
695         return (*it);
696 }
697
698
699 Menu const & MenuBackend::getMenubar() const
700 {
701         return menubar_;
702 }