KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > DrawEngineLineView


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.editor;
21
22 import java.awt.Graphics JavaDoc;
23 import java.awt.Point JavaDoc;
24 import java.awt.Rectangle JavaDoc;
25 import java.awt.Shape JavaDoc;
26 import javax.swing.event.DocumentEvent JavaDoc;
27 import javax.swing.text.AbstractDocument JavaDoc;
28 import javax.swing.text.BadLocationException JavaDoc;
29 import javax.swing.text.Caret JavaDoc;
30 import javax.swing.text.Document JavaDoc;
31 import javax.swing.text.Element JavaDoc;
32 import javax.swing.text.JTextComponent JavaDoc;
33 import javax.swing.text.Position JavaDoc;
34 import javax.swing.text.View JavaDoc;
35 import javax.swing.text.ViewFactory JavaDoc;
36 import org.netbeans.editor.view.spi.EstimatedSpanView;
37 import org.netbeans.editor.view.spi.LockView;
38 import org.netbeans.editor.view.spi.ViewLayoutState;
39 import org.netbeans.editor.view.spi.ViewUtilities;
40 import org.openide.ErrorManager;
41
42 /**
43  * Line view implementation. It works over LineElement and
44  * delegates drawing to DrawEngine.
45  *
46  * @author Martin Roskanin
47  */

48 class DrawEngineLineView extends View JavaDoc implements ViewLayoutState, EstimatedSpanView {
49     
50     /**
51      * Bit that indicates whether x is the major axis.
52      */

53     private static final int X_MAJOR_AXIS_BIT = 1;
54     
55     /**
56      * Bit that indicates that the major axis info is valid.
57      */

58     private static final int MAJOR_AXIS_PREFERENCE_CHANGED_BIT = 2;
59
60     /**
61      * Bit that indicates that the minor axis info is valid.
62      */

63     private static final int MINOR_AXIS_PREFERENCE_CHANGED_BIT = 4;
64     
65     /**
66      * Bit that indicates that size of the view is valid.
67      */

68     private static final int VIEW_SIZE_INVALID_BIT = 8;
69     
70     /**
71      * Bit value in <code>statusBits</code> determining
72      * whether there is a pending layout update scheduled
73      * for this layout state.
74      */

75     private static final int UPDATE_LAYOUT_PENDING_BIT = 16;
76     
77     private static final int ESTIMATED_SPAN_BIT = 32;
78
79     protected static final int LAST_USED_BIT = ESTIMATED_SPAN_BIT;
80
81     /**
82      * Bit composition being used to test whether
83      * the layout is up-to-date or not.
84      */

85     private static final int ANY_INVALID
86         = MAJOR_AXIS_PREFERENCE_CHANGED_BIT
87         | MINOR_AXIS_PREFERENCE_CHANGED_BIT
88         | VIEW_SIZE_INVALID_BIT;
89
90
91     private int statusBits; // 4 bytes
92

93     private int viewRawIndex; // 8 bytes
94

95     private double layoutMajorAxisRawOffset; // double => 16 bytes
96

97     // major axis
98
private float layoutMajorAxisPreferredSpan; // 20 bytes
99

100     // minor axis
101
private float layoutMinorAxisPreferredSpan; // 24 bytes
102

103     
104     /** Draw graphics for converting position to coords */
105     //ModelToViewDG modelToViewDG; // 28 bytes
106

107     /** Draw graphics for converting coords to position */
108     ViewToModelDG viewToModelDG; // 32 bytes
109

110
111     public DrawEngineLineView(Element JavaDoc elem) {
112         super(elem);
113     }
114     
115     private int getBaseX(int orig) {
116         return orig + getEditorUI().getTextMargin().left;
117     }
118     
119     private JTextComponent JavaDoc getComponent() {
120         return (JTextComponent JavaDoc)getContainer();
121     }
122     
123     private BaseTextUI getBaseTextUI(){
124         return (BaseTextUI)getComponent().getUI();
125     }
126     
127     private EditorUI getEditorUI(){
128         return getBaseTextUI().getEditorUI();
129     }
130     
131     private ModelToViewDG getModelToViewDG() {
132         /* fix of issue #55419
133         if (modelToViewDG == null) {
134             modelToViewDG = new ModelToViewDG();
135         }
136         return modelToViewDG;
137          */

138         return new ModelToViewDG();
139     }
140     
141     private ViewToModelDG getViewToModelDG() {
142         if (viewToModelDG == null) {
143             viewToModelDG = new ViewToModelDG();
144         }
145         return viewToModelDG;
146     }
147     
148     public boolean isEstimatedSpan() {
149         return isStatusBitsNonZero(ESTIMATED_SPAN_BIT);
150     }
151     
152     public void setEstimatedSpan(boolean estimatedSpan) {
153         if (isEstimatedSpan() != estimatedSpan) { // really changed
154
if (estimatedSpan) {
155                 setStatusBits(ESTIMATED_SPAN_BIT);
156             } else { // changing from true to false
157
clearStatusBits(ESTIMATED_SPAN_BIT);
158
159                 getParent().preferenceChanged(this, true, true);
160             }
161         }
162     }
163     
164     protected boolean isFragment(){
165         return false;
166     }
167     
168     /**
169      * Get the offset prior to ending '\n' in the corresponding line element.
170      */

171     private int getEOLffset(){
172         return super.getEndOffset() - 1; // offset prior to ending '\n'
173
}
174     
175     /**
176      * Get either the EOL offset or the end of the fragment
177      * if the fragment is inside the view.
178      */

179     private int getAdjustedEOLOffset() {
180         return Math.min(getEndOffset(), getEOLffset());
181     }
182     
183     public void insertUpdate(DocumentEvent JavaDoc e, Shape JavaDoc a, ViewFactory JavaDoc f) {
184         preferenceChanged(this, true, false);
185     }
186     
187     public void removeUpdate(DocumentEvent JavaDoc e, Shape JavaDoc a, ViewFactory JavaDoc f) {
188         preferenceChanged(this, true, false);
189     }
190     
191     public float getAlignment(int axis) {
192     return 0f;
193     }
194     
195     public void paint(Graphics JavaDoc g, Shape JavaDoc a) {
196         if (!(getDocument() instanceof BaseDocument)) return; //#48134
197
// When painting make sure the estimated span is set to false
198
setEstimatedSpan(false);
199         // No modifications to allocReadOnly variable!
200
Rectangle JavaDoc allocReadOnly = (a instanceof Rectangle JavaDoc) ? (Rectangle JavaDoc)a : a.getBounds();
201         int startOffset = getStartOffset();
202         int endOffset = getAdjustedEOLOffset();
203         try{
204             if (isFragment()){
205                 Rectangle JavaDoc oldClipRect = g.getClipBounds();
206                 Rectangle JavaDoc newClip = new Rectangle JavaDoc(oldClipRect);
207                 Rectangle JavaDoc startOffsetClip = modelToView(startOffset, a, Position.Bias.Forward).getBounds();
208                 Rectangle JavaDoc endOffsetClip = modelToView(endOffset, a, Position.Bias.Forward).getBounds();
209                 View JavaDoc parent = getParent();
210                 if (parent instanceof FoldMultiLineView && !equals(parent.getView(parent.getViewCount() - 1))) {
211                     newClip.width = Math.min(oldClipRect.width, endOffsetClip.x);
212                     
213                     if (newClip.width + newClip.x > endOffsetClip.x) {
214                         newClip.width = newClip.width - (newClip.width + newClip.x - endOffsetClip.x);
215                     }
216                     
217                     g.setClip(newClip);
218                 }
219
220                 int shift = startOffsetClip.x - getEditorUI().getTextMargin().left - allocReadOnly.x;
221                 g.translate(-shift,0);
222                 
223                 DrawEngine.getDrawEngine().draw(this, new DrawGraphics.GraphicsDG(g),
224                 getEditorUI(), startOffset, endOffset,
225                 getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE);
226                 
227                 g.translate(shift,0);
228                 g.setClip(oldClipRect);
229
230             }else{
231                 JTextComponent JavaDoc component = getComponent();
232                 if (component!=null){
233                     DrawEngine drawEngine = (DrawEngine)component.getClientProperty(DrawEngine.PreinitializedDrawEngine.class);
234                     if (drawEngine != null){
235                         drawEngine.draw(this, new DrawGraphics.GraphicsDG(g),
236                         getEditorUI(), startOffset, endOffset,
237                         getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE);
238                     }else{
239                         DrawEngine.getDrawEngine().draw(this, new DrawGraphics.GraphicsDG(g),
240                         getEditorUI(), startOffset, endOffset,
241                         getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE);
242                     }
243
244                 }
245             }
246         }catch(BadLocationException JavaDoc ble){
247             ble.printStackTrace();
248         }
249     }
250     
251     public float getPreferredSpan(int axis) {
252         switch (axis) {
253             case Y_AXIS:
254                 /*try{
255                     Shape retShape = modelToView(getStartOffset(), new Rectangle(), Position.Bias.Forward);
256                     int ret = retShape.getBounds().height;
257                     return Math.max(ret, 1f);
258                 }catch(BadLocationException ble){
259                     ble.printStackTrace();
260                 }
261                  */

262                 return getEditorUI().getLineHeight();
263             case X_AXIS:
264                 try{
265                     int offset = Math.max(0, getEndOffset() - 1);
266                     Shape JavaDoc retShape = modelToView(offset, new Rectangle JavaDoc(), Position.Bias.Forward, false);
267                     int ret = retShape.getBounds().x + retShape.getBounds().width;
268                     return Math.max(ret, 1f);
269                 }catch(BadLocationException JavaDoc ble){
270                     ble.printStackTrace();
271                 }
272         }
273         
274         return 1f;
275     }
276     
277     private Rectangle JavaDoc getModel2ViewRect(int startOffset, int endOffset, int startX, int startY, int targetOffset){
278         Rectangle JavaDoc ret = new Rectangle JavaDoc();
279         ret.y = startY;
280         if (isEstimatedSpan()) {
281             ret.height = getEditorUI().getLineHeight();
282             ret.x = startX;
283             ret.width = 1;
284
285         } else { // exact measurements
286
try{
287                 ModelToViewDG modelToViewDG = getModelToViewDG();
288 // synchronized (modelToViewDG){ - view access is single-threaded
289
modelToViewDG.r = ret; // set the current rectangle
290
DrawEngine.getDrawEngine().draw(this, modelToViewDG, getEditorUI(),
291                         startOffset, endOffset,
292                         startX, startY, targetOffset);
293                     LockView lv = LockView.get(this);
294                     if (lv!=null && (lv.getLockThread() != Thread.currentThread())){
295                         throw new IllegalStateException JavaDoc("View access without view lock"); // NOI18N
296
}
297                     modelToViewDG.r = null;
298 // }
299
}catch(BadLocationException JavaDoc ble){
300                 ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ble);
301             }
302         }
303         return ret;
304     }
305     
306     public Shape JavaDoc modelToView(int pos, Shape JavaDoc a, Position.Bias JavaDoc b) throws BadLocationException JavaDoc {
307         return modelToView(pos, a, b, true); // ensure exact span (not estimated)
308
}
309
310     public Shape JavaDoc modelToView(int pos, Shape JavaDoc a, Position.Bias JavaDoc b, boolean exactSpan) throws BadLocationException JavaDoc {
311         if (exactSpan) { // ensure that span will not be estimated
312
setEstimatedSpan(false);
313         }
314
315     Document JavaDoc d = getDocument();
316     if (!(d instanceof BaseDocument)) {
317         return new Rectangle JavaDoc();
318     }
319         BaseDocument doc = (BaseDocument)d;
320         if (pos < 0 || pos > doc.getLength()) {
321             throw new BadLocationException JavaDoc("Invalid offset=" + pos, pos); // NOI18N
322
}
323
324         // No modifications to allocReadOnly variable!
325
Rectangle JavaDoc allocReadOnly = (a instanceof Rectangle JavaDoc) ? (Rectangle JavaDoc)a : a.getBounds();
326         Rectangle JavaDoc ret = getModel2ViewRect(
327             getStartOffset(),
328             getAdjustedEOLOffset(),
329             getBaseX(allocReadOnly.x),
330             allocReadOnly.y,
331             pos
332         );
333         
334         return ret;
335     }
336     
337     public int viewToModel(float x, float y, Shape JavaDoc a, Position.Bias JavaDoc[] biasReturn) {
338         if (isEstimatedSpan()) {
339             return getStartOffset();
340         }
341
342         int intX = (int)x;
343         int intY = (int)y;
344         if (biasReturn != null) {
345             biasReturn[0] = Position.Bias.Forward;
346         }
347         int pos = getStartOffset();
348         Rectangle JavaDoc shapeRect = (a!=null) ? a.getBounds() : new Rectangle JavaDoc();
349         
350         try {
351             int eolPos = getAdjustedEOLOffset();
352             ViewToModelDG viewToModelDG = getViewToModelDG();
353 // synchronized (viewToModelDG) { - view access is single-threaded
354
viewToModelDG.setTargetX(intX);
355                 viewToModelDG.setEOLOffset(eolPos);
356                 DrawEngine.getDrawEngine().draw(this, viewToModelDG, getEditorUI(), getStartOffset() , eolPos,
357                 getBaseX(0) + shapeRect.x, shapeRect.y, -1);
358                 pos = viewToModelDG.getOffset();
359                 return pos;
360 // }
361
} catch (BadLocationException JavaDoc e) {
362             // return begining of line in this case
363
}
364         return pos;
365     }
366
367     
368     final class ViewToModelDG extends DrawGraphics.SimpleDG {
369
370         int targetX;
371         int offset;
372         int eolOffset;
373         
374         void setTargetX(int targetX) {
375             this.targetX = targetX;
376         }
377         
378         void setEOLOffset(int eolOffset) {
379             this.eolOffset = eolOffset;
380             this.offset = eolOffset;
381         }
382         
383         int getOffset() {
384             return offset;
385         }
386         
387         public boolean targetOffsetReached(int offset, char ch, int x,
388         int charWidth, DrawContext ctx) {
389
390             if (offset <= eolOffset) {
391                 if (x + charWidth < targetX) {
392                     this.offset = offset;
393                     return true;
394                 } else { // target position inside the char
395
this.offset = offset;
396                     if (targetX > x + charWidth / 2) {
397                         Document JavaDoc doc = getDocument();
398                         if (ch != '\n' && doc != null && offset < doc.getLength()) {
399                             this.offset++;
400                         }
401                     }
402                     return false;
403                 }
404             }
405             return false;
406         }
407     }
408     
409     final class ModelToViewDG extends DrawGraphics.SimpleDG {
410         
411         Rectangle JavaDoc r;
412         
413         public boolean targetOffsetReached(int pos, char ch, int x,
414         int charWidth, DrawContext ctx) {
415             r.x = x;
416             r.y = getY();
417             r.width = charWidth;
418             r.height = getEditorUI().getLineHeight();
419             return false;
420         }
421         
422     }
423
424     
425     public View JavaDoc createFragment(int p0, int p1){
426         Element JavaDoc elem = getElement();
427         return // necessary conditions in accordance with javadoc
428
p0>=0 && p0>=elem.getStartOffset() && p0<elem.getEndOffset() &&
429                 p1>0 && p1<=elem.getEndOffset() && p1>elem.getStartOffset() &&
430                 // create fragment only if one of the element differs from valid start or end offset
431
(p0!=elem.getStartOffset() || p1!=elem.getEndOffset()) ?
432                     new FragmentView(getElement(), p0 - elem.getStartOffset(), p1 - p0) :
433                     this;
434     }
435
436     public double getLayoutMajorAxisPreferredSpan() {
437         return layoutMajorAxisPreferredSpan;
438     }
439     
440     public float getLayoutMajorAxisPreferredSpanFloat() {
441         return layoutMajorAxisPreferredSpan;
442     }
443
444     protected void setLayoutMajorAxisPreferredSpan(float layoutMajorAxisPreferredSpan) {
445         this.layoutMajorAxisPreferredSpan = layoutMajorAxisPreferredSpan;
446     }
447     
448     public double getLayoutMajorAxisRawOffset() {
449         return layoutMajorAxisRawOffset;
450     }
451     
452     public void setLayoutMajorAxisRawOffset(double layoutMajorAxisRawOffset) {
453         this.layoutMajorAxisRawOffset = layoutMajorAxisRawOffset;
454     }
455     
456     public float getLayoutMinorAxisAlignment() {
457         return getAlignment(getMinorAxis()); // not cached
458
}
459     
460     public float getLayoutMinorAxisMaximumSpan() {
461         return getLayoutMinorAxisPreferredSpan();
462     }
463     
464     public float getLayoutMinorAxisMinimumSpan() {
465         return getLayoutMinorAxisPreferredSpan();
466     }
467     
468     public float getLayoutMinorAxisPreferredSpan() {
469         return layoutMinorAxisPreferredSpan;
470     }
471     
472     protected void setLayoutMinorAxisPreferredSpan(float layoutMinorAxisPreferredSpan) {
473         this.layoutMinorAxisPreferredSpan = layoutMinorAxisPreferredSpan;
474     }
475
476     public View JavaDoc getView() {
477         return this;
478     }
479     
480     public int getViewRawIndex() {
481         return viewRawIndex;
482     }
483     
484     public void setViewRawIndex(int viewRawIndex) {
485         this.viewRawIndex = viewRawIndex;
486     }
487     
488     public boolean isFlyweight() {
489         return false;
490     }
491     
492     public ViewLayoutState selectLayoutMajorAxis(int majorAxis) {
493 // assert ViewUtilities.isAxisValid(majorAxis);
494

495         if (majorAxis == View.X_AXIS) {
496             setStatusBits(X_MAJOR_AXIS_BIT);
497         } else { // y axis
498
clearStatusBits(X_MAJOR_AXIS_BIT);
499         }
500         
501         return this;
502     }
503     
504     protected final ViewLayoutState.Parent getLayoutStateParent() {
505         View JavaDoc parent = getView().getParent();
506         return (parent instanceof ViewLayoutState.Parent)
507             ? ((ViewLayoutState.Parent)parent)
508             : null;
509     }
510
511     public void updateLayout() {
512         // First check whether the layout still need updates
513
if (isLayoutValid()) {
514             return; // nothing to do
515
}
516
517         ViewLayoutState.Parent lsParent = getLayoutStateParent();
518         if (lsParent == null) {
519             return;
520         }
521
522         // Check whether minor axis has changed
523
if (isStatusBitsNonZero(MINOR_AXIS_PREFERENCE_CHANGED_BIT)) { // minor not valid
524
clearStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT);
525
526             int minorAxis = getMinorAxis();
527             if (minorAxisUpdateLayout(minorAxis)) {
528                 lsParent.minorAxisPreferenceChanged(this);
529             }
530         }
531
532         // Check whether major axis has changed
533
if (isStatusBitsNonZero(MAJOR_AXIS_PREFERENCE_CHANGED_BIT)) { // major not valid
534
clearStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT);
535
536             float oldSpan = getLayoutMajorAxisPreferredSpanFloat();
537             float newSpan = getPreferredSpan(getMajorAxis());
538             setLayoutMajorAxisPreferredSpan(newSpan);
539             double majorAxisSpanDelta = newSpan - oldSpan;
540             if (majorAxisSpanDelta != 0) {
541                 lsParent.majorAxisPreferenceChanged(this, majorAxisSpanDelta);
542             }
543         }
544
545         // Check whether size must be set on the view
546
if (isStatusBitsNonZero(VIEW_SIZE_INVALID_BIT)) {
547             clearStatusBits(VIEW_SIZE_INVALID_BIT);
548
549             float width;
550             float height;
551             float majorAxisSpan = (float)getLayoutMajorAxisPreferredSpan();
552             float minorAxisSpan = lsParent.getMinorAxisSpan(this);
553             if (isXMajorAxis()) { // x is major axis
554
width = majorAxisSpan;
555                 height = minorAxisSpan;
556             } else {
557                 width = minorAxisSpan;
558                 height = majorAxisSpan;
559             }
560
561             setSize(width, height);
562         }
563         
564         // Possibly update layout again
565
updateLayout();
566     }
567     
568     protected boolean minorAxisUpdateLayout(int minorAxis) {
569         boolean minorAxisPreferenceChanged = false;
570         float val;
571         
572         val = getPreferredSpan(minorAxis);
573         if (val != getLayoutMinorAxisPreferredSpan()) {
574             setLayoutMinorAxisPreferredSpan(val);
575             minorAxisPreferenceChanged = true;
576         }
577         
578         return minorAxisPreferenceChanged;
579     }
580
581     public void viewPreferenceChanged(boolean width, boolean height) {
582         if (isXMajorAxis()) { // x is major axis
583
if (width) {
584                 setStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT); // major no longer valid
585
}
586             if (height) {
587                 setStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT); // minor no longer valid
588
}
589         } else {
590             if (width) {
591                 setStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT); // minor no longer valid
592
}
593             if (height) {
594                 setStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT); // major no longer valid
595
}
596         }
597         setStatusBits(VIEW_SIZE_INVALID_BIT); // child size no longer valid
598
}
599     
600     public void markViewSizeInvalid() {
601         setStatusBits(VIEW_SIZE_INVALID_BIT);
602     }
603
604     public boolean isLayoutValid() {
605         return !isStatusBitsNonZero(ANY_INVALID);
606     }
607
608     protected final boolean isXMajorAxis() {
609         return isStatusBitsNonZero(X_MAJOR_AXIS_BIT);
610     }
611     
612     protected final int getMajorAxis() {
613         return isXMajorAxis() ? View.X_AXIS : View.Y_AXIS;
614     }
615     
616     protected final int getMinorAxis() {
617         return isXMajorAxis() ? View.Y_AXIS : View.X_AXIS;
618     }
619     
620     protected final int getStatusBits(int bits) {
621         return (statusBits & bits);
622     }
623     
624     protected final boolean isStatusBitsNonZero(int bits) {
625         return (getStatusBits(bits) != 0);
626     }
627     
628     protected final void setStatusBits(int bits) {
629         statusBits |= bits;
630     }
631     
632     protected final void clearStatusBits(int bits) {
633         statusBits &= ~bits;
634     }
635
636     
637     /** Fragment View of DrawEngineLineView, typicaly created via createFragment method */
638     static class FragmentView extends DrawEngineLineView{
639         
640         private Position JavaDoc startPos;
641         private Position JavaDoc endPos;
642         
643         public FragmentView(Element JavaDoc elem, int offset, int length){
644             super(elem);
645             try {
646                 Document JavaDoc doc = elem.getDocument();
647                 this.startPos = doc.createPosition(super.getStartOffset() + offset);
648                 this.endPos = doc.createPosition(startPos.getOffset() + length);
649             } catch (BadLocationException JavaDoc e) {
650                 ErrorManager.getDefault().notify(e);
651             }
652         }
653
654         protected boolean isFragment(){
655             return true;
656         }
657
658         public int getStartOffset() {
659             return startPos.getOffset();
660         }
661         
662         public int getEndOffset() {
663             return endPos.getOffset();
664         }
665         
666     }
667     
668 }
669
Popular Tags