1 /**
2  * \file qt4/Menus.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Asger Alstrup
8  * \author Lars Gullik Bjønnes
9  * \author Jean-Marc Lasgouttes
10  * \author André Pönitz
11  * \author Dekel Tsur
12  * \author Martin Vermeer
13  *
14  * Full author contact details are available in file CREDITS.
15  */
16 
17 #include <config.h>
18 
19 #include "Menus.h"
20 
21 #include "Action.h"
22 #include "GuiApplication.h"
23 #include "GuiView.h"
24 #include "GuiWorkArea.h"
25 #include "qt_helpers.h"
26 
27 #include "BiblioInfo.h"
28 #include "BranchList.h"
29 #include "Buffer.h"
30 #include "BufferList.h"
31 #include "BufferParams.h"
32 #include "BufferView.h"
33 #include "Converter.h"
34 #include "CutAndPaste.h"
35 #include "Floating.h"
36 #include "FloatList.h"
37 #include "Format.h"
38 #include "FuncRequest.h"
39 #include "FuncStatus.h"
40 #include "IndicesList.h"
41 #include "KeyMap.h"
42 #include "Language.h"
43 #include "Layout.h"
44 #include "Lexer.h"
45 #include "LyXAction.h"
46 #include "LyX.h"
47 #include "LyXRC.h"
48 #include "lyxfind.h"
49 #include "Paragraph.h"
50 #include "ParagraphParameters.h"
51 #include "ParIterator.h"
52 #include "Session.h"
53 #include "SpellChecker.h"
54 #include "TextClass.h"
55 #include "Text.h"
56 #include "TocBackend.h"
57 #include "Toolbars.h"
58 #include "WordLangTuple.h"
59 
60 #include "insets/Inset.h"
61 #include "insets/InsetCitation.h"
62 #include "insets/InsetGraphics.h"
63 #include "insets/InsetQuotes.h"
64 
65 #include "support/lassert.h"
66 #include "support/convert.h"
67 #include "support/debug.h"
68 #include "support/docstring_list.h"
69 #include "support/filetools.h"
70 #include "support/gettext.h"
71 #include "support/lstrings.h"
72 
73 #include <QCursor>
74 #include <QHash>
75 #include <QList>
76 #include <QMenuBar>
77 #include <QString>
78 #if QT_VERSION >= 0x040600
79 #include <QProxyStyle>
80 #endif
81 
82 #include <algorithm>
83 #include <memory>
84 #include <vector>
85 
86 using namespace std;
87 using namespace lyx::support;
88 
89 
90 namespace lyx {
91 namespace frontend {
92 
93 namespace {
94 
95 // MacOSX specific stuff is at the end.
96 
97 class MenuDefinition;
98 
99 ///
100 class MenuItem {
101 public:
102 	/// The type of elements that can be in a menu
103 	enum Kind {
104 		///
105 		Command,
106 		///
107 		Submenu,
108 		///
109 		Separator,
110 		/** This type of item explains why something is unavailable. If this
111 		    menuitem is in a submenu, the submenu is enabled to make sure the
112 		    user sees the information. */
113 		Help,
114 		/** This type of item merely shows that there might be a list or
115 		    something alike at this position, but the list is still empty.
116 		    If this item is in a submenu, the submenu will not always be
117 		    enabled. */
118 		Info,
119 		/** This is the list of last opened file,
120 		    typically for the File menu. */
121 		Lastfiles,
122 		/** This is the list of opened Documents,
123 		    typically for the Documents menu. */
124 		Documents,
125 		/** This is the bookmarks */
126 		Bookmarks,
127 		///
128 		Toc,
129 		/** This is a list of viewable formats
130 		    typically for the File->View menu. */
131 		ViewFormats,
132 		/** This is a list of updatable formats
133 		    typically for the File->Update menu. */
134 		UpdateFormats,
135 		/** This is a list of exportable formats
136 		    typically for the File->Export menu. */
137 		ExportFormats,
138 		/** This exports the document default format
139 		    typically for the File->Export menu. */
140 		ExportFormat,
141 		/** This is a list of importable formats
142 		    typically for the File->Import menu. */
143 		ImportFormats,
144 		/** This is the list of elements available
145 		 * for insertion into document. */
146 		CharStyles,
147 		/** This is the list of user-configurable
148 		insets to insert into document */
149 		Custom,
150 		/** This is the list of XML elements to
151 		insert into the document */
152 		Elements,
153 		/** This is the list of floats that we can
154 		    insert a list for. */
155 		FloatListInsert,
156 		/** This is the list of floats that we can
157 		    insert. */
158 		FloatInsert,
159 		/** This is the list of selections that can
160 		    be pasted. */
161 		PasteRecent,
162 		/** toolbars */
163 		Toolbars,
164 		/** Available branches in document */
165 		Branches,
166 		/** Available indices in document */
167 		Indices,
168 		/** Context menu for indices in document */
169 		IndicesContext,
170 		/** Available index lists in document */
171 		IndicesLists,
172 		/** Context menu for available indices lists in document */
173 		IndicesListsContext,
174 		/** Available citation styles for a given citation */
175 		CiteStyles,
176 		/** Available graphics groups */
177 		GraphicsGroups,
178 		/// Words suggested by the spellchecker.
179 		SpellingSuggestions,
180 		/** Used Languages */
181 		LanguageSelector,
182 		/** This is the list of arguments available
183 		    for insertion into the current layout. */
184 		Arguments,
185 		/** This is the list of arguments available
186 		    in the InsetArgument context menu. */
187 		SwitchArguments,
188 		/** This is the list of captions available
189 		in the current layout. */
190 		Captions,
191 		/** This is the list of captions available
192 		in the InsetCaption context menu. */
193 		SwitchCaptions,
194 		/** Commands to separate environments. */
195 		EnvironmentSeparators,
196 		/** Commands to separate environments (context menu version). */
197 		EnvironmentSeparatorsContext,
198 		/** This is the list of quotation marks available */
199 		SwitchQuotes
200 	};
201 
MenuItem(Kind kind)202 	explicit MenuItem(Kind kind) : kind_(kind), optional_(false) {}
203 
MenuItem(Kind kind,QString const & label,QString const & submenu=QString (),QString const & tooltip=QString (),bool optional=false)204 	MenuItem(Kind kind,
205 		 QString const & label,
206 		 QString const & submenu = QString(),
207 		 QString const & tooltip = QString(),
208 		 bool optional = false)
209 		: kind_(kind), label_(label), func_(make_shared<FuncRequest>()),
210 		  submenuname_(submenu), tooltip_(tooltip), optional_(optional)
211 	{
212 		LATTEST(kind == Submenu || kind == Help || kind == Info);
213 	}
214 
MenuItem(Kind kind,QString const & label,FuncRequest const & func,QString const & tooltip=QString (),bool optional=false,FuncRequest::Origin origin=FuncRequest::MENU)215 	MenuItem(Kind kind,
216 		 QString const & label,
217 		 FuncRequest const & func,
218 		 QString const & tooltip = QString(),
219 		 bool optional = false,
220 		 FuncRequest::Origin origin = FuncRequest::MENU)
221 		: kind_(kind), label_(label), func_(make_shared<FuncRequest>(func)),
222 		  tooltip_(tooltip), optional_(optional)
223 	{
224 		func_->setOrigin(origin);
225 	}
226 
227 	/// The label of a given menuitem
label() const228 	QString label() const
229 	{
230 		int const index = label_.lastIndexOf('|');
231 		return index == -1 ? label_ : label_.left(index);
232 	}
233 
234 	/// The keyboard shortcut (usually underlined in the entry)
shortcut() const235 	QString shortcut() const
236 	{
237 		int const index = label_.lastIndexOf('|');
238 		return index == -1 ? QString() : label_.mid(index + 1);
239 	}
240 	/// The complete label, with label and shortcut separated by a '|'
fulllabel() const241 	QString fulllabel() const { return label_; }
242 	/// The kind of entry
kind() const243 	Kind kind() const { return kind_; }
244 	/// the action (if relevant)
func() const245 	shared_ptr<FuncRequest const> func() const { return func_; }
246 	/// the tooltip
tooltip() const247 	QString const & tooltip() const { return tooltip_; }
248 	/// returns true if the entry should be omitted when disabled
optional() const249 	bool optional() const { return optional_; }
250 	/// returns the status of the lfun associated with this entry
status() const251 	FuncStatus const & status() const { return status_; }
252 	/// returns the status of the lfun associated with this entry
status()253 	FuncStatus & status() { return status_; }
254 	/// returns the status of the lfun associated with this entry
status(FuncStatus const & status)255 	void status(FuncStatus const & status) { status_ = status; }
256 
257 	///returns the binding associated to this action.
binding() const258 	QString binding() const
259 	{
260 		if (kind_ != Command)
261 			return QString();
262 		// Get the keys bound to this action, but keep only the
263 		// first one later
264 		KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(*func_);
265 		if (!bindings.empty())
266 			return toqstr(bindings.begin()->print(KeySequence::ForGui));
267 
268 		LYXERR(Debug::KBMAP, "No binding for "
269 			<< lyxaction.getActionName(func_->action())
270 			<< '(' << func_->argument() << ')');
271 		return QString();
272 	}
273 
274 	/// the description of the  submenu (if relevant)
submenuname() const275 	QString const & submenuname() const { return submenuname_; }
276 	/// set the description of the  submenu
submenuname(QString const & name)277 	void submenuname(QString const & name) { submenuname_ = name; }
278 	///
hasSubmenu() const279 	bool hasSubmenu() const { return !submenu_.isEmpty(); }
280 	///
submenu() const281 	MenuDefinition const & submenu() const { return submenu_.at(0); }
submenu()282 	MenuDefinition & submenu() { return submenu_[0]; }
283 	///
setSubmenu(MenuDefinition const & menu)284 	void setSubmenu(MenuDefinition const & menu)
285 	{
286 		submenu_.clear();
287 		submenu_.append(menu);
288 	}
289 
290 private:
291 	///
292 	Kind kind_;
293 	///
294 	QString label_;
295 	///
296 	shared_ptr<FuncRequest> func_;// non-null
297 	///
298 	QString submenuname_;
299 	///
300 	QString tooltip_;
301 	///
302 	bool optional_;
303 	///
304 	FuncStatus status_;
305 	/// contains 0 or 1 item.
306 	QList<MenuDefinition> submenu_;
307 };
308 
309 ///
310 class MenuDefinition {
311 public:
312 	///
313 	typedef std::vector<MenuItem> ItemList;
314 	///
315 	typedef ItemList::const_iterator const_iterator;
316 	///
MenuDefinition(QString const & name=QString ())317 	explicit MenuDefinition(QString const & name = QString()) : name_(name) {}
318 
319 	///
320 	void read(Lexer &);
321 	///
name() const322 	QString const & name() const { return name_; }
323 	///
empty() const324 	bool empty() const { return items_.empty(); }
325 	/// Clear the menu content.
clear()326 	void clear() { items_.clear(); }
327 	///
size() const328 	size_t size() const { return items_.size(); }
329 	///
begin() const330 	const_iterator begin() const { return items_.begin(); }
331 	///
end() const332 	const_iterator end() const { return items_.end(); }
333 	///
334 	void cat(MenuDefinition const & other);
335 	///
336 	void catSub(docstring const & name);
337 
338 	// search for func in this menu iteratively, and put menu
339 	// names in a stack.
340 	bool searchMenu(FuncRequest const & func, docstring_list & names)
341 		const;
342 	///
343 	bool hasFunc(FuncRequest const &) const;
344 	/// Add the menu item unconditionally
add(MenuItem const & item)345 	void add(MenuItem const & item) { items_.push_back(item); }
346 	/// Checks the associated FuncRequest status before adding the
347 	/// menu item.
348 	void addWithStatusCheck(MenuItem const &);
349 	// Check whether the menu shortcuts are unique
350 	void checkShortcuts() const;
351 	///
352 	void expandLastfiles();
353 	void expandDocuments();
354 	void expandBookmarks();
355 	void expandFormats(MenuItem::Kind const kind, Buffer const * buf);
356 	void expandFloatListInsert(Buffer const * buf);
357 	void expandFloatInsert(Buffer const * buf);
358 	void expandFlexInsert(Buffer const * buf, InsetLayout::InsetLyXType type);
359 	void expandTocSubmenu(std::string const & type, Toc const & toc_list);
360 	void expandToc2(Toc const & toc_list, size_t from, size_t to, int depth, string toc_type);
361 	void expandToc(Buffer const * buf);
362 	void expandPasteRecent(Buffer const * buf);
363 	void expandToolbars();
364 	void expandBranches(Buffer const * buf);
365 	void expandIndices(Buffer const * buf, bool listof = false);
366 	void expandIndicesContext(Buffer const * buf, bool listof = false);
367 	void expandCiteStyles(BufferView const *);
368 	void expandGraphicsGroups(BufferView const *);
369 	void expandSpellingSuggestions(BufferView const *);
370 	void expandLanguageSelector(Buffer const * buf);
371 	void expandArguments(BufferView const *, bool switcharg = false);
372 	void expandCaptions(Buffer const * buf, bool switchcap = false);
373 	void expandEnvironmentSeparators(BufferView const *, bool contextmenu = false);
374 	void expandQuotes(BufferView const *);
375 	///
376 	ItemList items_;
377 	///
378 	QString name_;
379 };
380 
381 
382 /// Helper for std::find_if
383 class MenuNamesEqual
384 {
385 public:
MenuNamesEqual(QString const & name)386 	MenuNamesEqual(QString const & name) : name_(name) {}
operator ()(MenuDefinition const & menu) const387 	bool operator()(MenuDefinition const & menu) const { return menu.name() == name_; }
388 private:
389 	QString name_;
390 };
391 
392 
393 ///
394 typedef std::vector<MenuDefinition> MenuList;
395 ///
396 typedef MenuList::const_iterator const_iterator;
397 ///
398 typedef MenuList::iterator iterator;
399 
400 /////////////////////////////////////////////////////////////////////
401 // MenuDefinition implementation
402 /////////////////////////////////////////////////////////////////////
403 
addWithStatusCheck(MenuItem const & i)404 void MenuDefinition::addWithStatusCheck(MenuItem const & i)
405 {
406 	switch (i.kind()) {
407 
408 	case MenuItem::Command: {
409 		FuncStatus status = lyx::getStatus(*i.func());
410 		if (status.unknown() || (!status.enabled() && i.optional()))
411 			break;
412 		items_.push_back(i);
413 		items_.back().status(status);
414 		break;
415 	}
416 
417 	case MenuItem::Submenu: {
418 		bool enabled = false;
419 		if (i.hasSubmenu()) {
420 			for (const_iterator cit = i.submenu().begin();
421 				  cit != i.submenu().end(); ++cit) {
422 				// Only these kind of items affect the status of the submenu
423 				if ((cit->kind() == MenuItem::Command
424 				     || cit->kind() == MenuItem::Submenu
425 				     || cit->kind() == MenuItem::Help)
426 				    && cit->status().enabled()) {
427 					enabled = true;
428 					break;
429 				}
430 			}
431 		}
432 		if (enabled || !i.optional()) {
433 			items_.push_back(i);
434 			items_.back().status().setEnabled(enabled);
435 		}
436 		break;
437 	}
438 
439 	case MenuItem::Separator:
440 		if (!items_.empty() && items_.back().kind() != MenuItem::Separator)
441 			items_.push_back(i);
442 		break;
443 
444 	default:
445 		items_.push_back(i);
446 	}
447 }
448 
449 
read(Lexer & lex)450 void MenuDefinition::read(Lexer & lex)
451 {
452 	enum {
453 		md_item = 1,
454 		md_branches,
455 		md_citestyles,
456 		md_documents,
457 		md_bookmarks,
458 		md_charstyles,
459 		md_custom,
460 		md_elements,
461 		md_endmenu,
462 		md_exportformat,
463 		md_exportformats,
464 		md_importformats,
465 		md_indices,
466 		md_indicescontext,
467 		md_indiceslists,
468 		md_indiceslistscontext,
469 		md_lastfiles,
470 		md_optitem,
471 		md_optsubmenu,
472 		md_separator,
473 		md_submenu,
474 		md_toc,
475 		md_updateformats,
476 		md_viewformats,
477 		md_floatlistinsert,
478 		md_floatinsert,
479 		md_pasterecent,
480 		md_toolbars,
481 		md_graphicsgroups,
482 		md_spellingsuggestions,
483 		md_languageselector,
484 		md_arguments,
485 		md_switcharguments,
486 		md_captions,
487 		md_switchcaptions,
488 		md_env_separators,
489 		md_env_separatorscontext,
490 		md_switchquotes
491 	};
492 
493 	LexerKeyword menutags[] = {
494 		{ "arguments", md_arguments },
495 		{ "bookmarks", md_bookmarks },
496 		{ "branches", md_branches },
497 		{ "captions", md_captions },
498 		{ "charstyles", md_charstyles },
499 		{ "citestyles", md_citestyles },
500 		{ "custom", md_custom },
501 		{ "documents", md_documents },
502 		{ "elements", md_elements },
503 		{ "end", md_endmenu },
504 		{ "environmentseparators", md_env_separators },
505 		{ "environmentseparatorscontext", md_env_separatorscontext },
506 		{ "exportformat", md_exportformat },
507 		{ "exportformats", md_exportformats },
508 		{ "floatinsert", md_floatinsert },
509 		{ "floatlistinsert", md_floatlistinsert },
510 		{ "graphicsgroups", md_graphicsgroups },
511 		{ "importformats", md_importformats },
512 		{ "indices", md_indices },
513 		{ "indicescontext", md_indicescontext },
514 		{ "indiceslists", md_indiceslists },
515 		{ "indiceslistscontext", md_indiceslistscontext },
516 		{ "item", md_item },
517 		{ "languageselector", md_languageselector },
518 		{ "lastfiles", md_lastfiles },
519 		{ "optitem", md_optitem },
520 		{ "optsubmenu", md_optsubmenu },
521 		{ "pasterecent", md_pasterecent },
522 		{ "separator", md_separator },
523 		{ "spellingsuggestions", md_spellingsuggestions },
524 		{ "submenu", md_submenu },
525 		{ "switcharguments", md_switcharguments },
526 		{ "switchcaptions", md_switchcaptions },
527 		{ "switchquotes", md_switchquotes },
528 		{ "toc", md_toc },
529 		{ "toolbars", md_toolbars },
530 		{ "updateformats", md_updateformats },
531 		{ "viewformats", md_viewformats }
532 	};
533 
534 	lex.pushTable(menutags);
535 	lex.setContext("MenuDefinition::read: ");
536 
537 	int md_type = 0;
538 	while (lex.isOK() && md_type != md_endmenu) {
539 		switch (md_type = lex.lex()) {
540 		case md_optitem:
541 		case md_item: {
542 			lex.next(true);
543 			docstring const name = translateIfPossible(lex.getDocString());
544 			lex.next(true);
545 			string const command = lex.getString();
546 			FuncRequest func = lyxaction.lookupFunc(command);
547 			FuncRequest::Origin origin = FuncRequest::MENU;
548 			if (name_.startsWith("context-toc-"))
549 				origin = FuncRequest::TOC;
550 			bool const optional = (md_type == md_optitem);
551 			add(MenuItem(MenuItem::Command, toqstr(name), func, QString(), optional, origin));
552 			break;
553 		}
554 
555 		case md_separator:
556 			add(MenuItem(MenuItem::Separator));
557 			break;
558 
559 		case md_lastfiles:
560 			add(MenuItem(MenuItem::Lastfiles));
561 			break;
562 
563 		case md_charstyles:
564 			add(MenuItem(MenuItem::CharStyles));
565 			break;
566 
567 		case md_custom:
568 			add(MenuItem(MenuItem::Custom));
569 			break;
570 
571 		case md_elements:
572 			add(MenuItem(MenuItem::Elements));
573 			break;
574 
575 		case md_documents:
576 			add(MenuItem(MenuItem::Documents));
577 			break;
578 
579 		case md_bookmarks:
580 			add(MenuItem(MenuItem::Bookmarks));
581 			break;
582 
583 		case md_toc:
584 			add(MenuItem(MenuItem::Toc));
585 			break;
586 
587 		case md_viewformats:
588 			add(MenuItem(MenuItem::ViewFormats));
589 			break;
590 
591 		case md_updateformats:
592 			add(MenuItem(MenuItem::UpdateFormats));
593 			break;
594 
595 		case md_exportformats:
596 			add(MenuItem(MenuItem::ExportFormats));
597 			break;
598 
599 		case md_exportformat:
600 			add(MenuItem(MenuItem::ExportFormat));
601 			break;
602 
603 		case md_importformats:
604 			add(MenuItem(MenuItem::ImportFormats));
605 			break;
606 
607 		case md_floatlistinsert:
608 			add(MenuItem(MenuItem::FloatListInsert));
609 			break;
610 
611 		case md_floatinsert:
612 			add(MenuItem(MenuItem::FloatInsert));
613 			break;
614 
615 		case md_pasterecent:
616 			add(MenuItem(MenuItem::PasteRecent));
617 			break;
618 
619 		case md_toolbars:
620 			add(MenuItem(MenuItem::Toolbars));
621 			break;
622 
623 		case md_branches:
624 			add(MenuItem(MenuItem::Branches));
625 			break;
626 
627 		case md_citestyles:
628 			add(MenuItem(MenuItem::CiteStyles));
629 			break;
630 
631 		case md_graphicsgroups:
632 			add(MenuItem(MenuItem::GraphicsGroups));
633 			break;
634 
635 		case md_spellingsuggestions:
636 			add(MenuItem(MenuItem::SpellingSuggestions));
637 			break;
638 
639 		case md_languageselector:
640 			add(MenuItem(MenuItem::LanguageSelector));
641 			break;
642 
643 		case md_indices:
644 			add(MenuItem(MenuItem::Indices));
645 			break;
646 
647 		case md_indicescontext:
648 			add(MenuItem(MenuItem::IndicesContext));
649 			break;
650 
651 		case md_indiceslists:
652 			add(MenuItem(MenuItem::IndicesLists));
653 			break;
654 
655 		case md_indiceslistscontext:
656 			add(MenuItem(MenuItem::IndicesListsContext));
657 			break;
658 
659 		case md_arguments:
660 			add(MenuItem(MenuItem::Arguments));
661 			break;
662 
663 		case md_switcharguments:
664 			add(MenuItem(MenuItem::SwitchArguments));
665 			break;
666 
667 		case md_captions:
668 			add(MenuItem(MenuItem::Captions));
669 			break;
670 
671 		case md_switchcaptions:
672 			add(MenuItem(MenuItem::SwitchCaptions));
673 			break;
674 
675 		case md_env_separators:
676 			add(MenuItem(MenuItem::EnvironmentSeparators));
677 			break;
678 
679 		case md_env_separatorscontext:
680 			add(MenuItem(MenuItem::EnvironmentSeparatorsContext));
681 			break;
682 
683 		case md_switchquotes:
684 			add(MenuItem(MenuItem::SwitchQuotes));
685 			break;
686 
687 		case md_optsubmenu:
688 		case md_submenu: {
689 			lex.next(true);
690 			docstring const mlabel = translateIfPossible(lex.getDocString());
691 			lex.next(true);
692 			docstring const mname = lex.getDocString();
693 			bool const optional = (md_type == md_optsubmenu);
694 			add(MenuItem(MenuItem::Submenu,
695 				toqstr(mlabel), toqstr(mname), QString(), optional));
696 			break;
697 		}
698 
699 		case md_endmenu:
700 			break;
701 
702 		default:
703 			lex.printError("Unknown menu tag");
704 			break;
705 		}
706 	}
707 	lex.popTable();
708 }
709 
710 
hasFunc(FuncRequest const & func) const711 bool MenuDefinition::hasFunc(FuncRequest const & func) const
712 {
713 	for (const_iterator it = begin(), et = end(); it != et; ++it)
714 		if (*it->func() == func)
715 			return true;
716 	return false;
717 }
718 
719 
catSub(docstring const & name)720 void MenuDefinition::catSub(docstring const & name)
721 {
722 	add(MenuItem(MenuItem::Submenu,
723 		     qt_("More...|M"), toqstr(name), QString(), false));
724 }
725 
cat(MenuDefinition const & other)726 void MenuDefinition::cat(MenuDefinition const & other)
727 {
728 	const_iterator et = other.end();
729 	for (const_iterator it = other.begin(); it != et; ++it)
730 		add(*it);
731 }
732 
733 
checkShortcuts() const734 void MenuDefinition::checkShortcuts() const
735 {
736 	// This is a quadratic algorithm, but we do not care because
737 	// menus are short enough
738 	for (const_iterator it1 = begin(); it1 != end(); ++it1) {
739 		QString shortcut = it1->shortcut();
740 		if (shortcut.isEmpty())
741 			continue;
742 		if (!it1->label().contains(shortcut))
743 			LYXERR0("Menu warning: menu entry \""
744 			       << it1->label()
745 			       << "\" does not contain shortcut `"
746 			       << shortcut << "'.");
747 		for (const_iterator it2 = begin(); it2 != it1 ; ++it2) {
748 			if (!it2->shortcut().compare(shortcut, Qt::CaseInsensitive)) {
749 				LYXERR0("Menu warning: menu entries "
750 				       << '"' << it1->fulllabel()
751 				       << "\" and \"" << it2->fulllabel()
752 				       << "\" share the same shortcut.");
753 			}
754 		}
755 	}
756 }
757 
758 
searchMenu(FuncRequest const & func,docstring_list & names) const759 bool MenuDefinition::searchMenu(FuncRequest const & func, docstring_list & names) const
760 {
761 	const_iterator m = begin();
762 	const_iterator m_end = end();
763 	for (; m != m_end; ++m) {
764 		if (m->kind() == MenuItem::Command && *m->func() == func) {
765 			names.push_back(qstring_to_ucs4(m->label()));
766 			return true;
767 		}
768 		if (m->kind() == MenuItem::Submenu) {
769 			names.push_back(qstring_to_ucs4(m->label()));
770 			if (!m->hasSubmenu()) {
771 				LYXERR(Debug::GUI, "Warning: non existing sub menu label="
772 					<< m->label() << " name=" << m->submenuname());
773 				names.pop_back();
774 				continue;
775 			}
776 			if (m->submenu().searchMenu(func, names))
777 				return true;
778 			names.pop_back();
779 		}
780 	}
781 	return false;
782 }
783 
784 
limitStringLength(docstring const & str)785 QString limitStringLength(docstring const & str)
786 {
787 	size_t const max_item_length = 45;
788 	docstring ret = str.substr(0, max_item_length + 1);
789 	support::truncateWithEllipsis(ret, max_item_length);
790 	return toqstr(ret);
791 }
792 
793 
expandGraphicsGroups(BufferView const * bv)794 void MenuDefinition::expandGraphicsGroups(BufferView const * bv)
795 {
796 	if (!bv)
797 		return;
798 	set<string> grp;
799 	graphics::getGraphicsGroups(bv->buffer(), grp);
800 	if (grp.empty())
801 		return;
802 
803 	set<string>::const_iterator it = grp.begin();
804 	set<string>::const_iterator end = grp.end();
805 	add(MenuItem(MenuItem::Command, qt_("No Group"),
806 		     FuncRequest(LFUN_SET_GRAPHICS_GROUP)));
807 	for (; it != end; ++it) {
808 		addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(*it) + '|',
809 				FuncRequest(LFUN_SET_GRAPHICS_GROUP, *it)));
810 	}
811 }
812 
813 
expandSpellingSuggestions(BufferView const * bv)814 void MenuDefinition::expandSpellingSuggestions(BufferView const * bv)
815 {
816 	if (!bv)
817 		return;
818 	Cursor const & cur = bv->cursor();
819 	if (!cur.inTexted())
820 		return;
821 	WordLangTuple wl;
822 	docstring_list suggestions;
823 	Paragraph const & par = cur.paragraph();
824 	pos_type from = cur.pos();
825 	pos_type to = from;
826 	SpellChecker::Result res = par.spellCheck(from, to, wl, suggestions, true, true);
827 	switch (res) {
828 	case SpellChecker::UNKNOWN_WORD:
829 		if (lyxrc.spellcheck_continuously) {
830 			LYXERR(Debug::GUI, "Misspelled Word! Suggested Words = ");
831 			docstring const & selection = cur.selectionAsString(false);
832 			if (!cur.selection() || selection == wl.word()) {
833 				size_t i = 0;
834 				size_t m = 10; // first submenu index
835 				MenuItem item(MenuItem::Submenu, qt_("More Spelling Suggestions"));
836 				item.setSubmenu(MenuDefinition(qt_("More Spelling Suggestions")));
837 				for (; i != suggestions.size(); ++i) {
838 					docstring const & suggestion = suggestions[i];
839 					LYXERR(Debug::GUI, suggestion);
840 					MenuItem w(MenuItem::Command, toqstr(suggestion),
841 						FuncRequest(LFUN_WORD_REPLACE,
842 							replace2string(suggestion, selection,
843 								true,     // case sensitive
844 								true,     // match word
845 								false,    // all words
846 								true,     // forward
847 								false))); // find next
848 					if (i < m)
849 						add(w);
850 					else
851 						item.submenu().add(w);
852 				}
853 				if (i > m)
854 					add(item);
855 				if (i > 0)
856 					add(MenuItem(MenuItem::Separator));
857 				docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang());
858 				add(MenuItem(MenuItem::Command, qt_("Add to personal dictionary|n"),
859 						FuncRequest(LFUN_SPELLING_ADD, arg)));
860 				add(MenuItem(MenuItem::Command, qt_("Ignore all|I"),
861 						FuncRequest(LFUN_SPELLING_IGNORE, arg)));
862 			}
863 		}
864 		break;
865 	case SpellChecker::LEARNED_WORD: {
866 			LYXERR(Debug::GUI, "Learned Word.");
867 			docstring const arg = wl.word() + " " + from_ascii(wl.lang()->lang());
868 			add(MenuItem(MenuItem::Command, qt_("Remove from personal dictionary|r"),
869 					FuncRequest(LFUN_SPELLING_REMOVE, arg)));
870 		}
871 		break;
872 	case SpellChecker::NO_DICTIONARY:
873 		LYXERR(Debug::GUI, "No dictionary for language " + from_ascii(wl.lang()->lang()));
874 		// FALLTHROUGH
875 	case SpellChecker::WORD_OK:
876 	case SpellChecker::COMPOUND_WORD:
877 	case SpellChecker::ROOT_FOUND:
878 	case SpellChecker::IGNORED_WORD:
879 		break;
880 	}
881 }
882 
883 struct sortLanguageByName {
operator ()lyx::frontend::__anonc26ba47a0111::sortLanguageByName884 	bool operator()(const Language * a, const Language * b) const {
885 		return qt_(a->display()).localeAwareCompare(qt_(b->display())) < 0;
886 	}
887 };
888 
expandLanguageSelector(Buffer const * buf)889 void MenuDefinition::expandLanguageSelector(Buffer const * buf)
890 {
891 	if (!buf)
892 		return;
893 
894 	std::set<Language const *> languages_buffer =
895 		buf->masterBuffer()->getLanguages();
896 
897 	if (languages_buffer.size() < 2)
898 		return;
899 
900 	std::set<Language const *, sortLanguageByName> languages;
901 
902 	std::set<Language const *>::const_iterator const beg =
903 		languages_buffer.begin();
904 	for (std::set<Language const *>::const_iterator cit = beg;
905 	     cit != languages_buffer.end(); ++cit) {
906 		languages.insert(*cit);
907 	}
908 
909 	MenuItem item(MenuItem::Submenu, qt_("Language|L"));
910 	item.setSubmenu(MenuDefinition(qt_("Language")));
911 	QString morelangs = qt_("More Languages ...|M");
912 	QStringList accelerators;
913 	if (morelangs.contains('|'))
914 		accelerators.append(morelangs.section('|', -1));
915 	std::set<Language const *, sortLanguageByName>::const_iterator const begin = languages.begin();
916 	for (std::set<Language const *, sortLanguageByName>::const_iterator cit = begin;
917 	     cit != languages.end(); ++cit) {
918 		QString label = qt_((*cit)->display());
919 		// try to add an accelerator
920 		bool success = false;
921 		// try capitals first
922 		for (int i = 0; i < label.size(); ++i) {
923 			QChar const ch = label[i];
924 			if (!ch.isUpper())
925 				continue;
926 			if (!accelerators.contains(ch, Qt::CaseInsensitive)) {
927 				label = label + toqstr("|") + ch;
928 				accelerators.append(ch);
929 				success = true;
930 				break;
931 			}
932 		}
933 		// if all capitals are taken, try the rest
934 		if (!success) {
935 			for (int i = 0; i < label.size(); ++i) {
936 				if (label[i].isSpace())
937 					continue;
938 				QString const ch = QString(label[i]);
939 				if (!accelerators.contains(ch, Qt::CaseInsensitive)) {
940 					label = label + toqstr("|") + ch;
941 					accelerators.append(ch);
942 					break;
943 				}
944 			}
945 		}
946 		MenuItem w(MenuItem::Command, label,
947 			FuncRequest(LFUN_LANGUAGE, (*cit)->lang() + " set"));
948 		item.submenu().addWithStatusCheck(w);
949 	}
950 	item.submenu().add(MenuItem(MenuItem::Separator));
951 	item.submenu().add(MenuItem(MenuItem::Command, morelangs,
952 			FuncRequest(LFUN_DIALOG_SHOW, "character")));
953 	add(item);
954 }
955 
956 
expandLastfiles()957 void MenuDefinition::expandLastfiles()
958 {
959 	LastFilesSection::LastFiles const & lf = theSession().lastFiles().lastFiles();
960 	LastFilesSection::LastFiles::const_iterator lfit = lf.begin();
961 
962 	unsigned int ii = 1;
963 
964 	for (; lfit != lf.end() && ii <= lyxrc.num_lastfiles; ++lfit, ++ii) {
965 		string const file = lfit->absFileName();
966 		QString const short_path = toqstr(makeDisplayPath(file, 30));
967 		QString const long_path = toqstr(makeDisplayPath(file));
968 		QString label;
969 		if (ii < 10)
970 			label = QString("%1. %2|%3").arg(ii).arg(short_path).arg(ii);
971 		else
972 			label = QString("%1. %2").arg(ii).arg(short_path);
973 		add(MenuItem(MenuItem::Command, label,
974 			FuncRequest(LFUN_FILE_OPEN, file), long_path));
975 	}
976 }
977 
978 
expandDocuments()979 void MenuDefinition::expandDocuments()
980 {
981 	MenuItem item(MenuItem::Submenu, qt_("Hidden|H"));
982 	item.setSubmenu(MenuDefinition(qt_("Hidden|H")));
983 
984 	Buffer * first = theBufferList().first();
985 	if (!first) {
986 		add(MenuItem(MenuItem::Info, qt_("<No Documents Open>")));
987 		return;
988 	}
989 
990 	int i = 0;
991 	while (true) {
992 		if (!guiApp->currentView())
993 			break;
994 		GuiWorkArea * wa = guiApp->currentView()->workArea(i);
995 		if (!wa)
996 			break;
997 		Buffer const & b = wa->bufferView().buffer();
998 		QString label = toqstr(b.fileName().displayName(20));
999 		if (!b.isClean())
1000 			label += "*";
1001 		if (b.notifiesExternalModification())
1002 			label += QChar(0x26a0);
1003 		if (i < 10)
1004 			label = QString::number(i) + ". " + label + '|' + QString::number(i);
1005 		add(MenuItem(MenuItem::Command, label,
1006 			FuncRequest(LFUN_BUFFER_SWITCH, b.absFileName())));
1007 		++i;
1008 	}
1009 
1010 
1011 	i = 0;
1012 	Buffer * b = first;
1013 	// We cannot use a for loop as the buffer list cycles.
1014 	do {
1015 		if (!(guiApp->currentView()
1016 		    && guiApp->currentView()->workArea(*b))) {
1017 			QString label = toqstr(b->fileName().displayName(20));
1018 			if (!b->isClean())
1019 				label += "*";
1020 			if (b->notifiesExternalModification())
1021 				label += QChar(0x26a0);
1022 			if (i < 10)
1023 				label = QString::number(i) + ". " + label + '|' + QString::number(i);
1024 			item.submenu().add(MenuItem(MenuItem::Command, label,
1025 				FuncRequest(LFUN_BUFFER_SWITCH, b->absFileName())));
1026 			++i;
1027 		}
1028 		b = theBufferList().next(b);
1029 	} while (b != first);
1030 
1031 	if (!item.submenu().empty())
1032 		add(item);
1033 }
1034 
1035 
expandBookmarks()1036 void MenuDefinition::expandBookmarks()
1037 {
1038 	lyx::BookmarksSection const & bm = theSession().bookmarks();
1039 
1040 	bool empty = true;
1041 	for (size_t i = 1; i <= bm.size(); ++i) {
1042 		if (bm.isValid(i)) {
1043 			string const file = bm.bookmark(i).filename.absFileName();
1044 			QString const label = QString("%1. %2|%3").arg(i)
1045 				.arg(toqstr(makeDisplayPath(file, 20))).arg(i);
1046 			add(MenuItem(MenuItem::Command, label,
1047 				FuncRequest(LFUN_BOOKMARK_GOTO, convert<docstring>(i))));
1048 			empty = false;
1049 		}
1050 	}
1051 	if (empty)
1052 		add(MenuItem(MenuItem::Info, qt_("<No Bookmarks Saved Yet>")));
1053 }
1054 
1055 
expandFormats(MenuItem::Kind const kind,Buffer const * buf)1056 void MenuDefinition::expandFormats(MenuItem::Kind const kind, Buffer const * buf)
1057 {
1058 	if (!buf && kind != MenuItem::ImportFormats)
1059 		return;
1060 
1061 	FormatList formats;
1062 	FuncCode action = LFUN_NOACTION;
1063 
1064 	switch (kind) {
1065 	case MenuItem::ImportFormats:
1066 		formats = theConverters().importableFormats();
1067 		action = LFUN_BUFFER_IMPORT;
1068 		break;
1069 	case MenuItem::ViewFormats:
1070 		formats = buf->params().exportableFormats(true);
1071 		action = LFUN_BUFFER_VIEW;
1072 		break;
1073 	case MenuItem::UpdateFormats:
1074 		formats = buf->params().exportableFormats(true);
1075 		action = LFUN_BUFFER_UPDATE;
1076 		break;
1077 	case MenuItem::ExportFormats:
1078 		formats = buf->params().exportableFormats(false);
1079 		action = LFUN_BUFFER_EXPORT;
1080 		break;
1081 	default:
1082 		LATTEST(false);
1083 		return;
1084 	}
1085 
1086 	bool const view_update = (kind == MenuItem::ViewFormats
1087 			|| kind == MenuItem::UpdateFormats);
1088 
1089 	QString smenue;
1090 	if (view_update)
1091 		smenue = (kind == MenuItem::ViewFormats
1092 			? qt_("View (Other Formats)|F")
1093 			: qt_("Update (Other Formats)|p"));
1094 	MenuItem item(MenuItem::Submenu, smenue);
1095 	item.setSubmenu(MenuDefinition(smenue));
1096 
1097 	for (Format const * f : formats) {
1098 		if (f->dummy())
1099 			continue;
1100 
1101 		docstring lab = f->prettyname();
1102 		docstring const scut = from_utf8(f->shortcut());
1103 		docstring const tmplab = lab;
1104 
1105 		if (!scut.empty())
1106 			lab += char_type('|') + scut;
1107 		docstring const lab_i18n = translateIfPossible(lab);
1108 		docstring const shortcut = split(lab_i18n, lab, '|');
1109 
1110 		bool const untranslated = (lab == lab_i18n);
1111 		docstring label = untranslated ? translateIfPossible(tmplab) : lab;
1112 
1113 		switch (kind) {
1114 		case MenuItem::ImportFormats:
1115 			if (f->noMenu())
1116 				continue;
1117 			label += from_ascii("...");
1118 			break;
1119 		case MenuItem::ViewFormats:
1120 		case MenuItem::UpdateFormats:
1121 			if (f->name() == buf->params().getDefaultOutputFormat()) {
1122 				docstring lbl = (kind == MenuItem::ViewFormats
1123 					? bformat(_("View [%1$s]|V"), label)
1124 					: bformat(_("Update [%1$s]|U"), label));
1125 				add(MenuItem(MenuItem::Command, toqstr(lbl), FuncRequest(action)));
1126 				continue;
1127 			}
1128 			break;
1129 		case MenuItem::ExportFormats:
1130 			if (!f->inExportMenu())
1131 				continue;
1132 			break;
1133 		default:
1134 			// we already asserted earlier in this case
1135 			// LATTEST(false);
1136 			continue;
1137 		}
1138 		if (!shortcut.empty())
1139 			label += '|' + shortcut;
1140 
1141 		if (view_update) {
1142 			// note that at this point, we know that buf is not null
1143 			LATTEST(buf);
1144 			item.submenu().addWithStatusCheck(MenuItem(MenuItem::Command,
1145 				toqstr(label), FuncRequest(action, f->name())));
1146 		} else {
1147 			if (buf)
1148 				addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
1149 					FuncRequest(action, f->name())));
1150 			else
1151 				add(MenuItem(MenuItem::Command, toqstr(label),
1152 					FuncRequest(action, f->name())));
1153 		}
1154 	}
1155 	if (view_update)
1156 		add(item);
1157 }
1158 
1159 
expandFloatListInsert(Buffer const * buf)1160 void MenuDefinition::expandFloatListInsert(Buffer const * buf)
1161 {
1162 	if (!buf)
1163 		return;
1164 
1165 	FloatList const & floats = buf->params().documentClass().floats();
1166 	FloatList::const_iterator cit = floats.begin();
1167 	FloatList::const_iterator end = floats.end();
1168 	set<string> seen;
1169 	for (; cit != end; ++cit) {
1170 		if (!cit->second.usesFloatPkg()) {
1171 			// Different floats could declare the same ListCommand. We only
1172 			// want it on the list once, though.
1173 			string const & list_cmd = cit->second.listCommand();
1174 			if (list_cmd.empty())
1175 				// we do not know how to generate such a list
1176 				continue;
1177 			// This form of insert returns an iterator pointing to the newly
1178 			// inserted element OR the existing element with that value, and
1179 			// a bool indicating whether we inserted a new element. So we can
1180 			// see if one is there and insert it if not all at once.
1181 			pair<set<string>::iterator, bool> ret = seen.insert(list_cmd);
1182 			if (!ret.second)
1183 				continue;
1184 		}
1185 		string const & list_name = cit->second.listName();
1186 		addWithStatusCheck(MenuItem(MenuItem::Command, qt_(list_name),
1187 			FuncRequest(LFUN_FLOAT_LIST_INSERT, cit->second.floattype())));
1188 	}
1189 }
1190 
1191 
expandFloatInsert(Buffer const * buf)1192 void MenuDefinition::expandFloatInsert(Buffer const * buf)
1193 {
1194 	if (!buf)
1195 		return;
1196 
1197 	FloatList const & floats = buf->params().documentClass().floats();
1198 	FloatList::const_iterator cit = floats.begin();
1199 	FloatList::const_iterator end = floats.end();
1200 	for (; cit != end; ++cit) {
1201 		// normal float
1202 		QString const label = qt_(cit->second.name());
1203 		addWithStatusCheck(MenuItem(MenuItem::Command, label,
1204 				    FuncRequest(LFUN_FLOAT_INSERT,
1205 						cit->second.floattype())));
1206 	}
1207 }
1208 
1209 
expandFlexInsert(Buffer const * buf,InsetLayout::InsetLyXType type)1210 void MenuDefinition::expandFlexInsert(
1211 		Buffer const * buf, InsetLayout::InsetLyXType type)
1212 {
1213 	if (!buf)
1214 		return;
1215 
1216 	TextClass::InsetLayouts const & insetLayouts =
1217 		buf->params().documentClass().insetLayouts();
1218 	TextClass::InsetLayouts::const_iterator cit = insetLayouts.begin();
1219 	TextClass::InsetLayouts::const_iterator end = insetLayouts.end();
1220 	for (; cit != end; ++cit) {
1221 		if (cit->second.lyxtype() == type) {
1222 			if (!cit->second.obsoleted_by().empty())
1223 				continue;
1224 			docstring label = cit->first;
1225 			// we remove the "Flex:" prefix, if it is present
1226 			if (prefixIs(label, from_ascii("Flex:")))
1227 				label = label.substr(5);
1228 			addWithStatusCheck(MenuItem(MenuItem::Command,
1229 				toqstr(translateIfPossible(label)),
1230 				FuncRequest(LFUN_FLEX_INSERT, Lexer::quoteString(label))));
1231 		}
1232 	}
1233 	// FIXME This is a little clunky.
1234 	if (items_.empty() && type == InsetLayout::CUSTOM && !buf->hasReadonlyFlag())
1235 		add(MenuItem(MenuItem::Help, qt_("No Custom Insets Defined!")));
1236 }
1237 
1238 
1239 // Threshold before we stop displaying sub-items alongside items
1240 // (for display purposes). Ideally this should fit on a screen.
1241 size_t const max_number_of_items = 30;
1242 // Size limit for the menu. This is for performance purposes,
1243 // because qt already displays a scrollable menu when necessary.
1244 // Ideally this should be the menu size from which scrollable
1245 // menus become unpractical.
1246 size_t const menu_size_limit = 80;
1247 
expandToc2(Toc const & toc_list,size_t from,size_t to,int depth,string toc_type)1248 void MenuDefinition::expandToc2(Toc const & toc_list,
1249                                 size_t from, size_t to, int depth,
1250                                 string toc_type)
1251 {
1252 	int shortcut_count = 0;
1253 
1254 	// check whether depth is smaller than the smallest depth in toc.
1255 	int min_depth = 1000;
1256 	for (size_t i = from; i < to; ++i)
1257 		min_depth = min(min_depth, toc_list[i].depth());
1258 	if (min_depth > depth)
1259 		depth = min_depth;
1260 
1261 	if (to - from <= max_number_of_items) {
1262 		for (size_t i = from; i < to; ++i) {
1263 			QString label(4 * max(0, toc_list[i].depth() - depth), ' ');
1264 			label += limitStringLength(toc_list[i].asString());
1265 			if (toc_list[i].depth() == depth) {
1266 				label += '|';
1267 			    if (shortcut_count < 9) {
1268 					if (label.contains(QString::number(shortcut_count + 1)))
1269 						label += QString::number(++shortcut_count);
1270 				}
1271 			}
1272 			add(MenuItem(MenuItem::Command, label,
1273 					    FuncRequest(toc_list[i].action())));
1274 			// separator after the menu heading
1275 			if (toc_list[i].depth() < depth)
1276 				add(MenuItem(MenuItem::Separator));
1277 		}
1278 	} else {
1279 		size_t pos = from;
1280 		size_t size = 1;
1281 		while (pos < to) {
1282 			size_t new_pos = pos + 1;
1283 			while (new_pos < to && toc_list[new_pos].depth() > depth)
1284 				++new_pos;
1285 
1286 			QString label(4 * max(0, toc_list[pos].depth() - depth), ' ');
1287 			label += limitStringLength(toc_list[pos].asString());
1288 			if (toc_list[pos].depth() == depth) {
1289 				label += '|';
1290 			    if (shortcut_count < 9) {
1291 					if (label.contains(QString::number(shortcut_count + 1)))
1292 						label += QString::number(++shortcut_count);
1293 				}
1294 			}
1295 			if (size >= menu_size_limit) {
1296 				FuncRequest f(LFUN_DIALOG_SHOW, "toc " + toc_type);
1297 				add(MenuItem(MenuItem::Command, "...", f));
1298 				break;
1299 			}
1300 			if (new_pos == pos + 1) {
1301 				add(MenuItem(MenuItem::Command,
1302 						    label, FuncRequest(toc_list[pos].action())));
1303 			} else {
1304 				MenuDefinition sub;
1305 				sub.expandToc2(toc_list, pos, new_pos, depth + 1, toc_type);
1306 				MenuItem item(MenuItem::Submenu, label);
1307 				item.setSubmenu(sub);
1308 				add(item);
1309 			}
1310 			++size;
1311 			pos = new_pos;
1312 		}
1313 	}
1314 }
1315 
1316 
expandToc(Buffer const * buf)1317 void MenuDefinition::expandToc(Buffer const * buf)
1318 {
1319 	// To make things very cleanly, we would have to pass buf to
1320 	// all MenuItem constructors and to expandToc2. However, we
1321 	// know that all the entries in a TOC will be have status_ ==
1322 	// OK, so we avoid this unnecessary overhead (JMarc)
1323 	if (!buf) {
1324 		add(MenuItem(MenuItem::Info, qt_("(No Document Open)")));
1325 		return;
1326 	}
1327 	// Add an entry for the master doc if this is a child doc
1328 	Buffer const * const master = buf->masterBuffer();
1329 	if (buf != master) {
1330 		ParIterator const pit = par_iterator_begin(master->inset());
1331 		string const arg = convert<string>(pit->id());
1332 		FuncRequest f(LFUN_PARAGRAPH_GOTO, arg);
1333 		add(MenuItem(MenuItem::Command, qt_("Master Document"), f));
1334 	}
1335 
1336 	MenuDefinition other_lists;
1337 	// In the navigation menu, only add tocs from this document
1338 	TocBackend const & backend = buf->tocBackend();
1339 	TocList const & toc_list = backend.tocs();
1340 	for (pair<string, shared_ptr<Toc>> const & toc : toc_list) {
1341 		// Handle table of contents later
1342 		if (toc.first == "tableofcontents" || toc.second->empty())
1343 			continue;
1344 		MenuDefinition submenu;
1345 		submenu.expandTocSubmenu(toc.first, *toc.second);
1346 		docstring const toc_name = backend.outlinerName(toc.first);
1347 		MenuItem item(MenuItem::Submenu, toqstr(toc_name));
1348 		item.setSubmenu(submenu);
1349 		// deserves to be in the main menu?
1350 		if (!TocBackend::isOther(toc.first))
1351 			add(item);
1352 		else
1353 			other_lists.add(item);
1354 	}
1355 	if (!other_lists.empty()) {
1356 		MenuItem item(MenuItem::Submenu, qt_("Other Lists"));
1357 		item.setSubmenu(other_lists);
1358 		add(item);
1359 	}
1360 	// Handle normal TOC
1361 	add(MenuItem(MenuItem::Separator));
1362 	TocList::const_iterator cit = toc_list.find("tableofcontents");
1363 	if (cit == toc_list.end())
1364 		LYXERR(Debug::GUI, "No table of contents.");
1365 	else {
1366 		if (!cit->second->empty())
1367 			expandToc2(*cit->second, 0, cit->second->size(), 0,
1368 			           "tableofcontents");
1369 		else
1370 			add(MenuItem(MenuItem::Info, qt_("(Empty Table of Contents)")));
1371 	}
1372 }
1373 
1374 
expandTocSubmenu(std::string const & type,Toc const & toc)1375 void MenuDefinition::expandTocSubmenu(std::string const & type, Toc const & toc)
1376 {
1377 	// "Open outliner..." entry
1378 	FuncRequest f(LFUN_DIALOG_SHOW, "toc " + type);
1379 	add(MenuItem(MenuItem::Command, qt_("Open Outliner..."), f));
1380 	add(MenuItem(MenuItem::Separator));
1381 	// add entries
1382 	expandToc2(toc, 0, toc.size(), 0, type);
1383 }
1384 
1385 
expandPasteRecent(Buffer const * buf)1386 void MenuDefinition::expandPasteRecent(Buffer const * buf)
1387 {
1388 	docstring_list const sel = cap::availableSelections(buf);
1389 
1390 	docstring_list::const_iterator cit = sel.begin();
1391 	docstring_list::const_iterator end = sel.end();
1392 
1393 	for (unsigned int index = 0; cit != end; ++cit, ++index) {
1394 		add(MenuItem(MenuItem::Command, toqstr(*cit) + '|',
1395 				    FuncRequest(LFUN_PASTE, convert<string>(index))));
1396 	}
1397 }
1398 
1399 
expandToolbars()1400 void MenuDefinition::expandToolbars()
1401 {
1402 	MenuDefinition other_lists;
1403 	// extracts the toolbars from the backend
1404 	Toolbars::Infos::const_iterator cit = guiApp->toolbars().begin();
1405 	Toolbars::Infos::const_iterator end = guiApp->toolbars().end();
1406 	for (; cit != end; ++cit) {
1407 		MenuItem const item(MenuItem::Command, toqstr(cit->gui_name),
1408 				FuncRequest(LFUN_TOOLBAR_TOGGLE, cit->name));
1409 		if (guiApp->toolbars().isMainToolbar(cit->name))
1410 			add(item);
1411 		else
1412 			other_lists.add(item);
1413 	}
1414 
1415 	if (!other_lists.empty()) {
1416 		MenuItem item(MenuItem::Submenu, qt_("Other Toolbars"));
1417 		item.setSubmenu(other_lists);
1418 		add(item);
1419 	}
1420 }
1421 
1422 
expandBranches(Buffer const * buf)1423 void MenuDefinition::expandBranches(Buffer const * buf)
1424 {
1425 	if (!buf || buf->hasReadonlyFlag())
1426 		return;
1427 
1428 	BufferParams const & master_params = buf->masterBuffer()->params();
1429 	BufferParams const & params = buf->params();
1430 	if (params.branchlist().empty() && master_params.branchlist().empty() ) {
1431 		add(MenuItem(MenuItem::Help, qt_("No Branches Set for Document!")));
1432 		return;
1433 	}
1434 
1435 	BranchList::const_iterator cit = master_params.branchlist().begin();
1436 	BranchList::const_iterator end = master_params.branchlist().end();
1437 
1438 	for (int ii = 1; cit != end; ++cit, ++ii) {
1439 		docstring label = cit->branch();
1440 		if (ii < 10) {
1441 			label = convert<docstring>(ii) + ". " + label
1442 				+ char_type('|') + convert<docstring>(ii);
1443 		}
1444 		addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
1445 				    FuncRequest(LFUN_BRANCH_INSERT,
1446 						cit->branch())));
1447 	}
1448 
1449 	if (buf == buf->masterBuffer())
1450 		return;
1451 
1452 	MenuDefinition child_branches;
1453 
1454 	BranchList::const_iterator ccit = params.branchlist().begin();
1455 	BranchList::const_iterator cend = params.branchlist().end();
1456 
1457 	for (int ii = 1; ccit != cend; ++ccit, ++ii) {
1458 		docstring label = ccit->branch();
1459 		if (ii < 10) {
1460 			label = convert<docstring>(ii) + ". " + label
1461 				+ char_type('|') + convert<docstring>(ii);
1462 		} else
1463 			label += char_type('|');
1464 		child_branches.addWithStatusCheck(MenuItem(MenuItem::Command,
1465 				    toqstr(label),
1466 				    FuncRequest(LFUN_BRANCH_INSERT,
1467 						ccit->branch())));
1468 	}
1469 
1470 	if (!child_branches.empty()) {
1471 		MenuItem item(MenuItem::Submenu, qt_("Child Document"));
1472 		item.setSubmenu(child_branches);
1473 		add(item);
1474 	}
1475 }
1476 
1477 
expandIndices(Buffer const * buf,bool listof)1478 void MenuDefinition::expandIndices(Buffer const * buf, bool listof)
1479 {
1480 	if (!buf)
1481 		return;
1482 
1483 	BufferParams const & params = buf->masterBuffer()->params();
1484 	if (!params.use_indices) {
1485 		if (listof)
1486 			addWithStatusCheck(MenuItem(MenuItem::Command,
1487 					   qt_("Index List|I"),
1488 					   FuncRequest(LFUN_INDEX_PRINT,
1489 						  from_ascii("idx"))));
1490 		else
1491 			addWithStatusCheck(MenuItem(MenuItem::Command,
1492 					   qt_("Index Entry|d"),
1493 					   FuncRequest(LFUN_INDEX_INSERT,
1494 						  from_ascii("idx"))));
1495 		return;
1496 	}
1497 
1498 	if (params.indiceslist().empty())
1499 		return;
1500 
1501 	IndicesList::const_iterator cit = params.indiceslist().begin();
1502 	IndicesList::const_iterator end = params.indiceslist().end();
1503 
1504 	for (int ii = 1; cit != end; ++cit, ++ii) {
1505 		if (listof) {
1506 			docstring const label =
1507 				bformat(_("Index: %1$s"), cit->index());
1508 			addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
1509 					   FuncRequest(LFUN_INDEX_PRINT, cit->shortcut())));
1510 		} else {
1511 			docstring const label =
1512 				bformat(_("Index Entry (%1$s)"), cit->index());
1513 			addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
1514 					   FuncRequest(LFUN_INDEX_INSERT, cit->shortcut())));
1515 		}
1516 	}
1517 }
1518 
1519 
expandIndicesContext(Buffer const * buf,bool listof)1520 void MenuDefinition::expandIndicesContext(Buffer const * buf, bool listof)
1521 {
1522 	if (!buf)
1523 		return;
1524 
1525 	BufferParams const & params = buf->masterBuffer()->params();
1526 	if (!params.use_indices || params.indiceslist().empty())
1527 		return;
1528 
1529 	IndicesList::const_iterator cit = params.indiceslist().begin();
1530 	IndicesList::const_iterator end = params.indiceslist().end();
1531 
1532 	for (int ii = 1; cit != end; ++cit, ++ii) {
1533 		if (listof) {
1534 			InsetCommandParams p(INDEX_PRINT_CODE);
1535 			p["type"] = cit->shortcut();
1536 			string const data = InsetCommand::params2string(p);
1537 			addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(cit->index()),
1538 					   FuncRequest(LFUN_INSET_MODIFY, data)));
1539 		} else {
1540 			docstring const label =
1541 					bformat(_("Index Entry (%1$s)"), cit->index());
1542 			addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
1543 					   FuncRequest(LFUN_INSET_MODIFY,
1544 						  from_ascii("changetype ") + cit->shortcut())));
1545 		}
1546 	}
1547 }
1548 
1549 
expandCiteStyles(BufferView const * bv)1550 void MenuDefinition::expandCiteStyles(BufferView const * bv)
1551 {
1552 	if (!bv)
1553 		return;
1554 
1555 	Inset const * inset = bv->cursor().nextInset();
1556 	if (!inset || inset->lyxCode() != CITE_CODE) {
1557 		add(MenuItem(MenuItem::Command,
1558 				    qt_("No Citation in Scope!"),
1559 				    FuncRequest(LFUN_NOACTION)));
1560 		return;
1561 	}
1562 	InsetCitation const * citinset =
1563 				static_cast<InsetCitation const *>(inset);
1564 
1565 	Buffer const * buf = &bv->buffer();
1566 	BufferParams const & bp = buf->masterParams();
1567 	string const cmd = citinset->params().getCmdName();
1568 
1569 	docstring const & key = citinset->getParam("key");
1570 	if (key.empty()) {
1571 		add(MenuItem(MenuItem::Command,
1572 				    qt_("No citations selected!"),
1573 				    FuncRequest(LFUN_NOACTION)));
1574 		return;
1575 	}
1576 
1577 	bool const force = isUpperCase(cmd[0]);
1578 	bool const star = suffixIs(cmd, '*');
1579 
1580 	vector<docstring> const keys = getVectorFromString(key);
1581 
1582 	vector<CitationStyle> const citeStyleList = bp.citeStyles();
1583 
1584 	CitationStyle cs = citinset->getCitationStyle(bp, cmd, citeStyleList);
1585 	bool const qualified = cs.hasQualifiedList
1586 		&& (keys.size() > 1
1587 		    || !citinset->getParam("pretextlist").empty()
1588 		    || !citinset->getParam("posttextlist").empty());
1589 	std::map<docstring, docstring> pres =
1590 		citinset->getQualifiedLists(citinset->getParam("pretextlist"));
1591 	std::map<docstring, docstring> posts =
1592 		citinset->getQualifiedLists(citinset->getParam("posttextlist"));
1593 
1594 	CiteItem ci;
1595 	ci.textBefore = citinset->getParam("before");
1596 	ci.textAfter = citinset->getParam("after");
1597 	ci.forceUpperCase = force;
1598 	ci.Starred = star;
1599 	ci.context = CiteItem::Dialog;
1600 	ci.max_size = 40;
1601 	ci.isQualified = qualified;
1602 	ci.pretexts = pres;
1603 	ci.posttexts = posts;
1604 	BiblioInfo::CiteStringMap citeStrings =
1605 		buf->masterBibInfo().getCiteStrings(keys, citeStyleList, bv->buffer(), ci);
1606 
1607 	BiblioInfo::CiteStringMap::const_iterator cit = citeStrings.begin();
1608 	BiblioInfo::CiteStringMap::const_iterator end = citeStrings.end();
1609 
1610 	for (int ii = 1; cit != end; ++cit, ++ii) {
1611 		docstring label = cit->second;
1612 		CitationStyle cs = citeStyleList[ii - 1];
1613 		cs.forceUpperCase &= force;
1614 		cs.hasStarredVersion &= star;
1615 		addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(label),
1616 				    FuncRequest(LFUN_INSET_MODIFY,
1617 						"changetype " + from_utf8(citationStyleToString(cs)))));
1618 	}
1619 
1620 	if (cs.hasStarredVersion) {
1621 		docstring starred = _("All authors|h");
1622 		// Check if we have a custom string/tooltip for the starred version
1623 		if (!cs.stardesc.empty()) {
1624 			string val =
1625 				bp.documentClass().getCiteMacro(buf->params().citeEngineType(), cs.stardesc);
1626 			if (!val.empty())
1627 				starred = translateIfPossible(from_utf8(val));
1628 			// Transform qt-style accelerators to menu-style
1629 			int const amps = count_char(starred, '&');
1630 			if (amps > 0) {
1631 				if (amps > 1)
1632 					starred = subst(starred, from_ascii("&&"), from_ascii("<:amp:>"));
1633 				size_t n = starred.find('&');
1634 				char_type accel = char_type();
1635 				if (n != docstring::npos && n < starred.size() - 1)
1636 					accel = starred[n + 1];
1637 				starred = subst(starred, from_ascii("&"), from_ascii(""));
1638 				if (amps > 1)
1639 					starred = subst(starred, from_ascii("<:amp:>"), from_ascii("&&"));
1640 				if (accel != char_type())
1641 					starred = starred + '|' + accel;
1642 			}
1643 		}
1644 		add(MenuItem(MenuItem::Separator));
1645 		addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(starred),
1646 				    FuncRequest(LFUN_INSET_MODIFY, "toggleparam star")));
1647 	}
1648 
1649 	if (cs.forceUpperCase) {
1650 		if (!cs.hasStarredVersion)
1651 			add(MenuItem(MenuItem::Separator));
1652 		addWithStatusCheck(MenuItem(MenuItem::Command, toqstr(_("Force upper case|u")),
1653 				    FuncRequest(LFUN_INSET_MODIFY, "toggleparam casing")));
1654 	}
1655 }
1656 
1657 
expandArguments(BufferView const * bv,bool switcharg)1658 void MenuDefinition::expandArguments(BufferView const * bv, bool switcharg)
1659 {
1660 	if (!bv)
1661 		return;
1662 
1663 	if (!bv->cursor().inTexted())
1664 		return;
1665 
1666 	Inset const * inset = &bv->cursor().inset();
1667 	Layout::LaTeXArgMap args = bv->cursor().paragraph().layout().args();
1668 	if (inset && args.empty())
1669 		args = inset->getLayout().args();
1670 	if (args.empty() || (switcharg && args.size() == 1))
1671 		return;
1672 	Layout::LaTeXArgMap::const_iterator lait = args.begin();
1673 	Layout::LaTeXArgMap::const_iterator const laend = args.end();
1674 	for (; lait != laend; ++lait) {
1675 		Layout::latexarg arg = (*lait).second;
1676 		docstring str = arg.menustring.empty()? arg.labelstring : arg.menustring;
1677 		QString item = toqstr(translateIfPossible(str));
1678 		if (switcharg)
1679 			add(MenuItem(MenuItem::Command, item,
1680 				     FuncRequest(LFUN_INSET_MODIFY,
1681 						 from_ascii("changetype ")
1682 						 + from_ascii((*lait).first))));
1683 		else
1684 			add(MenuItem(MenuItem::Command, item,
1685 				     FuncRequest(LFUN_ARGUMENT_INSERT,
1686 						 from_ascii((*lait).first))));
1687 	}
1688 }
1689 
1690 
expandCaptions(Buffer const * buf,bool switchcap)1691 void MenuDefinition::expandCaptions(Buffer const * buf, bool switchcap)
1692 {
1693 	if (!buf)
1694 		return;
1695 
1696 	DocumentClass const & dc = buf->params().documentClass();
1697 	vector< pair<docstring, FuncRequest> > caps;
1698 	for (pair<docstring, InsetLayout> const & il : dc.insetLayouts()) {
1699 		docstring instype;
1700 		docstring const type = split(il.first, instype, ':');
1701 		if (instype == from_ascii("Caption")) {
1702 			// skip forbidden caption types
1703 			FuncRequest const cmd = switchcap
1704 				? FuncRequest(LFUN_INSET_MODIFY, from_ascii("changetype ") + type)
1705 				: FuncRequest(LFUN_CAPTION_INSERT, type);
1706 			if (getStatus(cmd).enabled())
1707 				caps.push_back(make_pair(type, cmd));
1708 		}
1709 	}
1710 
1711 	if (caps.empty() || (switchcap && caps.size() == 1))
1712 		return;
1713 	if (caps.size() == 1) {
1714 		add(MenuItem(MenuItem::Command, qt_("Caption"), caps.front().second));
1715 		return;
1716 	}
1717 
1718 	MenuDefinition captions;
1719 	for (pair<docstring, FuncRequest> const & cap : caps) {
1720 		docstring const type = cap.first;
1721 		docstring const trtype = translateIfPossible(type);
1722 		docstring const cmitem = bformat(_("Caption (%1$s)"), trtype);
1723 		if (switchcap)
1724 			add(MenuItem(MenuItem::Command, toqstr(cmitem), cap.second));
1725 		else
1726 			captions.add(MenuItem(MenuItem::Command, toqstr(trtype), cap.second));
1727 	}
1728 	if (!captions.empty()) {
1729 		MenuItem item(MenuItem::Submenu, qt_("Caption"));
1730 		item.setSubmenu(captions);
1731 		add(item);
1732 	}
1733 }
1734 
1735 
expandQuotes(BufferView const * bv)1736 void MenuDefinition::expandQuotes(BufferView const * bv)
1737 {
1738 	if (!bv)
1739 		return;
1740 
1741 	if (!bv->cursor().inTexted())
1742 		return;
1743 
1744 	Inset const * inset = bv->cursor().nextInset();
1745 	if (!inset || inset->lyxCode() != QUOTE_CODE) {
1746 		add(MenuItem(MenuItem::Command,
1747 				    qt_("No Quote in Scope!"),
1748 				    FuncRequest(LFUN_NOACTION)));
1749 		return;
1750 	}
1751 	InsetQuotes const * qinset =
1752 		static_cast<InsetQuotes const *>(inset);
1753 
1754 	map<string, docstring> styles = quoteparams.getTypes();
1755 	string const qtype = qinset->getType();
1756 
1757 	map<string, docstring>::const_iterator qq = styles.begin();
1758 	map<string, docstring>::const_iterator end = styles.end();
1759 
1760 	MenuDefinition aqs;
1761 
1762 	BufferParams const & bp = bv->buffer().masterBuffer()->params();
1763 
1764 	// The global setting
1765 	InsetQuotesParams::QuoteStyle globalqs = bp.quotes_style;
1766 	char const globalqsc = quoteparams.getStyleChar(globalqs);
1767 
1768 	// The current language's default
1769 	InsetQuotesParams::QuoteStyle langdefqs =
1770 		bp.getQuoteStyle(bv->cursor().current_font.language()->quoteStyle());
1771 	char const langqs = quoteparams.getStyleChar(langdefqs);
1772 
1773 	bool main_global_qs = false;
1774 	bool main_langdef_qs = false;
1775 	bool main_dynamic_qs = false;
1776 	docstring const subcmd = from_ascii("changetype ");
1777 	docstring const wildcards = from_ascii("..");
1778 	// Add the items
1779 	// First the top level menu (all glyphs of the current style) ...
1780 	// Begin with dynamic (if they are current style),
1781 	if (qtype[0] == 'x') {
1782 		FuncRequest cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + from_ascii("xld"));
1783 		docstring desc = bformat(_("%1$s (dynamic)"),
1784 			quoteparams.getShortGuiLabel(globalqsc + from_ascii("ld")));
1785 		add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1786 		cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + from_ascii("xls"));
1787 		desc = bformat(_("%1$s (dynamic)"),
1788 			quoteparams.getShortGuiLabel(globalqsc + from_ascii("ls")));
1789 		add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1790 		cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + from_ascii("xrd"));
1791 		desc = bformat(_("%1$s (dynamic)"),
1792 			quoteparams.getShortGuiLabel(globalqsc + from_ascii("rd")));
1793 		add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1794 		cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + from_ascii("xrs"));
1795 		desc = bformat(_("%1$s (dynamic)"),
1796 			quoteparams.getShortGuiLabel(globalqsc + from_ascii("rs")));
1797 		add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1798 		main_dynamic_qs = true;
1799 	}
1800 	// now traverse through the static styles ...
1801 	for (; qq != end; ++qq) {
1802 		docstring const style = from_ascii(qq->first);
1803 		bool langdef = (style[0] == langqs);
1804 		bool globaldef = (style[0] == globalqsc);
1805 
1806 		if (prefixIs(style, qtype[0])) {
1807 			FuncRequest cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + style);
1808 			docstring const desc = quoteparams.getShortGuiLabel(style);
1809 			add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1810 			main_global_qs = globaldef;
1811 			main_langdef_qs = langdef;
1812 		}
1813 		else if (!langdef && !globaldef && suffixIs(style, from_ascii("ld"))) {
1814 			docstring const desc =
1815 				quoteparams.getGuiLabel(quoteparams.getQuoteStyle(to_ascii(style)));
1816 			FuncRequest cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + style[0] + "..");
1817 			aqs.add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1818 		}
1819 	}
1820 
1821 	add(MenuItem(MenuItem::Separator));
1822 
1823 	bool display_static = false;
1824 	// ... then potentially items to reset to the defaults and to dynamic style ...
1825 	if (!main_dynamic_qs && globalqsc != 'x') {
1826 		FuncRequest cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + 'x' + wildcards);
1827 		docstring const desc = bformat(_("Use dynamic quotes (%1$s)|d"),
1828 						quoteparams.getGuiLabel(globalqs));
1829 		add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1830 		display_static = true;
1831 	}
1832 	if (!main_global_qs && langdefqs != globalqs) {
1833 		docstring const variant = main_dynamic_qs ? _("dynamic[[Quotes]]") : _("static[[Quotes]]");
1834 		FuncRequest cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + globalqsc + wildcards);
1835 		docstring const desc = bformat(_("Reset to document default (%1$s, %2$s)|o"),
1836 						quoteparams.getGuiLabel(globalqs), variant);
1837 		add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1838 	}
1839 	if (!main_langdef_qs) {
1840 		FuncRequest cmd = FuncRequest(LFUN_INSET_MODIFY, subcmd + globalqsc + wildcards);
1841 		docstring const desc = (main_dynamic_qs || display_static)
1842 					? bformat(_("Reset to language default (%1$s, %2$s)|l"),
1843 						  quoteparams.getGuiLabel(langdefqs), _("static[[Quotes]]"))
1844 					: bformat(_("Reset to language default (%1$s)|l"),
1845 						  quoteparams.getGuiLabel(langdefqs));
1846 		add(MenuItem(MenuItem::Command, toqstr(desc), cmd));
1847 	}
1848 
1849 	add(MenuItem(MenuItem::Separator));
1850 
1851 	// ... and a subitem with the rest
1852 	MenuItem item(MenuItem::Submenu, qt_("Change Style|y"));
1853 	item.setSubmenu(aqs);
1854 	add(item);
1855 }
1856 
1857 
expandEnvironmentSeparators(BufferView const * bv,bool contextmenu)1858 void MenuDefinition::expandEnvironmentSeparators(BufferView const * bv,
1859 						 bool contextmenu)
1860 {
1861 	if (!bv)
1862 		return;
1863 	Text const * text = bv->cursor().text();
1864 	// no paragraphs and no separators exist in math
1865 	if (!text)
1866 		return;
1867 
1868 	pit_type pit = bv->cursor().selBegin().pit();
1869 	Paragraph const & par = text->getPar(pit);
1870 	docstring const curlayout = par.layout().name();
1871 	docstring outerlayout;
1872 	docstring prevlayout;
1873 	depth_type current_depth = par.params().depth();
1874 	// check if we have an environment in our scope
1875 	Paragraph cpar = par;
1876 	while (true) {
1877 		if (pit == 0)
1878 			break;
1879 		--pit;
1880 		cpar = text->getPar(pit);
1881 		if (cpar.layout().isEnvironment() && prevlayout.empty()
1882 		    && cpar.params().depth() <= current_depth)
1883 				prevlayout = cpar.layout().name();
1884 		if (cpar.params().depth() < current_depth
1885 		    && cpar.layout().isEnvironment()) {
1886 				outerlayout = cpar.layout().name();
1887 				current_depth = cpar.params().depth();
1888 		}
1889 		if (cpar.params().depth() == 0)
1890 			break;
1891 	}
1892 	if (par.layout().isEnvironment()) {
1893 		docstring label = contextmenu ?
1894 					bformat(_("Insert Separated %1$s Above"),
1895 						translateIfPossible(curlayout)) :
1896 					bformat(_("Separated %1$s Above"),
1897 						translateIfPossible(curlayout));
1898 		add(MenuItem(MenuItem::Command, toqstr(label),
1899 			     FuncRequest(LFUN_ENVIRONMENT_SPLIT,
1900 					 from_ascii("before"))));
1901 		label = contextmenu ?
1902 				bformat(_("Insert Separated %1$s Below"),
1903 					translateIfPossible(curlayout)):
1904 				bformat(_("Separated %1$s Below"),
1905 					translateIfPossible(curlayout));
1906 		// We use command-alternatives here since this is how the binding is defined
1907 		// (otherwise, the binding is not displayed in the menu)
1908 		if (getStatus(FuncRequest(LFUN_ENVIRONMENT_SPLIT)).enabled())
1909 			add(MenuItem(MenuItem::Command, toqstr(label),
1910 				     FuncRequest(LFUN_COMMAND_ALTERNATIVES,
1911 						 from_ascii("environment-split ; environment-split previous"))));
1912 	}
1913 	else if (!prevlayout.empty()) {
1914 		docstring const label = contextmenu ?
1915 			bformat(_("Insert Separated %1$s Below"),
1916 				translateIfPossible(prevlayout)) :
1917 			bformat(_("Separated %1$s Below"),
1918 				translateIfPossible(prevlayout));
1919 		// We use command-alternatives here since this is how the binding is defined
1920 		// (otherwise, the binding is not displayed in the menu)
1921 		if (getStatus(FuncRequest(LFUN_ENVIRONMENT_SPLIT)).enabled())
1922 			add(MenuItem(MenuItem::Command, toqstr(label),
1923 				     FuncRequest(LFUN_COMMAND_ALTERNATIVES,
1924 						 from_ascii("environment-split ; environment-split previous"))));
1925 	}
1926 	if (!outerlayout.empty()) {
1927 		docstring label;
1928 		if (contextmenu) {
1929 			label = (outerlayout == curlayout) ?
1930 				bformat(_("Insert Separated Outer %1$s Below"),
1931 					translateIfPossible(outerlayout)) :
1932 				bformat(_("Insert Separated %1$s Below"),
1933 					translateIfPossible(outerlayout));
1934 		} else {
1935 			label = (outerlayout == curlayout) ?
1936 				bformat(_("Separated Outer %1$s Below"),
1937 					translateIfPossible(outerlayout)) :
1938 				bformat(_("Separated %1$s Below"),
1939 					translateIfPossible(outerlayout));
1940 		}
1941 		add(MenuItem(MenuItem::Command, toqstr(label),
1942 			     FuncRequest(LFUN_ENVIRONMENT_SPLIT,
1943 					 from_ascii("outer"))));
1944 	}
1945 }
1946 
1947 } // namespace
1948 
1949 
1950 /////////////////////////////////////////////////////////////////////
1951 // Menu::Impl definition and implementation
1952 /////////////////////////////////////////////////////////////////////
1953 
1954 struct Menu::Impl
1955 {
1956 	/// populates the menu or one of its submenu
1957 	/// This is used as a recursive function
1958 	void populate(QMenu * qMenu, MenuDefinition const & menu);
1959 
1960 	/// Only needed for top level menus.
1961 	MenuDefinition * top_level_menu;
1962 	/// our owning view
1963 	GuiView * view;
1964 	/// the name of this menu
1965 	QString name;
1966 };
1967 
1968 
1969 
1970 /// Get a MenuDefinition item label from the menu backend
label(MenuItem const & mi)1971 static QString label(MenuItem const & mi)
1972 {
1973 	QString label = mi.label();
1974 	label.replace("&", "&&");
1975 
1976 	QString shortcut = mi.shortcut();
1977 	if (!shortcut.isEmpty()) {
1978 		int pos = label.indexOf(shortcut);
1979 		if (pos != -1)
1980 			//label.insert(pos, 1, char_type('&'));
1981 			label.replace(pos, 0, "&");
1982 	}
1983 
1984 	QString const binding = mi.binding();
1985 	if (!binding.isEmpty())
1986 		label += '\t' + binding;
1987 
1988 	return label;
1989 }
1990 
populate(QMenu * qMenu,MenuDefinition const & menu)1991 void Menu::Impl::populate(QMenu * qMenu, MenuDefinition const & menu)
1992 {
1993 	LYXERR(Debug::GUI, "populating menu " << menu.name());
1994 	if (menu.empty()) {
1995 		LYXERR(Debug::GUI, "\tERROR: empty menu " << menu.name());
1996 		return;
1997 	}
1998 	LYXERR(Debug::GUI, " *****  menu entries " << menu.size());
1999 	for (MenuItem const & m : menu)
2000 		switch (m.kind()) {
2001 		case MenuItem::Separator:
2002 			qMenu->addSeparator();
2003 			break;
2004 		case MenuItem::Submenu: {
2005 			QMenu * subMenu = qMenu->addMenu(label(m));
2006 			populate(subMenu, m.submenu());
2007 			subMenu->setEnabled(!subMenu->isEmpty());
2008 			break;
2009 		}
2010 		case MenuItem::Command:
2011 		default:
2012 			// FIXME: A previous comment assured that MenuItem::Command was the
2013 			// only possible case in practice, but this is wrong.  It would be
2014 			// good to document which cases are actually treated here.
2015 			qMenu->addAction(new Action(m.func(), QIcon(), label(m),
2016 			                            m.tooltip(), qMenu));
2017 			break;
2018 		}
2019 }
2020 
2021 #if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && (QT_VERSION >= 0x040600)
2022 class AlwaysMnemonicStyle : public QProxyStyle {
2023 public:
styleHint(StyleHint hint,const QStyleOption * opt=0,const QWidget * widget=0,QStyleHintReturn * returnData=0) const2024 	int styleHint(StyleHint hint, const QStyleOption *opt = 0, const QWidget *widget = 0,
2025 		QStyleHintReturn *returnData = 0) const
2026 	{
2027 		if (hint == QStyle::SH_UnderlineShortcut)
2028 			return 1;
2029 		return QProxyStyle::styleHint(hint, opt, widget, returnData);
2030 	}
2031 };
2032 #endif
2033 
2034 /////////////////////////////////////////////////////////////////////
2035 // Menu implementation
2036 /////////////////////////////////////////////////////////////////////
2037 
Menu(GuiView * gv,QString const & name,bool top_level,bool keyboard)2038 Menu::Menu(GuiView * gv, QString const & name, bool top_level, bool keyboard)
2039 : QMenu(gv), d(new Menu::Impl)
2040 {
2041 #if (defined(Q_OS_WIN) || defined(Q_CYGWIN_WIN)) && (QT_VERSION >= 0x040600)
2042 	if (keyboard)
2043 		setStyle(new AlwaysMnemonicStyle);
2044 #else
2045 	(void) keyboard;
2046 #endif
2047 	d->top_level_menu = top_level? new MenuDefinition : 0;
2048 	d->view = gv;
2049 	d->name = name;
2050 	setTitle(name);
2051 	if (d->top_level_menu)
2052 		connect(this, SIGNAL(aboutToShow()), this, SLOT(updateView()));
2053 }
2054 
2055 
~Menu()2056 Menu::~Menu()
2057 {
2058 	delete d->top_level_menu;
2059 	delete d;
2060 }
2061 
2062 
updateView()2063 void Menu::updateView()
2064 {
2065 	guiApp->menus().updateMenu(this);
2066 }
2067 
2068 
clear()2069 void Menu::clear()
2070 {
2071 	QList<QAction *> items = actions();
2072 	for (int i = 0; i != items.size(); ++i) {
2073 		// QAction::menu() returns 0 if there's no submenu.
2074 		delete items.at(i)->menu();
2075 	}
2076 	QMenu::clear();
2077 }
2078 
2079 
2080 /////////////////////////////////////////////////////////////////////
2081 // Menus::Impl definition and implementation
2082 /////////////////////////////////////////////////////////////////////
2083 
2084 struct Menus::Impl {
2085 	///
2086 	bool hasMenu(QString const &) const;
2087 	///
2088 	MenuDefinition & getMenu(QString const &);
2089 	///
2090 	MenuDefinition const & getMenu(QString const &) const;
2091 
2092 	/// Expands some special entries of the menu
2093 	/** The entries with the following kind are expanded to a
2094 	    sequence of Command MenuItems: Lastfiles, Documents,
2095 	    ViewFormats, ExportFormats, UpdateFormats, Branches,
2096 	    Indices, Arguments, SwitchArguments, Captions, SwitchCaptions,
2097 	    EnvironmentSeparators
2098 	*/
2099 	void expand(MenuDefinition const & frommenu, MenuDefinition & tomenu,
2100 		BufferView const *) const;
2101 
2102 	/// Initialize specific MACOS X menubar
2103 	void macxMenuBarInit(QMenuBar * qmb);
2104 
2105 	/// Mac special menu.
2106 	/** This defines a menu whose entries list the FuncRequests
2107 	    that will be removed by expand() in other menus. This is
2108 	    used by the Qt/Mac code.
2109 
2110 	    NOTE: Qt does not remove the menu items when clearing a QMenuBar,
2111 	    such that the items will keep accessing the FuncRequests in
2112 	    the MenuDefinition. While Menus::Impl might be recreated,
2113 	    we keep mac_special_menu_ in memory by making it static.
2114 	*/
2115 	static MenuDefinition mac_special_menu_;
2116 
2117 	///
2118 	MenuList menulist_;
2119 	///
2120 	MenuDefinition menubar_;
2121 
2122 	typedef QMap<GuiView *, QHash<QString, Menu*> > NameMap;
2123 
2124 	/// name to menu for \c menu() method.
2125 	NameMap name_map_;
2126 };
2127 
2128 
2129 MenuDefinition Menus::Impl::mac_special_menu_;
2130 
2131 
2132 /*
2133   Here is what the Qt documentation says about how a menubar is chosen:
2134 
2135      1) If the window has a QMenuBar then it is used. 2) If the window
2136      is a modal then its menubar is used. If no menubar is specified
2137      then a default menubar is used (as documented below) 3) If the
2138      window has no parent then the default menubar is used (as
2139      documented below).
2140 
2141      The above 3 steps are applied all the way up the parent window
2142      chain until one of the above are satisifed. If all else fails a
2143      default menubar will be created, the default menubar on Qt/Mac is
2144      an empty menubar, however you can create a different default
2145      menubar by creating a parentless QMenuBar, the first one created
2146      will thus be designated the default menubar, and will be used
2147      whenever a default menubar is needed.
2148 
2149   Thus, for Qt/Mac, we add the menus to a free standing menubar, so
2150   that this menubar will be used also when one of LyX' dialogs has
2151   focus. (JMarc)
2152 */
macxMenuBarInit(QMenuBar * qmb)2153 void Menus::Impl::macxMenuBarInit(QMenuBar * qmb)
2154 {
2155 	/* Since Qt 4.2, the qt/mac menu code has special code for
2156 	   specifying the role of a menu entry. However, it does not
2157 	   work very well with our scheme of creating menus on demand,
2158 	   and therefore we need to put these entries in a special
2159 	   invisible menu. (JMarc)
2160 	*/
2161 
2162 	/* The entries of our special mac menu. If we add support for
2163 	 * special entries in Menus, we could imagine something
2164 	 * like
2165 	 *    SpecialItem About " "About LyX" "dialog-show aboutlyx"
2166 	 * and therefore avoid hardcoding. I am not sure it is worth
2167 	 * the hassle, though. (JMarc)
2168 	 */
2169 	struct MacMenuEntry {
2170 		FuncCode action;
2171 		char const * arg;
2172 		char const * label;
2173 		QAction::MenuRole role;
2174 	};
2175 
2176 	static const MacMenuEntry entries[] = {
2177 		{LFUN_DIALOG_SHOW, "aboutlyx", "About LyX",
2178 		 QAction::AboutRole},
2179 		{LFUN_DIALOG_SHOW, "prefs", "Preferences",
2180 		 QAction::PreferencesRole},
2181 #if !(defined(QT_MAC_USE_COCOA) || (QT_VERSION >= 0x050000))
2182 		/* This doesn't work with Cocoa. */
2183 		{LFUN_RECONFIGURE, "", "Reconfigure",
2184 		 QAction::ApplicationSpecificRole},
2185 #endif
2186 		{LFUN_LYX_QUIT, "", "Quit LyX", QAction::QuitRole}
2187 	};
2188 	const size_t num_entries = sizeof(entries) / sizeof(entries[0]);
2189 	const bool first_call = mac_special_menu_.empty();
2190 
2191 	LYXERR(Debug::GUI, "Creating Mac OS X special menu bar");
2192 	// the special menu for Menus. Fill it up only once.
2193 	if (first_call) {
2194 		for (size_t i = 0 ; i < num_entries ; ++i) {
2195 			FuncRequest const func(entries[i].action,
2196 				from_utf8(entries[i].arg));
2197 			mac_special_menu_.add(MenuItem(MenuItem::Command,
2198 				entries[i].label, func));
2199 		}
2200 	}
2201 
2202 	// add the entries to a QMenu that will eventually be empty
2203 	// and therefore invisible.
2204 	QMenu * qMenu = qmb->addMenu("special");
2205 	size_t i = 0;
2206 	for (MenuItem const & m : mac_special_menu_) {
2207 		Action * action = new Action(m.func(), QIcon(), m.label(),
2208 		                             QString(), qMenu);
2209 		action->setMenuRole(entries[i].role);
2210 		qMenu->addAction(action);
2211 		++i;
2212 	}
2213 }
2214 
2215 
expand(MenuDefinition const & frommenu,MenuDefinition & tomenu,BufferView const * bv) const2216 void Menus::Impl::expand(MenuDefinition const & frommenu,
2217 	MenuDefinition & tomenu, BufferView const * bv) const
2218 {
2219 	if (!tomenu.empty())
2220 		tomenu.clear();
2221 
2222 	for (MenuDefinition::const_iterator cit = frommenu.begin();
2223 	     cit != frommenu.end() ; ++cit) {
2224 		Buffer const * buf = bv ? &bv->buffer() : 0;
2225 		switch (cit->kind()) {
2226 		case MenuItem::Lastfiles:
2227 			tomenu.expandLastfiles();
2228 			break;
2229 
2230 		case MenuItem::Documents:
2231 			tomenu.expandDocuments();
2232 			break;
2233 
2234 		case MenuItem::Bookmarks:
2235 			tomenu.expandBookmarks();
2236 			break;
2237 
2238 		case MenuItem::ImportFormats:
2239 		case MenuItem::ViewFormats:
2240 		case MenuItem::UpdateFormats:
2241 		case MenuItem::ExportFormats:
2242 			tomenu.expandFormats(cit->kind(), buf);
2243 			break;
2244 
2245 		case MenuItem::ExportFormat: {
2246 			if (!buf)
2247 				break;
2248 			string const format = buf->params().getDefaultOutputFormat();
2249 			Format const * f = theFormats().getFormat(format);
2250 			docstring const name = f ? f->prettyname() : from_utf8(format);
2251 			docstring const label = bformat(_("Export [%1$s]|E"), name);
2252 			MenuItem item(MenuItem::Command, toqstr(label),
2253 			              FuncRequest(LFUN_BUFFER_EXPORT));
2254 			tomenu.addWithStatusCheck(item);
2255 			break;
2256 		}
2257 
2258 		case MenuItem::CharStyles:
2259 			tomenu.expandFlexInsert(buf, InsetLayout::CHARSTYLE);
2260 			break;
2261 
2262 		case MenuItem::Custom:
2263 			tomenu.expandFlexInsert(buf, InsetLayout::CUSTOM);
2264 			break;
2265 
2266 		case MenuItem::Elements:
2267 			tomenu.expandFlexInsert(buf, InsetLayout::ELEMENT);
2268 			break;
2269 
2270 		case MenuItem::FloatListInsert:
2271 			tomenu.expandFloatListInsert(buf);
2272 			break;
2273 
2274 		case MenuItem::FloatInsert:
2275 			tomenu.expandFloatInsert(buf);
2276 			break;
2277 
2278 		case MenuItem::PasteRecent:
2279 			tomenu.expandPasteRecent(buf);
2280 			break;
2281 
2282 		case MenuItem::Toolbars:
2283 			tomenu.expandToolbars();
2284 			break;
2285 
2286 		case MenuItem::Branches:
2287 			tomenu.expandBranches(buf);
2288 			break;
2289 
2290 		case MenuItem::Indices:
2291 			tomenu.expandIndices(buf);
2292 			break;
2293 
2294 		case MenuItem::IndicesContext:
2295 			tomenu.expandIndicesContext(buf);
2296 			break;
2297 
2298 		case MenuItem::IndicesLists:
2299 			tomenu.expandIndices(buf, true);
2300 			break;
2301 
2302 		case MenuItem::IndicesListsContext:
2303 			tomenu.expandIndicesContext(buf, true);
2304 			break;
2305 
2306 		case MenuItem::CiteStyles:
2307 			tomenu.expandCiteStyles(bv);
2308 			break;
2309 
2310 		case MenuItem::Toc:
2311 			tomenu.expandToc(buf);
2312 			break;
2313 
2314 		case MenuItem::GraphicsGroups:
2315 			tomenu.expandGraphicsGroups(bv);
2316 			break;
2317 
2318 		case MenuItem::SpellingSuggestions:
2319 			tomenu.expandSpellingSuggestions(bv);
2320 			break;
2321 
2322 		case MenuItem::LanguageSelector:
2323 			tomenu.expandLanguageSelector(buf);
2324 			break;
2325 
2326 		case MenuItem::Arguments:
2327 			tomenu.expandArguments(bv, false);
2328 			break;
2329 
2330 		case MenuItem::SwitchArguments:
2331 			tomenu.expandArguments(bv, true);
2332 			break;
2333 
2334 		case MenuItem::Captions:
2335 			tomenu.expandCaptions(buf, false);
2336 			break;
2337 
2338 		case MenuItem::SwitchCaptions:
2339 			tomenu.expandCaptions(buf, true);
2340 			break;
2341 
2342 		case MenuItem::EnvironmentSeparators:
2343 			tomenu.expandEnvironmentSeparators(bv);
2344 			break;
2345 
2346 		case MenuItem::EnvironmentSeparatorsContext:
2347 			tomenu.expandEnvironmentSeparators(bv, true);
2348 			break;
2349 
2350 		case MenuItem::SwitchQuotes:
2351 			tomenu.expandQuotes(bv);
2352 			break;
2353 
2354 		case MenuItem::Submenu: {
2355 			MenuItem item(*cit);
2356 			item.setSubmenu(MenuDefinition(cit->submenuname()));
2357 			expand(getMenu(cit->submenuname()), item.submenu(), bv);
2358 			tomenu.addWithStatusCheck(item);
2359 		}
2360 		break;
2361 
2362 		case MenuItem::Info:
2363 		case MenuItem::Help:
2364 		case MenuItem::Separator:
2365 			tomenu.addWithStatusCheck(*cit);
2366 			break;
2367 
2368 		case MenuItem::Command:
2369 			if (!mac_special_menu_.hasFunc(*cit->func()))
2370 				tomenu.addWithStatusCheck(*cit);
2371 		}
2372 	}
2373 
2374 	// we do not want the menu to end with a separator
2375 	if (!tomenu.empty() && tomenu.items_.back().kind() == MenuItem::Separator)
2376 		tomenu.items_.pop_back();
2377 
2378 	// Check whether the shortcuts are unique
2379 	tomenu.checkShortcuts();
2380 }
2381 
2382 
hasMenu(QString const & name) const2383 bool Menus::Impl::hasMenu(QString const & name) const
2384 {
2385 	return find_if(menulist_.begin(), menulist_.end(),
2386 		MenuNamesEqual(name)) != menulist_.end();
2387 }
2388 
2389 
getMenu(QString const & name) const2390 MenuDefinition const & Menus::Impl::getMenu(QString const & name) const
2391 {
2392 	const_iterator cit = find_if(menulist_.begin(), menulist_.end(),
2393 		MenuNamesEqual(name));
2394 	if (cit == menulist_.end()) {
2395 		LYXERR0("No submenu named " << name);
2396 		LASSERT(false, { static const MenuDefinition m; return m; });
2397 	}
2398 	return (*cit);
2399 }
2400 
2401 
getMenu(QString const & name)2402 MenuDefinition & Menus::Impl::getMenu(QString const & name)
2403 {
2404 	iterator it = find_if(menulist_.begin(), menulist_.end(),
2405 		MenuNamesEqual(name));
2406 	if (it == menulist_.end()) {
2407 		LYXERR0("No submenu named " << name);
2408 		LASSERT(false, { static MenuDefinition m; return m; });
2409 	}
2410 	return (*it);
2411 }
2412 
2413 
2414 /////////////////////////////////////////////////////////////////////
2415 //
2416 // Menus
2417 //
2418 /////////////////////////////////////////////////////////////////////
2419 
Menus()2420 Menus::Menus() : d(new Impl) {}
2421 
2422 
~Menus()2423 Menus::~Menus()
2424 {
2425 	delete d;
2426 }
2427 
2428 
reset()2429 void Menus::reset()
2430 {
2431 	delete d;
2432 	d = new Impl;
2433 }
2434 
2435 
read(Lexer & lex)2436 void Menus::read(Lexer & lex)
2437 {
2438 	enum {
2439 		md_menu,
2440 		md_menubar,
2441 		md_endmenuset
2442 	};
2443 
2444 	LexerKeyword menutags[] = {
2445 		{ "end", md_endmenuset },
2446 		{ "menu", md_menu },
2447 		{ "menubar", md_menubar }
2448 	};
2449 
2450 	// consistency check
2451 	if (compare_ascii_no_case(lex.getString(), "menuset"))
2452 		LYXERR0("Menus::read: ERROR wrong token: `" << lex.getString() << '\'');
2453 
2454 	lex.pushTable(menutags);
2455 	lex.setContext("Menus::read");
2456 
2457 	bool quit = false;
2458 
2459 	while (lex.isOK() && !quit) {
2460 		switch (lex.lex()) {
2461 		case md_menubar:
2462 			d->menubar_.read(lex);
2463 			break;
2464 		case md_menu: {
2465 			lex.next(true);
2466 			QString const name = toqstr(lex.getDocString());
2467 			if (d->hasMenu(name))
2468 				d->getMenu(name).read(lex);
2469 			else {
2470 				MenuDefinition menu(name);
2471 				menu.read(lex);
2472 				d->menulist_.push_back(menu);
2473 			}
2474 			break;
2475 		}
2476 		case md_endmenuset:
2477 			quit = true;
2478 			break;
2479 		default:
2480 			lex.printError("Unknown menu tag");
2481 			break;
2482 		}
2483 	}
2484 	lex.popTable();
2485 }
2486 
2487 
searchMenu(FuncRequest const & func,docstring_list & names) const2488 bool Menus::searchMenu(FuncRequest const & func,
2489 	docstring_list & names) const
2490 {
2491 	MenuDefinition menu;
2492 	d->expand(d->menubar_, menu, 0);
2493 	return menu.searchMenu(func, names);
2494 }
2495 
2496 
fillMenuBar(QMenuBar * qmb,GuiView * view,bool initial)2497 void Menus::fillMenuBar(QMenuBar * qmb, GuiView * view, bool initial)
2498 {
2499 	if (initial) {
2500 #ifdef Q_OS_MAC
2501 		// setup special mac specific menu items, but only do this
2502 		// the first time a QMenuBar is created. Otherwise Qt will
2503 		// create duplicate items in the application menu. It seems
2504 		// that Qt does not remove them when the QMenubar is cleared.
2505 		d->macxMenuBarInit(qmb);
2506 #endif
2507 	} else {
2508 		// Clear all menubar contents before filling it.
2509 		qmb->clear();
2510 #if (QT_VERSION >= 0x050000 && defined(Q_OS_MAC))
2511 		d->macxMenuBarInit(qmb);
2512 #endif
2513 	}
2514 
2515 	LYXERR(Debug::GUI, "populating menu bar" << d->menubar_.name());
2516 
2517 	if (d->menubar_.empty()) {
2518 		LYXERR(Debug::GUI, "\tERROR: empty menu bar"
2519 			<< d->menubar_.name());
2520 		return;
2521 	}
2522 	LYXERR(Debug::GUI, "menu bar entries " << d->menubar_.size());
2523 
2524 	MenuDefinition menu;
2525 	BufferView * bv = 0;
2526 	if (view)
2527 		bv = view->currentBufferView();
2528 	d->expand(d->menubar_, menu, bv);
2529 
2530 	MenuDefinition::const_iterator m = menu.begin();
2531 	MenuDefinition::const_iterator end = menu.end();
2532 
2533 	for (; m != end; ++m) {
2534 
2535 		if (m->kind() != MenuItem::Submenu) {
2536 			LYXERR(Debug::GUI, "\tERROR: not a submenu " << m->label());
2537 			continue;
2538 		}
2539 
2540 		LYXERR(Debug::GUI, "menu bar item " << m->label()
2541 			<< " is a submenu named " << m->submenuname());
2542 
2543 		QString name = m->submenuname();
2544 		if (!d->hasMenu(name)) {
2545 			LYXERR(Debug::GUI, "\tERROR: " << name
2546 				<< " submenu has no menu!");
2547 			continue;
2548 		}
2549 
2550 		Menu * menu = new Menu(view, m->submenuname(), true);
2551 		menu->setTitle(label(*m));
2552 
2553 #if defined(Q_OS_MAC) && (defined(QT_MAC_USE_COCOA) || (QT_VERSION >= 0x050000))
2554 		// On Mac OS with QT/cocoa, the menu is not displayed if there is no action
2555 		// so we create a temporary one here
2556 		QAction * action = new QAction(menu);
2557 		menu->addAction(action);
2558 #endif
2559 
2560 		qmb->addMenu(menu);
2561 
2562 		d->name_map_[view][name] = menu;
2563 	}
2564 }
2565 
2566 
updateMenu(Menu * qmenu)2567 void Menus::updateMenu(Menu * qmenu)
2568 {
2569 	LYXERR(Debug::GUI, "Triggered menu: " << qmenu->d->name);
2570 	qmenu->clear();
2571 
2572 	if (qmenu->d->name.isEmpty())
2573 		return;
2574 
2575 	docstring identifier = qstring_to_ucs4(qmenu->d->name);
2576 	MenuDefinition fromLyxMenu(qmenu->d->name);
2577 	while (!identifier.empty()) {
2578 		docstring menu_name;
2579 		identifier = split(identifier, menu_name, ';');
2580 
2581 		if (!d->hasMenu(toqstr(menu_name))) {
2582 			LYXERR(Debug::GUI, "\tWARNING: non existing menu: "
2583 				<< menu_name);
2584 			continue;
2585 		}
2586 
2587 		MenuDefinition cat_menu = d->getMenu(toqstr(menu_name));
2588 		//FIXME: 50 is a wild guess. We should take into account here
2589 		//the expansion of menu items, disabled optional items etc.
2590 		bool const in_sub_menu = !fromLyxMenu.empty()
2591 			&& fromLyxMenu.size() + cat_menu.size() > 50 ;
2592 		if (in_sub_menu)
2593 			fromLyxMenu.catSub(menu_name);
2594 		else
2595 			fromLyxMenu.cat(cat_menu);
2596 		fromLyxMenu.add(MenuItem(MenuItem::Separator));
2597 	}
2598 
2599 	if (fromLyxMenu.empty()) {
2600 		qmenu->addAction(qt_("No Action Defined!"));
2601 		return;
2602 	}
2603 
2604 	BufferView * bv = 0;
2605 	if (qmenu->d->view)
2606 		bv = qmenu->d->view->currentBufferView();
2607 	d->expand(fromLyxMenu, *qmenu->d->top_level_menu, bv);
2608 	qmenu->d->populate(qmenu, *qmenu->d->top_level_menu);
2609 }
2610 
2611 
menu(QString const & name,GuiView & view,bool keyboard)2612 Menu * Menus::menu(QString const & name, GuiView & view, bool keyboard)
2613 {
2614 	LYXERR(Debug::GUI, "Context menu requested: " << name);
2615 	Menu * menu = d->name_map_[&view].value(name, 0);
2616 	if (!menu && !name.startsWith("context-")) {
2617 		LYXERR0("requested context menu not found: " << name);
2618 		return 0;
2619 	}
2620 
2621 	menu = new Menu(&view, name, true, keyboard);
2622 	d->name_map_[&view][name] = menu;
2623 	return menu;
2624 }
2625 
2626 } // namespace frontend
2627 } // namespace lyx
2628 
2629 #include "moc_Menus.cpp"
2630