blob: 2f1dd153434447023f945b0f19e346d96573033e [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 Huntf542d842015-02-11 16:20:33 -080026 var $log, fs, flash, 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] )
34 sendEvent( type, {payload} )
35 */
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
41 haveDetails = false, // do we have details of one or more nodes?
42 useDetails = true; // should we show details if we have 'em?
43
44 // ==========================
45
46 function nSel() {
47 return selectOrder.length;
48 }
49 function getSel(idx) {
50 return selections[selectOrder[idx]];
51 }
52 function allSelectionsClass(cls) {
53 for (var i=0, n=nSel(); i<n; i++) {
54 if (getSel(i).obj.class !== cls) {
55 return false;
56 }
57 }
58 return true;
59 }
60
61 // ==========================
62
63 function nodeMouseOver(m) {
64 if (!m.dragStarted) {
65 $log.debug("MouseOver()...", m);
66 if (hovered != m) {
67 hovered = m;
Simon Huntf542d842015-02-11 16:20:33 -080068 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080069 }
70 }
71 }
72
73 function nodeMouseOut(m) {
74 if (!m.dragStarted) {
75 if (hovered) {
76 hovered = null;
Simon Huntf542d842015-02-11 16:20:33 -080077 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -080078 }
79 $log.debug("MouseOut()...", m);
80 }
81 }
82
83 // ==========================
84
85 function selectObject(obj) {
86 var el = this,
87 ev = d3.event.sourceEvent,
88 n;
89
90 if (api.zoomingOrPanning(ev)) {
91 return;
92 }
93
94 if (el) {
95 n = d3.select(el);
96 } else {
97 api.node().each(function (d) {
98 if (d == obj) {
99 n = d3.select(el = this);
100 }
101 });
102 }
103 if (!n) return;
104
105 if (ev.shiftKey && n.classed('selected')) {
106 deselectObject(obj.id);
107 updateDetail();
108 return;
109 }
110
111 if (!ev.shiftKey) {
112 deselectAll();
113 }
114
115 selections[obj.id] = { obj: obj, el: el };
116 selectOrder.push(obj.id);
117
118 n.classed('selected', true);
119 api.updateDeviceColors(obj);
120 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800121 }
122
123 function deselectObject(id) {
124 var obj = selections[id];
125 if (obj) {
126 d3.select(obj.el).classed('selected', false);
127 delete selections[id];
128 fs.removeFromArray(id, selectOrder);
129 api.updateDeviceColors(obj.obj);
130 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800131 }
132
133 function deselectAll() {
134 // deselect all nodes in the network...
135 api.node().classed('selected', false);
136 selections = {};
137 selectOrder = [];
138 api.updateDeviceColors();
139 updateDetail();
Simon Hunt08f841d02015-02-10 14:39:20 -0800140 }
141
142 // === -----------------------------------------------------
143
144 function requestDetails() {
145 var data = getSel(0).obj;
146 api.sendEvent('requestDetails', {
147 id: data.id,
148 class: data.class
149 });
150 }
151
152 // === -----------------------------------------------------
153
154 function updateDetail() {
155 var nSel = selectOrder.length;
156 if (!nSel) {
157 emptySelect();
158 } else if (nSel === 1) {
159 singleSelect();
160 } else {
161 multiSelect();
162 }
163 }
164
165 function emptySelect() {
166 haveDetails = false;
167 tps.hideDetailPanel();
Simon Huntf542d842015-02-11 16:20:33 -0800168 tts.cancelTraffic();
Simon Hunt08f841d02015-02-10 14:39:20 -0800169 }
170
171 function singleSelect() {
172 // NOTE: detail is shown from 'showDetails' event callback
173 requestDetails();
Simon Huntf542d842015-02-11 16:20:33 -0800174 tts.cancelTraffic();
175 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -0800176 }
177
178 function multiSelect() {
179 haveDetails = true;
180
181 // display the selected nodes in the detail panel
182 tps.displayMulti(selectOrder);
183
184 // always add the 'show traffic' action
Simon Huntf542d842015-02-11 16:20:33 -0800185 tps.addAction('Show Related Traffic', tts.showRelatedIntentsAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800186
187 // add other actions, based on what is selected...
188 if (nSel() === 2 && allSelectionsClass('host')) {
Simon Huntf542d842015-02-11 16:20:33 -0800189 tps.addAction('Create Host-to-Host Flow', tts.addHostIntentAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800190 } else if (nSel() >= 2 && allSelectionsClass('host')) {
Simon Huntf542d842015-02-11 16:20:33 -0800191 tps.addAction('Create Multi-Source Flow', tts.addMultiSourceIntentAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800192 }
193
Simon Huntf542d842015-02-11 16:20:33 -0800194 tts.cancelTraffic();
195 tts.requestTrafficForMode();
Simon Hunt08f841d02015-02-10 14:39:20 -0800196 }
197
198
199 // === -----------------------------------------------------
200 // Event Handlers
201
202 function showDetails(data) {
203 haveDetails = true;
204
205 // display the data for the single selected node
206 tps.displaySingle(data);
207
208 // always add the 'show traffic' action
Simon Huntf542d842015-02-11 16:20:33 -0800209 tps.addAction('Show Related Traffic', tts.showRelatedIntentsAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800210
211 // add other actions, based on what is selected...
212 if (data.type === 'switch') {
Simon Huntf542d842015-02-11 16:20:33 -0800213 tps.addAction('Show Device Flows', tts.showDeviceLinkFlowsAction);
Simon Hunt08f841d02015-02-10 14:39:20 -0800214 }
215
216 // only show the details panel if the user hasn't "hidden" it
217 if (useDetails) {
218 tps.showDetailPanel();
219 }
220 }
221
Simon Hunt6036b192015-02-11 11:20:26 -0800222 function toggleDetails() {
223 useDetails = !useDetails;
224 if (useDetails) {
225 flash.flash('Enable details panel');
226 if (haveDetails) {
227 tps.showDetailPanel();
228 }
229 } else {
230 flash.flash('Disable details panel');
231 tps.hideDetailPanel();
232 }
233 }
234
Simon Huntf542d842015-02-11 16:20:33 -0800235 function validateSelectionContext() {
236 if (!hovered && !nSel()) {
237 tts.cancelTraffic();
238 return false;
239 }
240 return true;
Simon Hunt08f841d02015-02-10 14:39:20 -0800241 }
Simon Hunt08f841d02015-02-10 14:39:20 -0800242
243 // === -----------------------------------------------------
244 // === MODULE DEFINITION ===
245
246 angular.module('ovTopo')
Simon Hunt75ec9692015-02-11 16:40:36 -0800247 .factory('TopoSelectService',
Simon Hunt6036b192015-02-11 11:20:26 -0800248 ['$log', 'FnService', 'FlashService', 'TopoPanelService',
Simon Huntf542d842015-02-11 16:20:33 -0800249 'TopoTrafficService',
Simon Hunt08f841d02015-02-10 14:39:20 -0800250
Simon Huntf542d842015-02-11 16:20:33 -0800251 function (_$log_, _fs_, _flash_, _tps_, _tts_) {
Simon Hunt6036b192015-02-11 11:20:26 -0800252 $log = _$log_;
253 fs = _fs_;
254 flash = _flash_;
255 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}());