KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > editor > hyperlink > HyperlinkOperation


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.lib.editor.hyperlink;
21
22 import java.awt.Color JavaDoc;
23 import java.awt.Cursor JavaDoc;
24 import java.awt.event.InputEvent JavaDoc;
25 import java.awt.event.KeyEvent JavaDoc;
26 import java.awt.event.KeyListener JavaDoc;
27 import java.awt.event.MouseEvent JavaDoc;
28 import java.awt.event.MouseListener JavaDoc;
29 import java.awt.event.MouseMotionListener JavaDoc;
30 import java.beans.PropertyChangeEvent JavaDoc;
31 import java.beans.PropertyChangeListener JavaDoc;
32 import java.util.ArrayList JavaDoc;
33 import java.util.Collections JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.List JavaDoc;
37 import javax.swing.SwingUtilities JavaDoc;
38 import javax.swing.text.BadLocationException JavaDoc;
39 import javax.swing.text.Document JavaDoc;
40 import javax.swing.text.EditorKit JavaDoc;
41 import javax.swing.text.JTextComponent JavaDoc;
42 import javax.swing.text.Position JavaDoc;
43 import org.netbeans.editor.*;
44 import org.netbeans.lib.editor.hyperlink.spi.HyperlinkProvider;
45 import org.openide.ErrorManager;
46
47 /**
48  *
49  * @author Jan Lahoda
50  */

51 public class HyperlinkOperation implements MouseListener JavaDoc, MouseMotionListener JavaDoc, PropertyChangeListener JavaDoc, KeyListener JavaDoc {
52
53     private static Cursor JavaDoc HAND_CURSOR = null;
54     
55     private JTextComponent JavaDoc component;
56     private Document JavaDoc currentDocument;
57     private HyperlinkLayer layer;
58     private String JavaDoc mimeType;
59     private Cursor JavaDoc oldComponentsMouseCursor;
60     private boolean hyperlinkUp;
61     private boolean listenersSetUp;
62
63     private boolean hyperlinkEnabled;
64     private int actionKeyMask;
65     
66     public static HyperlinkOperation create(JTextComponent JavaDoc component, String JavaDoc mimeType) {
67         return new HyperlinkOperation(component, mimeType);
68     }
69     
70     private static synchronized Cursor JavaDoc getHandCursor() {
71         if (HAND_CURSOR == null)
72             HAND_CURSOR = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
73         
74         return HAND_CURSOR;
75     }
76     
77     /** Creates a new instance of HoveringImpl */
78     private HyperlinkOperation(JTextComponent JavaDoc component, String JavaDoc mimeType) {
79         this.component = component;
80         this.mimeType = mimeType;
81         this.oldComponentsMouseCursor = null;
82         this.hyperlinkUp = false;
83         this.listenersSetUp = false;
84         
85         readSettings();
86         
87         if (hyperlinkEnabled) {
88             //Fix for #57409:
89
EditorUI ui = Utilities.getEditorUI(component);
90             
91             layer = new HyperlinkLayer();
92             
93             if (ui != null)
94                 ui.addLayer(layer, 1100);
95             
96             component.addPropertyChangeListener("document", this); // NOI18N
97
}
98     }
99     
100     private void documentUpdated() {
101         if (!hyperlinkEnabled)
102             return ;
103         
104         currentDocument = component.getDocument();
105         
106         if (currentDocument instanceof BaseDocument) {
107             if (!listenersSetUp) {
108                 component.addMouseListener(this);
109                 component.addMouseMotionListener(this);
110                 component.addKeyListener(this);
111                 listenersSetUp = true;
112             }
113         }
114     }
115     
116     private void readSettings() {
117         String JavaDoc hyperlinkActivationKeyPropertyValue = System.getProperty("org.netbeans.lib.editor.hyperlink.HyperlinkOperation.activationKey");
118         
119         if (hyperlinkActivationKeyPropertyValue != null) {
120             if ("off".equals(hyperlinkActivationKeyPropertyValue)) { // NOI18N
121
this.hyperlinkEnabled = false;
122                 this.actionKeyMask = (-1);
123             } else {
124                 this.hyperlinkEnabled = true;
125                 this.actionKeyMask = (-1);
126                 
127                 for (int cntr = 0; cntr < hyperlinkActivationKeyPropertyValue.length(); cntr++) {
128                     int localMask = 0;
129                     
130                     switch (hyperlinkActivationKeyPropertyValue.charAt(cntr)) {
131                         case 'S': localMask = InputEvent.SHIFT_DOWN_MASK; break;
132                         case 'C': localMask = InputEvent.CTRL_DOWN_MASK; break;
133                         case 'A': localMask = InputEvent.ALT_DOWN_MASK; break;
134                         case 'M': localMask = InputEvent.META_DOWN_MASK; break;
135                         default:
136                             ErrorManager.getDefault().log(ErrorManager.WARNING, "Incorrect value of org.netbeans.lib.editor.hyperlink.HyperlinkOperation.activationKey property (only letters CSAM are allowed): " + hyperlinkActivationKeyPropertyValue.charAt(cntr));
137                     }
138                     
139                     if (localMask == 0) {
140                         //some problem, ignore
141
this.actionKeyMask = (-1);
142                         break;
143                     }
144                     
145                     if (this.actionKeyMask == (-1))
146                         this.actionKeyMask = localMask;
147                     else
148                         this.actionKeyMask |= localMask;
149                 }
150                 
151                 if (this.actionKeyMask == (-1)) {
152                     ErrorManager.getDefault().log(ErrorManager.WARNING, "Some problem with property org.netbeans.lib.editor.hyperlink.HyperlinkOperation.activationKey, more information might be given above. Falling back to the default behaviour.");
153                 } else {
154                     return;
155                 }
156             }
157         }
158         
159         this.hyperlinkEnabled = true;
160         
161         Object JavaDoc activation = Settings.getValue(Utilities.getKitClass(component), SettingsNames.HYPERLINK_ACTIVATION_MODIFIERS);
162         
163         if (activation != null && activation instanceof Integer JavaDoc) {
164             this.actionKeyMask = ((Integer JavaDoc) activation).intValue();
165         } else {
166             this.actionKeyMask = InputEvent.CTRL_DOWN_MASK;
167         }
168     }
169     
170     public void mouseMoved(MouseEvent JavaDoc e) {
171         if (isHyperlinkEvent(e)) {
172             int position = component.viewToModel(e.getPoint());
173             
174             if (position < 0) {
175                 unHyperlink(true);
176                 
177                 return ;
178             }
179             
180             performHyperlinking(position);
181         } else {
182             unHyperlink(true);
183         }
184     }
185     
186     public void mouseDragged(MouseEvent JavaDoc e) {
187         //ignored
188
}
189     
190     private boolean isHyperlinkEvent(InputEvent JavaDoc e) {
191         return ((e.getModifiers() | e.getModifiersEx()) & actionKeyMask) == actionKeyMask;
192     }
193     
194     private void performHyperlinking(int position) {
195         HyperlinkProvider provider = findProvider(position);
196         
197         if (provider != null) {
198             int[] offsets = provider.getHyperlinkSpan(component.getDocument(), position);
199             
200             if (offsets != null)
201                 makeHyperlink(offsets[0], offsets[1]);
202         } else {
203             unHyperlink(true);
204         }
205     }
206     
207     private void performAction(int position) {
208         HyperlinkProvider provider = findProvider(position);
209         
210         if (provider != null) {
211             unHyperlink(true);
212             
213             //make sure the position is correct and the JumpList works:
214
component.getCaret().setDot(position);
215             JumpList.checkAddEntry(component, position);
216             
217             provider.performClickAction(component.getDocument(), position);
218         }
219     }
220     
221     private HyperlinkProvider findProvider(int position) {
222         Object JavaDoc mimeTypeObj = component.getDocument().getProperty("mimeType"); //NOI18N
223
String JavaDoc mimeType;
224         
225         if (mimeTypeObj instanceof String JavaDoc)
226             mimeType = (String JavaDoc) mimeTypeObj;
227         else {
228             mimeType = this.mimeType;
229         }
230         
231         List JavaDoc/*<HyperlinkProvider>*/ providers = HyperlinkProviderManager.getDefault().getHyperlinkProviders(mimeType);
232         
233         for (Iterator JavaDoc/*<HyperlinkProvider>*/ i = providers.iterator(); i.hasNext(); ) {
234             HyperlinkProvider provider = (HyperlinkProvider) i.next();
235             
236             if (provider.isHyperlinkPoint(component.getDocument(), position)) {
237                 return provider;
238             }
239         }
240         
241         return null;
242     }
243     
244     private synchronized void makeHyperlink(final int start, final int end) {
245         boolean makeCursorSnapshot = true;
246         
247         if (hyperlinkUp) {
248             unHyperlink(false);
249             makeCursorSnapshot = false;
250         }
251         
252         try {
253             Position JavaDoc startPos = layer.hyperlinkStart = component.getDocument().createPosition(start);
254             Position JavaDoc endPos = layer.hyperlinkEnd = component.getDocument().createPosition(end);
255             
256             hyperlinkUp = true;
257             
258             damageRange(startPos, endPos);
259             
260             if (makeCursorSnapshot) {
261                 if (component.isCursorSet()) {
262                     oldComponentsMouseCursor = component.getCursor();
263                 } else {
264                     oldComponentsMouseCursor = null;
265                 }
266                 component.setCursor(getHandCursor());
267             }
268         } catch (BadLocationException JavaDoc e) {
269             ErrorManager.getDefault().notify(e);
270             unHyperlink(false);
271         }
272     }
273     
274     private void damageRange(final Position JavaDoc start, final Position JavaDoc end) {
275         if (start != null && end != null) {
276             SwingUtilities.invokeLater(new Runnable JavaDoc() {
277                 public void run() {
278                     component.getDocument().render(new Runnable JavaDoc() {
279                         public void run() {
280                             int startIndex = start.getOffset();
281                             int endIndex = end.getOffset();
282                             
283                             component.getUI().damageRange(component, startIndex, endIndex);
284                         }
285                     });
286                 }
287             });
288         }
289     }
290     private synchronized void unHyperlink(boolean removeCursor) {
291         if (!hyperlinkUp)
292             return ;
293         
294         final Position JavaDoc start = layer.hyperlinkStart;
295         final Position JavaDoc end = layer.hyperlinkEnd;
296         
297         layer.hyperlinkStart = null;
298         layer.hyperlinkEnd = null;
299         
300         damageRange(start, end);
301         
302         if (removeCursor) {
303             if (component.isCursorSet() && component.getCursor() == getHandCursor()) {
304                 component.setCursor(oldComponentsMouseCursor);
305             }
306             oldComponentsMouseCursor = null;
307         }
308         
309         hyperlinkUp = false;
310     }
311     
312     public void propertyChange(PropertyChangeEvent JavaDoc evt) {
313         if (currentDocument != component.getDocument())
314             documentUpdated();
315     }
316     
317     public void keyTyped(KeyEvent JavaDoc e) {
318         //ignored
319
}
320
321     public void keyReleased(KeyEvent JavaDoc e) {
322         if ((e.getModifiers() & actionKeyMask) == 0)
323             unHyperlink(true);
324     }
325
326     public void keyPressed(KeyEvent JavaDoc e) {
327        //ignored
328
}
329
330     public void mouseReleased(MouseEvent JavaDoc e) {
331         //ignored
332
}
333
334     public void mousePressed(MouseEvent JavaDoc e) {
335         //ignored
336
}
337
338     public void mouseExited(MouseEvent JavaDoc e) {
339         //ignored
340
}
341
342     public void mouseEntered(MouseEvent JavaDoc e) {
343         //ignored
344
}
345
346     public void mouseClicked(MouseEvent JavaDoc e) {
347         if (isHyperlinkEvent(e) && !e.isPopupTrigger() && e.getClickCount() == 1 && e.getButton() == MouseEvent.BUTTON1) {
348             int position = component.viewToModel(e.getPoint());
349             
350             if (position < 0) {
351                 return ;
352             }
353             
354             performAction(position);
355         }
356     }
357     
358     private static class HyperlinkLayer extends DrawLayer.AbstractLayer {
359         
360         private Position JavaDoc hyperlinkStart = null;
361         private Position JavaDoc hyperlinkEnd = null;
362         
363         public static final String JavaDoc NAME = "hyperlink-layer"; // NOI18N
364

365         public static final int VISIBILITY = 1050;
366         
367         private boolean initialized = false;
368         
369         public HyperlinkLayer() {
370             super(NAME);
371             
372             this.initialized = false;
373         }
374         
375         private void checkDocument(Document JavaDoc doc) {
376         }
377         
378         public boolean extendsEOL() {
379             return true;
380         }
381         
382         public synchronized void init(final DrawContext ctx) {
383             if (!initialized) {
384                 Document JavaDoc doc = ctx.getEditorUI().getDocument();
385                 
386                 initialized = true;
387                 checkDocument(doc);
388             }
389             
390             if (isActive())
391                 setNextActivityChangeOffset(hyperlinkStart.getOffset());
392         }
393         
394         private boolean isActive() {
395             return hyperlinkStart != null && hyperlinkEnd != null;
396         }
397         
398         public boolean isActive(DrawContext ctx, MarkFactory.DrawMark mark) {
399             return isActive();
400         }
401         
402         private boolean isIn(int offset) {
403             return offset >= hyperlinkStart.getOffset() && offset < hyperlinkEnd.getOffset();
404         }
405         
406         private static Coloring hoverColoring = new Coloring(null, 0, Color.BLUE, null, Color.BLUE, null);
407         
408         public void updateContext(DrawContext ctx) {
409             if (!isActive())
410                 return ;
411             
412             int currentOffset = ctx.getFragmentOffset();
413             
414             if (isIn(currentOffset)) {
415                 hoverColoring.apply(ctx);
416                 
417                 if (isIn(currentOffset + ctx.getFragmentLength()))
418                     setNextActivityChangeOffset(currentOffset + ctx.getFragmentLength());
419             }
420         }
421         
422     }
423         
424 }
425
Popular Tags