VisualizationTopologyView.java 23.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab
 *
 * This file is part of PeerfactSim.KOM.
 * 
 * PeerfactSim.KOM is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * PeerfactSim.KOM is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PeerfactSim.KOM.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.topology.views;

import java.awt.AWTEvent;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
29
import java.awt.Frame;
30
31
32
33
34
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.Toolkit;
35
import java.awt.event.*;
36
import java.awt.geom.Point2D;
37
import java.util.Collection;
38
39
40
41
42
43
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArrayList;

44
import javax.swing.*;
45
46
47
48
49
50
51

import com.google.common.collect.Maps;

import de.tud.kom.p2psim.api.linklayer.mac.Link;
import de.tud.kom.p2psim.api.linklayer.mac.MacAddress;
import de.tud.kom.p2psim.api.linklayer.mac.MacLayer;
import de.tud.kom.p2psim.api.linklayer.mac.PhyType;
52
import de.tud.kom.p2psim.api.topology.Topology;
53
54
55
56
57
58
import de.tud.kom.p2psim.api.topology.TopologyComponent;
import de.tud.kom.p2psim.api.topology.obstacles.ObstacleModel;
import de.tud.kom.p2psim.api.topology.views.TopologyView;
import de.tud.kom.p2psim.api.topology.waypoints.WaypointModel;
import de.tud.kom.p2psim.impl.topology.views.visualization.ComponentVisManager;
import de.tud.kom.p2psim.impl.topology.views.visualization.ComponentVisManager.VisInfo;
59
60
import de.tud.kom.p2psim.impl.topology.views.visualization.ComponentVisManager.VisualizationListener;
import de.tud.kom.p2psim.impl.topology.views.visualization.ui.InteractiveVisualizationComponent;
61
62
import de.tud.kom.p2psim.impl.topology.views.visualization.ui.PlottingView;
import de.tud.kom.p2psim.impl.topology.views.visualization.ui.SimControlPanel;
63
import de.tud.kom.p2psim.impl.topology.views.visualization.ui.VisualizationComponent;
64
65
66
67
68
import de.tud.kom.p2psim.impl.topology.views.visualization.world.NodeVisInteractionListener;
import de.tud.kom.p2psim.impl.topology.views.visualization.world.ObstacleComponentVis;
import de.tud.kom.p2psim.impl.topology.views.visualization.world.StrongWaypointComponentVis;
import de.tud.kom.p2psim.impl.topology.views.visualization.world.WeakWaypointComponentVis;
import de.tud.kom.p2psim.impl.util.NotSupportedException;
69
import de.tudarmstadt.maki.simonstrator.api.Binder;
70
import de.tudarmstadt.maki.simonstrator.api.Host;
71
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
72
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
73
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationListener;
74
75
76
77
78
79
80
81
82
83
84
85
86
87

/**
 * A very basic visualization of a Topology (ie. positioning and movement), just
 * to ease further debugging and development of movement and obstacle-related
 * classes.
 * 
 * It is added to the Topology as a TopologyView with no PHY-Type (and therefore
 * it is never used inside a MAC). Lateron an API will be provided that allows
 * other components (analyzers) to display information using this basic
 * Visualization.
 * 
 * @author Bjoern Richerzhagen
 * @version 1.0, 19.03.2012
 */
88
public class VisualizationTopologyView extends JFrame
89
		implements TopologyView, Runnable, VisualizationListener {
90
91
92
93
94
95
96
97
98

	protected final int WORLD_X;

	protected final int WORLD_Y;

	protected final WorldPanel worldPanel;

	protected SimControlPanel simControls;

99
100
	protected JSplitPane splitPane;

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
	public static final Stroke STROKE_BASIC = new BasicStroke(0.8f);

	public static final Stroke STROKE_THICK = new BasicStroke(2f);

	public static final Font FONT_TINY = new Font("SansSerif", Font.PLAIN, 7);

	public static final Font FONT_MEDIUM = new Font("SansSerif", Font.PLAIN, 9);

	private ComponentVisManager visManager;

	private WeakWaypointComponentVis weakWaypointComponentVis;

	private ObstacleComponentVis obstacleComponentVis;

	private StrongWaypointComponentVis strongWaypointComponentVis;

	/**
	 * 
	 */
120
	public VisualizationTopologyView() {
121
122
123
124
		WORLD_X = (int) Binder.getComponentOrNull(Topology.class)
				.getWorldDimensions().getX();
		WORLD_Y = (int) Binder.getComponentOrNull(Topology.class)
				.getWorldDimensions().getY();
125
126
127
128
129
		worldPanel = new WorldPanel();
		visManager = new ComponentVisManager(worldPanel);

		VisualizationInjector.setVariables(this, visManager, worldPanel,
				WORLD_X, WORLD_Y);
130
131
132
133
134
135

		splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation(1.0);
		splitPane.setResizeWeight(1.0);

136
137
138
		worldPanel.setPreferredSize(
				new Dimension(VisualizationInjector.getWorldX(),
						VisualizationInjector.getWorldY()));
139
		this.setPreferredSize(new Dimension(800, 600));
140
141
		this.setExtendedState(Frame.MAXIMIZED_BOTH);

142

143
144
145
146
147
148
149
150
151
152
153
		Thread t = new Thread(this);
		t.start();
	}

	@Override
	public void run() {

		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setLayout(new BorderLayout());
		this.setTitle("Topology Visualization");

154
155
		visManager.addVisualizationListener(this);

156
157
158
159
		/*
		 * World-view
		 */
		JScrollPane mapScrollPanel = new JScrollPane(worldPanel,
160
161
				ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
				ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
162
163
164
		mapScrollPanel.getVerticalScrollBar().setUnitIncrement(50);
		mapScrollPanel.getHorizontalScrollBar().setUnitIncrement(50);
		mapScrollPanel.setBackground(Color.DARK_GRAY);
165
166
		splitPane.setLeftComponent(mapScrollPanel);
		getContentPane().add(BorderLayout.CENTER, splitPane);
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187

		buildSimControls();

		getRootPane().setDoubleBuffered(true);

		/*
		 * Finally, lights!
		 */
		this.pack();
		this.setVisible(true);

		// for the movement of the line
		final Timer timer = new Timer(250, null);
		timer.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				worldPanel.invalidate();
				timer.setDelay(250);
			}
		});
		timer.start();
	}
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

	@Override
	public void visualizationAdded(VisInfo visInfo) {
		/*
		 * Need demo controls?
		 */
		VisualizationComponent visComp = visInfo.getVisualizationComponent();
		if (visComp != null
				&& visComp instanceof InteractiveVisualizationComponent) {
			JComponent sidebar = ((InteractiveVisualizationComponent) visComp)
					.getSidebarComponent();
			if (sidebar != null) {
				splitPane.setRightComponent(sidebar);
				splitPane.setDividerLocation(0.8);
				splitPane.validate();
			}
		}
	}

	@Override
	public void visualizationRemoved(VisInfo visInfo) {
		// do not care
210
211
212
	}

	private void buildSimControls() {
213
		simControls = new SimControlPanel(visManager);
214
		getContentPane().add(BorderLayout.NORTH, simControls);
215
216
		getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
				.put(KeyStroke.getKeyStroke(KeyEvent.VK_P, 0), "pause");
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
		Action pauseAction = new AbstractAction() {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {
				getSimControls().togglePlayPause();
			}
		};
		getRootPane().getActionMap().put("pause", pauseAction);
	}

	public SimControlPanel getSimControls() {
		return simControls;
	}

	@Override
	public void addedComponent(TopologyComponent comp) {
233
		worldPanel.addTopologyComponent(comp);
234
235
236
237
238
239
240
241
242
243
244
245
	}

	@Override
	public void changedWaypointModel(WaypointModel model) {
		if (weakWaypointComponentVis != null) {
			this.visManager.removeComponent(weakWaypointComponentVis);
			this.visManager.removeComponent(strongWaypointComponentVis);
			// worldPanel.remove(waypointComponentVis);
		}

		if (model == null)
			return;
246
247
		weakWaypointComponentVis = new WeakWaypointComponentVis(model);
		strongWaypointComponentVis = new StrongWaypointComponentVis(model);
248
		this.visManager.addComponent("Streets", 0, weakWaypointComponentVis);
249
250
		this.visManager.addComponent("Waypoints", 0,
				strongWaypointComponentVis);
251
252
253
254
255
256
257
258
259
260
261
		// worldPanel.add(waypointComponentVis);
	}

	@Override
	public void changedObstacleModel(ObstacleModel model) {
		if (obstacleComponentVis != null)
			this.visManager.removeComponent(obstacleComponentVis);
		// worldPanel.remove(obstacleComponentVis);

		if (model == null)
			return;
262
		obstacleComponentVis = new ObstacleComponentVis(model);
263
264
265
266
267
268
269
270
271
272
273
274
		this.visManager.addComponent("Buildings", 0, obstacleComponentVis);
		// worldPanel.add(obstacleComponentVis);
	}

	/*
	 * @Override public void addedObstacle(Obstacle obstacle) { if (obstacle
	 * instanceof PolygonObstacle) { ObstacleComponentVis obsVis = new
	 * ObstacleComponentVis( ((PolygonObstacle) obstacle).getAwtPolygon(),
	 * obstacle); worldPanel.add(obsVis); } }
	 */

	@Override
275
	public void onLocationChanged(Host host, Location location) {
276
		// The NodeInformation objects are registered as listener.
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
	}

	@Override
	public Link getBestNextLink(MacAddress source, MacAddress lastHop,
			MacAddress currentHop, MacAddress destination) {
		throw new UnsupportedOperationException();
	}

	@Override
	public Link getLinkBetween(MacAddress source, MacAddress destination) {
		throw new UnsupportedOperationException();
	}

	@Override
	public MacLayer getMac(MacAddress address) {
		throw new UnsupportedOperationException();
	}

295
296
297
298
	@Override
	public Collection<MacLayer> getAllMacs() {
		throw new UnsupportedOperationException();
	}
299

300
301
302
303
304
305
306
307
308
309
	@Override
	public List<MacAddress> getNeighbors(MacAddress address) {
		throw new UnsupportedOperationException();
	}

	@Override
	public PhyType getPhyType() {
		return null;
	}

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
	/**
	 * Mini-Container for per-node information
	 * 
	 * @author bjoern
	 * @version 1.0, Jul 5, 2016
	 */
	private static class NodeInformation implements LocationListener {

		public Point2D position;

		public boolean clicked = false;

		public final long hostId;

		public NodeInformation(TopologyComponent comp) {
			this.hostId = comp.getHost().getId().value();
			this.position = comp.getRealPosition().asPoint();
		}

		@Override
		public void onLocationChanged(Host host, Location location) {
			this.position.setLocation(
					VisualizationInjector.scaleValue(location.getLongitude()),
					VisualizationInjector.scaleValue(location.getLatitude()));
		}

	}

338
339
340
341
342
343
344
345
	/**
	 * The world
	 * 
	 * @author Bjoern Richerzhagen
	 * @version 1.0, 19.03.2012
	 */
	protected class WorldPanel extends JLayeredPane {

346
347
348
349
		protected HashMap<INodeID, NodeInformation> nodeInformation = new HashMap<INodeID, NodeInformation>();

		protected final static int PADDING = 16;

350
351
352
353
		private static final long serialVersionUID = -3023020559483652110L;

		public WorldPanel() {
			this.setLayout(null);
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
			this.setDoubleBuffered(true);
			this.addMouseListener(new MouseAdapter() {

				/**
				 * Stores the mouse position, if the mouse button is pressed
				 */
				@Override
				public void mousePressed(MouseEvent e) {
					boolean turnedSomeoneOff = false;
					for (NodeInformation node : nodeInformation.values()) {
						// Make it easier to turn things off.
						if (node.clicked && node.position
								.distance(e.getPoint()) < PADDING + 2) {
							node.clicked = !node.clicked;
							VisualizationInjector
									.notifyInteractionListenersOnClick(
											node.hostId, node.clicked);
							turnedSomeoneOff = true;
						}
					}
					if (!turnedSomeoneOff) {
						// Turn sth on (limit to one node)
						for (NodeInformation node : nodeInformation.values()) {
							if (!node.clicked && node.position
									.distance(e.getPoint()) < PADDING) {
								node.clicked = !node.clicked;
								VisualizationInjector
										.notifyInteractionListenersOnClick(
												node.hostId, node.clicked);
								break;
							}
						}
					}
				}

			});
		}

		public void addTopologyComponent(TopologyComponent comp) {
			if (!nodeInformation.containsKey(comp.getHost().getId())) {
				NodeInformation tVis = new NodeInformation(comp);
				comp.requestLocationUpdates(null, tVis);
				nodeInformation.put(comp.getHost().getId(), tVis);
			}
398
399
400
401
402
403
404
405
406
407
408
409
410
411
		}

		@Override
		public void invalidate() {
			super.invalidate();
			repaint();
		}

		@Override
		protected void paintComponent(Graphics g) {
			Graphics2D g2 = (Graphics2D) g;
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
			paintPlayingFieldBorder(g2);
412
			paintNodes(g2);
413
			super.paintComponent(g);
414
415
		}

416
417
		private void paintNodes(Graphics2D g2) {
			for (NodeInformation node : nodeInformation.values()) {
Nils Richerzhagen's avatar
Nils Richerzhagen committed
418
				// TODO FIXME add way to provide draw() impl for nodes
419
420
421
422
423
				if (node.clicked) {
					g2.setColor(Color.MAGENTA);
					g2.fillOval((int) node.position.getX() - PADDING,
							(int) node.position.getY() - PADDING,
							PADDING * 2 + 1, PADDING * 2 + 1);
Nils Richerzhagen's avatar
Nils Richerzhagen committed
424
425
				} else {
					g2.setColor(Color.BLACK);
426
427
					g2.fillOval((int) node.position.getX() - 2,
							(int) node.position.getY() - 2, 5, 5);
428
429
430
431
				}
			}
		}

432
433
434
435
		private void paintPlayingFieldBorder(Graphics2D g2) {
			g2.setColor(Color.DARK_GRAY);
			g2.fillRect(0, 0, getWidth(), getHeight());
			g2.setColor(Color.WHITE);
436
437
			g2.fillRect(0, 0, VisualizationInjector.getWorldX(),
					VisualizationInjector.getWorldY());
438
439
440
			g2.setPaint(Color.MAGENTA);
			g2.setStroke(new BasicStroke(2));
			g2.drawLine(0, 0, VisualizationInjector.scaleValue(100), 0);
441
442
443
444
		}

	}

445
446
	public void notifyInteractionListenersOnClick(long hostid,
			boolean isActive) {
447
448
449
450
451
452
453
		VisualizationInjector.notifyInteractionListenersOnClick(hostid,
				isActive);
	}

	@Deprecated
	public void setPhy(String phy) {
		// legacy
454
455
		System.err.println(
				"The VisualizationTopologyView does no longer require PHY to be configured!");
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
	}

	/**
	 * This component allows the injection of JComponents into the visualization
	 * from anywhere in the simulator. It is marked deprecated due to its hacky
	 * nature. Integrate the visualization into the topology view instead.
	 * 
	 * TODO: Add checks for every component or plotting view if a graphical
	 * environment is present. This will allow calls to the
	 * VisualizationInjector on servers without failing. e.g. supply dummy a
	 * PlottingView or add additional checks to the plotting view for such
	 * cases.
	 * 
	 * @author Fabio Zöllner
	 * @version 1.0, 03.04.2012
	 */
	public static class VisualizationInjector {
		protected static WorldPanel worldPanel;

		public static ComponentVisManager visManager;

		protected static ConcurrentLinkedQueue<VisInfo> components = new ConcurrentLinkedQueue<VisInfo>();

		protected static Map<String, VisInfo> nameToVisInfoMap = Maps
				.newConcurrentMap();

		protected static ConcurrentLinkedQueue<DisplayString> displayStrings = new ConcurrentLinkedQueue<DisplayString>();

		protected static List<MouseClickListener> mouseListeners = new CopyOnWriteArrayList<MouseClickListener>();

		protected static VisualizationTopologyView view;

		protected static ConcurrentLinkedQueue<NodeVisInteractionListener> interactionListeners = new ConcurrentLinkedQueue<NodeVisInteractionListener>();

		protected static long eventMask = AWTEvent.MOUSE_MOTION_EVENT_MASK
				+ AWTEvent.MOUSE_EVENT_MASK;

493
494
495
		private static int WORLD_X = 0;

		private static int WORLD_Y = 0;
496

497
		private static double SCALE = 1;
498
499
500
501
502
503
504
505
506
507

		protected static Map<String, PlottingView> plottingViews = Maps
				.newHashMap();

		public static PlottingView createPlottingView(String name) {
			PlottingView view = new PlottingView(name);
			plottingViews.put(name, view);
			return view;
		}

508
509
		public static PlottingView createPlottingView(String name, int x,
				int y) {
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
			PlottingView view = new PlottingView(name);
			plottingViews.put(name, view);

			view.setBounds(x, y, view.getWidth(), view.getHeight());
			view.setVisible(true);

			return view;
		}

		public static PlottingView getPlottingView(String name) {
			return plottingViews.get(name);
		}

		protected static void notifyInteractionListenersOnClick(long hostid,
				boolean isActive) {
			for (NodeVisInteractionListener listener : VisualizationInjector.interactionListeners) {
				listener.onHostClick(hostid, isActive);
			}
		}

		protected static void setVariables(VisualizationTopologyView view,
				ComponentVisManager visManager, WorldPanel worldPanel,
				int WORLD_X, int WORLD_Y) {
			VisualizationInjector.view = view;
			VisualizationInjector.worldPanel = worldPanel;
			VisualizationInjector.visManager = visManager;
			VisualizationInjector.WORLD_X = WORLD_X;
			VisualizationInjector.WORLD_Y = WORLD_Y;

			for (VisInfo visInfo : components) {
				visManager.addComponent(visInfo);
				// worldPanel.add(comp);
			}

			worldPanel.add(new DrawDisplayStrings(WORLD_X, WORLD_Y));

			setupAWTEventListener();
		}

		public static VisualizationTopologyView getTopologyView() {
			return view;
		}

		private static void setupAWTEventListener() {
			Toolkit tk = Toolkit.getDefaultToolkit();

			tk.addAWTEventListener(new AWTEventListener() {
				@Override
				public void eventDispatched(AWTEvent e) {
					if (e instanceof MouseEvent) {
						MouseEvent me = (MouseEvent) e;
Nils Richerzhagen's avatar
Nils Richerzhagen committed
561
562
563
564
						if (me.getID() == MouseEvent.MOUSE_CLICKED
								|| me.getID() == MouseEvent.MOUSE_DRAGGED
								|| me.getID() == MouseEvent.MOUSE_PRESSED
								|| me.getID() == MouseEvent.MOUSE_RELEASED) {
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
							// Another dirty hack until we have another graphic
							// drawing system
							if (((JFrame) worldPanel.getTopLevelAncestor())
									.isActive()) {
								int x = me.getXOnScreen();
								int y = me.getYOnScreen();

								if (x > (int) worldPanel.getLocationOnScreen()
										.getX()
										&& y > (int) worldPanel
												.getLocationOnScreen().getY()
										&& x < (int) worldPanel
												.getLocationOnScreen().getX()
												+ worldPanel.getWidth()
										&& y < (int) worldPanel
												.getLocationOnScreen().getX()
												+ worldPanel.getHeight()) {

									x -= (int) worldPanel.getLocationOnScreen()
											.getX();
									y -= (int) worldPanel.getLocationOnScreen()
											.getY();

									for (MouseClickListener l : mouseListeners) {
Nils Richerzhagen's avatar
Nils Richerzhagen committed
589
590
591

										if (me.getID() == MouseEvent.MOUSE_CLICKED) {
											l.mouseClicked(x, y);
592
											l.mouseClicked(x, y, me.getModifiers());
Nils Richerzhagen's avatar
Nils Richerzhagen committed
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
										} else {
											if (l instanceof MouseListener) {
												switch (me.getID()) {
												case MouseEvent.MOUSE_DRAGGED:
													((MouseListener) l)
															.mouseDragged(x, y);
													break;

												case MouseEvent.MOUSE_PRESSED:
													((MouseListener) l)
															.mousePressed(x, y);
													break;

												case MouseEvent.MOUSE_RELEASED:
													((MouseListener) l)
															.mouseReleased(x,
																	y);
													break;
												default:
													break;
												}
											}
										}
616
617
618
619
620
621
622
623
624
625
									}
								}

							}
						}
					}
				}
			}, eventMask);
		}

626

627
		public static interface MouseClickListener {
628
629
630
631
632
633
634
635

			@Deprecated
			/**
			 * Method is deprecated. Uses mouseClicked(int x, int y, int modifier) instead.
			 */
			default void mouseClicked(int x, int y){}

			default void mouseClicked(int x, int y, int modifier) {}
636
637
		}

638
639
		public interface MouseListener extends MouseClickListener {
			void mouseDragged(int x, int y);
Nils Richerzhagen's avatar
Nils Richerzhagen committed
640

641
			void mousePressed(int x, int y);
Nils Richerzhagen's avatar
Nils Richerzhagen committed
642

643
			void mouseReleased(int x, int y);
Nils Richerzhagen's avatar
Nils Richerzhagen committed
644
645
		}

646
647
648
649
650
651
652
653
654
655
656
657
		public static JComponent getWorldPanel() {
			return worldPanel;
		}

		public static void addMouseListener(MouseClickListener listener) {
			mouseListeners.add(listener);
		}

		public static void removeMouseListener(MouseClickListener listener) {
			mouseListeners.remove(listener);
		}

658

659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
		/**
		 * Listener is informed, whenever the user clicks on a node
		 * 
		 * @param listener
		 */
		public static void addInteractionListener(
				NodeVisInteractionListener listener) {
			interactionListeners.add(listener);
		}

		public static void removeInteractionListener(
				NodeVisInteractionListener listener) {
			interactionListeners.remove(listener);
		}

		protected static class DrawDisplayStrings extends JComponent {
			public DrawDisplayStrings(int WORLD_X, int WORLD_Y) {
				super();

				setBounds(0, 0, WORLD_X, WORLD_Y);
				setOpaque(false);
				setVisible(true);
			}

			@Override
			protected void paintComponent(Graphics g) {
				super.paintComponent(g);
				Graphics2D g2 = (Graphics2D) g;

				g2.setColor(Color.BLACK);
				g2.setFont(new Font("SansSerif", Font.PLAIN, 9));

				int height = 10;
				for (DisplayString str : displayStrings) {
					g2.drawString(str.getDisplayString(), 0, height);
					height += 10;
				}
			}
		}

699
700
701
702
703
704
		/**
		 * Returns a SCALED x-length of the world size, only to be used by
		 * visualizations.
		 * 
		 * @return
		 */
705
		public static int getWorldX() {
706
707
			// return WORLD_X;
			return (int) (WORLD_X * getScale());
708
709
		}

710
711
712
713
714
715
		/**
		 * Returns a SCALED y-length of the world size, only to be used by
		 * visualizations.
		 * 
		 * @return
		 */
716
		public static int getWorldY() {
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
			// return WORLD_Y;
			return (int) (WORLD_Y * getScale());
		}

		/**
		 * Still needed by OSM-loader (Fabio), however: should not be used
		 * anymore. The coordinates should be final.
		 * 
		 * @param x
		 * @param y
		 */
		@Deprecated
		public static void setWorldCoordinates(int x, int y) {
			WORLD_X = x;
			WORLD_Y = y;
		}

		public static double getScale() {
			return SCALE;
		}

		public static void setScale(double scale) {
			SCALE = scale;
		}

		public static int scaleValue(double value) {
			return (int) (value * getScale());
744
745
		}

746
747
748
749
750
751
752
753
754
755
756
757
758
759
		/**
		 * Recommended way to add a custom visualization layer.
		 * 
		 * @param comp
		 */
		public static void injectComponent(VisualizationComponent comp) {
			visManager.addComponent(comp);
		}

		/**
		 * @deprecated use injectComponent with a {@link VisualizationComponent}
		 *             instead
		 */
		@Deprecated
760
761
762
763
764
		public static void injectComponent(String name, int priority,
				JComponent component) {
			injectComponent(name, priority, component, true);
		}

765
766
767
768
769
		/**
		 * @deprecated use injectComponent with a {@link VisualizationComponent}
		 *             instead
		 */
		@Deprecated
770
771
		public static void injectComponent(String name, int priority,
				JComponent component, boolean active) {
772
773
774
			injectComponent(name, priority, component, active, true);
		}

775
776
777
778
779
		/**
		 * @deprecated use injectComponent with a {@link VisualizationComponent}
		 *             instead
		 */
		@Deprecated
780
781
		public static void injectComponent(String name, int priority,
				JComponent component, boolean active, boolean showInList) {
782
783
			VisInfo visInfo = new VisInfo(name, priority, component);
			visInfo.setActiveByDefault(active);
784
			visInfo.setShowInList(showInList);
785
786
787
788
789
790
791
792
793

			components.add(visInfo);
			nameToVisInfoMap.put(name, visInfo);

			if (visManager != null) {
				visManager.addComponent(visInfo);
			}
		}

794
795
796
797
798
		/**
		 * @deprecated use removeInjectedComponent with a
		 *             {@link VisualizationComponent} instead
		 */
		@Deprecated
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
		public static void removeInjectedComponent(String name) {
			if (nameToVisInfoMap.containsKey(name)) {
				VisInfo visInfo = nameToVisInfoMap.get(name);
				nameToVisInfoMap.remove(name);
				components.remove(visInfo);
				visManager.removeComponent(name);
			}
		}

		public static void invalidate() {
			worldPanel.invalidate();
		}

		public static void addDisplayString(DisplayString str) {
			displayStrings.add(str);
		}

		public static void removeDisplayString(DisplayString str) {
			displayStrings.remove(str);
		}

		public static interface DisplayString {
			public String getDisplayString();
		}
	}

	@Override
826
	public Location getPosition(MacAddress address) {
827
828
829
830
831
832
833
834
		throw new NotSupportedException();
	}

	@Override
	public double getDistance(MacAddress addressA, MacAddress addressB) {
		throw new NotSupportedException();
	}

835
836
837
838
839
	@Override
	public boolean hasRealLinkLayer() {
		throw new NotSupportedException();
	}

840
}