]> git.lyx.org Git - features.git/blob - src/MenuBackend.C
Import patch from Dekel.
[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-2000 The LyX Team.
8  *
9  *
10  * ====================================================== */
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 #include <config.h>
17 #include <memory>
18 #include "support/LAssert.h"
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 "bufferlist.h"
26 #include "converter.h"
27 #include "exporter.h"
28 #include "support/filetools.h"
29 #include "support/lyxfunctional.h"
30
31 extern LyXAction lyxaction;
32 extern LastFiles * lastfiles; 
33 extern BufferList bufferlist;
34
35 using std::endl;
36 using std::vector;
37 using std::pair;
38 using std::find_if;
39 using std::sort;
40
41 // This is the global menu definition
42 MenuBackend menubackend;
43
44
45 MenuItem::MenuItem(Kind kind, string const & label, 
46                    string const & command, bool optional) 
47         : kind_(kind), label_(label), optional_(optional)
48 {
49         switch (kind) {
50         case Separator:
51         case Documents:
52         case Lastfiles:
53         case Toc:
54         case References:
55         case ViewFormats:
56         case UpdateFormats:
57         case ExportFormats:
58         case ImportFormats:
59                 break;
60         case Command:
61                 action_ = lyxaction.LookupFunc(command);
62
63                 if (action_ == LFUN_UNKNOWN_ACTION) {
64                         lyxerr << "MenuItem(): LyX command `"
65                                << command << "' does not exist." << endl; 
66                 }
67                 if (optional_)
68                         lyxerr[Debug::GUI] << "Optional item " 
69                                            << command << endl; 
70                 break;
71         case Submenu:
72                 submenu_ = command;
73                 break;
74         }
75 }
76
77
78 Menu & Menu::add(MenuItem const & i)
79 {
80         items_.push_back(i);
81         return *this;
82 }
83
84
85 Menu & Menu::read(LyXLex & lex)
86 {
87         enum Menutags {
88                 md_item = 1,
89                 md_documents,
90                 md_endmenu,
91                 md_exportformats,
92                 md_importformats,
93                 md_lastfiles,
94                 md_optitem,
95                 md_references,
96                 md_separator,
97                 md_submenu,
98                 md_toc,
99                 md_updateformats,
100                 md_viewformats,
101                 md_last
102         };
103
104         struct keyword_item menutags[md_last - 1] = {
105                 { "documents", md_documents },
106                 { "end", md_endmenu },
107                 { "exportformats", md_exportformats },
108                 { "importformats", md_importformats },
109                 { "item", md_item },
110                 { "lastfiles", md_lastfiles },
111                 { "optitem", md_optitem }, 
112                 { "references", md_references },
113                 { "separator", md_separator },
114                 { "submenu", md_submenu },
115                 { "toc", md_toc },
116                 { "updateformats", md_updateformats },
117                 { "viewformats", md_viewformats }
118         };
119
120         lex.pushTable(menutags, md_last - 1);
121         if (lyxerr.debugging(Debug::PARSER))
122                 lex.printTable(lyxerr);
123
124         bool quit = false;
125         bool optional = false;
126
127         while (lex.IsOK() && !quit) {
128                 switch (lex.lex()) {
129                 case md_optitem:
130                         optional = true;
131                         // fallback to md_item
132                 case md_item: {
133                         lex.next();
134                         string name = _(lex.GetString());
135                         lex.next();
136                         string const command = lex.GetString();
137                         add(MenuItem(MenuItem::Command, name, 
138                                      command, optional));
139                         optional = false;
140                         break;
141                 }
142
143                 case md_separator:
144                         add(MenuItem(MenuItem::Separator));
145                         break;
146
147                 case md_lastfiles:
148                         add(MenuItem(MenuItem::Lastfiles));
149                         break;
150
151                 case md_documents:
152                         add(MenuItem(MenuItem::Documents));
153                         break;
154
155                 case md_toc:
156                         add(MenuItem(MenuItem::Toc));
157                         break;
158
159                 case md_references:
160                         add(MenuItem(MenuItem::References));
161                         break;
162
163                 case md_viewformats:
164                         add(MenuItem(MenuItem::ViewFormats));
165                         break;
166
167                 case md_updateformats:
168                         add(MenuItem(MenuItem::UpdateFormats));
169                         break;
170
171                 case md_exportformats:
172                         add(MenuItem(MenuItem::ExportFormats));
173                         break;
174
175                 case md_importformats:
176                         add(MenuItem(MenuItem::ImportFormats));
177                         break;
178
179                 case md_submenu: {
180                         lex.next();
181                         string mlabel = _(lex.GetString());
182                         lex.next();
183                         string mname = lex.GetString();
184                         add(MenuItem(MenuItem::Submenu, mlabel, mname));
185                         break;
186                 }
187
188                 case md_endmenu:
189                         quit = true;
190                         break;
191
192                 default:
193                         lex.printError("menubar::read: "
194                                        "Unknown menu tag: `$$Token'");
195                         break;
196                 }
197         }
198         lex.popTable();
199         return *this;
200 }
201
202 struct compare_formatpair {
203         bool operator()(FormatPair const & a, FormatPair const & b) {
204                 return a.format->prettyname < b.format->prettyname; 
205         }
206 };
207
208 void Menu::checkShortcuts() const
209 {
210         // This is a quadratic algorithm, but we do not care because
211         // it is used for debugging only.
212         for (const_iterator it1 = begin(); it1 != end(); ++it1) {
213                 string shortcut = it1->shortcut();
214                 if (shortcut.empty())
215                         continue;
216                 if (!contains(it1->label(), shortcut))
217                         lyxerr << "Menu warning: menu entry \""
218                                << it1->label()
219                                << "\" does not contain shortcut `"
220                                << shortcut << '\'' << endl;
221                 for (const_iterator it2 = begin(); it2 != it1 ; ++it2) {
222                         if (!compare_no_case(it2->shortcut(), shortcut)) {
223                                 lyxerr << "Menu warning: menu entries "
224                                        << '"' << it1->fulllabel()
225                                        << "\" and \"" << it2->fulllabel()
226                                        << "\" share the same shortcut."
227                                        << endl;
228                         }
229                 }
230         }
231 }
232
233 void Menu::expand(Menu & tomenu, Buffer * buf) const
234 {
235         for (const_iterator cit = begin();
236              cit != end() ; ++cit) {
237                 switch ((*cit).kind()) {
238                 case MenuItem::Lastfiles: {
239                         int ii = 1;
240                         for (LastFiles::const_iterator lfit = lastfiles->begin();
241                              lfit != lastfiles->end() && ii < 10;
242                              ++lfit, ++ii) {
243                                 string label = tostr(ii) + ". "
244                                         + MakeDisplayPath((*lfit), 30)
245                                         + '|' + tostr(ii);
246                                 int action = lyxaction.
247                                         getPseudoAction(LFUN_FILE_OPEN,
248                                                         (*lfit));
249                                 tomenu.add(MenuItem(MenuItem::Command,
250                                                     label, action));
251                         }
252                 }
253                 break;
254                 
255                 case MenuItem::Documents: {
256                         vector<string> names = bufferlist.getFileNames();
257                         
258                         if (names.empty()) {
259                                 tomenu.add(MenuItem(MenuItem::Command,
260                                                     _("No Documents Open!"),
261                                                     LFUN_NOACTION));
262                                 break;
263                         }
264
265                         for (vector<string>::const_iterator docit = names.begin();
266                              docit != names.end() ; ++docit) {
267                                 int action =
268                                         lyxaction.getPseudoAction(LFUN_SWITCHBUFFER, *docit);
269                                 string label = MakeDisplayPath(*docit, 30);
270                                 tomenu.add(MenuItem(MenuItem::Command,
271                                                     label, action));
272                         }
273                 }
274                 break;
275
276                 case MenuItem::ViewFormats:
277                 case MenuItem::UpdateFormats:
278                 case MenuItem::ExportFormats: {
279                         vector<FormatPair> names;
280                         kb_action action;
281                         if ((*cit).kind() == MenuItem::ViewFormats) {
282                                 names = Exporter::GetExportableFormats(buf, true);
283                                 action = LFUN_PREVIEW;
284                         } else if ((*cit).kind() == MenuItem::UpdateFormats) {
285                                 names = Exporter::GetExportableFormats(buf, true);
286                                 action = LFUN_UPDATE;
287                         } else {
288                                 names = Exporter::GetExportableFormats(buf, false);
289                                 action = LFUN_EXPORT;
290                         }
291                         sort(names.begin(), names.end(), compare_formatpair());
292
293                         for (vector<FormatPair>::const_iterator fit = names.begin();
294                              fit != names.end() ; ++fit) {
295                                 if ((*fit).format->dummy())
296                                         continue;
297                                 string fmt = (*fit).format->name;
298                                 string label = (*fit).format->prettyname;
299                                 bool same_before = 
300                                         fit != names.begin() &&
301                                         (*fit).format == (*(fit-1)).format;
302                                 bool same_after = 
303                                         fit+1 != names.end() &&
304                                         (*fit).format == (*(fit+1)).format;
305                                 if ((*fit).from &&
306                                     (same_before || same_after)) {
307                                         fmt += ":" + (*fit).from->name;
308                                         string head;
309                                         split((*fit).command, head, ' ');
310                                         label += _(" (using ") + head + ")";
311                                         if (!(*fit).format->shortcut.empty() &&
312                                             !same_before)
313                                                 label += "|" + (*fit).format->shortcut;
314                                 } else if (!(*fit).format->shortcut.empty())
315                                         label += "|" + (*fit).format->shortcut;
316                                 int action2 = lyxaction.getPseudoAction(action, fmt);
317                                 tomenu.add(MenuItem(MenuItem::Command,
318                                                     label, action2));
319                         }
320                 }
321                 break;
322
323                 case MenuItem::ImportFormats: {
324                         vector<FormatPair> names = Converter::GetReachableTo("lyx");
325                         sort(names.begin(), names.end(), compare_formatpair());
326
327                         for (vector<FormatPair>::const_iterator fit = names.begin();
328                              fit != names.end() ; ++fit) {
329                                 if ((*fit).format->dummy())
330                                         continue;
331                                 string fmt = (*fit).format->name;
332                                 string label = (*fit).format->prettyname;
333                                 if (!(*fit).format->shortcut.empty())
334                                         label += "|" + (*fit).format->shortcut;
335                                 int action2 = lyxaction.getPseudoAction(LFUN_IMPORT, fmt);
336                                 tomenu.add(MenuItem(MenuItem::Command,
337                                                     label, action2));
338                         }
339                 }
340                 break;
341                         
342                 default:
343                         tomenu.add(*cit);
344                 }
345         }
346
347         // Check whether the shortcuts are unique
348         if (lyxerr.debugging(Debug::GUI))
349                 checkShortcuts();
350 }
351
352
353 void MenuBackend::read(LyXLex & lex)
354 {
355         enum Menutags {
356                 md_menu = 1,
357                 md_menubar,
358                 md_endmenuset,
359                 md_last
360         };
361
362         struct keyword_item menutags[md_last - 1] = {
363                 { "end", md_endmenuset },
364                 { "menu", md_menu },
365                 { "menubar", md_menubar }
366         };
367
368         //consistency check
369         if (compare_no_case(lex.GetString(), "menuset"))
370                 lyxerr << "Menubackend::read: ERROR wrong token:`"
371                        << lex.GetString() << '\'' << endl;
372
373         lex.pushTable(menutags, md_last - 1);
374         if (lyxerr.debugging(Debug::PARSER))
375                 lex.printTable(lyxerr);
376
377         bool quit = false;
378         bool menubar = false;
379
380         while (lex.IsOK() && !quit) {
381                 switch (lex.lex()) {
382                 case md_menubar: 
383                         menubar = true;
384                         // fallback to md_menu
385                 case md_menu: {
386                         lex.next();
387                         string name = lex.GetString();
388                         if (hasMenu(name)) {
389                                 if (getMenu(name).menubar() == menubar) {
390                                         getMenu(name).read(lex);
391                                 } else {
392                                         lex.printError("Cannot append to menu `$$Token' unless it is of the same type");
393                                         return;
394                                 }
395                         } else {                                
396                                 Menu menu(name, menubar);
397                                 menu.read(lex);
398                                 add(menu);
399                         }
400                         menubar = false;
401                         break;
402                 }
403                 case md_endmenuset:
404                         quit = true;
405                         break;
406                 default:
407                         lex.printError("menubackend::read: "
408                                        "Unknown menu tag: `$$Token'");
409                         break;
410                 }
411         }
412         lex.popTable();
413 }
414
415
416 void MenuBackend::defaults()
417 {
418         menulist_.clear();
419
420         lyxerr[Debug::GUI] << "MenuBackend::defaults: using default values" 
421                            << endl;
422
423         Menu file("file");
424         file
425                 .add(MenuItem(MenuItem::Command, _("New...|N"), "buffer-new"))
426                 .add(MenuItem(MenuItem::Command, _("Open...|O"), "buffer-open"))
427                 .add(MenuItem(MenuItem::Submenu, _("Import|I"), "import"))
428                 .add(MenuItem(MenuItem::Command, _("Quit|Q"), "lyx-quit"))
429                 .add(MenuItem(MenuItem::Separator))
430                 .add(MenuItem(MenuItem::Lastfiles));
431         add(file);
432
433         Menu import("import");
434         import
435                 .add(MenuItem(MenuItem::Command,
436                               _("LaTeX...|L"), "buffer-import latex"))
437                 .add(MenuItem(MenuItem::Command,
438                               _("LinuxDoc...|L"), "buffer-import linuxdoc"));
439         add(import);
440  
441         Menu edit("edit");
442         edit
443                 .add(MenuItem(MenuItem::Command, _("Cut"), "cut"))
444                 .add(MenuItem(MenuItem::Command, _("Copy"), "copy"))
445                 .add(MenuItem(MenuItem::Command, _("Paste"), "paste"))
446                 .add(MenuItem(MenuItem::Command, _("Emphasize"), "font-emph"));
447         add(edit);
448
449         Menu documents("documents");
450         documents.add(MenuItem(MenuItem::Documents));
451         add(documents);
452
453         Menu main("main", true);
454         main
455                 .add(MenuItem(MenuItem::Submenu, _("File|F"), "file"))
456                 .add(MenuItem(MenuItem::Submenu, _("Edit|E"), "edit"))
457                 .add(MenuItem(MenuItem::Submenu,
458                               _("Documents|D"), "documents"));
459         add(main);
460
461         Menu main_nobuffer("main_nobuffer", true);
462         main_nobuffer.add(MenuItem(MenuItem::Submenu, _("File|F"), "file"));
463         add(main_nobuffer);
464
465         if (lyxerr.debugging(Debug::GUI)) {
466                 for (const_iterator cit = begin();
467                     cit != end() ; ++cit)
468                         lyxerr << "Menu name: " << cit->name() 
469                                << ", Menubar: " << cit->menubar() 
470                                << endl;
471         }
472 }
473
474
475 void MenuBackend::add(Menu const & menu)
476 {
477         menulist_.push_back(menu);
478 }
479
480
481 bool MenuBackend::hasMenu(string const & name) const
482 {
483         return find_if(begin(), end(),
484                        compare_memfun(&Menu::name, name)) != end();
485 }
486
487
488 Menu const & MenuBackend::getMenu(string const & name) const
489 {
490         const_iterator cit = find_if(begin(), end(),
491                                      compare_memfun(&Menu::name, name));
492         Assert(cit != end());
493         return (*cit);
494 }
495
496
497 Menu & MenuBackend::getMenu(string const & name)
498 {
499         MenuList::iterator it = find_if(menulist_.begin(), menulist_.end(),
500                                         compare_memfun(&Menu::name, name));
501         Assert(it != menulist_.end());
502         return (*it);
503 }