001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.bridge;
020:
021: import java.awt.Cursor;
022:
023: import java.util.Iterator;
024: import java.util.List;
025:
026: import org.apache.batik.dom.events.AbstractEvent;
027: import org.apache.batik.dom.events.NodeEventTarget;
028: import org.apache.batik.gvt.GraphicsNode;
029: import org.apache.batik.util.XMLConstants;
030:
031: import org.w3c.dom.Element;
032: import org.w3c.dom.events.Event;
033: import org.w3c.dom.events.EventListener;
034: import org.w3c.dom.svg.SVGAElement;
035:
036: /**
037: * Bridge class for the <a> element.
038: *
039: * @author <a href="mailto:tkormann@apache.org">Thierry Kormann</a>
040: * @version $Id: SVGAElementBridge.java 503215 2007-02-03 14:53:42Z deweese $
041: */
042: public class SVGAElementBridge extends SVGGElementBridge {
043:
044: protected AnchorListener al;
045: protected CursorMouseOverListener bl;
046: protected CursorMouseOutListener cl;
047:
048: /**
049: * Constructs a new bridge for the <a> element.
050: */
051: public SVGAElementBridge() {
052: }
053:
054: /**
055: * Returns 'a'.
056: */
057: public String getLocalName() {
058: return SVG_A_TAG;
059: }
060:
061: /**
062: * Returns a new instance of this bridge.
063: */
064: public Bridge getInstance() {
065: return new SVGAElementBridge();
066: }
067:
068: /**
069: * Builds using the specified BridgeContext and element, the
070: * specified graphics node.
071: *
072: * @param ctx the bridge context to use
073: * @param e the element that describes the graphics node to build
074: * @param node the graphics node to build
075: */
076: public void buildGraphicsNode(BridgeContext ctx, Element e,
077: GraphicsNode node) {
078:
079: super .buildGraphicsNode(ctx, e, node);
080:
081: if (ctx.isInteractive()) {
082: NodeEventTarget target = (NodeEventTarget) e;
083: CursorHolder ch = new CursorHolder(
084: CursorManager.DEFAULT_CURSOR);
085:
086: al = new AnchorListener(ctx.getUserAgent(), ch);
087: target.addEventListenerNS(
088: XMLConstants.XML_EVENTS_NAMESPACE_URI,
089: SVG_EVENT_CLICK, al, false, null);
090: ctx.storeEventListenerNS(target,
091: XMLConstants.XML_EVENTS_NAMESPACE_URI,
092: SVG_EVENT_CLICK, al, false);
093:
094: bl = new CursorMouseOverListener(ctx.getUserAgent(), ch);
095: target.addEventListenerNS(
096: XMLConstants.XML_EVENTS_NAMESPACE_URI,
097: SVG_EVENT_MOUSEOVER, bl, false, null);
098: ctx.storeEventListenerNS(target,
099: XMLConstants.XML_EVENTS_NAMESPACE_URI,
100: SVG_EVENT_MOUSEOVER, bl, false);
101:
102: cl = new CursorMouseOutListener(ctx.getUserAgent(), ch);
103: target.addEventListenerNS(
104: XMLConstants.XML_EVENTS_NAMESPACE_URI,
105: SVG_EVENT_MOUSEOUT, cl, false, null);
106: ctx.storeEventListenerNS(target,
107: XMLConstants.XML_EVENTS_NAMESPACE_URI,
108: SVG_EVENT_MOUSEOUT, cl, false);
109: }
110: }
111:
112: public void dispose() {
113: NodeEventTarget target = (NodeEventTarget) e;
114: if (al != null) {
115: target.removeEventListenerNS(
116: XMLConstants.XML_EVENTS_NAMESPACE_URI,
117: SVG_EVENT_CLICK, al, false);
118: al = null;
119: }
120: if (bl != null) {
121: target.removeEventListenerNS(
122: XMLConstants.XML_EVENTS_NAMESPACE_URI,
123: SVG_EVENT_MOUSEOVER, bl, false);
124: bl = null;
125: }
126: if (cl != null) {
127: target.removeEventListenerNS(
128: XMLConstants.XML_EVENTS_NAMESPACE_URI,
129: SVG_EVENT_MOUSEOUT, cl, false);
130: cl = null;
131: }
132: super .dispose();
133: }
134:
135: /**
136: * Returns true as the <a> element is a container.
137: */
138: public boolean isComposite() {
139: return true;
140: }
141:
142: public static class CursorHolder {
143: Cursor cursor = null;
144:
145: public CursorHolder(Cursor c) {
146: cursor = c;
147: }
148:
149: public void holdCursor(Cursor c) {
150: cursor = c;
151: }
152:
153: public Cursor getCursor() {
154: return cursor;
155: }
156: }
157:
158: /**
159: * To handle a click on an anchor.
160: */
161: public static class AnchorListener implements EventListener {
162: protected UserAgent userAgent;
163: protected CursorHolder holder;
164:
165: public AnchorListener(UserAgent ua, CursorHolder ch) {
166: userAgent = ua;
167: holder = ch;
168: }
169:
170: public void handleEvent(Event evt) {
171: if (!(evt instanceof AbstractEvent))
172: return;
173: final AbstractEvent ae = (AbstractEvent) evt;
174:
175: List l = ae.getDefaultActions();
176: if (l != null) {
177: Iterator i = l.iterator();
178: while (i.hasNext()) {
179: Object o = i.next();
180: if (o instanceof AnchorDefaultActionable)
181: return; // only one anchor in default list...
182: }
183: }
184:
185: SVGAElement elt = (SVGAElement) evt.getCurrentTarget();
186: ae.addDefaultAction(new AnchorDefaultActionable(elt,
187: userAgent, holder));
188: }
189: }
190:
191: public static class AnchorDefaultActionable implements Runnable {
192:
193: protected SVGAElement elt;
194: protected UserAgent userAgent;
195: protected CursorHolder holder;
196:
197: public AnchorDefaultActionable(SVGAElement e, UserAgent ua,
198: CursorHolder ch) {
199: elt = e;
200: userAgent = ua;
201: holder = ch;
202: }
203:
204: public void run() {
205: userAgent.setSVGCursor(holder.getCursor());
206: userAgent.openLink(elt);
207: }
208: }
209:
210: /**
211: * To handle a mouseover on an anchor and set the cursor.
212: */
213: public static class CursorMouseOverListener implements
214: EventListener {
215:
216: protected UserAgent userAgent;
217: protected CursorHolder holder;
218:
219: public CursorMouseOverListener(UserAgent ua, CursorHolder ch) {
220: userAgent = ua;
221: holder = ch;
222: }
223:
224: public void handleEvent(Event evt) {
225: if (!(evt instanceof AbstractEvent))
226: return;
227: final AbstractEvent ae = (AbstractEvent) evt;
228:
229: List l = ae.getDefaultActions();
230: if (l != null) {
231: Iterator i = l.iterator();
232: while (i.hasNext()) {
233: Object o = i.next();
234: if (o instanceof MouseOverDefaultActionable)
235: return; // only one anchor in default list...
236: }
237: }
238:
239: Element target = (Element) ae.getTarget();
240: SVGAElement elt = (SVGAElement) ae.getCurrentTarget();
241:
242: ae.addDefaultAction(new MouseOverDefaultActionable(target,
243: elt, userAgent, holder));
244: }
245: }
246:
247: public static class MouseOverDefaultActionable implements Runnable {
248:
249: protected Element target;
250: protected SVGAElement elt;
251: protected UserAgent userAgent;
252: protected CursorHolder holder;
253:
254: public MouseOverDefaultActionable(Element t, SVGAElement e,
255: UserAgent ua, CursorHolder ch) {
256: target = t;
257: elt = e;
258: userAgent = ua;
259: holder = ch;
260: }
261:
262: public void run() {
263: // Only modify the cursor if the target's cursor property is
264: // 'auto'. Note that we do not need to check the value of
265: // anchor element as the target's cursor value is resulting
266: // from the CSS cascade which has accounted for inheritance.
267: // This means that our behavior is to set the cursor to a
268: // hand cursor for any content on which the cursor property is
269: // 'auto' inside an anchor element. If, for example, the
270: // content was:
271: // <a cusor="wait">
272: // <g cursor="auto">
273: // <rect />
274: // </g>
275: // </a>
276: //
277: // The cursor on the inside rect will be set to the hand cursor and
278: // not the wait cursor
279: //
280: if (CSSUtilities.isAutoCursor(target)) {
281: holder.holdCursor(CursorManager.DEFAULT_CURSOR);
282: // The target's cursor value is 'auto': use the hand cursor
283: userAgent.setSVGCursor(CursorManager.ANCHOR_CURSOR);
284: }
285:
286: //
287: // In all cases, display the href in the userAgent
288: //
289: if (elt != null) {
290: String href = elt.getHref().getAnimVal();
291: userAgent.displayMessage(href);
292: }
293: }
294: }
295:
296: /**
297: * To handle a mouseout on an anchor and set the cursor.
298: */
299: public static class CursorMouseOutListener implements EventListener {
300:
301: protected UserAgent userAgent;
302: protected CursorHolder holder;
303:
304: public CursorMouseOutListener(UserAgent ua, CursorHolder ch) {
305: userAgent = ua;
306: holder = ch;
307: }
308:
309: public void handleEvent(Event evt) {
310: if (!(evt instanceof AbstractEvent))
311: return;
312: final AbstractEvent ae = (AbstractEvent) evt;
313:
314: List l = ae.getDefaultActions();
315: if (l != null) {
316: Iterator i = l.iterator();
317: while (i.hasNext()) {
318: Object o = i.next();
319: if (o instanceof MouseOutDefaultActionable)
320: return; // only one anchor in default list...
321: }
322: }
323:
324: SVGAElement elt = (SVGAElement) evt.getCurrentTarget();
325: ae.addDefaultAction(new MouseOutDefaultActionable(elt,
326: userAgent, holder));
327: }
328: }
329:
330: public static class MouseOutDefaultActionable implements Runnable {
331:
332: protected SVGAElement elt;
333: protected UserAgent userAgent;
334: protected CursorHolder holder;
335:
336: public MouseOutDefaultActionable(SVGAElement e, UserAgent ua,
337: CursorHolder ch) {
338: elt = e;
339: userAgent = ua;
340: holder = ch;
341: }
342:
343: public void run() {
344: // No need to set the cursor on out events: this is taken care of
345: // by the BridgeContext(?)
346:
347: // Hide the href in the userAgent
348: if (elt != null) {
349: userAgent.displayMessage("");
350: }
351: }
352: }
353: }
|