blob: 8d9d3c6febb7c2b04baa70452069c85552f564db [file] [log] [blame]
Sean Condon83fc39f2018-04-19 18:56:13 +01001/*
2 * Copyright 2015-present Open Networking Foundation
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 */
16import { Injectable } from '@angular/core';
17import { GlyphService } from './glyph.service';
18import { LogService } from '../../log.service';
19import { SvgUtilService } from './svgutil.service';
20import * as d3 from 'd3';
21
22const vboxSize = 50;
23const cornerSize = vboxSize / 10;
24const viewBox = '0 0 ' + vboxSize + ' ' + vboxSize;
25
26export const glyphMapping = new Map<string, string>([
27 // Maps icon ID to the glyph ID it uses.
28 // NOTE: icon ID maps to a CSS class for styling that icon
29 ['active', 'checkMark'],
30 ['inactive','xMark'],
31
32 ['plus','plus'],
33 ['minus','minus'],
34 ['play','play'],
35 ['stop','stop'],
36
37 ['upload','upload'],
38 ['download','download'],
39 ['delta','delta'],
40 ['nonzero','nonzero'],
41 ['close','xClose'],
42
43 ['topo','topo'],
44
45 ['refresh','refresh'],
46 ['query','query'],
47 ['garbage','garbage'],
48
49
50 ['upArrow','triangleUp'],
51 ['downArrow','triangleDown'],
52
53 ['appInactive','unknown'],
54
55 ['node','node'],
56 ['devIcon_SWITCH','switch'],
57 ['devIcon_ROADM','roadm'],
58 ['devIcon_OTN','otn'],
59
60 ['portIcon_DEFAULT','m_ports'],
61
62 ['meter','meterTable'], // TODO: m_meter icon?
63
64 ['deviceTable','switch'],
65 ['flowTable','flowTable'],
66 ['portTable','portTable'],
67 ['groupTable','groupTable'],
68 ['meterTable','meterTable'],
69 ['pipeconfTable','pipeconfTable'],
70
71 ['hostIcon_endstation','endstation'],
72 ['hostIcon_router','router'],
73 ['hostIcon_bgpSpeaker','bgpSpeaker'],
74
75 // navigation menu icons...
76 ['nav_apps','bird'],
77 ['nav_settings','cog'],
78 ['nav_cluster','node'],
79 ['nav_processors','allTraffic'],
80
81 ['nav_topo','topo'],
82 ['nav_topo2','m_cloud'],
83 ['nav_devs','switch'],
84 ['nav_links','ports'],
85 ['nav_hosts','endstation'],
86 ['nav_intents','relatedIntents'],
87 ['nav_tunnels','ports'], // TODO: use tunnel glyph, when available
88 ['nav_yang','yang'],
89]);
90
91/**
92 * ONOS GUI -- SVG -- Icon Service
93 */
94@Injectable()
95export class IconService {
96
97 constructor(
98 private gs: GlyphService,
99 private log: LogService,
100 private sus: SvgUtilService
101 ) {
102
103 this.log.debug('IconService constructed');
104 }
105
106 ensureIconLibDefs() {
107 let body = d3.select('body');
108 let svg = body.select('svg#IconLibDefs');
109 if (svg.empty()) {
110 svg = body.append('svg').attr('id', 'IconLibDefs');
111 svg.append('defs');
112 }
113 return svg.select('defs');
114 }
115
116 /**
117 * Load an icon
118 *
119 * @param div A D3 selection of the '&lt;div&gt;' element into which icon should load
120 * @param glyphId Identifies the glyph to use
121 * @param size The dimension of icon in pixels. Defaults to 20.
122 * @param installGlyph If truthy, will cause the glyph to be added to
123 * well-known defs element. Defaults to false.
124 * @param svgClass The CSS class used to identify the SVG layer.
125 * Defaults to 'embeddedIcon'.
126 */
127 loadIcon(div, glyphId: string = 'unknown', size: number = 20, installGlyph: boolean = true, svgClass: string = 'embeddedIcon') {
128 let dim = size || 20;
129 let svgCls = svgClass || 'embeddedIcon';
130 let gid = glyphId || 'unknown';
131 let g;
132 let svgIcon: any;
133
134 if (installGlyph) {
135 this.gs.loadDefs(this.ensureIconLibDefs(), [gid], true);
136 }
137 this.log.warn('loadEmbeddedIcon. install done');
138
139 svgIcon = div
140 .append('svg')
141 .attr('class', svgCls)
142 .attr('width', dim)
143 .attr('height', dim)
144 .attr('viewBox', viewBox);
145
146 g = svgIcon.append('g')
147 .attr('class', 'icon');
148
149 g.append('rect')
150 .attr('width', vboxSize)
151 .attr('height', vboxSize)
152 .attr('rx', cornerSize);
153
154 g.append('use')
155 .attr('width', vboxSize)
156 .attr('height', vboxSize)
157 .attr('class', 'glyph')
158 .attr('xlink:href', '#' + gid);
159 }
160
161 /**
162 * Load an icon by class.
163 * @param div A D3 selection of the <DIV> element into which icon should load
164 * @param iconCls The CSS class used to identify the icon
165 * @param {number} size The dimension of icon in pixels. Defaults to 20.
166 * @param {boolean} installGlyph If truthy, will cause the glyph to be added to
167 * well-known defs element. Defaults to false.
168 * @param svgClass The CSS class used to identify the SVG layer.
169 * Defaults to 'embeddedIcon'.
170 */
171 loadIconByClass(div, iconCls: string, size: number, installGlyph: boolean, svgClass='embeddedIcon') {
172 this.loadIcon(div, glyphMapping.get(iconCls), size, installGlyph, svgClass);
173 div.select('svg g').classed(iconCls, true);
174 }
175
176 /**
177 * Load an embedded icon.
178 */
179 loadEmbeddedIcon(div, iconCls: string, size: number) {
180 this.loadIconByClass(div, iconCls, size, true);
181 }
182
183 /**
184 * Load an icon only to the svg defs collection
185 *
186 * Note: This is added for use with IconComponent, where the icon's
187 * svg element is defined in the component template (and not built
188 * inline using d3 manipulation
189 *
190 * @param iconCls The icon class as a string
191 */
192 loadIconDef(iconCls: string): void {
193 this.gs.loadDefs(this.ensureIconLibDefs(), [glyphMapping.get(iconCls)], true);
194 this.log.debug('icon defintion', iconCls, 'added to defs');
195 }
196
197
198 /**
199 * Add a device icon
200 *
201 * Adds a device glyph to the specified element.
202 * Returns the D3 selection of the glyph (use) element.
203 */
204 addDeviceIcon(elem, glyphId, iconDim) {
205 let gid = this.gs.glyphDefined(glyphId) ? glyphId : 'query';
206 return elem.append('use').attr({
207 'xlink:href': '#' + gid,
208 width: iconDim,
209 height: iconDim,
210 });
211 }
212
213 addHostIcon(elem, radius, glyphId) {
214 let dim = radius * 1.5;
215 let xlate = -dim / 2;
216 let g = elem.append('g')
217 .attr('class', 'svgIcon hostIcon');
218
219 g.append('circle').attr('r', radius);
220
221 g.append('use').attr({
222 'xlink:href': '#' + glyphId,
223 width: dim,
224 height: dim,
225 transform: this.sus.translate(xlate, xlate),
226 });
227 return g;
228 }
229
230 registerIconMapping(iconId, glyphId) {
231 if (glyphMapping[iconId]) {
232 this.log.warn('Icon with id', iconId, 'already mapped. Ignoring.');
233 } else {
234 // map icon-->glyph
235 glyphMapping[iconId] = glyphId;
236 // make sure definition is installed
237 this.gs.loadDefs(this.ensureIconLibDefs(), [glyphId], true);
238 }
239 }
240}