mxDefaultPopupMenu.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /**
  2. * Copyright (c) 2006-2015, JGraph Ltd
  3. * Copyright (c) 2006-2015, Gaudenz Alder
  4. */
  5. /**
  6. * Class: mxDefaultPopupMenu
  7. *
  8. * Creates popupmenus for mouse events. This object holds an XML node
  9. * which is a description of the popup menu to be created. In
  10. * <createMenu>, the configuration is applied to the context and
  11. * the resulting menu items are added to the menu dynamically. See
  12. * <createMenu> for a description of the configuration format.
  13. *
  14. * This class does not create the DOM nodes required for the popup menu, it
  15. * only parses an XML description to invoke the respective methods on an
  16. * <mxPopupMenu> each time the menu is displayed.
  17. *
  18. * Codec:
  19. *
  20. * This class uses the <mxDefaultPopupMenuCodec> to read configuration
  21. * data into an existing instance, however, the actual parsing is done
  22. * by this class during program execution, so the format is described
  23. * below.
  24. *
  25. * Constructor: mxDefaultPopupMenu
  26. *
  27. * Constructs a new popupmenu-factory based on given configuration.
  28. *
  29. * Paramaters:
  30. *
  31. * config - XML node that contains the configuration data.
  32. */
  33. function mxDefaultPopupMenu(config)
  34. {
  35. this.config = config;
  36. };
  37. /**
  38. * Variable: imageBasePath
  39. *
  40. * Base path for all icon attributes in the config. Default is null.
  41. */
  42. mxDefaultPopupMenu.prototype.imageBasePath = null;
  43. /**
  44. * Variable: config
  45. *
  46. * XML node used as the description of new menu items. This node is
  47. * used in <createMenu> to dynamically create the menu items if their
  48. * respective conditions evaluate to true for the given arguments.
  49. */
  50. mxDefaultPopupMenu.prototype.config = null;
  51. /**
  52. * Function: createMenu
  53. *
  54. * This function is called from <mxEditor> to add items to the
  55. * given menu based on <config>. The config is a sequence of
  56. * the following nodes and attributes.
  57. *
  58. * Child Nodes:
  59. *
  60. * add - Adds a new menu item. See below for attributes.
  61. * separator - Adds a separator. No attributes.
  62. * condition - Adds a custom condition. Name attribute.
  63. *
  64. * The add-node may have a child node that defines a function to be invoked
  65. * before the action is executed (or instead of an action to be executed).
  66. *
  67. * Attributes:
  68. *
  69. * as - Resource key for the label (needs entry in property file).
  70. * action - Name of the action to execute in enclosing editor.
  71. * icon - Optional icon (relative/absolute URL).
  72. * iconCls - Optional CSS class for the icon.
  73. * if - Optional name of condition that must be true (see below).
  74. * enabled-if - Optional name of condition that specifies if the menu item
  75. * should be enabled.
  76. * name - Name of custom condition. Only for condition nodes.
  77. *
  78. * Conditions:
  79. *
  80. * nocell - No cell under the mouse.
  81. * ncells - More than one cell selected.
  82. * notRoot - Drilling position is other than home.
  83. * cell - Cell under the mouse.
  84. * notEmpty - Exactly one cell with children under mouse.
  85. * expandable - Exactly one expandable cell under mouse.
  86. * collapsable - Exactly one collapsable cell under mouse.
  87. * validRoot - Exactly one cell which is a possible root under mouse.
  88. * swimlane - Exactly one cell which is a swimlane under mouse.
  89. *
  90. * Example:
  91. *
  92. * To add a new item for a given action to the popupmenu:
  93. *
  94. * (code)
  95. * <mxDefaultPopupMenu as="popupHandler">
  96. * <add as="delete" action="delete" icon="images/delete.gif" if="cell"/>
  97. * </mxDefaultPopupMenu>
  98. * (end)
  99. *
  100. * To add a new item for a custom function:
  101. *
  102. * (code)
  103. * <mxDefaultPopupMenu as="popupHandler">
  104. * <add as="action1"><![CDATA[
  105. * function (editor, cell, evt)
  106. * {
  107. * editor.execute('action1', cell, 'myArg');
  108. * }
  109. * ]]></add>
  110. * </mxDefaultPopupMenu>
  111. * (end)
  112. *
  113. * The above example invokes action1 with an additional third argument via
  114. * the editor instance. The third argument is passed to the function that
  115. * defines action1. If the add-node has no action-attribute, then only the
  116. * function defined in the text content is executed, otherwise first the
  117. * function and then the action defined in the action-attribute is
  118. * executed. The function in the text content has 3 arguments, namely the
  119. * <mxEditor> instance, the <mxCell> instance under the mouse, and the
  120. * native mouse event.
  121. *
  122. * Custom Conditions:
  123. *
  124. * To add a new condition for popupmenu items:
  125. *
  126. * (code)
  127. * <condition name="condition1"><![CDATA[
  128. * function (editor, cell, evt)
  129. * {
  130. * return cell != null;
  131. * }
  132. * ]]></condition>
  133. * (end)
  134. *
  135. * The new condition can then be used in any item as follows:
  136. *
  137. * (code)
  138. * <add as="action1" action="action1" icon="action1.gif" if="condition1"/>
  139. * (end)
  140. *
  141. * The order in which the items and conditions appear is not significant as
  142. * all connditions are evaluated before any items are created.
  143. *
  144. * Parameters:
  145. *
  146. * editor - Enclosing <mxEditor> instance.
  147. * menu - <mxPopupMenu> that is used for adding items and separators.
  148. * cell - Optional <mxCell> which is under the mousepointer.
  149. * evt - Optional mouse event which triggered the menu.
  150. */
  151. mxDefaultPopupMenu.prototype.createMenu = function(editor, menu, cell, evt)
  152. {
  153. if (this.config != null)
  154. {
  155. var conditions = this.createConditions(editor, cell, evt);
  156. var item = this.config.firstChild;
  157. this.addItems(editor, menu, cell, evt, conditions, item, null);
  158. }
  159. };
  160. /**
  161. * Function: addItems
  162. *
  163. * Recursively adds the given items and all of its children into the given menu.
  164. *
  165. * Parameters:
  166. *
  167. * editor - Enclosing <mxEditor> instance.
  168. * menu - <mxPopupMenu> that is used for adding items and separators.
  169. * cell - Optional <mxCell> which is under the mousepointer.
  170. * evt - Optional mouse event which triggered the menu.
  171. * conditions - Array of names boolean conditions.
  172. * item - XML node that represents the current menu item.
  173. * parent - DOM node that represents the parent menu item.
  174. */
  175. mxDefaultPopupMenu.prototype.addItems = function(editor, menu, cell, evt, conditions, item, parent)
  176. {
  177. var addSeparator = false;
  178. while (item != null)
  179. {
  180. if (item.nodeName == 'add')
  181. {
  182. var condition = item.getAttribute('if');
  183. if (condition == null || conditions[condition])
  184. {
  185. var as = item.getAttribute('as');
  186. as = mxResources.get(as) || as;
  187. var funct = mxUtils.eval(mxUtils.getTextContent(item));
  188. var action = item.getAttribute('action');
  189. var icon = item.getAttribute('icon');
  190. var iconCls = item.getAttribute('iconCls');
  191. var enabledCond = item.getAttribute('enabled-if');
  192. var enabled = enabledCond == null || conditions[enabledCond];
  193. if (addSeparator)
  194. {
  195. menu.addSeparator(parent);
  196. addSeparator = false;
  197. }
  198. if (icon != null && this.imageBasePath)
  199. {
  200. icon = this.imageBasePath + icon;
  201. }
  202. var row = this.addAction(menu, editor, as, icon, funct, action, cell, parent, iconCls, enabled);
  203. this.addItems(editor, menu, cell, evt, conditions, item.firstChild, row);
  204. }
  205. }
  206. else if (item.nodeName == 'separator')
  207. {
  208. addSeparator = true;
  209. }
  210. item = item.nextSibling;
  211. }
  212. };
  213. /**
  214. * Function: addAction
  215. *
  216. * Helper method to bind an action to a new menu item.
  217. *
  218. * Parameters:
  219. *
  220. * menu - <mxPopupMenu> that is used for adding items and separators.
  221. * editor - Enclosing <mxEditor> instance.
  222. * lab - String that represents the label of the menu item.
  223. * icon - Optional URL that represents the icon of the menu item.
  224. * action - Optional name of the action to execute in the given editor.
  225. * funct - Optional function to execute before the optional action. The
  226. * function takes an <mxEditor>, the <mxCell> under the mouse and the
  227. * mouse event that triggered the call.
  228. * cell - Optional <mxCell> to use as an argument for the action.
  229. * parent - DOM node that represents the parent menu item.
  230. * iconCls - Optional CSS class for the menu icon.
  231. * enabled - Optional boolean that specifies if the menu item is enabled.
  232. * Default is true.
  233. */
  234. mxDefaultPopupMenu.prototype.addAction = function(menu, editor, lab, icon, funct, action, cell, parent, iconCls, enabled)
  235. {
  236. var clickHandler = function(evt)
  237. {
  238. if (typeof(funct) == 'function')
  239. {
  240. funct.call(editor, editor, cell, evt);
  241. }
  242. if (action != null)
  243. {
  244. editor.execute(action, cell, evt);
  245. }
  246. };
  247. return menu.addItem(lab, icon, clickHandler, parent, iconCls, enabled);
  248. };
  249. /**
  250. * Function: createConditions
  251. *
  252. * Evaluates the default conditions for the given context.
  253. */
  254. mxDefaultPopupMenu.prototype.createConditions = function(editor, cell, evt)
  255. {
  256. // Creates array with conditions
  257. var model = editor.graph.getModel();
  258. var childCount = model.getChildCount(cell);
  259. // Adds some frequently used conditions
  260. var conditions = [];
  261. conditions['nocell'] = cell == null;
  262. conditions['ncells'] = editor.graph.getSelectionCount() > 1;
  263. conditions['notRoot'] = model.getRoot() !=
  264. model.getParent(editor.graph.getDefaultParent());
  265. conditions['cell'] = cell != null;
  266. var isCell = cell != null && editor.graph.getSelectionCount() == 1;
  267. conditions['nonEmpty'] = isCell && childCount > 0;
  268. conditions['expandable'] = isCell && editor.graph.isCellFoldable(cell, false);
  269. conditions['collapsable'] = isCell && editor.graph.isCellFoldable(cell, true);
  270. conditions['validRoot'] = isCell && editor.graph.isValidRoot(cell);
  271. conditions['emptyValidRoot'] = conditions['validRoot'] && childCount == 0;
  272. conditions['swimlane'] = isCell && editor.graph.isSwimlane(cell);
  273. // Evaluates dynamic conditions from config file
  274. var condNodes = this.config.getElementsByTagName('condition');
  275. for (var i=0; i<condNodes.length; i++)
  276. {
  277. var funct = mxUtils.eval(mxUtils.getTextContent(condNodes[i]));
  278. var name = condNodes[i].getAttribute('name');
  279. if (name != null && typeof(funct) == 'function')
  280. {
  281. conditions[name] = funct(editor, cell, evt);
  282. }
  283. }
  284. return conditions;
  285. };