001    // GraphLab Project: http://graphlab.sharif.edu
002    // Copyright (C) 2008 Mathematical Science Department of Sharif University of Technology
003    // Distributed under the terms of the GNU General Public License (GPL): http://www.gnu.org/licenses/
004    package graphlab.graph.graph;
005    
006    import graphlab.graph.atributeset.GraphNotifiableAttrSet;
007    import graphlab.graph.event.GraphModelListener;
008    import graphlab.platform.attribute.AttributeListener;
009    import graphlab.platform.core.BlackBoard;
010    
011    import javax.swing.*;
012    import java.awt.*;
013    import java.awt.image.BufferedImage;
014    import java.util.HashSet;
015    
016    /**
017     * The basic renderer interface
018     */
019    public abstract class AbstractGraphRenderer extends JPanel implements GraphModelListener, AttributeListener {
020        public static final String EVENT_KEY = "Graph View";
021        HashSet<PaintHandler> postPaintHandlers = new HashSet<PaintHandler>();
022        private GraphModel graph;
023        boolean ignoreRapaints;
024        int minx, miny;
025        boolean isGraphChanged = true;
026        private HashSet<PaintHandler<AbstractGraphRenderer>> prePaintHandlers = new HashSet<PaintHandler<AbstractGraphRenderer>>();
027    
028        public static AbstractGraphRenderer getCurrentGraphRenderer(BlackBoard b) {
029            return b.getData(EVENT_KEY);
030        }
031    
032        protected AbstractGraphRenderer() {
033            super();
034        }
035    
036        public void setGraph(GraphModel g) {
037            this.graph = g;
038            g.addGraphListener(this);
039            new GraphNotifiableAttrSet(g).addAttributeListener(this);
040        }
041    
042        public GraphModel getGraph() {
043            return graph;
044        }
045    
046        /**
047         * adds ph to Post Paint Handlers which means that ph.paint will be called after each rendering of graph
048         *
049         * @param ph
050         */
051        public void addPostPaintHandler(PaintHandler<AbstractGraphRenderer> ph) {
052            postPaintHandlers.add(ph);
053        }
054    
055        /**
056         * adds ph to Pre Paint Handlers which means that ph.paint will be called before each rendering of graph
057         *
058         * @param ph
059         */
060        public void addPrePaintHandler(PaintHandler<AbstractGraphRenderer> ph) {
061            prePaintHandlers.add(ph);
062        }
063    
064        /**
065         * removes ph from both pre and post paint handlers and then repaints the graph
066         *
067         * @param ph
068         */
069        public void removePaintHandler(PaintHandler ph) {
070            postPaintHandlers.remove(ph);
071            prePaintHandlers.remove(ph);
072            repaint();
073        }
074    
075        public abstract void render(Graphics2D gg, Boolean drawExtras);
076    
077        public void repaint() {
078            if (!ignoreRapaints) {
079                super.repaint();
080            }
081        }
082    
083        BufferedImage bi;
084        int lastWidth = 0, lastHeight = 0;
085        boolean xtrans = false, ytrans = false;
086    
087        final public void paint(Graphics gg) {
088            paint(gg, true);
089        }
090    
091        /**
092         * paint the graph on gg
093         *
094         * @param mainG
095         * @param e
096         * @param graph
097         * @param y
098         * @param labelx
099         * @param drawExtras specifies wheter to draw extra things such as Curved Edges Control Points, on graph or not,
100         */
101        final public void paint(Graphics mainG, Boolean drawExtras) {
102            int w = getWidth();
103            int h = getHeight();
104            if (w * h > 1000 * 1500) { //the limit which creating a bufferimage and double buffering consumes too much memory
105                doRender(mainG, w, h, mainG, drawExtras);
106                isGraphChanged = false;
107                bi = null;
108            } else {
109                if (w != lastWidth || h != lastHeight)
110                    isGraphChanged = true;
111    //        showTime(0);
112                if (isGraphChanged || bi == null) {
113    //            showTime(20);
114                    if (w != lastWidth || h != lastHeight) {
115                        bi = this.getGraphicsConfiguration().createCompatibleImage(getWidth(), getHeight());
116                        lastWidth = w;
117                        lastHeight = h;
118                    }
119    
120    //            showTime(21);
121                    Graphics bufferedG = bi.getGraphics();
122                    doRender(bufferedG, w, h, mainG, drawExtras);
123    
124                } else {
125    //            showTime(10);
126                    //painting the buffered graph
127    
128                    doTranslate(mainG);
129                    for (PaintHandler p : prePaintHandlers)
130                        p.paint(mainG, this, drawExtras);
131                    translateBack(mainG);
132    
133    
134                    mainG.drawImage(bi, 0, 0, this);
135    //            showTime(11);
136                    doTranslate(mainG);
137                    for (PaintHandler p : postPaintHandlers)
138                        p.paint(mainG, this, drawExtras);
139                    translateBack(mainG);
140    //            showTime(12);
141                    return;
142                }
143                isGraphChanged = false;
144            }
145    
146        }
147    
148        private void translateBack(Graphics mainG) {
149            if (xtrans) {
150                mainG.translate(minx, 0);
151            }
152            if (ytrans) {
153                mainG.translate(0, miny);
154            }
155        }
156    
157        private void doTranslate(Graphics mainG) {
158            xtrans = false;
159            ytrans = false;
160            if (minx < 0) {
161                mainG.translate(-minx, 0);
162                xtrans = true;
163            }
164            if (miny < 0) {
165                mainG.translate(0, -miny);
166                ytrans = true;
167            }
168        }
169    
170        private void doRender(Graphics bufferedG, int w, int h, Graphics mainG, Boolean drawExtras) {
171            boolean xtrans = false, ytrans = false;
172            if (!ignoreRapaints) {
173                if (minx < 0) {
174                    bufferedG.translate(-minx, 0);
175    //                mainG.translate(-minx, 0);
176                    xtrans = true;
177                }
178                if (miny < 0) {
179                    bufferedG.translate(0, -miny);
180    //                mainG.translate(0, -miny);
181                    ytrans = true;
182                }
183    //                showTime(3);
184                bufferedG.setColor(Color.white);
185    
186    //                g.setClip(minx, miny, w, h);
187                bufferedG.fillRect(minx, miny, w + Math.abs(minx), h + Math.abs(miny));
188    //                showTime(4);
189    //                g.clearRect(0,0, getWidth(), getHeight());
190    
191                for (PaintHandler p : prePaintHandlers)
192                    p.paint(mainG, this, drawExtras);
193                render((Graphics2D) bufferedG, drawExtras);
194    //                showTime(5);
195    
196                mainG.drawImage(bi, 0, 0, this);
197    //                showTime(6);
198                for (PaintHandler p : postPaintHandlers)
199                    p.paint(mainG, this, drawExtras);
200            }
201            if (!ignoreRapaints) {
202                if (xtrans) {
203                    bufferedG.translate(minx, 0);
204    //                mainG.translate(minx, 0);
205                }
206                if (ytrans) {
207                    bufferedG.translate(0, miny);
208    //                mainG.translate(0, miny);
209                }
210            }
211        }
212    
213        long lastTime;
214    
215        void showTime(int i) {
216            if (i == 0) {
217                lastTime = System.currentTimeMillis();
218            }
219    //        System.out.println(i + ":" + (System.currentTimeMillis() - lastTime));
220            lastTime = System.currentTimeMillis();
221        }
222    
223        /**
224         * ignores every repaint event on running the run
225         * <p/>
226         * This method is useful when you want to do a great
227         * change on graph structure
228         * <p/>
229         * It is equal to call ignoreRepaints(run, true)
230         *
231         * @param run
232         */
233        public void ignoreRepaints(Runnable run) {
234            ignoreRepaints(run, true);
235        }
236    
237        /**
238         * Runs run.run() and ignore all repaints until Its execution finishes
239         *
240         * @param run
241         * @param repaintAfter
242         */
243        public void ignoreRepaints(Runnable run, boolean repaintAfter) {
244            this.ignoreRapaints = true;
245            run.run();
246            this.ignoreRapaints = false;
247            isGraphChanged = true;
248            if (repaintAfter)
249                repaint();
250        }
251    
252        //currently listenin to graph attributes
253        public void attributeUpdated(String name, Object oldVal, Object newVal) {
254            isGraphChanged = true;
255        }
256    
257        public void repaintGraph() {
258            isGraphChanged = true;
259            repaint();
260        }
261    
262    
263        public int getMinx() {
264            return minx;
265        }
266    
267        public int getMiny() {
268            return miny;
269        }
270    }