selection_utils.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. /*******************************************************************************
  2. AToMPM - A Tool for Multi-Paradigm Modelling
  3. Copyright (c) 2011 Raphael Mannadiar (raphael.mannadiar@mail.mcgill.ca)
  4. Modified by Conner Hansen (chansen@crimson.ua.edu)
  5. This file is part of AToMPM.
  6. AToMPM is free software: you can redistribute it and/or modify it under the
  7. terms of the GNU Lesser General Public License as published by the Free Software
  8. Foundation, either version 3 of the License, or (at your option) any later
  9. version.
  10. AToMPM is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  12. PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public License along
  14. with AToMPM. If not, see <http://www.gnu.org/licenses/>.
  15. *******************************************************************************/
  16. var __selectionOverlay;
  17. var __highlighted = [];
  18. var __selection;
  19. function __isSelected(it)
  20. {
  21. return __selection != undefined && utils.contains(__selection['items'],it);
  22. }
  23. /* draw a selection overlay rectangle around the specified (via uri) icons
  24. 1. clear any past __selection
  25. 2. return undefined on undefined argument
  26. 2. extract uri from non-array argument (when select called with event.target)
  27. ... event.target may be:
  28. a) normal icon: selection becomes that icon's uri
  29. b) center-piece: selection becomes center-piece and all of its connected
  30. edges
  31. c) edge: selection becomes connected center-piece and all of its
  32. connected edges
  33. 2. remove selected items <edgeTo,centerPiece,edgeFrom> if any of the 3 are
  34. missing (e.g., when the canvas selection overlay contains only edgeTo or
  35. centerPiece)
  36. 3. foreach icon in the updated selection, add its contents if any/applicable
  37. except when the 'ignoreContents' is set to true
  38. 4. return undefined on empty updated selection
  39. 5. compute a bbox that contains all specified icons/edges
  40. 6. draw a selection overlay rectangle matching computed bbox and style it
  41. appropriately
  42. 7. remember the drawn rectangle, the computed bbox and the selection in
  43. __selection
  44. 8. give rectangle listener to report __EVENT_LEFT_PRESS_SELECTION
  45. 9. return true (to indicate successfully selecting something) */
  46. function __select(selection,ignoreContents)
  47. {
  48. if( __selection != undefined )
  49. {
  50. __selection['rect'].remove();
  51. __selection = undefined;
  52. GeometryUtils.hideGeometryControlsOverlay();
  53. GeometryUtils.hideTransformationPreviewOverlay();
  54. }
  55. if( selection == undefined )
  56. return;
  57. else if( ! utils.isArray(selection) )
  58. /* selection is event.target */
  59. {
  60. if( (uri = __vobj2uri(selection)) )
  61. selection = [uri].concat(
  62. (__isConnectionType(uri) ? __getConnectedEdges(uri) : []));
  63. else
  64. selection = __getConnectionParticipants( __edge2edgeId(selection) );
  65. }
  66. else
  67. /* filter canvas selection for incomplete connection tuples */
  68. {
  69. var missingFromSelection =
  70. function(required)
  71. {
  72. return required.some(
  73. function(edgeId)
  74. {return ! utils.contains(selection,edgeId);});
  75. };
  76. while(
  77. selection.length > 0 &&
  78. selection.some(
  79. function(it)
  80. {
  81. if( it in __edges )
  82. var items = __getConnectionParticipants(it);
  83. else if( __isConnectionType(it) )
  84. var items = [it].concat(__getConnectedEdges(it));
  85. if( items != undefined && missingFromSelection(items) )
  86. {
  87. utils.filter(selection,items);
  88. return true;
  89. }
  90. }) ) ;
  91. }
  92. /* add icon contents */
  93. if( !ignoreContents )
  94. {
  95. var containedIcons = [];
  96. selection.forEach(
  97. function(it)
  98. {
  99. if( it in __icons )
  100. containedIcons =
  101. containedIcons.concat( __getIconsInContainer(it) );
  102. });
  103. selection = selection.concat(
  104. containedIcons.filter(
  105. function(ci)
  106. {
  107. return ! utils.contains(selection,ci);
  108. }));
  109. }
  110. /* compute selection bbox */
  111. if( selection.length == 0 )
  112. return;
  113. else if( selection.length == 1 )
  114. var bbox = __icons[selection[0]]['icon'].getBBox();
  115. else
  116. var bbox = __getGlobalBBox(selection);
  117. __selection =
  118. {'items':selection,
  119. 'bbox':bbox,
  120. 'rect':__bbox2rect(bbox,'canvas_selection')};
  121. __selection['rect'].node.onmousedown =
  122. function(event)
  123. {
  124. if( event.button == 0 )
  125. BehaviorManager.handleUserEvent(__EVENT_LEFT_PRESS_SELECTION,event);
  126. };
  127. __selection['rect'].node.onmouseup =
  128. function(event)
  129. {
  130. if( event.button == 0 )
  131. BehaviorManager.handleUserEvent(__EVENT_LEFT_RELEASE_SELECTION,event);
  132. };
  133. return true;
  134. }
  135. /* returns true if __selection contains an 'instance' of the given type */
  136. function __selectionContainsType(type)
  137. {
  138. if( __selection == undefined )
  139. return false;
  140. return __selection['items'].some(
  141. function(it)
  142. {
  143. return (type == __EDGETYPE && it in __edges) ||
  144. (type == __NODETYPE && it in __icons);
  145. });
  146. }
  147. /* temporarily highlight specified icon (e.g., to draw attention on it) without
  148. disrupting other highlighted elements */
  149. function __flash(uri,color,timeout)
  150. {
  151. __icons[uri]['icon'].highlight({'color':color || 'MediumVioletRed','fill':true});
  152. function turnOff()
  153. {
  154. try {__icons[uri]['icon'].unhighlight();}
  155. catch(err) {}
  156. }
  157. window.setTimeout(turnOff,timeout || 500);
  158. }
  159. /* if 'uri' isn't already highlighted, unhighlights whatever is (if applicable)
  160. and highlights 'uri'... highlighting implies setting
  161. 1. highlighting 'uri'
  162. 2. possibly highlighting 'uri''s cross-formalism neighbors
  163. 3. possibly setting a timeout to remove the highlight
  164. 4. setting up the __highlighted object like so
  165. 'uri' :: 'uri'
  166. 'turnOff' :: a function that unhighlights 'uri' and nodes from step 2. if
  167. any... the try/catch blocks ensure safety against deletion of
  168. highlighted icons */
  169. function __highlight(uri,followCrossFormalismLinks,timeout,color)
  170. {
  171. if( ! isHighlighted(uri) )
  172. {
  173. __unhighlight(uri);
  174. __icons[uri]['icon'].highlight({'color':color || 'DarkTurquoise','fill':true});
  175. if( followCrossFormalismLinks != undefined )
  176. {
  177. var neighbors =
  178. __getCrossFormalismNeighbors(uri,followCrossFormalismLinks);
  179. neighbors.nodes.forEach(
  180. function(n)
  181. {
  182. if( n != uri )
  183. __icons[n]['icon'].highlight({'color':'Gold','fill':true});
  184. });
  185. }
  186. if( timeout != undefined )
  187. var tid = window.setTimeout(__unhighlight,timeout);
  188. __highlighted.push(
  189. {'uri':uri,
  190. 'turnOff':
  191. function()
  192. {
  193. try {__icons[uri]['icon'].unhighlight();}
  194. catch(err) {}
  195. if( followCrossFormalismLinks != undefined )
  196. neighbors.nodes.forEach(
  197. function(n) {
  198. try {__icons[n]['icon'].unhighlight();}
  199. catch(err) {}
  200. } );
  201. if( timeout != undefined )
  202. window.clearTimeout(tid);
  203. }});
  204. }
  205. }
  206. function isHighlighted(uri)
  207. {
  208. return __highlighted.length > 0 && __highlighted.filter(function(hl) {return uri == hl['uri']}).length == 1;
  209. }
  210. function __unhighlight(uri)
  211. {
  212. if( __highlighted.length > 0 )
  213. {
  214. __highlighted.filter(function(hl) {return !uri || uri == hl['uri']}).forEach(function(hl) {hl.turnOff()})
  215. if (!uri) {
  216. __highlighted = [];
  217. } else {
  218. __highlighted = __highlighted.filter(function(hl) {return uri != hl['uri']})
  219. }
  220. }
  221. }
  222. /* return the contents of the selection orverlay rectangle... the max parameter
  223. is used to stop searching after the specified number of contained icons/edges
  224. are found */
  225. function __getCanvasSelectionOverlayContents(max)
  226. {
  227. if( __selectionOverlay == undefined )
  228. return [];
  229. var sobbox = {'x' : __selectionOverlay.attr('x'),
  230. 'y' : __selectionOverlay.attr('y'),
  231. 'width' : __selectionOverlay.attr('width'),
  232. 'height': __selectionOverlay.attr('height')};
  233. contents = [];
  234. [__icons,__edges].some(
  235. function(A)
  236. {
  237. for( var a in A )
  238. {
  239. if( A[a]['icon'].isVisible() &&
  240. __isBBoxInside(A[a]['icon'].getBBox(),sobbox) )
  241. {
  242. contents.push(a);
  243. if( contents.length == max )
  244. return true;
  245. }
  246. }
  247. });
  248. return contents;
  249. }
  250. /* initialize a Raphael.rect originating at (x,y), appropriately styled, and
  251. that reports events (specifically, mouseup) as if it were the canvas
  252. NOTE:: _x0 and _y0 are use to remember the point from where the selection
  253. began */
  254. function __initCanvasSelectionOverlay(x,y)
  255. {
  256. if( __selectionOverlay != undefined )
  257. return;
  258. __selectionOverlay = __canvas.rect(x,y,0,0);
  259. __selectionOverlay.node.setAttribute('class','canvas_selection_overlay');
  260. __selectionOverlay.node.setAttribute('_x0',x);
  261. __selectionOverlay.node.setAttribute('_y0',y);
  262. __selectionOverlay.node.onmouseup =
  263. function(event)
  264. {
  265. if( event.button == 0 )
  266. BehaviorManager.handleUserEvent(__EVENT_LEFT_RELEASE_CANVAS,event);
  267. };
  268. }
  269. /* returns true if there is at least one icon fully encompassed within the
  270. selection overlay rectangle */
  271. function __isCanvasSelectionOverlayEmpty()
  272. {
  273. return __getCanvasSelectionOverlayContents(1).length == 0;
  274. }
  275. /* selects contents of and hides canvas selection overlay */
  276. function __selectSelection()
  277. {
  278. var selectedSomething = __select( __getCanvasSelectionOverlayContents() );
  279. __selectionOverlay.remove();
  280. __selectionOverlay = undefined;
  281. return selectedSomething;
  282. }
  283. /* resizes the selection overlay rectangle following a mouse motion... resizing
  284. may involve changing x and y attributes because widths and heights can not be
  285. negative */
  286. function __updateCanvasSelectionOverlay(x,y)
  287. {
  288. var w = x - parseInt(__selectionOverlay.node.getAttribute('_x0')),
  289. h = y - parseInt(__selectionOverlay.node.getAttribute('_y0'));
  290. if( w < 0 )
  291. {
  292. __selectionOverlay.attr('x', x);
  293. __selectionOverlay.attr('width', -w);
  294. }
  295. else
  296. __selectionOverlay.attr('width', w);
  297. if( h < 0 )
  298. {
  299. __selectionOverlay.attr('y', y);
  300. __selectionOverlay.attr('height', -h);
  301. }
  302. else
  303. __selectionOverlay.attr('height', h);
  304. }