KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > swing > JSVGScrollPane


1 /*
2
3    Copyright 2003-2004 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.swing;
19
20 import java.awt.Rectangle JavaDoc;
21 import java.awt.Dimension JavaDoc;
22 import java.awt.Component JavaDoc;
23 import java.awt.BorderLayout JavaDoc;
24
25 import java.awt.event.ComponentAdapter JavaDoc;
26 /*
27 import java.awt.event.MouseWheelEvent;
28 import java.awt.event.MouseWheelListener;
29 */

30
31 import java.awt.geom.Rectangle2D JavaDoc;
32 import java.awt.geom.AffineTransform JavaDoc;
33
34 import java.awt.event.ComponentEvent JavaDoc;
35
36 import javax.swing.BoundedRangeModel JavaDoc;
37 import javax.swing.JScrollBar JavaDoc;
38 import javax.swing.Box JavaDoc;
39 import javax.swing.JPanel JavaDoc;
40
41 import javax.swing.event.ChangeListener JavaDoc;
42 import javax.swing.event.ChangeEvent JavaDoc;
43
44 import org.apache.batik.bridge.ViewBox;
45 import org.apache.batik.bridge.UpdateManagerListener;
46 import org.apache.batik.bridge.UpdateManagerEvent;
47
48 import org.apache.batik.gvt.GraphicsNode;
49
50 import org.apache.batik.swing.JSVGCanvas;
51 import org.apache.batik.swing.gvt.JGVTComponentListener;
52 import org.apache.batik.swing.svg.SVGDocumentLoaderAdapter;
53 import org.apache.batik.swing.svg.SVGDocumentLoaderEvent;
54 import org.apache.batik.swing.svg.GVTTreeBuilderListener;
55 import org.apache.batik.swing.svg.GVTTreeBuilderEvent;
56
57 import org.apache.batik.util.SVGConstants;
58
59 import org.w3c.dom.svg.SVGSVGElement;
60 import org.w3c.dom.svg.SVGDocument;
61
62 import org.w3c.dom.events.Event JavaDoc;
63 import org.w3c.dom.events.EventListener JavaDoc;
64
65
66 /**
67 * This is implements a 2D scroller that will scroll an JSVGCanvas.
68 * <p>
69 * Reimplimentation, rather than imlementing the Scrollable interface,
70 * provides several advantages. The main advantage is the ability to
71 * more precisely control ScrollBar events; fewer JSVGCanvas updates
72 * are required when scrolling. This creates a significant performance
73 * (reflected by an increase in scroll speed) advantage compared to
74 * implementating the Scrollable interface.
75 * <p>
76 * @author Zach DelProposto
77 */

78 public class JSVGScrollPane extends JPanel JavaDoc
79 {
80     protected JSVGCanvas canvas;
81     
82     protected JPanel JavaDoc horizontalPanel;
83     protected JScrollBar JavaDoc vertical;
84     protected JScrollBar JavaDoc horizontal;
85     protected Component JavaDoc cornerBox;
86     protected SBListener hsbListener;
87     protected SBListener vsbListener;
88     
89     protected Rectangle2D JavaDoc viewBox = null; // SVG Root element viewbox
90
protected boolean ignoreScrollChange = false;
91     
92
93     /**
94      * Creates a JSVGScrollPane, which will scroll an JSVGCanvas.
95      *
96      */

97     public JSVGScrollPane(JSVGCanvas canvas)
98     {
99         super();
100         this.canvas = canvas;
101         canvas.setRecenterOnResize(false);
102
103         // create components
104
vertical = new JScrollBar JavaDoc(JScrollBar.VERTICAL, 0, 0, 0, 0);
105         horizontal = new JScrollBar JavaDoc(JScrollBar.HORIZONTAL, 0, 0, 0, 0);
106         
107         // create a spacer next to the horizontal bar
108
horizontalPanel = new JPanel JavaDoc(new BorderLayout JavaDoc());
109         horizontalPanel.add(horizontal, BorderLayout.CENTER);
110         cornerBox = Box.createRigidArea
111             (new Dimension JavaDoc(vertical.getPreferredSize().width,
112                            horizontal.getPreferredSize().height));
113         horizontalPanel.add(cornerBox, BorderLayout.EAST);
114         
115         // listeners
116
hsbListener = createScrollBarListener(false);
117         horizontal.getModel().addChangeListener(hsbListener);
118         
119         vsbListener = createScrollBarListener(true);
120         vertical.getModel().addChangeListener(vsbListener);
121         
122         // by default, scrollbars are not visible
123
horizontalPanel.setVisible(false);
124         vertical.setVisible(false);
125         
126         // addMouseWheelListener(new WheelListener());
127

128         // layout
129
setLayout(new BorderLayout JavaDoc());
130         add(canvas, BorderLayout.CENTER);
131         add(vertical, BorderLayout.EAST);
132         add(horizontalPanel, BorderLayout.SOUTH);
133         
134         // inform of ZOOM events (to print sizes, such as in a status bar)
135
canvas.addSVGDocumentLoaderListener
136             (new SVGScrollDocumentLoaderListener());
137         
138         // canvas listeners
139
ScrollListener xlistener = new ScrollListener();
140         this.addComponentListener(xlistener);
141         canvas.addJGVTComponentListener(xlistener);
142         canvas.addGVTTreeBuilderListener(xlistener);
143         canvas.addUpdateManagerListener(xlistener);
144     }// JSVGScrollPane()
145

146
147     /**
148      * Scrollbar listener factory method so subclasses can
149      * use a subclass of SBListener if needed.
150      */

151     protected SBListener createScrollBarListener(boolean isVertical) {
152         return new SBListener(isVertical);
153     }
154
155     public JSVGCanvas getCanvas() {
156         return canvas;
157     }
158
159
160     class SVGScrollDocumentLoaderListener extends SVGDocumentLoaderAdapter {
161         public void documentLoadingCompleted(SVGDocumentLoaderEvent e) {
162             SVGSVGElement root = e.getSVGDocument().getRootElement();
163             root.addEventListener
164                 (SVGConstants.SVG_SVGZOOM_EVENT_TYPE,
165                  new EventListener JavaDoc() {
166                      public void handleEvent(Event JavaDoc evt) {
167                          if (!(evt.getTarget() instanceof SVGSVGElement))
168                              return;
169                          // assert(evt.getType() ==
170
// SVGConstants.SVG_SVGZOOM_EVENT_TYPE);
171
SVGSVGElement svg = (SVGSVGElement) evt.getTarget();
172                          scaleChange(svg.getCurrentScale());
173                      } // handleEvent()
174
}, false);
175         }// documentLoadingCompleted()
176
};
177     
178     
179     /**
180      * Resets this object (for reloads),
181      * releasing any cached data and recomputing
182      * scroll extents.
183      */

184     public void reset()
185     {
186         viewBox = null;
187         horizontalPanel.setVisible(false);
188         vertical.setVisible(false);
189         revalidate();
190     }// reset()
191

192     
193     /**
194      * Sets the translation portion of the transform based upon the
195      * current scroll bar position
196      */

197     protected void setScrollPosition() {
198         checkAndSetViewBoxRect();
199         if (viewBox == null) return;
200
201         AffineTransform JavaDoc crt = canvas.getRenderingTransform();
202         AffineTransform JavaDoc vbt = canvas.getViewBoxTransform();
203         if (crt == null) crt = new AffineTransform JavaDoc();
204         if (vbt == null) vbt = new AffineTransform JavaDoc();
205
206         Rectangle JavaDoc r2d = vbt.createTransformedShape(viewBox).getBounds();
207         // System.err.println("Pre : " + r2d);
208
int tx = 0, ty = 0;
209         if (r2d.x < 0) tx -= r2d.x;
210         if (r2d.y < 0) ty -= r2d.y;
211
212         int deltaX = horizontal.getValue()-tx;
213         int deltaY = vertical.getValue() -ty;
214
215         // System.err.println("tx = "+tx+"; ty = "+ty);
216
// System.err.println("dx = "+deltaX+"; dy = "+deltaY);
217
// System.err.println("Pre CRT: " + crt);
218

219         crt.preConcatenate
220             (AffineTransform.getTranslateInstance(-deltaX, -deltaY));
221         canvas.setRenderingTransform(crt);
222     }// setScrollPosition()
223

224     
225     
226     /**
227      * MouseWheel Listener
228      * <p>
229      * Provides mouse wheel support. The mouse wheel will scroll the currently
230      * displayed scroll bar, if only one is displayed. If two scrollbars are
231      * displayed, the mouse wheel will only scroll the vertical scrollbar.
232      *
233      * This is commented out because it requires JDK 1.4 and currently
234      * Batik targets JDK 1.3.
235      */

236     /*
237     protected class WheelListener implements MouseWheelListener
238     {
239         public void mouseWheelMoved(MouseWheelEvent e)
240         {
241             final JScrollBar sb = (vertical.isVisible()) ?
242                 vertical : horizontal; // vertical is preferred
243             
244             if(e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
245                 final int amt = e.getUnitsToScroll() * sb.getUnitIncrement();
246                 sb.setValue(sb.getValue() + amt);
247             } else if(e.getScrollType() == MouseWheelEvent.WHEEL_BLOCK_SCROLL){
248                 final int amt = e.getWheelRotation() * sb.getBlockIncrement();
249                 sb.setValue(sb.getValue() + amt);
250             }
251             
252         }// mouseWheelMoved()
253     }// inner class WheelListener
254     */

255     
256     
257     /**
258      * Advanced JScrollBar listener.
259      * <p>
260      * <b>A separate listener must be attached to each scrollbar,
261      * since we keep track of mouse state for each scrollbar
262      * separately!</b>
263      * <p>
264      * This coalesces drag events so we don't track them, and
265      * 'passes through' click events. It doesn't coalesce as many
266      * events as it should, but it helps * considerably.
267      */

268     protected class SBListener implements ChangeListener JavaDoc
269     {
270         // 'true' if we are in a drag (versus a click)
271
protected boolean inDrag = false;
272         protected int startValue;
273
274         protected boolean isVertical;
275
276         public SBListener(boolean vertical)
277         {
278             isVertical = vertical;
279         }// SBListener()
280

281         public synchronized void stateChanged(ChangeEvent JavaDoc e)
282         {
283             // only respond to changes if we are NOT being dragged
284
// and ignoreScrollChange is not set
285
if(ignoreScrollChange) return;
286
287             Object JavaDoc src = e.getSource();
288             if (!(src instanceof BoundedRangeModel JavaDoc))
289                 return;
290
291             int val = ((isVertical)?vertical.getValue():
292                        horizontal.getValue());
293
294             BoundedRangeModel JavaDoc brm = (BoundedRangeModel JavaDoc)src;
295             if (brm.getValueIsAdjusting()) {
296                 if (!inDrag) {
297                     inDrag = true;
298                     startValue = val;
299                 } else {
300                     AffineTransform JavaDoc at;
301                     if (isVertical) {
302                         at = AffineTransform.getTranslateInstance
303                             (0, startValue-val);
304                     } else {
305                         at = AffineTransform.getTranslateInstance
306                             (startValue-val, 0);
307                     }
308                     canvas.setPaintingTransform(at);
309                 }
310             } else {
311                 if (inDrag) {
312                     inDrag = false;
313                     if (val == startValue) {
314                         canvas.setPaintingTransform(new AffineTransform JavaDoc());
315                         return;
316                     }
317                 }
318                 setScrollPosition();
319             }
320         }// stateChanged()
321
}// inner class SBListener
322

323     
324     
325     /** Handle scroll, zoom, and resize events */
326     protected class ScrollListener extends ComponentAdapter JavaDoc
327         implements JGVTComponentListener, GVTTreeBuilderListener,
328                    UpdateManagerListener
329     {
330         protected boolean isReady = false;
331         
332         public void componentTransformChanged(ComponentEvent JavaDoc evt)
333         {
334             if(isReady)
335                 resizeScrollBars();
336         }// componentTransformChanged()
337

338         
339         public void componentResized(ComponentEvent JavaDoc evt)
340         {
341             if(isReady)
342                 resizeScrollBars();
343         }// componentResized()
344

345         
346         public void gvtBuildPrepare (GVTTreeBuilderEvent e) {
347             isReady = false;
348             // Start by assuming we won't need them.
349
vertical .setVisible(false);
350             horizontalPanel.setVisible(false);
351             cornerBox .setVisible(false);
352         }
353         public void gvtBuildCompleted(GVTTreeBuilderEvent e)
354         {
355             isReady = true;
356             viewBox = null; // new document forget old viewBox if any.
357
}// gvtRenderingCompleted()
358

359         public void updateCompleted(UpdateManagerEvent e) {
360             if (viewBox == null) {
361                 resizeScrollBars();
362                 return;
363             }
364
365             Rectangle2D JavaDoc newview = getViewBoxRect();
366             if ((newview.getX() != viewBox.getX()) ||
367                 (newview.getY() != viewBox.getY()) ||
368                 (newview.getWidth() != viewBox.getWidth()) ||
369                 (newview.getHeight() != viewBox.getHeight())) {
370                 viewBox = newview;
371                 resizeScrollBars();
372             }
373         }
374         
375
376         public void gvtBuildCancelled(GVTTreeBuilderEvent e) { }
377         public void gvtBuildFailed (GVTTreeBuilderEvent e) { }
378         public void gvtBuildStarted (GVTTreeBuilderEvent e) { }
379
380         public void managerStarted (UpdateManagerEvent e) { }
381         public void managerSuspended(UpdateManagerEvent e) { }
382         public void managerResumed (UpdateManagerEvent e) { }
383         public void managerStopped (UpdateManagerEvent e) { }
384         public void updateStarted (UpdateManagerEvent e) { }
385         public void updateFailed (UpdateManagerEvent e) { }
386
387     }// inner class ScrollListener
388

389     
390     /**
391      * Compute the scrollbar extents, and determine if
392      * scrollbars should be visible.
393      *
394      */

395     protected void resizeScrollBars()
396     {
397         // System.out.println("** resizeScrollBars()");
398

399         ignoreScrollChange = true;
400
401         checkAndSetViewBoxRect();
402         if (viewBox == null) return;
403
404         AffineTransform JavaDoc vbt = canvas.getViewBoxTransform();
405         if (vbt == null) vbt = new AffineTransform JavaDoc();
406
407         Rectangle JavaDoc r2d = vbt.createTransformedShape(viewBox).getBounds();
408         // System.out.println("VB: " + r2d);
409

410         // compute translation
411
int maxW = r2d.width;
412         int maxH = r2d.height;
413         int tx = 0, ty = 0;
414         if (r2d.x > 0) maxW += r2d.x;
415         else tx -= r2d.x;
416         if (r2d.y > 0) maxH += r2d.y;
417         else ty -= r2d.y;
418
419         // System.err.println(" maxW = "+maxW+"; maxH = "+maxH +
420
// " tx = "+tx+"; ty = "+ty);
421
vertical.setValue(ty);
422         horizontal.setValue(tx);
423
424         // Changing scrollbar visibility may change the
425
// canvas's dimensions so get the end result.
426
Dimension JavaDoc vpSize = updateScrollbarVisibility
427             (tx, ty, maxW, maxH);
428
429         // set scroll params
430
vertical. setValues(ty, vpSize.height, 0, maxH);
431         horizontal.setValues(tx, vpSize.width, 0, maxW);
432         
433         // set block scroll; this should be equal to a full 'page',
434
// minus a small amount to keep a portion in view
435
// that small amount is 10%.
436
vertical. setBlockIncrement( (int) (0.9f * vpSize.height) );
437         horizontal.setBlockIncrement( (int) (0.9f * vpSize.width) );
438         
439         // set unit scroll. This is arbitrary, but we define
440
// it to be 20% of the current viewport.
441
vertical. setUnitIncrement( (int) (0.2f * vpSize.height) );
442         horizontal.setUnitIncrement( (int) (0.2f * vpSize.width) );
443         
444         ignoreScrollChange = false;
445         //System.out.println(" -- end resizeScrollBars()");
446
}// resizeScrollBars()
447

448     protected Dimension JavaDoc updateScrollbarVisibility(int tx, int ty,
449                                                   int maxW, int maxH) {
450         // display scrollbars, if appropriate
451
// (if scaled document size is larger than viewport size)
452
// The tricky bit is ensuring that you properly track
453
// the effects of making one scroll bar visible on the
454
// need for the other scroll bar.
455

456         Dimension JavaDoc vpSize = canvas.getSize();
457         // maxVPW/H is the viewport W/H without scrollbars.
458
// minVPW/H is the viewport W/H with scrollbars.
459
int maxVPW = vpSize.width; int minVPW = vpSize.width;
460         int maxVPH = vpSize.height; int minVPH = vpSize.height;
461         if (vertical.isVisible()) {
462             maxVPW += vertical.getPreferredSize().width;
463         } else {
464             minVPW -= vertical.getPreferredSize().width;
465         }
466         if (horizontalPanel.isVisible()) {
467             maxVPH += horizontal.getPreferredSize().height;
468         } else {
469             minVPH -= horizontal.getPreferredSize().height;
470         }
471
472         // System.err.println("W: [" + minVPW + "," + maxVPW + "] " +
473
// "H: [" + minVPH + "," + maxVPH + "]");
474
// System.err.println("MAX: [" + maxW + "," + maxH + "]");
475

476         // Fist check if we need either scrollbar (given maxVPW/H).
477
boolean vVis = (maxH > maxVPH) || (vertical.getValue() != 0);
478         boolean hVis = (maxW > maxVPW) || (horizontal.getValue() != 0);
479         Dimension JavaDoc ret = new Dimension JavaDoc();
480         
481         // This makes sure that if one scrollbar is visible
482
// we 'recheck' the other scroll bar with the minVPW/H
483
// since making one visible makes the room for displaying content
484
// in the other dimension smaller. (This also makes the
485
// 'corner box' visible if both scroll bars are visible).
486
if (vVis) {
487             if (hVis) {
488                 horizontalPanel.setVisible(true);
489                 vertical.setVisible(true);
490                 cornerBox.setVisible(true);
491
492                 ret.width = minVPW;
493                 ret.height = minVPH;
494             } else {
495                 vertical.setVisible(true);
496                 ret.width = minVPW;
497                 if (maxW > minVPW) {
498                     horizontalPanel.setVisible(true);
499                     cornerBox.setVisible(true);
500                     ret.height = minVPH;
501                 } else {
502                     horizontalPanel.setVisible(false);
503                     cornerBox.setVisible(false);
504                     ret.height = maxVPH;
505                 }
506             }
507         } else {
508             if (hVis) {
509                 horizontalPanel.setVisible(true);
510                 ret.height = minVPH;
511                 if (maxH > minVPH) {
512                     vertical.setVisible(true);
513                     cornerBox.setVisible(true);
514                     ret.width = minVPW;
515                 } else {
516                     vertical.setVisible(false);
517                     cornerBox.setVisible(false);
518                     ret.width = maxVPW;
519                 }
520             } else {
521                 vertical .setVisible(false);
522                 horizontalPanel.setVisible(false);
523                 cornerBox .setVisible(false);
524                 ret.width = maxVPW;
525                 ret.height = maxVPH;
526             }
527         }
528
529         // Return the new size of the canvas.
530
return ret;
531     }
532     
533     /**
534      * Derives the SVG Viewbox from the SVG root element.
535      * Caches it. Assumes that it will not change.
536      *
537      */

538     protected void checkAndSetViewBoxRect() {
539         if (viewBox != null) return;
540
541         viewBox = getViewBoxRect();
542         // System.out.println(" ** viewBox rect set: "+viewBox);
543
// System.out.println(" ** doc size: "+
544
// canvas.getSVGDocumentSize());
545
}// checkAndSetViewBoxRect()
546

547
548     protected Rectangle2D JavaDoc getViewBoxRect() {
549         SVGDocument doc = canvas.getSVGDocument();
550         if (doc == null) return null;
551         SVGSVGElement el = doc.getRootElement();
552         if (el == null) return null;
553
554         String JavaDoc viewBoxStr = el.getAttributeNS
555             (null, SVGConstants.SVG_VIEW_BOX_ATTRIBUTE);
556         if (viewBoxStr.length() != 0) {
557             float[] rect = ViewBox.parseViewBoxAttribute(el, viewBoxStr);
558             return new Rectangle2D.Float JavaDoc(rect[0], rect[1],
559                                             rect[2], rect[3]);
560         }
561         GraphicsNode gn = canvas.getGraphicsNode();
562         if (gn == null) return null;
563
564         return (Rectangle2D JavaDoc)gn.getBounds().clone();
565     }
566     
567     /**
568      * Called when the scale size changes. The scale factor
569      * (1.0 == original size). By default, this method does
570      * nothing, but may be overidden to display a scale
571      * (zoom) factor in a status bar, for example.
572      */

573     public void scaleChange(float scale)
574     {
575         // do nothing
576
}
577 }// class JSVGScrollPane
578

579
580
Popular Tags