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