001    /*
002      Copyright (C) 2003 Laurent Martelli <laurent@aopsys.com>
003    
004      This program is free software; you can redistribute it and/or modify
005      it under the terms of the GNU Lesser General Public License as
006      published by the Free Software Foundation; either version 2 of the
007      License, or (at your option) any later version.
008    
009      This program is distributed in the hope that it will be useful,
010      but WITHOUT ANY WARRANTY; without even the implied warranty of
011      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
012      GNU Lesser General Public License for more details.
013    
014      You should have received a copy of the GNU Lesser General Public
015      License along with this program; if not, write to the Free Software
016      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
017    */
018    
019    package org.objectweb.jac.ide.diagrams;
020    
021    import CH.ifa.draw.contrib.AutoscrollHelper;
022    import CH.ifa.draw.framework.ConnectionFigure;
023    import CH.ifa.draw.framework.Connector;
024    import CH.ifa.draw.framework.Drawing;
025    import CH.ifa.draw.framework.DrawingChangeEvent;
026    import CH.ifa.draw.framework.DrawingEditor;
027    import CH.ifa.draw.framework.DrawingView;
028    import CH.ifa.draw.framework.Figure;
029    import CH.ifa.draw.framework.FigureEnumeration;
030    import CH.ifa.draw.framework.FigureSelection;
031    import CH.ifa.draw.framework.FigureSelectionListener;
032    import CH.ifa.draw.framework.Handle;
033    import CH.ifa.draw.framework.Painter;
034    import CH.ifa.draw.framework.PointConstrainer;
035    import CH.ifa.draw.framework.Tool;
036    import CH.ifa.draw.standard.FigureEnumerator;
037    import CH.ifa.draw.standard.SimpleUpdateStrategy;
038    import CH.ifa.draw.standard.StandardFigureSelection;
039    import CH.ifa.draw.util.Geom;
040    import java.awt.Color;
041    import java.awt.Dimension;
042    import java.awt.Graphics;
043    import java.awt.Insets;
044    import java.awt.Point;
045    import java.awt.PrintGraphics;
046    import java.awt.Rectangle;
047    import java.awt.dnd.Autoscroll;
048    import java.awt.event.KeyEvent;
049    import java.awt.event.KeyListener;
050    import java.awt.event.MouseEvent;
051    import java.awt.event.MouseListener;
052    import java.awt.event.MouseMotionListener;
053    import java.util.Enumeration;
054    import java.util.Vector;
055    import javax.swing.JPanel;
056    import javax.swing.Scrollable;
057    
058    /**
059     * Same as CH.ifa.draw.standard.StandardDrawingView, but with specific
060     * handling of "remove".  
061     */
062    public  class IDEDrawingView extends JPanel
063        implements DrawingView, KeyListener, Autoscroll, Scrollable {
064    
065        /**
066         * The DrawingEditor of the view.
067         * @see #tool
068         * @see #setStatus
069         */
070        transient private DrawingEditor   fEditor;
071    
072        /** the registered listeners for selection changes */
073        private transient Vector fSelectionListeners;
074            
075        /** The shown drawing. */
076        private Drawing         fDrawing;
077    
078        /** the accumulated damaged area */
079        private transient Rectangle fDamage = null;
080    
081        /**
082         * The list of currently selected figures.
083         */
084        transient private Vector fSelection;
085    
086        /**
087         * The shown selection handles.
088         */
089        transient private Vector fSelectionHandles;
090    
091        /**
092         * The preferred size of the view
093         */
094        private Dimension fViewSize;
095    
096        /**
097         * The position of the last mouse click
098         * inside the view.
099         */
100        private Point fLastClick;
101    
102        /**
103         * A vector of optional backgrounds. The vector maintains
104         * a list a view painters that are drawn before the contents,
105         * that is in the background.
106         */
107        private Vector fBackgrounds = null;
108    
109        /**
110         * A vector of optional foregrounds. The vector maintains
111         * a list a view painters that are drawn after the contents,
112         * that is in the foreground.
113         */
114        private Vector fForegrounds = null;
115    
116        /**
117         * The update strategy used to repair the view.
118         */
119        private Painter fUpdateStrategy;
120    
121        /**
122         * The grid used to constrain points for snap to
123         * grid functionality.
124         */
125        private PointConstrainer fConstrainer;
126    
127        /**
128         * Scrolling increment
129         */
130        public static final int MINIMUM_WIDTH = 400;
131        public static final int MINIMUM_HEIGHT = 300;
132        public static final int SCROLL_INCR = 100;
133        public static final int SCROLL_OFFSET = 10;
134    
135        public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
136            return SCROLL_OFFSET;
137        }
138        public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
139            return SCROLL_INCR;
140        }
141    
142        public boolean getScrollableTracksViewportWidth() {
143            return false;
144        }
145        public boolean getScrollableTracksViewportHeight() {
146            return false;
147        }
148        public Dimension getPreferredScrollableViewportSize() {
149            return getPreferredSize();
150        }
151    
152        /*
153         * Serialization support. In JavaDraw only the Drawing is serialized.
154         * However, for beans support StandardDrawingView supports
155         * serialization
156         */
157        private static final long serialVersionUID = -3878153366174603336L;
158    
159        /**
160         * Constructs the view.
161         */
162        public IDEDrawingView(DrawingEditor editor) {
163            this(editor, MINIMUM_WIDTH, MINIMUM_HEIGHT);
164        }
165            
166        public IDEDrawingView(DrawingEditor editor, int width, int height) {
167            setAutoscrolls(true);
168            counter++;
169            fEditor = editor;
170            fViewSize = new Dimension(width,height);
171            fSelectionListeners = new Vector();
172            addFigureSelectionListener(editor());
173            fLastClick = new Point(0, 0);
174            fConstrainer = null;
175            fSelection = new Vector();
176            // JFC/Swing uses double buffering automatically as default
177            setDisplayUpdate(new SimpleUpdateStrategy());
178            // TODO: Test FastBufferedUpdateStrategy with JFC/Swing double buffering
179            //setDisplayUpdate(new FastBufferedUpdateStrategy());
180            setBackground(Color.lightGray);
181    
182            addMouseListener(ml);
183            addMouseMotionListener(mml);
184            addKeyListener(this);
185        }
186    
187        MouseListener ml = new MouseListener() {
188                // listener methods we are not interested in
189                public void mouseClicked(MouseEvent e) {}
190                public void mouseEntered(MouseEvent e) {}
191                public void mouseExited(MouseEvent e) {}
192                /**
193                 * Handles mouse down events. The event is delegated to the
194                 * currently active tool.
195                 * @return whether the event was handled.
196                 */
197                public void mousePressed(MouseEvent e) {
198                    if (tool()!=null) {
199                        requestFocus(); // JDK1.1
200                        Point p = constrainPoint(new Point(e.getX(), e.getY()));
201                        fLastClick = new Point(e.getX(), e.getY());
202                        tool().mouseDown(e, p.x, p.y);
203                        checkDamage();
204                    }
205                }
206                /**
207                 * Handles mouse up events. The event is delegated to the
208                 * currently active tool.
209                 * @return whether the event was handled.
210                 */
211                public void mouseReleased(MouseEvent e) {
212                    if (tool()!=null) {
213                        Point p = constrainPoint(new Point(e.getX(), e.getY()));
214                        tool().mouseUp(e, p.x, p.y);
215                        checkDamage();
216                    }
217                }
218            };
219    
220        MouseMotionListener mml = new MouseMotionListener() {
221                /**
222                 * Handles mouse drag events. The event is delegated to the
223                 * currently active tool.
224                 * @return whether the event was handled.
225                 */
226                public void mouseDragged(MouseEvent e) {
227                    if (tool()!=null) {
228                        Point p = constrainPoint(new Point(e.getX(), e.getY()));
229                        tool().mouseDrag(e, p.x, p.y);
230                        checkDamage();
231                    }
232                }
233    
234                /**
235                 * Handles mouse move events. The event is delegated to the
236                 * currently active tool.
237                 * @return whether the event was handled.
238                 */
239                public void mouseMoved(MouseEvent e) {
240                    if (tool()!=null) {
241                        tool().mouseMove(e, e.getX(), e.getY());
242                    }
243                }
244            };
245    
246        /**
247         * Sets the view's editor.
248         */
249        public void setEditor(DrawingEditor editor) {
250            fEditor = editor;
251        }
252    
253        /**
254         * Gets the current tool.
255         */
256        public Tool tool() {
257            return editor().tool();
258        }
259    
260        /**
261         * Gets the drawing.
262         */
263        public Drawing drawing() {
264            return fDrawing;
265        }
266    
267        /**
268         * Sets and installs another drawing in the view.
269         */
270        public void setDrawing(Drawing d) {
271            if (fDrawing != null) {
272                clearSelection();
273                fDrawing.removeDrawingChangeListener(this);
274            }
275            fDrawing = d;
276            if (fDrawing != null) {
277                fDrawing.addDrawingChangeListener(this);
278            }
279            checkMinimumSize();
280            repaint();
281        }
282    
283        /**
284         * Gets the editor.
285         */
286        public DrawingEditor editor() {
287            return fEditor;
288        }
289    
290        /**
291         * Adds a figure to the drawing.
292         * @return the added figure.
293         */
294        public Figure add(Figure figure) {
295            return drawing().add(figure);
296        }
297    
298        /**
299         * Removes a figure from the drawing.
300         * @return the removed figure
301         */
302        public Figure remove(Figure figure) {
303            return drawing().remove(figure);
304        }
305    
306        /**
307         * Adds a vector of figures to the drawing.
308         */
309        public void addAll(Vector figures) {
310            FigureEnumeration k = new FigureEnumerator(figures);
311            while (k.hasMoreElements()) {
312                add(k.nextFigure());
313            }
314        }
315    
316        /**
317         * Check existance of figure in the drawing
318         */
319        public boolean figureExists(Figure inf, FigureEnumeration e) {
320            while(e.hasMoreElements()) {
321                Figure figure = e.nextFigure();
322    
323                if(figure.includes(inf)) {
324                    return true;
325                }
326            }
327    
328            return false;    
329        }
330    
331        /**
332         * Inserts a vector of figures and translates them by the
333         * given offset. This function is used to insert figures from clipboards (cut/copy)
334         *
335         * @return enumeration which has been added to the drawing. The figures in the enumeration
336         *         can have changed during adding them (e.g. they could have been decorated).
337         */
338        public FigureEnumeration insertFigures(FigureEnumeration fe, int dx, int dy, boolean bCheck) {
339            if (fe == null) {
340                return FigureEnumerator.getEmptyEnumeration();
341            }
342            
343            Vector addedFigures = new Vector();
344            Vector vCF = new Vector(10);
345            
346            while (fe.hasMoreElements()) {
347                Figure figure = fe.nextFigure();
348                if (figure instanceof ConnectionFigure) {
349                    vCF.addElement(figure);
350                }
351                else if (figure != null) {
352                    figure.moveBy(dx, dy);
353                    figure = add(figure);
354                    addToSelection(figure);
355                                    // figure might has changed during adding so add it afterwards
356                    addedFigures.addElement(figure);
357                }
358            }
359            
360            FigureEnumeration ecf = new FigureEnumerator(vCF);
361              
362            while (ecf.hasMoreElements()) {
363                ConnectionFigure cf = (ConnectionFigure) ecf.nextFigure();      
364                Figure sf = cf.startFigure();
365                Figure ef = cf.endFigure();
366    
367                if (figureExists(sf, drawing().figures()) &&
368                    figureExists(ef, drawing().figures()) &&
369                    (!bCheck || cf.canConnect(sf, ef))) {
370    
371                    if (bCheck) {
372                        Point sp = sf.center();
373                        Point ep = ef.center();            
374                        Connector fStartConnector = cf.startFigure().connectorAt(ep.x, ep.y);
375                        Connector fEndConnector = cf.endFigure().connectorAt(sp.x, sp.y);
376                    
377                        if (fEndConnector != null && fStartConnector != null) {
378                            cf.connectStart(fStartConnector);
379                            cf.connectEnd(fEndConnector);
380                            cf.updateConnection();
381                        }
382                    }
383                    
384                    Figure nf = add(cf);
385                    addToSelection(nf);
386                                    // figure might has changed during adding so add it afterwards
387                    addedFigures.addElement(nf);
388                }
389            }
390                    
391            return new FigureEnumerator(addedFigures);
392        }
393    
394        /**
395         * Returns a vector of connectionfigures attached to this figure
396         */
397        public Vector getConnectionFigures(Figure inFigure) {
398            // If no figure or figure is non connectable, just return null
399            if (inFigure == null || !inFigure.canConnect()) {
400                return null;
401            }
402                    
403            // if (inFigure instanceof ConnectionFigure)
404            //  return null;
405    
406            Vector result = new Vector(5);
407            FigureEnumeration figures = drawing().figures();
408    
409            // Find all connection figures
410            while (figures.hasMoreElements()) {
411                Figure f= figures.nextFigure();
412                    
413                if ((f instanceof ConnectionFigure) && !(isFigureSelected(f))) {
414                    ConnectionFigure cf = (ConnectionFigure) f;
415                      
416                    if (cf.startFigure().includes(inFigure) ||
417                        cf.endFigure().includes(inFigure)) {
418                        result.addElement(f);
419                    }
420                }
421            }
422    
423            return result;
424        }
425    
426        /**
427         * Gets the minimum dimension of the drawing.
428         */
429        public Dimension getMinimumSize() {
430            return fViewSize;
431        }
432    
433        /**
434         * Gets the preferred dimension of the drawing..
435         */
436        public Dimension getPreferredSize() {
437            return getMinimumSize();
438        }
439    
440        /**
441         * Sets the current display update strategy.
442         * @see Painter
443         */
444        public void setDisplayUpdate(Painter updateStrategy) {
445            fUpdateStrategy = updateStrategy;
446        }
447    
448        /**
449         * Sets the current display update strategy.
450         * @see Painter
451         */
452        public Painter getDisplayUpdate() {
453            return fUpdateStrategy;
454        }
455    
456        /**
457         * Gets the currently selected figures.
458         * @return a vector with the selected figures. The vector
459         * is a copy of the current selection.
460         */
461        public Vector selection() {
462            // protect the vector with the current selection
463            return (Vector)fSelection.clone();
464        }
465    
466        /**
467         * Gets an enumeration over the currently selected figures.
468         */
469        public FigureEnumeration selectionElements() {
470            return new FigureEnumerator(selectionZOrdered());
471        }
472    
473        /**
474         * Gets the currently selected figures in Z order.
475         * @see #selection
476         * @return a vector with the selected figures. The vector
477         * is a copy of the current selection.
478         */
479        public Vector selectionZOrdered() {
480            Vector result = new Vector(selectionCount());
481            FigureEnumeration figures = drawing().figures();
482    
483            while (figures.hasMoreElements()) {
484                Figure f= figures.nextFigure();
485                if (isFigureSelected(f)) {
486                    result.addElement(f);
487                }
488            }
489            return result;
490        }
491    
492        /**
493         * Gets the number of selected figures.
494         */
495        public int selectionCount() {
496            return fSelection.size();
497        }
498    
499        /**
500         * Test whether a given figure is selected.
501         */
502        public boolean isFigureSelected(Figure checkFigure) {
503            return fSelection.contains(checkFigure);
504        }
505    
506        /**
507         * Adds a figure to the current selection. The figure is only selected if
508         * it is also contained in the Drawing associated with this DrawingView.
509         */
510        public void addToSelection(Figure figure) {
511            if (!isFigureSelected(figure) && drawing().includes(figure)) {
512                fSelection.addElement(figure);
513                fSelectionHandles = null;
514                figure.invalidate();
515                fireSelectionChanged();
516            }
517        }
518    
519        /**
520         * Adds a vector of figures to the current selection.
521         */
522        public void addToSelectionAll(Vector figures) {
523            addToSelectionAll(new FigureEnumerator(figures));
524        }
525    
526        /**
527         * Adds a FigureEnumeration to the current selection.
528         */
529        public void addToSelectionAll(FigureEnumeration fe) {
530            while (fe.hasMoreElements()) {
531                addToSelection(fe.nextFigure());
532            }
533        }
534    
535        /**
536         * Removes a figure from the selection.
537         */
538        public void removeFromSelection(Figure figure) {
539            if (isFigureSelected(figure)) {
540                fSelection.removeElement(figure);
541                fSelectionHandles = null;
542                figure.invalidate();
543                fireSelectionChanged();
544            }
545        }
546    
547        /**
548         * If a figure isn't selected it is added to the selection.
549         * Otherwise it is removed from the selection.
550         */
551        public void toggleSelection(Figure figure) {
552            if (isFigureSelected(figure)) {
553                removeFromSelection(figure);
554            }
555            else {
556                addToSelection(figure);
557            }
558            fireSelectionChanged();
559        }
560    
561        /**
562         * Clears the current selection.
563         */
564        public void clearSelection() {
565            // there is nothing selected
566            if (fSelectionHandles == null) {
567                // avoid unnecessary selection changed event when nothing has to be cleared
568                return;
569            }
570    
571            FigureEnumeration fe = selectionElements();
572            while (fe.hasMoreElements()) {
573                fe.nextFigure().invalidate();
574            }
575            fSelection = new Vector();
576            fSelectionHandles = null;
577            fireSelectionChanged();
578        }
579    
580        /**
581         * Gets an enumeration of the currently active handles.
582         */
583        private Enumeration selectionHandles() {
584            if (fSelectionHandles == null) {
585                fSelectionHandles = new Vector();
586                FigureEnumeration k = selectionElements();
587                while (k.hasMoreElements()) {
588                    Figure figure = k.nextFigure();
589                    Enumeration kk = figure.handles().elements();
590                    while (kk.hasMoreElements()) {
591                        fSelectionHandles.addElement(kk.nextElement());
592                    }
593                }
594            }
595            return fSelectionHandles.elements();
596        }
597    
598        /**
599         * Gets the current selection as a FigureSelection. A FigureSelection
600         * can be cut, copied, pasted.
601         */
602        public FigureSelection getFigureSelection() {
603            return new StandardFigureSelection(new FigureEnumerator(selectionZOrdered()), selectionCount());
604        }
605    
606        /**
607         * Finds a handle at the given coordinates.
608         * @return the hit handle, null if no handle is found.
609         */
610        public Handle findHandle(int x, int y) {
611            Handle handle;
612    
613            Enumeration k = selectionHandles();
614            while (k.hasMoreElements()) {
615                handle = (Handle) k.nextElement();
616                if (handle.containsPoint(x, y)) {
617                    return handle;
618                }
619            }
620            return null;
621        }
622    
623        /**
624         * Informs that the current selection changed.
625         * By default this event is forwarded to the
626         * drawing editor.
627         */
628        protected void fireSelectionChanged() {
629            if (fSelectionListeners != null) {
630                for (int i = 0; i < fSelectionListeners.size(); i++) {
631                    FigureSelectionListener l = (FigureSelectionListener)fSelectionListeners.elementAt(i);
632                    l.figureSelectionChanged(this);
633                }
634            }
635        }
636    
637        /**
638         * Gets the position of the last click inside the view.
639         */
640        public Point lastClick() {
641            return fLastClick;
642        }
643    
644        /**
645         * Sets the grid spacing that is used to constrain points.
646         */
647        public void setConstrainer(PointConstrainer c) {
648            fConstrainer = c;
649        }
650    
651        /**
652         * Gets the current constrainer.
653         */
654        public PointConstrainer getConstrainer() {
655            return fConstrainer;
656        }
657    
658        /**
659         * Constrains a point to the current grid.
660         */
661        protected Point constrainPoint(Point p) {
662            // constrin to view size
663            Dimension size = getSize();
664            //p.x = Math.min(size.width, Math.max(1, p.x));
665            //p.y = Math.min(size.height, Math.max(1, p.y));
666            p.x = Geom.range(1, size.width, p.x);
667            p.y = Geom.range(1, size.height, p.y);
668    
669            if (fConstrainer != null ) {
670                return fConstrainer.constrainPoint(p);
671            }
672            return p;
673        }
674    
675    
676        /**
677         * Handles key down events. Cursor keys are handled
678         * by the view the other key events are delegated to the
679         * currently active tool.
680         * @return whether the event was handled.
681         */
682        public void keyPressed(KeyEvent e) {
683            int code = e.getKeyCode();
684            if ((code == KeyEvent.VK_BACK_SPACE) || (code == KeyEvent.VK_DELETE)) {
685                // Do nothing, let DiagramView do that stuff.
686            } else if (code == KeyEvent.VK_DOWN || code == KeyEvent.VK_UP ||
687                       code == KeyEvent.VK_RIGHT || code == KeyEvent.VK_LEFT) {
688                handleCursorKey(code);
689            } else {
690                tool().keyDown(e, code);
691            }
692            checkDamage();
693        }
694    
695        /**
696         * Handles cursor keys by moving all the selected figures
697         * one grid point in the cursor direction.
698         */
699        protected void handleCursorKey(int key) {
700            int dx = 0, dy = 0;
701            int stepX = 1, stepY = 1;
702            // should consider Null Object.
703            if (fConstrainer != null) {
704                stepX = fConstrainer.getStepX();
705                stepY = fConstrainer.getStepY();
706            }
707    
708            switch (key) {
709                case KeyEvent.VK_DOWN:
710                    dy = stepY;
711                    break;
712                case KeyEvent.VK_UP:
713                    dy = -stepY;
714                    break;
715                case KeyEvent.VK_RIGHT:
716                    dx = stepX;
717                    break;
718                case KeyEvent.VK_LEFT:
719                    dx = -stepX;
720                    break;
721            }
722            moveSelection(dx, dy);
723        }
724    
725        private void moveSelection(int dx, int dy) {
726            FigureEnumeration figures = selectionElements();
727            while (figures.hasMoreElements()) {
728                figures.nextFigure().moveBy(dx, dy);
729            }
730            checkDamage();
731        }
732    
733        /**
734         * Refreshes the drawing if there is some accumulated damage
735         */
736        public synchronized void checkDamage() {
737        }
738    
739        public void repairDamage() {
740            if (fDamage != null) {
741                repaint(fDamage.x, fDamage.y, fDamage.width, fDamage.height);
742                fDamage = null;
743            }
744        }
745    
746        public void drawingInvalidated(DrawingChangeEvent e) {
747            Rectangle r = e.getInvalidatedRectangle();
748            if (fDamage == null) {
749                fDamage = r;
750            } else {
751                fDamage.add(r);
752            }
753        }
754    
755        public void drawingRequestUpdate(DrawingChangeEvent e) {
756            repairDamage();
757        }
758    
759        /**
760         * Paints the drawing view. The actual drawing is delegated to
761         * the current update strategy.
762         * @see Painter
763         */
764        protected void paintComponent(Graphics g) {
765            getDisplayUpdate().draw(g, this);
766        }
767    
768        /**
769         * Draws the contents of the drawing view.
770         * The view has three layers: background, drawing, handles.
771         * The layers are drawn in back to front order.
772         */
773        public void drawAll(Graphics g) {
774            boolean isPrinting = g instanceof PrintGraphics;
775            drawBackground(g);
776            if (fBackgrounds != null && !isPrinting) {
777                drawPainters(g, fBackgrounds);
778            }
779            drawDrawing(g);
780            if (fForegrounds != null && !isPrinting) {
781                drawPainters(g, fForegrounds);
782            }
783            if (!isPrinting) {
784                drawHandles(g);
785            }
786        }
787    
788        /**
789         * Draws the given figures.
790         * The view has three layers: background, drawing, handles.
791         * The layers are drawn in back to front order.
792         * No background is drawn.
793         */
794        public void draw(Graphics g, FigureEnumeration fe) {
795            boolean isPrinting = g instanceof PrintGraphics;
796            //drawBackground(g);
797            if (fBackgrounds != null && !isPrinting) {
798                drawPainters(g, fBackgrounds);
799            }
800            fDrawing.draw(g, fe);
801            if (fForegrounds != null && !isPrinting) {
802                drawPainters(g, fForegrounds);
803            }
804            if (!isPrinting) {
805                drawHandles(g);
806            }
807        }
808    
809        /**
810         * Draws the currently active handles.
811         */
812        public void drawHandles(Graphics g) {
813            Enumeration k = selectionHandles();
814            while (k.hasMoreElements()) {
815                ((Handle) k.nextElement()).draw(g);
816            }
817        }
818    
819        /**
820         * Draws the drawing.
821         */
822        public void drawDrawing(Graphics g) {
823            fDrawing.draw(g);
824        }
825    
826        /**
827         * Draws the background. If a background pattern is set it
828         * is used to fill the background. Otherwise the background
829         * is filled in the background color.
830         */
831        public void drawBackground(Graphics g) {
832            g.setColor(getBackground());
833            g.fillRect(0, 0, getBounds().width, getBounds().height);
834        }
835    
836        private void drawPainters(Graphics g, Vector v) {
837            for (int i = 0; i < v.size(); i++) {
838                ((Painter)v.elementAt(i)).draw(g, this);
839            }
840        }
841    
842        /**
843         * Adds a background.
844         */
845        public void addBackground(Painter painter)  {
846            if (fBackgrounds == null) {
847                fBackgrounds = new Vector(3);
848            }
849            fBackgrounds.addElement(painter);
850            repaint();
851        }
852    
853        /**
854         * Removes a background.
855         */
856        public void removeBackground(Painter painter)  {
857            if (fBackgrounds != null) {
858                fBackgrounds.removeElement(painter);
859            }
860            repaint();
861        }
862    
863        /**
864         * Removes a foreground.
865         */
866        public void removeForeground(Painter painter)  {
867            if (fForegrounds != null) {
868                fForegrounds.removeElement(painter);
869            }
870            repaint();
871        }
872    
873        /**
874         * Adds a foreground.
875         */
876        public void addForeground(Painter painter)  {
877            if (fForegrounds == null) {
878                fForegrounds = new Vector(3);
879            }
880            fForegrounds.addElement(painter);
881            repaint();
882        }
883    
884        /**
885         * Freezes the view by acquiring the drawing lock.
886         * @see Drawing#lock
887         */
888        public void freezeView() {
889            drawing().lock();
890        }
891    
892        /**
893         * Unfreezes the view by releasing the drawing lock.
894         * @see Drawing#unlock
895         */
896        public void unfreezeView() {
897            drawing().unlock();
898        }
899    
900        private void checkMinimumSize() {
901            FigureEnumeration k = drawing().figures();
902            Dimension d = new Dimension(0, 0);
903            while (k.hasMoreElements()) {
904                Rectangle r = k.nextFigure().displayBox();
905                d.width = Math.max(d.width, r.x+r.width);
906                d.height = Math.max(d.height, r.y+r.height);
907            }
908            if (fViewSize.height < d.height || fViewSize.width < d.width) {
909                fViewSize.height = d.height + SCROLL_OFFSET;
910                fViewSize.width = d.width + SCROLL_OFFSET;
911                setSize(fViewSize);
912            }
913        }
914    
915        public boolean isFocusTraversable() {
916            return true;
917        }
918    
919        public boolean isInteractive() {
920            return true;
921        }
922            
923        public void keyTyped(KeyEvent e) {}
924        public void keyReleased(KeyEvent e) {}
925    
926        /**
927         * Add a listener for selection changes.
928         * @param fsl jhotdraw.framework.FigureSelectionListener
929         */
930        public void addFigureSelectionListener(FigureSelectionListener fsl) {
931            fSelectionListeners.add(fsl);
932        }
933    
934        /**
935         * Remove a listener for selection changes.
936         * @param fsl jhotdraw.framework.FigureSelectionListener
937         */
938        public void removeFigureSelectionListener(FigureSelectionListener fsl) {
939            fSelectionListeners.remove(fsl);
940        }
941    
942        public int getDefaultDNDActions() {
943            return java.awt.dnd.DnDConstants.ACTION_COPY_OR_MOVE;
944        }
945    
946    
947        /***** Autoscroll support *****/
948        private ASH ash = new ASH(10);
949    
950        public void autoscroll(java.awt.Point p) {
951            ash.autoscroll(p);
952        }
953        public Insets getAutoscrollInsets() {
954            return ash.getAutoscrollInsets();
955        }
956        class ASH extends AutoscrollHelper {
957            public ASH(int margin) {
958                super(margin);
959            }
960            public Dimension getSize() {
961                return IDEDrawingView.this.getSize();
962            }
963            public Rectangle getVisibleRect() {
964                return IDEDrawingView.this.getVisibleRect();
965            }
966            public void scrollRectToVisible(Rectangle aRect) {
967                IDEDrawingView.this.scrollRectToVisible(aRect);
968            }
969        }
970            
971        public String toString() {
972            return "DrawingView Nr: " + myCounter;
973        }
974            
975        static int counter;
976        int myCounter = counter;
977    }