KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > css > editor > CssEditorSupport


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-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 /*
21  * CssEditorSupport.java
22  *
23  * Created on December 8, 2004, 11:07 PM
24  */

25
26 package org.netbeans.modules.css.editor;
27
28 import java.util.logging.Logger JavaDoc;
29 import org.netbeans.api.lexer.Language;
30 import org.netbeans.api.lexer.TokenId;
31 import org.netbeans.modules.css.loader.CssDataObject;
32 import org.netbeans.modules.css.visual.model.CssMetaModel;
33 import org.netbeans.modules.css.visual.model.CssStyleData;
34 import org.netbeans.modules.css.visual.parser.CssStyleParser;
35 import java.awt.Color JavaDoc;
36 import java.beans.PropertyChangeEvent JavaDoc;
37 import java.beans.PropertyChangeListener JavaDoc;
38 import java.io.IOException JavaDoc;
39 import javax.swing.JEditorPane JavaDoc;
40 import javax.swing.SwingUtilities JavaDoc;
41 import javax.swing.event.CaretEvent JavaDoc;
42 import javax.swing.event.CaretListener JavaDoc;
43 import javax.swing.event.DocumentEvent JavaDoc;
44 import javax.swing.event.DocumentListener JavaDoc;
45 import javax.swing.text.BadLocationException JavaDoc;
46 import javax.swing.text.Document JavaDoc;
47 import javax.swing.text.SimpleAttributeSet JavaDoc;
48 import javax.swing.text.Style JavaDoc;
49 import javax.swing.text.StyleConstants JavaDoc;
50 import javax.swing.text.StyledDocument JavaDoc;
51 import org.netbeans.api.lexer.TokenHierarchy;
52 import org.netbeans.api.lexer.TokenSequence;
53 import org.netbeans.editor.BaseDocument;
54 import org.netbeans.editor.TokenItem;
55 import org.netbeans.spi.lexer.LanguageProvider;
56 import org.openide.cookies.EditCookie;
57 import org.openide.cookies.EditorCookie;
58 import org.openide.cookies.OpenCookie;
59 import org.openide.cookies.PrintCookie;
60 import org.openide.cookies.SaveCookie;
61 import org.openide.filesystems.FileLock;
62 import org.openide.filesystems.FileObject;
63 import org.openide.text.CloneableEditor;
64 import org.openide.text.DataEditorSupport;
65 import org.openide.text.NbDocument;
66 import org.openide.windows.CloneableOpenSupport;
67
68
69 /**
70  * Editor Support for document of type text/css
71  * @author Winston Prakash
72  * @version 1.0
73  */

74 public class CssEditorSupport extends DataEditorSupport implements OpenCookie, EditCookie,
75         EditorCookie.Observable, PrintCookie, PropertyChangeListener JavaDoc {
76     
77     private final SimpleAttributeSet JavaDoc ATTR_ADD = new SimpleAttributeSet JavaDoc();
78     private final SimpleAttributeSet JavaDoc ATTR_REMOVE = new SimpleAttributeSet JavaDoc();
79     
80     CssDataObject cssDataObject = null;
81     
82     int currentOffset = -1;
83     int currentLength = -1;
84     int currentHighlightStart = -1;
85     int currentHighlightEnd = -1;
86     
87     EditorCookie editorCookie = null;
88     
89     boolean highLighted = false;
90     
91     private CssCustomEditor cssCustomEditor = null;
92     private CssCloneableEditor cssCloneableEditor = null;
93     
94     private JEditorPane JavaDoc activePane;
95     
96     /** Implements <code>SaveCookie</code> interface. */
97     private final SaveCookie saveCookie = new SaveCookie() {
98         public void save() throws IOException JavaDoc {
99             CssEditorSupport.this.saveDocument();
100             CssEditorSupport.this.getDataObject().setModified(false);
101         }
102     };
103     
104     /** Creates a new instance of CssEditorSupport */
105     public CssEditorSupport(CssDataObject dataObject) {
106         super(dataObject, new CssEnvironment(dataObject));
107         cssDataObject = dataObject;
108         addPropertyChangeListener(this);
109         ATTR_ADD.addAttribute(StyleConstants.Background, Color.red);
110         ATTR_REMOVE.addAttribute(StyleConstants.Background, Color.white);
111     }
112     
113     /**
114      * Add the Save Cookie becuase the file is modified
115      */

116     protected boolean notifyModified() {
117         if (!super.notifyModified()) return false;
118         cssDataObject.addSaveCookie(saveCookie);
119         return true;
120     }
121     
122     public void updateRules(){
123         // if(cssRuleNavigationSupport != null){
124
// cssRuleNavigationSupport.startRuleNavigationUpdate();
125
// }
126
}
127     
128     /**
129      * Remove the Save Cookie becuase the file is saved
130      */

131     protected void notifyUnmodified() {
132         super.notifyUnmodified();
133         cssDataObject.removeSaveCookie(saveCookie);
134     }
135     
136     protected CloneableEditor createCloneableEditor() {
137         return new CssCloneableEditor(this);
138     }
139     
140     /**
141      * Listen to the Editor Pane opened event and attach the Caret Lister
142      * to the editor Pane. Strange the PropertyChangeEvent does not have the
143      * JEditor Pane (expect to get is using PropertyChangeEvent.getNewValue())
144      * Using workaround - ugly but works - Winston
145      */

146     public void propertyChange(PropertyChangeEvent JavaDoc evt){
147         if (evt.getPropertyName().equals(EditorCookie.Observable.PROP_OPENED_PANES)){
148             CssMetaModel.setDataObject(cssDataObject);
149             // cssSyntaxErrorSupport = new CssSyntaxErrorSupport(this);
150
JEditorPane JavaDoc[] panes = this.getOpenedPanes();
151             if (panes != null){
152                 activePane = panes[0];
153                 if(activePane != null){
154                     cssCustomEditor = getCssCustomEditor(activePane);
155                     cssCloneableEditor = getCssCloneableEditor(activePane);
156                     if(cssCustomEditor != null) {
157                         cssCustomEditor.setDataObject(cssDataObject);
158                     }else{
159                         System.out.println("CssEditorSupport.propertyChange - Warning! CSS Custome Editor Can not be null. Check! - Winston");
160                     }
161                     if(cssCloneableEditor != null) {
162                         cssCloneableEditor.requestActive();
163                         cssCloneableEditor.setDataObject(cssDataObject);
164                     }else{
165                         System.out.println("CssEditorSupport.propertyChange - Warning! CSS Cloneable Editor Can not be null. Check! - Winston");
166                     }
167                     
168                     // cssRuleNavigationSupport = new CssRuleNavigationSupport(activePane);
169
activePane.addCaretListener(new CaretListener JavaDoc() {
170                         public void caretUpdate(CaretEvent JavaDoc ce) {
171                             if(ce.getSource() instanceof JEditorPane JavaDoc){
172                                 JEditorPane JavaDoc edPane = (JEditorPane JavaDoc)ce.getSource();
173                                 setActiveNode(edPane.getDocument(), ce.getDot(), false);
174                             }
175                         }
176                     });
177                     
178                     Document JavaDoc doc = activePane.getDocument();
179                     // syntaxSupport = new CssSyntaxSupport((BaseDocument)doc);
180

181                     doc.addDocumentListener(new DocumentListener JavaDoc(){
182                         public void insertUpdate(DocumentEvent JavaDoc de){
183                             setActiveNode(de.getDocument(), activePane.getCaret().getDot(), true);
184                             // cssSyntaxErrorSupport.startSyntaxCheck();
185
// cssRuleNavigationSupport.startRuleNavigationUpdate();
186
}
187                         public void removeUpdate(DocumentEvent JavaDoc de){
188                             setActiveNode(de.getDocument(), activePane.getCaret().getDot(), true);
189                             // cssSyntaxErrorSupport.startSyntaxCheck();
190
// cssRuleNavigationSupport.startRuleNavigationUpdate();
191
}
192                         public void changedUpdate(DocumentEvent JavaDoc de){
193                         }
194                     });
195                     
196                     // Find the first rule block and set the active node.
197
try{
198                         if(doc.getLength() > 0){
199                             boolean searchDone = false;
200                             String JavaDoc searchText;
201                             int searchPos = 0;
202                             do{
203                                 searchText = doc.getText(searchPos, 1);
204                                 if (searchText.equals("{")){
205                                     if(!isInComment(doc, searchPos)) {
206                                         break;
207                                     }
208                                     // TokenItem ti = syntaxSupport.getTokenChain(searchPos, searchPos+1);
209
// if((ti.getTokenID() != null) && (ti.getTokenID() != CssTokenContext.COMMENT)){
210
// break;
211
// }
212
}
213                                 searchPos++;
214                                 if(searchPos > doc.getLength()) break;
215                             }while(!searchDone);
216                             
217                             if(doc.getLength() > (searchPos + 1)){
218                                 activePane.getCaret().setDot(searchPos + 2);
219                             }
220                         }
221                     }catch(BadLocationException JavaDoc ble){
222                         
223                     }
224                 }
225             }
226         }
227     }
228     
229     private boolean isInComment(Document JavaDoc doc, int dotPos) {
230         //find css language
231
Language cssl = Language.find("text/x-css");
232         if(cssl != null) {
233             //find css comment token id
234
TokenId cssCommentTI = cssl.tokenId("css_comment");
235             if(cssCommentTI != null) {
236                 
237                 TokenHierarchy th = TokenHierarchy.get(doc);
238                 TokenSequence ts = th.tokenSequence();
239                 ts.move(dotPos);
240                 if(ts.moveNext()) {
241                     if(ts.token().id() == cssCommentTI) {
242                         return true; //do not activate node in comment
243
}
244                 }
245             } else {
246                 Logger.getLogger(this.getClass().getName()).info("No text/x-css language doesn't contain 'css_comment' token ID!");
247             }
248         } else {
249             Logger.getLogger(this.getClass().getName()).info("No text/x-css language found!");
250         }
251         return false;
252     }
253     
254     /**
255      * Get the CSS properties of the selected selector, create a CSS class node
256      * and set it as active node.
257      */

258     private void setActiveNode(final Document JavaDoc doc, int dotPos, boolean reparse){
259         // try{
260
if(isInComment(doc, dotPos)) {
261             return ;
262         }
263         
264         // TokenItem ti = syntaxSupport.getTokenChain(dotPos, dotPos+1);
265
// if((ti != null) && (ti.getTokenID() != null) && (ti.getTokenID() == CssTokenContext.COMMENT)){
266
// return;
267
// }
268
// }catch (Exception exc){
269
// exc.printStackTrace();
270
// }
271
int initialPos = -1;
272         int lastPos = -1;
273         int searchPos = -1;
274         if((currentOffset != -1) && (currentLength != -1) && !reparse){
275             if ((dotPos >= currentOffset) && (dotPos <= (currentOffset + currentLength))){
276                 highlight((StyledDocument JavaDoc)doc, currentOffset, currentLength);
277                 return;
278             }
279         }
280         try {
281             searchPos = (dotPos -1) < 0 ? 0 : dotPos -1;
282             String JavaDoc txtBefore = doc.getText(searchPos, 1);
283             while(!txtBefore.equals("{")){
284                 searchPos--;
285                 if((searchPos < 0) || txtBefore.equals("}")) break;
286                 txtBefore = doc.getText(searchPos, 1);
287             }
288             if(txtBefore.equals("{")){
289                 initialPos = searchPos;
290                 searchPos = (dotPos -1) < 0 ? 0 : dotPos -1;
291                 String JavaDoc txtAfter = doc.getText(searchPos, 1);
292                 while(!txtAfter.equals("}")){
293                     searchPos++;
294                     if((searchPos > doc.getLength()) || txtAfter.equals("{")) break;
295                     txtAfter = doc.getText(searchPos, 1);
296                 }
297                 if(txtAfter.equals("}")){
298                     lastPos = searchPos;
299                 }
300             }
301             if( (initialPos > -1) && (lastPos > -1) ){
302                 currentOffset = initialPos + 1;
303                 currentLength = lastPos - initialPos -1;
304                 highlight((StyledDocument JavaDoc)doc, currentOffset, currentLength);
305                 String JavaDoc cssProperties = doc.getText(currentOffset, currentLength);
306                 
307                 // XXX This is silly. CssSelectorNode node is created all the time.
308
// Create and set as active node only actual style values changes.
309
CssStyleParser cssStyleParser = new CssStyleParser();
310                 final CssStyleData cssStyleData = cssStyleParser.parse(cssProperties.trim());
311                 cssStyleData.addPropertyChangeListener(new PropertyChangeListener JavaDoc(){
312                     public void propertyChange(PropertyChangeEvent JavaDoc evt){
313                         replaceText((BaseDocument)doc, cssStyleData.getFormattedString());
314                     }
315                 });
316                 if(cssCustomEditor != null) {
317                     cssCustomEditor.setCssStyleData(cssStyleData);
318                 }else{
319                     System.out.println("CssEditorSupport.setActiveNode - Warning! CSS Custome Editor Can not be null. Check! - Winston"); //NOI18N
320
}
321                 
322                 if(cssCloneableEditor != null) {
323                     cssCloneableEditor.setCssStyleData(cssStyleData);
324                 }else{
325                     System.out.println("CssEditorSupport.setActiveNode - Warning! CSS Cloneable Editor Can not be null. Check! - Winston"); //NOI18N
326
}
327             }
328         } catch (Exception JavaDoc e) {
329             e.printStackTrace();
330         }
331     }
332     
333     /**
334      * Highlight the selected CSS Rule
335      */

336     private void highlight(StyledDocument JavaDoc doc, int offset, int length){
337         int initialPos = offset - 1;
338         int lastPos = length + initialPos + 1;
339         int firstLine = NbDocument.findLineNumber(doc,initialPos);
340         int lastLine = NbDocument.findLineNumber(doc,lastPos);
341         // Change highlight only if highlight region (CSS selector) has changed
342
if((firstLine != currentHighlightStart) || (lastLine != currentHighlightEnd)){
343             if (highLighted && (currentHighlightStart != -1) && (currentHighlightEnd != -1)){
344                 mark(doc, currentHighlightStart, currentHighlightEnd, false);
345             }
346             mark(doc, firstLine, lastLine, true);
347             highLighted = true;
348             
349             // Find the rule name and set it as selected rule to the meta model
350
initialPos = -1;
351             lastPos = -1;
352             try {
353                 int searchPos = offset - 1;
354                 String JavaDoc txtBefore = doc.getText(searchPos, 1);
355                 while(!txtBefore.equals("{")){
356                     searchPos--;
357                     if((searchPos < 0) || txtBefore.equals("}")) break;
358                     txtBefore = doc.getText(searchPos, 1);
359                 }
360                 if(txtBefore.equals("{")){
361                     lastPos = searchPos - 1;
362                     while(!txtBefore.equals("}")){
363                         searchPos--;
364                         if((searchPos < 0) || txtBefore.equals("}") || txtBefore.equals("/")) break;
365                         txtBefore = doc.getText(searchPos, 1);
366                     }
367                     if(txtBefore.equals("}") || txtBefore.equals("/")){
368                         initialPos = searchPos + 2;
369                     }else{
370                         initialPos = searchPos + 1;
371                     }
372                 }
373                 
374                 if ((initialPos != -1) && (lastPos != -1)){
375                     String JavaDoc ruleName = doc.getText(initialPos, lastPos - initialPos);
376                     CssMetaModel.getInstance().setSelectedRuleName(ruleName.trim());
377                 }
378             } catch (Exception JavaDoc e) {
379                 e.printStackTrace();
380             }
381             currentHighlightStart = firstLine;
382             currentHighlightEnd = lastLine;
383         }
384     }
385     
386     /**
387      * Mark/Unmark the lines from startLine to endLine in the Styled Document
388      */

389     public void mark(StyledDocument JavaDoc doc, int startLine, int endLine, boolean mark){
390         if ((startLine < 0) || (endLine < 0)) return;
391         int lastDoctLine = NbDocument.findLineNumber(doc, doc.getLength());
392         if ((startLine > lastDoctLine) || (endLine > lastDoctLine)) return;
393         for(int i=startLine; i <= endLine; i++){
394             int start = NbDocument.findLineOffset(doc, i);
395             if (mark){
396                 Style JavaDoc bp = doc.getStyle(NbDocument.CURRENT_STYLE_NAME);
397                 if (bp == null) {
398                     bp = doc.addStyle(NbDocument.CURRENT_STYLE_NAME, null);
399                     bp.addAttribute(StyleConstants.ColorConstants.Background, new Color JavaDoc(225,236,247));
400                 }
401                 doc.setLogicalStyle(start, bp);
402             }else{
403                 Style JavaDoc st = doc.getStyle(NbDocument.NORMAL_STYLE_NAME);
404                 if (st == null){
405                     st = doc.addStyle(NbDocument.NORMAL_STYLE_NAME, null);
406                 }
407                 doc.setLogicalStyle(start, st);
408             }
409         }
410     }
411     
412     /**
413      * Replace the selector text
414      */

415     boolean replaceText(BaseDocument doc, String JavaDoc text) {
416         int dotPos = activePane.getCaret().getDot();
417         doc.atomicLock();
418         try {
419             if (highLighted){
420                 mark((StyledDocument JavaDoc)doc, currentHighlightStart, currentHighlightEnd, false);
421                 highLighted = false;
422                 currentHighlightStart = -1;
423                 currentHighlightEnd = -1;
424             }
425             doc.remove(currentOffset, currentLength);
426             doc.insertString(currentOffset, text, null);
427             currentLength = text.length();
428             activePane.getCaret().setDot(currentOffset + 1);
429         } catch( BadLocationException JavaDoc exc ) {
430             return false;
431         } finally {
432             doc.atomicUnlock();
433         }
434         return true;
435     }
436     
437     /**
438      * Find the CSS cloneable Editor
439      */

440     private CssCloneableEditor getCssCloneableEditor(JEditorPane JavaDoc activePane) {
441         CssCloneableEditor cssCloneableEditor = (CssCloneableEditor)SwingUtilities.getAncestorOfClass(CssCloneableEditor.class, activePane);
442         return cssCloneableEditor;
443     }
444     
445     /**
446      * Find the Css Custom Editor
447      */

448     private CssCustomEditor getCssCustomEditor(JEditorPane JavaDoc activePane) {
449         CssCustomEditor cssCustomEditor = (CssCustomEditor)SwingUtilities.getAncestorOfClass(CssCustomEditor.class, activePane);
450         return cssCustomEditor;
451     }
452     
453     /**
454      * Environment that connects the CSS data object and the EditorSupport
455      */

456     private static class CssEnvironment extends DataEditorSupport.Env {
457         CssDataObject cssDataObject = null;
458         
459         public CssEnvironment(CssDataObject dataObject) {
460             super(dataObject);
461             cssDataObject = dataObject;
462         }
463         
464         protected FileObject getFile() {
465             return cssDataObject.getPrimaryFile();
466         }
467         
468         protected FileLock takeLock() throws IOException JavaDoc {
469             return cssDataObject.getPrimaryEntry().takeLock();
470         }
471         
472         public CloneableOpenSupport findCloneableOpenSupport() {
473             return (CssEditorSupport)cssDataObject.getCookie(CssEditorSupport.class);
474             
475         }
476     }
477 }
478
Popular Tags