blob: 8b554fb7ded0b915944af10a7485f4186ad9a900 [file] [log] [blame]
Simon Hunt08f841d02015-02-10 14:39:20 -08001/*
Brian O'Connor5ab426f2016-04-09 01:19:45 -07002 * Copyright 2015-present Open Networking Laboratory
Simon Hunt08f841d02015-02-10 14:39:20 -08003 *
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
Steven Burrows041c1aa2016-04-12 15:45:05 +010038 var hovered, selections, selectOrder, consumeClick;
39
40 function setInitialState () {
41 hovered = null; // the node over which the mouse is hovering
42 selections = {}; // currently selected nodes (by id)
43 selectOrder = []; // the order in which we made selections
Simon Hunt0c6b2d32015-03-26 17:46:29 -070044 consumeClick = false; // used to coordinate with SVG click handler
Steven Burrows041c1aa2016-04-12 15:45:05 +010045 }
Simon Hunt08f841d02015-02-10 14:39:20 -080046
47 // ==========================
48
49 function nSel() {
50 return selectOrder.length;
51 }
52 function getSel(idx) {
53 return selections[selectOrder[idx]];
54 }
55 function allSelectionsClass(cls) {
56 for (var i=0, n=nSel(); i<n; i++) {
57 if (getSel(i).obj.class !== cls) {
58 return false;
59 }
60 }
61 return true;
62 }
63
64 // ==========================
65
66 function nodeMouseOver(m) {
67 if (!m.dragStarted) {
Simon Hunt08f841d02015-02-10 14:39:20 -080068 if (hovered != m) {
69 hovered = m;
Simon Hunt584e92d2015-08-24 11:27:22 -070070 tov.hooks.mouseOver({
71 id: m.id,
72 class: m.class,
73 type: m.type
74 });
Simon Hunt08f841d02015-02-10 14:39:20 -080075 }
76 }
77 }
78
79 function nodeMouseOut(m) {
80 if (!m.dragStarted) {
81 if (hovered) {
82 hovered = null;
Simon Hunt584e92d2015-08-24 11:27:22 -070083 tov.hooks.mouseOut();
Simon Hunt08f841d02015-02-10 14:39:20 -080084 }
Simon Hunt08f841d02015-02-10 14:39:20 -080085 }
86 }
87
88 // ==========================
89
90 function selectObject(obj) {
91 var el = this,
Simon Hunt5aac2fc2015-06-09 12:34:07 -070092 nodeEv = el && el.tagName === 'g',
93 ev = d3.event.sourceEvent || {},
Simon Hunt08f841d02015-02-10 14:39:20 -080094 n;
95
96 if (api.zoomingOrPanning(ev)) {
97 return;
98 }
99
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700100 if (nodeEv) {
Simon Hunt08f841d02015-02-10 14:39:20 -0800101 n = d3.select(el);
102 } else {
103 api.node().each(function (d) {
104 if (d == obj) {
105 n = d3.select(el = this);
106 }
107 });
108 }
109 if (!n) return;
110
Simon Hunt5aac2fc2015-06-09 12:34:07 -0700111 if (nodeEv) {
112 consumeClick = true;
113 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700114 api.deselectLink();
115
Simon Hunt08f841d02015-02-10 14:39:20 -0800116 if (ev.shiftKey && n.classed('selected')) {
117 deselectObject(obj.id);
118 updateDetail();
119 return;
120 }
121
122 if (!ev.shiftKey) {
Simon Hunta17fa672015-08-19 18:42:22 -0700123 deselectAll(true);
Simon Hunt08f841d02015-02-10 14:39:20 -0800124 }
125
126 selections[obj.id] = { obj: obj, el: el };
127 selectOrder.push(obj.id);
128
129 n.classed('selected', true);
Simon Hunt4766dfb2016-06-14 17:16:22 -0700130 if (n.classed('device')) {
131 api.updateDeviceColors(obj);
132 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800133 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800134 }
135
136 function deselectObject(id) {
137 var obj = selections[id];
138 if (obj) {
139 d3.select(obj.el).classed('selected', false);
140 delete selections[id];
141 fs.removeFromArray(id, selectOrder);
142 api.updateDeviceColors(obj.obj);
143 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800144 }
145
Simon Hunta17fa672015-08-19 18:42:22 -0700146 function deselectAll(skipUpdate) {
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700147 var something = (selectOrder.length > 0);
148
Simon Hunt08f841d02015-02-10 14:39:20 -0800149 // deselect all nodes in the network...
150 api.node().classed('selected', false);
151 selections = {};
152 selectOrder = [];
153 api.updateDeviceColors();
Simon Hunta17fa672015-08-19 18:42:22 -0700154 if (!skipUpdate) {
155 updateDetail();
156 }
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700157
158 // return true if something was selected
159 return something;
Simon Hunt08f841d02015-02-10 14:39:20 -0800160 }
161
162 // === -----------------------------------------------------
163
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700164 function requestDetails(data) {
Simon Hunt237676b52015-03-10 19:04:26 -0700165 wss.sendEvent('requestDetails', {
Simon Hunt08f841d02015-02-10 14:39:20 -0800166 id: data.id,
167 class: data.class
168 });
169 }
170
171 // === -----------------------------------------------------
172
173 function updateDetail() {
174 var nSel = selectOrder.length;
175 if (!nSel) {
176 emptySelect();
177 } else if (nSel === 1) {
178 singleSelect();
179 } else {
180 multiSelect();
181 }
182 }
183
184 function emptySelect() {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700185 tov.hooks.emptySelect();
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700186 tps.displayNothing();
Simon Hunt08f841d02015-02-10 14:39:20 -0800187 }
188
189 function singleSelect() {
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700190 var data = getSel(0).obj;
191 requestDetails(data);
192 // NOTE: detail panel is shown as a response to receiving
193 // a 'showDetails' event from the server. See 'showDetails'
194 // callback function below...
Simon Hunt08f841d02015-02-10 14:39:20 -0800195 }
196
197 function multiSelect() {
Simon Hunt08f841d02015-02-10 14:39:20 -0800198 // display the selected nodes in the detail panel
199 tps.displayMulti(selectOrder);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700200 addHostSelectionActions();
201 tov.hooks.multiSelect(selectOrder);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700202 tps.displaySomething();
Simon Hunt08f841d02015-02-10 14:39:20 -0800203 }
204
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700205 function addHostSelectionActions() {
206 if (allSelectionsClass('host')) {
207 if (nSel() === 2) {
208 tps.addAction({
209 id: 'host-flow-btn',
210 gid: 'endstation',
211 cb: tts.addHostIntent,
212 tt: 'Create Host-to-Host Flow'
213 });
214 } else if (nSel() >= 2) {
215 tps.addAction({
216 id: 'mult-src-flow-btn',
217 gid: 'flows',
218 cb: tts.addMultiSourceIntent,
219 tt: 'Create Multi-Source Flow'
220 });
221 }
222 }
223 }
224
Simon Hunt08f841d02015-02-10 14:39:20 -0800225
226 // === -----------------------------------------------------
227 // Event Handlers
228
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700229 // display the data for the single selected node
Simon Hunt08f841d02015-02-10 14:39:20 -0800230 function showDetails(data) {
Simon Hunt3a0598f2015-08-04 19:59:04 -0700231 var buttons = fs.isA(data.buttons) || [];
Simon Hunt08f841d02015-02-10 14:39:20 -0800232 tps.displaySingle(data);
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700233 tov.installButtons(buttons, data, data.props['URI']);
234 tov.hooks.singleSelect(data);
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700235 tps.displaySomething();
Simon Hunt6036b192015-02-11 11:20:26 -0800236 }
237
Simon Huntd2862c32015-08-24 17:41:51 -0700238 // returns true if one or more nodes are selected.
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700239 function somethingSelected() {
Simon Huntd2862c32015-08-24 17:41:51 -0700240 return nSel();
Simon Hunt08f841d02015-02-10 14:39:20 -0800241 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800242
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700243 function clickConsumed(x) {
244 var cc = consumeClick;
245 consumeClick = !!x;
246 return cc;
247 }
248
Simon Hunt8d558082015-10-29 21:32:50 -0700249 // returns a selection context, providing info about what is selected
250 function selectionContext() {
251 var devices = [],
252 hosts = [],
253 types = {};
254
255 angular.forEach(selections, function (d) {
256 var o = d.obj,
257 c = o.class;
258
259 if (c === 'device') {
260 devices.push(o.id);
261 types[o.id] = o.type;
262 }
263 if (c === 'host') {
264 hosts.push(o.id);
265 types[o.id] = o.type;
266 }
267 });
268
269 return {
270 devices: devices,
271 hosts: hosts,
272 types: types
273 };
274 }
275
Simon Hunt08f841d02015-02-10 14:39:20 -0800276 // === -----------------------------------------------------
277 // === MODULE DEFINITION ===
278
279 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800280 .factory('TopoSelectService',
Simon Huntfb940112015-07-29 18:36:35 -0700281 ['$log', 'FnService', 'WebSocketService', 'TopoOverlayService',
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700282 'TopoPanelService', 'TopoTrafficService', 'NavService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800283
Simon Huntfb940112015-07-29 18:36:35 -0700284 function (_$log_, _fs_, _wss_, _tov_, _tps_, _tts_, _ns_) {
Simon Hunt6036b192015-02-11 11:20:26 -0800285 $log = _$log_;
286 fs = _fs_;
Simon Hunt237676b52015-03-10 19:04:26 -0700287 wss = _wss_;
Simon Huntfb940112015-07-29 18:36:35 -0700288 tov = _tov_;
Simon Hunt6036b192015-02-11 11:20:26 -0800289 tps = _tps_;
Simon Huntf542d842015-02-11 16:20:33 -0800290 tts = _tts_;
Bri Prebilic Cole8f07f0d2015-04-23 13:28:43 -0700291 ns = _ns_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800292
Simon Hunt6036b192015-02-11 11:20:26 -0800293 function initSelect(_api_) {
294 api = _api_;
Steven Burrows041c1aa2016-04-12 15:45:05 +0100295 setInitialState();
Simon Hunt6036b192015-02-11 11:20:26 -0800296 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800297
Simon Hunt6036b192015-02-11 11:20:26 -0800298 function destroySelect() { }
Simon Hunt08f841d02015-02-10 14:39:20 -0800299
Simon Hunt6036b192015-02-11 11:20:26 -0800300 return {
301 initSelect: initSelect,
302 destroySelect: destroySelect,
Simon Hunt08f841d02015-02-10 14:39:20 -0800303
Simon Hunt6036b192015-02-11 11:20:26 -0800304 showDetails: showDetails,
Simon Hunt08f841d02015-02-10 14:39:20 -0800305
Simon Hunt6036b192015-02-11 11:20:26 -0800306 nodeMouseOver: nodeMouseOver,
307 nodeMouseOut: nodeMouseOut,
308 selectObject: selectObject,
309 deselectObject: deselectObject,
310 deselectAll: deselectAll,
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700311 updateDetail: updateDetail,
Simon Huntf542d842015-02-11 16:20:33 -0800312
Simon Hunta0eb0a82015-02-11 12:30:06 -0800313 hovered: function () { return hovered; },
Simon Huntf542d842015-02-11 16:20:33 -0800314 selectOrder: function () { return selectOrder; },
Simon Hunt8d22c4b2015-08-06 16:24:43 -0700315 somethingSelected: somethingSelected,
Simon Hunt0c6b2d32015-03-26 17:46:29 -0700316
Simon Hunt8d558082015-10-29 21:32:50 -0700317 clickConsumed: clickConsumed,
318 selectionContext: selectionContext
Simon Hunt6036b192015-02-11 11:20:26 -0800319 };
320 }]);
Simon Hunt08f841d02015-02-10 14:39:20 -0800321}());