blob: 4ad76903e935ec3fba39148806818984bfb2ec50 [file] [log] [blame]
Simon Hunt08f841d02015-02-10 14:39:20 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 ONOS GUI -- Topology Selection Module.
19 Defines behavior when selecting nodes.
20 */
21
22(function () {
23 'use strict';
24
25 // injected refs
Simon Huntfb940112015-07-29 18:36:35 -070026 var $log, fs, wss, tov, tps, tts, ns;
Simon Hunt08f841d02015-02-10 14:39:20 -080027
28 // api to topoForce
29 var api;
30 /*
31 node() // get ref to D3 selection of nodes
32 zoomingOrPanning( ev )
33 updateDeviceColors( [dev] )
Simon Hunt0c6b2d32015-03-26 17:46:29 -070034 deselectLink()
Simon Hunt08f841d02015-02-10 14:39:20 -080035 */
36
37 // internal state
38 var hovered, // the node over which the mouse is hovering
39 selections = {}, // currently selected nodes (by id)
40 selectOrder = [], // the order in which we made selections
Simon Hunt0c6b2d32015-03-26 17:46:29 -070041 consumeClick = false; // used to coordinate with SVG click handler
Simon Hunt08f841d02015-02-10 14:39:20 -080042
43 // ==========================
44
45 function nSel() {
46 return selectOrder.length;
47 }
48 function getSel(idx) {
49 return selections[selectOrder[idx]];
50 }
51 function allSelectionsClass(cls) {
52 for (var i=0, n=nSel(); i<n; i++) {
53 if (getSel(i).obj.class !== cls) {
54 return false;
55 }
56 }
57 return true;
58 }
59
60 // ==========================
61
62 function nodeMouseOver(m) {
63 if (!m.dragStarted) {
Simon Hunt08f841d02015-02-10 14:39:20 -080064 if (hovered != m) {
65 hovered = m;
Simon Hunt584e92d2015-08-24 11:27:22 -070066 tov.hooks.mouseOver({
67 id: m.id,
68 class: m.class,
69 type: m.type
70 });
Simon Hunt08f841d02015-02-10 14:39:20 -080071 }
72 }
73 }
74
75 function nodeMouseOut(m) {
76 if (!m.dragStarted) {
77 if (hovered) {
78 hovered = null;
Simon Hunt584e92d2015-08-24 11:27:22 -070079 tov.hooks.mouseOut();
Simon Hunt08f841d02015-02-10 14:39:20 -080080 }
Simon Hunt08f841d02015-02-10 14:39:20 -080081 }
82 }
83
84 // ==========================
85
86 function selectObject(obj) {
87 var el = this,
Simon Hunt5aac2fc2015-06-09 12:34:07 -070088 nodeEv = el && el.tagName === 'g',
89 ev = d3.event.sourceEvent || {},
Simon Hunt08f841d02015-02-10 14:39:20 -080090 n;
91
92 if (api.zoomingOrPanning(ev)) {
93 return;
94 }
95
Simon Hunt5aac2fc2015-06-09 12:34:07 -070096 if (nodeEv) {
Simon Hunt08f841d02015-02-10 14:39:20 -080097 n = d3.select(el);
98 } else {
99 api.node().each(function (d) {
100 if (d == obj) {
101 n = d3.select(el = this);
102 }
103 });
104 }
105 if (!n) return;
106
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700107 if (nodeEv) {
108 consumeClick = true;
109 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700110 api.deselectLink();
111
Simon Hunt08f841d02015-02-10 14:39:20 -0800112 if (ev.shiftKey && n.classed('selected')) {
113 deselectObject(obj.id);
114 updateDetail();
115 return;
116 }
117
118 if (!ev.shiftKey) {
Simon Hunta17fa672015-08-19 18:42:22 -0700119 deselectAll(true);
Simon Hunt08f841d02015-02-10 14:39:20 -0800120 }
121
122 selections[obj.id] = { obj: obj, el: el };
123 selectOrder.push(obj.id);
124
125 n.classed('selected', true);
126 api.updateDeviceColors(obj);
127 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800128 }
129
130 function deselectObject(id) {
131 var obj = selections[id];
132 if (obj) {
133 d3.select(obj.el).classed('selected', false);
134 delete selections[id];
135 fs.removeFromArray(id, selectOrder);
136 api.updateDeviceColors(obj.obj);
137 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800138 }
139
Simon Hunta17fa672015-08-19 18:42:22 -0700140 function deselectAll(skipUpdate) {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700141 var something = (selectOrder.length > 0);
142
Simon Hunt08f841d02015-02-10 14:39:20 -0800143 // deselect all nodes in the network...
144 api.node().classed('selected', false);
145 selections = {};
146 selectOrder = [];
147 api.updateDeviceColors();
Simon Hunta17fa672015-08-19 18:42:22 -0700148 if (!skipUpdate) {
149 updateDetail();
150 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700151
152 // return true if something was selected
153 return something;
Simon Hunt08f841d02015-02-10 14:39:20 -0800154 }
155
156 // === -----------------------------------------------------
157
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700158 function requestDetails(data) {
Simon Hunt237676b52015-03-10 19:04:26 -0700159 wss.sendEvent('requestDetails', {
Simon Hunt08f841d02015-02-10 14:39:20 -0800160 id: data.id,
161 class: data.class
162 });
163 }
164
165 // === -----------------------------------------------------
166
167 function updateDetail() {
168 var nSel = selectOrder.length;
169 if (!nSel) {
170 emptySelect();
171 } else if (nSel === 1) {
172 singleSelect();
173 } else {
174 multiSelect();
175 }
176 }
177
178 function emptySelect() {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700179 tov.hooks.emptySelect();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700180 tps.displayNothing();
Simon Hunt08f841d02015-02-10 14:39:20 -0800181 }
182
183 function singleSelect() {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700184 var data = getSel(0).obj;
185 requestDetails(data);
186 // NOTE: detail panel is shown as a response to receiving
187 // a 'showDetails' event from the server. See 'showDetails'
188 // callback function below...
Simon Hunt08f841d02015-02-10 14:39:20 -0800189 }
190
191 function multiSelect() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800192 // display the selected nodes in the detail panel
193 tps.displayMulti(selectOrder);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700194 addHostSelectionActions();
195 tov.hooks.multiSelect(selectOrder);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700196 tps.displaySomething();
Simon Hunt08f841d02015-02-10 14:39:20 -0800197 }
198
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700199 function addHostSelectionActions() {
200 if (allSelectionsClass('host')) {
201 if (nSel() === 2) {
202 tps.addAction({
203 id: 'host-flow-btn',
204 gid: 'endstation',
205 cb: tts.addHostIntent,
206 tt: 'Create Host-to-Host Flow'
207 });
208 } else if (nSel() >= 2) {
209 tps.addAction({
210 id: 'mult-src-flow-btn',
211 gid: 'flows',
212 cb: tts.addMultiSourceIntent,
213 tt: 'Create Multi-Source Flow'
214 });
215 }
216 }
217 }
218
Simon Hunt08f841d02015-02-10 14:39:20 -0800219
220 // === -----------------------------------------------------
221 // Event Handlers
222
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700223 // display the data for the single selected node
Simon Hunt08f841d02015-02-10 14:39:20 -0800224 function showDetails(data) {
Simon Hunt3a0598f2015-08-04 19:59:04 -0700225 var buttons = fs.isA(data.buttons) || [];
Simon Hunt08f841d02015-02-10 14:39:20 -0800226 tps.displaySingle(data);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700227 tov.installButtons(buttons, data, data.props['URI']);
228 tov.hooks.singleSelect(data);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700229 tps.displaySomething();
Simon Hunt6036b192015-02-11 11:20:26 -0800230 }
231
Simon Huntd2862c32015-08-24 17:41:51 -0700232 // returns true if one or more nodes are selected.
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700233 function somethingSelected() {
Simon Huntd2862c32015-08-24 17:41:51 -0700234 return nSel();
Simon Hunt08f841d02015-02-10 14:39:20 -0800235 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800236
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700237 function clickConsumed(x) {
238 var cc = consumeClick;
239 consumeClick = !!x;
240 return cc;
241 }
242
Simon Hunt8d558082015-10-29 21:32:50 -0700243 // returns a selection context, providing info about what is selected
244 function selectionContext() {
245 var devices = [],
246 hosts = [],
247 types = {};
248
249 angular.forEach(selections, function (d) {
250 var o = d.obj,
251 c = o.class;
252
253 if (c === 'device') {
254 devices.push(o.id);
255 types[o.id] = o.type;
256 }
257 if (c === 'host') {
258 hosts.push(o.id);
259 types[o.id] = o.type;
260 }
261 });
262
263 return {
264 devices: devices,
265 hosts: hosts,
266 types: types
267 };
268 }
269
Simon Hunt08f841d02015-02-10 14:39:20 -0800270 // === -----------------------------------------------------
271 // === MODULE DEFINITION ===
272
273 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800274 .factory('TopoSelectService',
Simon Huntfb940112015-07-29 18:36:35 -0700275 ['$log', 'FnService', 'WebSocketService', 'TopoOverlayService',
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700276 'TopoPanelService', 'TopoTrafficService', 'NavService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800277
Simon Huntfb940112015-07-29 18:36:35 -0700278 function (_$log_, _fs_, _wss_, _tov_, _tps_, _tts_, _ns_) {
Simon Hunt6036b192015-02-11 11:20:26 -0800279 $log = _$log_;
280 fs = _fs_;
Simon Hunt237676b52015-03-10 19:04:26 -0700281 wss = _wss_;
Simon Huntfb940112015-07-29 18:36:35 -0700282 tov = _tov_;
Simon Hunt6036b192015-02-11 11:20:26 -0800283 tps = _tps_;
Simon Huntf542d842015-02-11 16:20:33 -0800284 tts = _tts_;
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700285 ns = _ns_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800286
Simon Hunt6036b192015-02-11 11:20:26 -0800287 function initSelect(_api_) {
288 api = _api_;
289 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800290
Simon Hunt6036b192015-02-11 11:20:26 -0800291 function destroySelect() { }
Simon Hunt08f841d02015-02-10 14:39:20 -0800292
Simon Hunt6036b192015-02-11 11:20:26 -0800293 return {
294 initSelect: initSelect,
295 destroySelect: destroySelect,
Simon Hunt08f841d02015-02-10 14:39:20 -0800296
Simon Hunt6036b192015-02-11 11:20:26 -0800297 showDetails: showDetails,
Simon Hunt08f841d02015-02-10 14:39:20 -0800298
Simon Hunt6036b192015-02-11 11:20:26 -0800299 nodeMouseOver: nodeMouseOver,
300 nodeMouseOut: nodeMouseOut,
301 selectObject: selectObject,
302 deselectObject: deselectObject,
303 deselectAll: deselectAll,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700304 updateDetail: updateDetail,
Simon Huntf542d842015-02-11 16:20:33 -0800305
Simon Hunta0eb0a82015-02-11 12:30:06 -0800306 hovered: function () { return hovered; },
Simon Huntf542d842015-02-11 16:20:33 -0800307 selectOrder: function () { return selectOrder; },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700308 somethingSelected: somethingSelected,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700309
Simon Hunt8d558082015-10-29 21:32:50 -0700310 clickConsumed: clickConsumed,
311 selectionContext: selectionContext
Simon Hunt6036b192015-02-11 11:20:26 -0800312 };
313 }]);
Simon Hunt08f841d02015-02-10 14:39:20 -0800314}());