001/*
002 * Copyright 2006 - 2013
003 *     Stefan Balev     <stefan.balev@graphstream-project.org>
004 *     Julien Baudry    <julien.baudry@graphstream-project.org>
005 *     Antoine Dutot    <antoine.dutot@graphstream-project.org>
006 *     Yoann Pigné      <yoann.pigne@graphstream-project.org>
007 *     Guilhelm Savin   <guilhelm.savin@graphstream-project.org>
008 * 
009 * This file is part of GraphStream <http://graphstream-project.org>.
010 * 
011 * GraphStream is a library whose purpose is to handle static or dynamic
012 * graph, create them from scratch, file or any source and display them.
013 * 
014 * This program is free software distributed under the terms of two licenses, the
015 * CeCILL-C license that fits European law, and the GNU Lesser General Public
016 * License. You can  use, modify and/ or redistribute the software under the terms
017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by
019 * the Free Software Foundation, either version 3 of the License, or (at your
020 * option) any later version.
021 * 
022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
024 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
025 * 
026 * You should have received a copy of the GNU Lesser General Public License
027 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
028 * 
029 * The fact that you are presently reading this means that you have had
030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms.
031 */
032package org.graphstream.ui.swingViewer;
033
034import java.awt.BorderLayout;
035import java.awt.Graphics;
036import java.awt.Graphics2D;
037import java.awt.event.WindowEvent;
038import java.awt.event.WindowListener;
039import java.util.ArrayList;
040
041import javax.swing.JFrame;
042
043import org.graphstream.ui.graphicGraph.GraphicElement;
044import org.graphstream.ui.graphicGraph.GraphicGraph;
045import org.graphstream.ui.swingViewer.util.Camera;
046import org.graphstream.ui.swingViewer.util.DefaultMouseManager;
047import org.graphstream.ui.swingViewer.util.DefaultShortcutManager;
048import org.graphstream.ui.swingViewer.util.MouseManager;
049import org.graphstream.ui.swingViewer.util.ShortcutManager;
050
051/**
052 * Base for constructing views.
053 * 
054 * <p>
055 * This base view is an abstract class that provides mechanism that are
056 * necessary in any view :
057 * <ul>
058 * <li>the painting and repainting mechanism.</li>
059 * <li>the optional frame handling.</li>
060 * <li>the frame closing protocol.</li>
061 * </ul>
062 * </p>
063 * 
064 * <p>
065 * This view also handle a current selection of nodes and sprites.
066 * </p>
067 * 
068 * <h3>The painting mechanism</h3>
069 * 
070 * <p>
071 * The main method to implement is {@link #render(Graphics2D)}. This method is
072 * called each time the graph needs to be rendered anew in the canvas.
073 * </p>
074 * 
075 * <p>
076 * The {@link #render(Graphics2D)} is called only when a repainting is really
077 * needed.
078 * </p>
079 * 
080 * <p>
081 * All the painting, by default, is deferred to a {@link GraphRenderer}
082 * instance. This mechanism allows developers that do not want to mess with the
083 * viewer/view mechanisms to render a graph in any Swing surface.
084 * </p>
085 * 
086 * <h3>The optional frame handling</h3>
087 * 
088 * <p>
089 * This abstract view is able to create a frame that is added around this panel
090 * (each view is a JPanel instance). The frame can be removed at any time.
091 * </p>
092 * 
093 * <h3>The frame closing protocol</h3>
094 * 
095 * <p>
096 * This abstract view handles the closing protocol. This means that it will
097 * close the view if needed, or only hide it to allow reopening it later.
098 * Furthermore it adds the "ui.viewClosed" attribute to the graph when the view
099 * is closed or hidden, and removes it when the view is shown. The value of this
100 * graph attribute is the identifier of the view.
101 * </p>
102 */
103public class DefaultView extends View implements WindowListener
104{
105        private static final long serialVersionUID = - 4489484861592064398L;
106
107        /**
108         * Parent viewer.
109         */
110        protected Viewer viewer;
111
112        /**
113         * The graph to render, shortcut to the viewers reference.
114         */
115        protected GraphicGraph graph;
116
117        /**
118         * The (optional) frame.
119         */
120        protected JFrame frame;
121
122        /**
123         * Manager for events with the keyboard.
124         */
125        protected ShortcutManager shortcuts;
126
127        /**
128         * Manager for events with the mouse.
129         */
130        protected MouseManager mouseClicks;
131
132        /**
133         * The graph renderer.
134         */
135        protected GraphRenderer renderer;
136
137        // Construction
138
139        public DefaultView(Viewer viewer, String identifier, GraphRenderer renderer) {
140                super(identifier);
141
142                this.viewer = viewer;
143                this.graph = viewer.getGraphicGraph();
144                this.renderer = renderer;
145
146                setOpaque(false);
147                setDoubleBuffered(true);
148                setMouseManager(null);
149                setShortcutManager(null);
150                renderer.open(graph, this);
151        }
152
153        // Access
154
155        // Command
156
157        @Override
158        public Camera getCamera() {
159                return renderer.getCamera();
160        }
161
162        @Override
163        public void display(GraphicGraph graph, boolean graphChanged) {
164                repaint();
165        }
166        
167        @Override
168        public void paintComponent(Graphics g) {
169                super.paintComponent(g);
170                checkTitle();
171                Graphics2D g2 = (Graphics2D) g;
172                render(g2);
173        }
174
175        protected void checkTitle() {
176                if (frame != null) {
177                        String titleAttr = String.format("ui.%s.title", getId());
178                        String title = (String) graph.getLabel(titleAttr);
179
180                        if (title == null) {
181                                title = (String) graph.getLabel("ui.default.title");
182
183                                if (title == null)
184                                        title = (String) graph.getLabel("ui.title");
185                        }
186
187                        if (title != null)
188                                frame.setTitle(title);
189                }
190        }
191
192        @Override
193        public void close(GraphicGraph graph) {
194                renderer.close();
195                graph.addAttribute("ui.viewClosed", getId());
196        
197                removeKeyListener(shortcuts);
198                shortcuts.release();
199                mouseClicks.release();
200                
201                openInAFrame(false);
202        }
203
204        @Override
205        public void resizeFrame(int width, int height) {
206                if (frame != null) {
207                        frame.setSize(width, height);
208                }
209        }
210
211        @Override
212        public void openInAFrame(boolean on) {
213                if (on) {
214                        if (frame == null) {
215                                frame = new JFrame("GraphStream");
216                                frame.setLayout(new BorderLayout());
217                                frame.add(this, BorderLayout.CENTER);
218                                frame.setSize(800, 600);
219                                frame.setVisible(true);
220                                frame.addWindowListener(this);
221                                frame.addKeyListener(shortcuts);
222                        } else {
223                                frame.setVisible(true);
224                        }
225                } else {
226                        if (frame != null) {
227                                frame.removeWindowListener(this);
228                                frame.removeKeyListener(shortcuts);
229                                frame.remove(this);
230                                frame.setVisible(false);
231                                frame.dispose();
232                        }
233                }
234        }
235
236        public void render(Graphics2D g) {
237                renderer.render(g, getX(), getY(), getWidth(), getHeight());
238
239                String screenshot = (String) graph.getLabel("ui.screenshot");
240
241                if (screenshot != null) {
242                        graph.removeAttribute("ui.screenshot");
243                        renderer.screenshot(screenshot, getWidth(), getHeight());
244                }
245        }
246
247        // Selection
248
249        @Override
250        public void beginSelectionAt(double x1, double y1) {
251                renderer.beginSelectionAt(x1, y1);
252                repaint();
253        }
254
255        @Override
256        public void selectionGrowsAt(double x, double y) {
257                renderer.selectionGrowsAt(x, y);
258                repaint();
259        }
260
261        @Override
262        public void endSelectionAt(double x2, double y2) {
263                renderer.endSelectionAt(x2, y2);
264                repaint();
265        }
266
267        // Window Listener
268
269        public void windowActivated(WindowEvent e) {
270        }
271
272        public void windowClosed(WindowEvent e) {
273        }
274
275        public void windowClosing(WindowEvent e) {
276                graph.addAttribute("ui.viewClosed", getId());
277
278                switch (viewer.getCloseFramePolicy()) {
279                case CLOSE_VIEWER:
280                        viewer.removeView(getId());
281                        break;
282                case HIDE_ONLY:
283                        if (frame != null)
284                                frame.setVisible(false);
285                        break;
286                case EXIT:
287                        System.exit(0);
288                default:
289                        throw new RuntimeException(
290                                        String
291                                                        .format(
292                                                                        "The %s view is not up to date, do not know %s CloseFramePolicy.",
293                                                                        getClass().getName(), viewer
294                                                                                        .getCloseFramePolicy()));
295                }
296        }
297
298        public void windowDeactivated(WindowEvent e) {
299        }
300
301        public void windowDeiconified(WindowEvent e) {
302        }
303
304        public void windowIconified(WindowEvent e) {
305        }
306
307        public void windowOpened(WindowEvent e) {
308                graph.removeAttribute("ui.viewClosed");
309        }
310
311        // Methods deferred to the renderer
312
313        @Override
314        public ArrayList<GraphicElement> allNodesOrSpritesIn(double x1, double y1,
315                        double x2, double y2) {
316                return renderer.allNodesOrSpritesIn(x1, y1, x2, y2);
317        }
318
319        @Override
320        public GraphicElement findNodeOrSpriteAt(double x, double y) {
321                return renderer.findNodeOrSpriteAt(x, y);
322        }
323
324        @Override
325        public void moveElementAtPx(GraphicElement element, double x, double y) {
326                // The feedback on the node positions is often off since not needed
327                // and generating lots of events. We activate it here since the
328                // movement of the node is decided by the viewer. This is one of the
329                // only moment when the viewer really moves a node.
330                boolean on = graph.feedbackXYZ();
331                graph.feedbackXYZ(true);
332                renderer.moveElementAtPx(element, x, y);
333                graph.feedbackXYZ(on);
334        }
335        
336        @Override
337        public void freezeElement(GraphicElement element, boolean frozen) {
338                if(frozen) {
339                        element.addAttribute("layout.frozen");
340                } else {
341                        element.removeAttribute("layout.frozen");
342                }
343        }
344
345        @Override
346        public void setBackLayerRenderer(LayerRenderer renderer) {
347                this.renderer.setBackLayerRenderer(renderer);
348                repaint();
349        }
350
351        @Override
352        public void setForeLayoutRenderer(LayerRenderer renderer) {
353                this.renderer.setForeLayoutRenderer(renderer);
354                repaint();
355        }
356
357        @Override
358        public void setMouseManager(MouseManager manager) {
359                if(mouseClicks != null)
360                        mouseClicks.release();
361                
362                if(manager == null)
363                        manager = new DefaultMouseManager();
364
365                manager.init(graph, this);
366                
367                mouseClicks = manager;
368        }
369
370        @Override
371        public void setShortcutManager(ShortcutManager manager) {
372                if(shortcuts != null)
373                        shortcuts.release();
374                
375                if(manager == null)
376                        manager = new DefaultShortcutManager();
377                
378                manager.init(graph, this);
379                
380                shortcuts = manager;
381        }
382}