3
Copyright 2002-2004 The Apache Software Foundation
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
3
Licensed to the Apache Software Foundation (ASF) under one or more
4
contributor license agreements. See the NOTICE file distributed with
5
this work for additional information regarding copyright ownership.
6
The ASF licenses this file to You under the Apache License, Version 2.0
7
(the "License"); you may not use this file except in compliance with
8
the License. You may obtain a copy of the License at
9
10
http://www.apache.org/licenses/LICENSE-2.0
37
38
import org.apache.batik.css.engine.SVGCSSEngine;
38
39
import org.apache.batik.css.engine.value.Value;
39
import org.apache.batik.dom.svg.XMLBaseSupport;
40
import org.apache.batik.dom.AbstractNode;
40
41
import org.apache.batik.dom.util.XLinkSupport;
41
42
import org.apache.batik.ext.awt.image.PadMode;
42
43
import org.apache.batik.ext.awt.image.renderable.AffineRable8Bit;
43
44
import org.apache.batik.ext.awt.image.renderable.Filter;
44
45
import org.apache.batik.ext.awt.image.renderable.PadRable8Bit;
46
import org.apache.batik.ext.awt.image.spi.BrokenLinkProvider;
45
47
import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
46
48
import org.apache.batik.gvt.GraphicsNode;
47
49
import org.apache.batik.util.ParsedURL;
50
import org.apache.batik.util.Platform;
48
51
import org.apache.batik.util.SVGConstants;
49
52
import org.apache.batik.util.SoftReferenceCache;
50
53
import org.w3c.dom.Element;
59
* The CursorManager class is a helper class which preloads the cursors
62
* The CursorManager class is a helper class which preloads the cursors
60
63
* corresponding to the SVG built in cursors.
62
65
* @author <a href="mailto:vincent.hardy@sun.com">Vincent Hardy</a>
63
* @version $Id: CursorManager.java,v 1.17 2005/03/27 08:58:30 cam Exp $
66
* @version $Id: CursorManager.java 594367 2007-11-13 00:40:53Z cam $
65
68
public class CursorManager implements SVGConstants, ErrorConstants {
96
99
* Static initialization of the cursorMap
102
Toolkit toolkit = Toolkit.getDefaultToolkit();
99
103
cursorMap = new Hashtable();
100
104
cursorMap.put(SVG_CROSSHAIR_VALUE,
101
105
Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
103
107
Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
104
108
cursorMap.put(SVG_POINTER_VALUE,
105
109
Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
106
cursorMap.put(SVG_MOVE_VALUE,
107
Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
108
110
cursorMap.put(SVG_E_RESIZE_VALUE,
109
111
Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
110
112
cursorMap.put(SVG_NE_RESIZE_VALUE,
125
127
Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
126
128
cursorMap.put(SVG_WAIT_VALUE,
127
129
Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
128
cursorMap.put(SVG_HELP_VALUE,
129
Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
130
Cursor moveCursor = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);
131
if (Platform.isOSX) {
133
Image img = toolkit.createImage
134
(CursorManager.class.getResource("resources/move.gif"));
135
moveCursor = toolkit.createCustomCursor
136
(img, new Point(11, 11), "move");
137
} catch (Exception ex) {
140
cursorMap.put(SVG_MOVE_VALUE, moveCursor);
143
Image img = toolkit.createImage
144
(CursorManager.class.getResource("resources/help.gif"));
145
helpCursor = toolkit.createCustomCursor
146
(img, new Point(1, 3), "help");
147
} catch (Exception ex) {
148
helpCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
150
cursorMap.put(SVG_HELP_VALUE, helpCursor);
175
193
if (cursorValue.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE
177
195
cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) {
178
// Single Value : should be one of the predefined cursors or
196
// Single Value : should be one of the predefined cursors or
180
198
cursorStr = cursorValue.getStringValue();
181
199
return convertBuiltInCursor(e, cursorStr);
182
} else if (cursorValue.getCssValueType() ==
200
} else if (cursorValue.getCssValueType() ==
183
201
CSSValue.CSS_VALUE_LIST) {
184
202
int nValues = cursorValue.getLength();
185
203
if (nValues == 1) {
186
204
cursorValue = cursorValue.item(0);
187
if (cursorValue.getPrimitiveType() ==
205
if (cursorValue.getPrimitiveType() ==
188
206
CSSPrimitiveValue.CSS_IDENT) {
189
207
cursorStr = cursorValue.getStringValue();
190
208
return convertBuiltInCursor(e, cursorStr);
197
215
return convertSVGCursor(e, cursorValue);
202
220
return convertBuiltInCursor(e, cursorStr);
205
223
public Cursor convertBuiltInCursor(Element e, String cursorStr) {
206
224
Cursor cursor = null;
208
226
// The CSS engine guarantees an non null, non empty string
209
227
// as the computed value for cursor. Therefore, the following
211
if (cursorStr.charAt(0) == 'a') {
229
if (cursorStr.charAt(0) == 'a') {
213
231
// Handle 'auto' value.
248
266
SVGConstants.SVG_TREF_TAG.equals(tag) ) {
249
267
cursor = CursorManager.TEXT_CURSOR;
250
268
} else if (SVGConstants.SVG_IMAGE_TAG.equals(tag)) {
251
// Do not change the cursor
269
// Do not change the cursor
254
272
cursor = CursorManager.DEFAULT_CURSOR;
260
278
// Specific, logical cursor
261
279
cursor = CursorManager.getPredefinedCursor(cursorStr);
269
* Returns a cursor for the given value list. Note that the
287
* Returns a cursor for the given value list. Note that the
270
288
* code assumes that the input value has at least two entries.
271
289
* So the caller should check that before calling the method.
272
290
* For example, CSSUtilities.convertCursor performs that check.
278
296
Value cursorValue = l.item(i);
279
297
if (cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_URI) {
280
298
String uri = cursorValue.getStringValue();
282
300
// If the uri does not resolve to a cursor element,
283
301
// then, this is not a type of cursor uri we can handle:
284
302
// go to the next or default to logical cursor
300
318
SVGConstants.SVG_CURSOR_TAG.equals
301
319
(cursorElement.getLocalName())) {
302
320
Cursor c = convertSVGCursorElement(cursorElement);
311
329
// If we got to that point, it means that no cursorElement
312
330
// produced a valid cursor, i.e., either a format we support
313
331
// or a valid referenced image (no broken image).
329
347
// Try to handle its image.
330
348
String uriStr = XLinkSupport.getXLinkHref(cursorElement);
331
349
if (uriStr.length() == 0) {
332
throw new BridgeException(cursorElement, ERR_ATTRIBUTE_MISSING,
350
throw new BridgeException(ctx, cursorElement, ERR_ATTRIBUTE_MISSING,
333
351
new Object[] {"xlink:href"});
336
String baseURI = XMLBaseSupport.getCascadedXMLBase(cursorElement);
354
String baseURI = AbstractNode.getBaseURI(cursorElement);
338
356
if (baseURI == null) {
339
357
purl = new ParsedURL(uriStr);
345
363
// Convert the cursor's hot spot
347
UnitProcessor.Context uctx
365
UnitProcessor.Context uctx
348
366
= UnitProcessor.createContext(ctx, cursorElement);
350
368
String s = cursorElement.getAttributeNS(null, SVG_X_ATTRIBUTE);
371
389
if (cachedCursor != null) {
372
390
return cachedCursor;
376
// Load image into Filter f and transform hotSpot to
394
// Load image into Filter f and transform hotSpot to
379
397
Point2D.Float hotSpot = new Point2D.Float(x, y);
380
Filter f = cursorHrefToFilter(cursorElement,
398
Filter f = cursorHrefToFilter(cursorElement,
384
402
cursorCache.clearCursor(desc);
388
// The returned Filter is guaranteed to create a
406
// The returned Filter is guaranteed to create a
389
407
// default rendering of the desired size
390
408
Rectangle cursorSize = f.getBounds2D().getBounds();
391
409
RenderedImage ri = f.createScaledRendering(cursorSize.width,
410
428
// The cursor image is now into 'img'
412
430
Cursor c = Toolkit.getDefaultToolkit()
413
.createCustomCursor(img,
431
.createCustomCursor(img,
414
432
new Point(Math.round(hotSpot.x),
415
433
Math.round(hotSpot.y)),
416
434
purl.toString());
418
436
cursorCache.putCursor(desc, c);
423
* Converts the input ParsedURL into a Filter and transforms the
441
* Converts the input ParsedURL into a Filter and transforms the
424
442
* input hotSpot point (in image space) to cursor space
426
protected Filter cursorHrefToFilter(Element cursorElement,
444
protected Filter cursorHrefToFilter(Element cursorElement,
428
446
Point2D hotSpot) {
430
448
AffineRable8Bit f = null;
431
449
String uriStr = purl.toString();
432
450
Dimension cursorSize = null;
434
452
// Try to load as an SVG Document
435
453
DocumentLoader loader = ctx.getDocumentLoader();
436
454
SVGDocument svgDoc = (SVGDocument)cursorElement.getOwnerDocument();
437
URIResolver resolver = new URIResolver(svgDoc, loader);
455
URIResolver resolver = ctx.createURIResolver(svgDoc, loader);
439
457
Element rootElement = null;
440
458
Node n = resolver.getNode(uriStr, cursorElement);
441
459
if (n.getNodeType() == Node.DOCUMENT_NODE) {
442
460
SVGDocument doc = (SVGDocument)n;
443
461
// FIXX: really should be subCtx here.
444
ctx.initializeDocument(doc);
462
ctx.initializeDocument(doc);
445
463
rootElement = doc.getRootElement();
447
throw new BridgeException
448
(cursorElement, ERR_URI_IMAGE_INVALID,
465
throw new BridgeException
466
(ctx, cursorElement, ERR_URI_IMAGE_INVALID,
449
467
new Object[] {uriStr});
451
469
GraphicsNode node = ctx.getGVTBuilder().build(ctx, rootElement);
454
// The cursorSize define the viewport into which the
455
// cursor is displayed. That viewport is platform
472
// The cursorSize define the viewport into which the
473
// cursor is displayed. That viewport is platform
456
474
// dependant and is not defined by the SVG content.
458
476
float width = DEFAULT_PREFERRED_WIDTH;
459
477
float height = DEFAULT_PREFERRED_HEIGHT;
460
UnitProcessor.Context uctx
478
UnitProcessor.Context uctx
461
479
= UnitProcessor.createContext(ctx, rootElement);
463
481
String s = rootElement.getAttribute(SVG_WIDTH_ATTRIBUTE);
465
483
width = UnitProcessor.svgHorizontalLengthToUserSpace
466
484
(s, SVG_WIDTH_ATTRIBUTE, uctx);
469
487
s = rootElement.getAttribute(SVG_HEIGHT_ATTRIBUTE);
470
488
if (s.length() != 0) {
471
489
height = UnitProcessor.svgVerticalLengthToUserSpace
472
490
(s, SVG_HEIGHT_ATTRIBUTE, uctx);
476
494
= Toolkit.getDefaultToolkit().getBestCursorSize
477
495
(Math.round(width), Math.round(height));
479
497
// Handle the viewBox transform
481
= ViewBox.getPreserveAspectRatioTransform(rootElement,
498
AffineTransform at = ViewBox.getPreserveAspectRatioTransform
499
(rootElement, cursorSize.width, cursorSize.height, ctx);
484
500
Filter filter = node.getGraphicsNodeRable(true);
485
501
f = new AffineRable8Bit(filter, at);
486
502
} catch (BridgeException ex) {
488
504
} catch (SecurityException ex) {
489
throw new BridgeException(cursorElement, ERR_URI_UNSECURE,
505
throw new BridgeException(ctx, cursorElement, ex, ERR_URI_UNSECURE,
490
506
new Object[] {uriStr});
491
507
} catch (Exception ex) {
505
521
// Check if we got a broken image
506
if (filter.getProperty
507
(SVGBrokenLinkProvider.SVG_BROKEN_LINK_DOCUMENT_PROPERTY) != null) {
522
if (BrokenLinkProvider.hasBrokenLinkProperty(filter)) {
511
526
Rectangle preferredSize = filter.getBounds2D().getBounds();
512
527
cursorSize = Toolkit.getDefaultToolkit().getBestCursorSize
513
528
(preferredSize.width, preferredSize.height);
515
530
if (preferredSize != null && preferredSize.width >0
516
531
&& preferredSize.height > 0 ) {
517
532
AffineTransform at = new AffineTransform();
518
if (preferredSize.width > cursorSize.width
533
if (preferredSize.width > cursorSize.width
520
535
preferredSize.height > cursorSize.height) {
521
536
at = ViewBox.getPreserveAspectRatioTransform
543
558
// In all cases, clip to the cursor boundaries
545
Rectangle cursorViewport
560
Rectangle cursorViewport
546
561
= new Rectangle(0, 0, cursorSize.width, cursorSize.height);
548
PadRable8Bit cursorImage
563
PadRable8Bit cursorImage
549
564
= new PadRable8Bit(f, cursorViewport,
550
565
PadMode.ZERO_PAD);
552
567
return cursorImage;
592
607
// Desc is used for hascode as well as for toString()
593
this.desc = this.getClass().getName() +
608
this.desc = this.getClass().getName() +
594
609
"\n\t:[" + this.purl + "]\n\t:[" + x + "]:[" + y + "]";
597
612
public boolean equals(Object obj) {
600
615
!(obj instanceof CursorDescriptor)) {
604
619
CursorDescriptor desc = (CursorDescriptor)obj;
606
621
this.purl.equals(desc.purl)
610
625
this.y == desc.y;