blob: af0e0413c794b5404bad2b5d522bc8c633f5b44d [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 Hunt237676b52015-03-10 19:04:26 -070026 var $log, fs, flash, wss, tps, tts;
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 Hunt08f841d02015-02-10 14:39:20 -080034 */
35
36 // internal state
37 var hovered, // the node over which the mouse is hovering
38 selections = {}, // currently selected nodes (by id)
39 selectOrder = [], // the order in which we made selections
40 haveDetails = false, // do we have details of one or more nodes?
41 useDetails = true; // should we show details if we have 'em?
42
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) {
64 $log.debug("MouseOver()...", m);
65 if (hovered != m) {
66 hovered = m;
Simon Huntf542d842015-02-11 16:20:33 -080067 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080068 }
69 }
70 }
71
72 function nodeMouseOut(m) {
73 if (!m.dragStarted) {
74 if (hovered) {
75 hovered = null;
Simon Huntf542d842015-02-11 16:20:33 -080076 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080077 }
78 $log.debug("MouseOut()...", m);
79 }
80 }
81
82 // ==========================
83
84 function selectObject(obj) {
85 var el = this,
86 ev = d3.event.sourceEvent,
87 n;
88
89 if (api.zoomingOrPanning(ev)) {
90 return;
91 }
92
93 if (el) {
94 n = d3.select(el);
95 } else {
96 api.node().each(function (d) {
97 if (d == obj) {
98 n = d3.select(el = this);
99 }
100 });
101 }
102 if (!n) return;
103
104 if (ev.shiftKey && n.classed('selected')) {
105 deselectObject(obj.id);
106 updateDetail();
107 return;
108 }
109
110 if (!ev.shiftKey) {
111 deselectAll();
112 }
113
114 selections[obj.id] = { obj: obj, el: el };
115 selectOrder.push(obj.id);
116
117 n.classed('selected', true);
118 api.updateDeviceColors(obj);
119 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800120 }
121
122 function deselectObject(id) {
123 var obj = selections[id];
124 if (obj) {
125 d3.select(obj.el).classed('selected', false);
126 delete selections[id];
127 fs.removeFromArray(id, selectOrder);
128 api.updateDeviceColors(obj.obj);
129 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800130 }
131
132 function deselectAll() {
133 // deselect all nodes in the network...
134 api.node().classed('selected', false);
135 selections = {};
136 selectOrder = [];
137 api.updateDeviceColors();
138 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800139 }
140
141 // === -----------------------------------------------------
142
143 function requestDetails() {
144 var data = getSel(0).obj;
Simon Hunt237676b52015-03-10 19:04:26 -0700145 wss.sendEvent('requestDetails', {
Simon Hunt08f841d02015-02-10 14:39:20 -0800146 id: data.id,
147 class: data.class
148 });
149 }
150
151 // === -----------------------------------------------------
152
153 function updateDetail() {
154 var nSel = selectOrder.length;
155 if (!nSel) {
156 emptySelect();
157 } else if (nSel === 1) {
158 singleSelect();
159 } else {
160 multiSelect();
161 }
162 }
163
164 function emptySelect() {
165 haveDetails = false;
166 tps.hideDetailPanel();
Simon Huntf542d842015-02-11 16:20:33 -0800167 tts.cancelTraffic();
Simon Hunt08f841d02015-02-10 14:39:20 -0800168 }
169
170 function singleSelect() {
171 // NOTE: detail is shown from 'showDetails' event callback
172 requestDetails();
Simon Huntf542d842015-02-11 16:20:33 -0800173 tts.cancelTraffic();
174 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -0800175 }
176
177 function multiSelect() {
178 haveDetails = true;
179
180 // display the selected nodes in the detail panel
181 tps.displayMulti(selectOrder);
182
183 // always add the 'show traffic' action
Simon Huntf542d842015-02-11 16:20:33 -0800184 tps.addAction('Show Related Traffic', tts.showRelatedIntentsAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800185
186 // add other actions, based on what is selected...
187 if (nSel() === 2 && allSelectionsClass('host')) {
Simon Huntf542d842015-02-11 16:20:33 -0800188 tps.addAction('Create Host-to-Host Flow', tts.addHostIntentAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800189 } else if (nSel() >= 2 && allSelectionsClass('host')) {
Simon Huntf542d842015-02-11 16:20:33 -0800190 tps.addAction('Create Multi-Source Flow', tts.addMultiSourceIntentAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800191 }
192
Simon Huntf542d842015-02-11 16:20:33 -0800193 tts.cancelTraffic();
194 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -0800195 }
196
197
198 // === -----------------------------------------------------
199 // Event Handlers
200
201 function showDetails(data) {
202 haveDetails = true;
203
204 // display the data for the single selected node
205 tps.displaySingle(data);
206
207 // always add the 'show traffic' action
Simon Huntf542d842015-02-11 16:20:33 -0800208 tps.addAction('Show Related Traffic', tts.showRelatedIntentsAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800209
210 // add other actions, based on what is selected...
211 if (data.type === 'switch') {
Simon Huntf542d842015-02-11 16:20:33 -0800212 tps.addAction('Show Device Flows', tts.showDeviceLinkFlowsAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800213 }
214
215 // only show the details panel if the user hasn't "hidden" it
216 if (useDetails) {
217 tps.showDetailPanel();
218 }
219 }
220
Simon Hunt6036b192015-02-11 11:20:26 -0800221 function toggleDetails() {
222 useDetails = !useDetails;
223 if (useDetails) {
224 flash.flash('Enable details panel');
225 if (haveDetails) {
226 tps.showDetailPanel();
227 }
228 } else {
229 flash.flash('Disable details panel');
230 tps.hideDetailPanel();
231 }
232 }
233
Simon Huntf542d842015-02-11 16:20:33 -0800234 function validateSelectionContext() {
235 if (!hovered && !nSel()) {
236 tts.cancelTraffic();
237 return false;
238 }
239 return true;
Simon Hunt08f841d02015-02-10 14:39:20 -0800240 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800241
242 // === -----------------------------------------------------
243 // === MODULE DEFINITION ===
244
245 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800246 .factory('TopoSelectService',
Simon Hunt237676b52015-03-10 19:04:26 -0700247 ['$log', 'FnService', 'FlashService', 'WebSocketService',
248 'TopoPanelService', 'TopoTrafficService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800249
Simon Hunt237676b52015-03-10 19:04:26 -0700250 function (_$log_, _fs_, _flash_, _wss_, _tps_, _tts_) {
Simon Hunt6036b192015-02-11 11:20:26 -0800251 $log = _$log_;
252 fs = _fs_;
253 flash = _flash_;
Simon Hunt237676b52015-03-10 19:04:26 -0700254 wss = _wss_;
Simon Hunt6036b192015-02-11 11:20:26 -0800255 tps = _tps_;
Simon Huntf542d842015-02-11 16:20:33 -0800256 tts = _tts_;
Simon Hunt08f841d02015-02-10 14:39:20 -0800257
Simon Hunt6036b192015-02-11 11:20:26 -0800258 function initSelect(_api_) {
259 api = _api_;
260 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800261
Simon Hunt6036b192015-02-11 11:20:26 -0800262 function destroySelect() { }
Simon Hunt08f841d02015-02-10 14:39:20 -0800263
Simon Hunt6036b192015-02-11 11:20:26 -0800264 return {
265 initSelect: initSelect,
266 destroySelect: destroySelect,
Simon Hunt08f841d02015-02-10 14:39:20 -0800267
Simon Hunt6036b192015-02-11 11:20:26 -0800268 showDetails: showDetails,
269 toggleDetails: toggleDetails,
Simon Hunt08f841d02015-02-10 14:39:20 -0800270
Simon Hunt6036b192015-02-11 11:20:26 -0800271 nodeMouseOver: nodeMouseOver,
272 nodeMouseOut: nodeMouseOut,
273 selectObject: selectObject,
274 deselectObject: deselectObject,
275 deselectAll: deselectAll,
Simon Huntf542d842015-02-11 16:20:33 -0800276
Simon Hunta0eb0a82015-02-11 12:30:06 -0800277 hovered: function () { return hovered; },
Simon Huntf542d842015-02-11 16:20:33 -0800278 haveDetails: function () { return haveDetails; },
279 selectOrder: function () { return selectOrder; },
280 validateSelectionContext: validateSelectionContext
Simon Hunt6036b192015-02-11 11:20:26 -0800281 };
282 }]);
Simon Hunt08f841d02015-02-10 14:39:20 -0800283}());