KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > lib > editor > view > GapDocumentView


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.view;
21
22 import java.awt.Component JavaDoc;
23 import java.awt.Graphics 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.Element JavaDoc;
29 import javax.swing.text.View JavaDoc;
30 import javax.swing.text.ViewFactory JavaDoc;
31 import org.netbeans.editor.view.spi.EstimatedSpanView;
32 import org.netbeans.editor.view.spi.LockView;
33 import org.netbeans.editor.view.spi.ViewLayoutQueue;
34 import org.netbeans.editor.view.spi.ViewLayoutState;
35
36 /**
37  * View responsible for holding all line views for a particular document.
38  * <br>
39  * There is one instance of this view per document.
40  *
41  * <p>
42  * It is expected that this view will not act as an active
43  * layout state i.e. that it will not be hosted by a view
44  * implementing <code>ViewLayoutState.Parent</code>.
45  * <br>
46  * The implementation tries to optimize calls to updateLayout()
47  * so that if there are multiple changes in children then
48  * they will all be serviced at once.
49  *
50  * @author Miloslav Metelka
51  * @version 1.00
52  */

53
54 public class GapDocumentView extends GapBoxView {
55     
56     private static final boolean debugPaint = Boolean.getBoolean(
57         "netbeans.debug.editor.view.paint"); // NOI18N
58
private static final boolean debugRepaint = Boolean.getBoolean(
59         "netbeans.debug.editor.view.repaint"); // NOI18N
60

61     /**
62      * The minimum number of children serviced
63      * during a particular operation necessary for a asynchronous
64      * task to be scheduled for updating of the children.
65      */

66     private static final int ASYNC_CHILDREN_UPDATE_COUNT = 20;
67     
68     /**
69      * If the estimated span flag is being changed in all the children
70      * (in response to estimated span change in the parent)
71      * this constant determines into how many subtasks should
72      * the total task be divided. At least one child will be done in each
73      * subtask but it can be more if there is many children.
74      * <br>
75      * For example if the constant is 100 and there is 6000 children
76      * then 6000 / 100 = 60 children will be serviced in each subtask.
77      */

78     private static final int CHILDREN_UPDATE_SUBTASK_COUNT = 50;
79     
80     /**
81      * Task that updates estimated spans in children
82      * to false sequentially when large view replaces are done
83      * or when estimated span of the parent view changes
84      * from true to false.
85      * <br>
86      * If a task is run it's remembered in this variable.
87      * If there is an additional requirement for a task
88      * (e.g. another large replace) the existing running task
89      * is reused. Although it consumes another four bytes
90      * in the variable space the potential two or more
91      * such tasks running in parallel and the resulting view
92      * preferred span/size updates at two or more "places"
93      * in the view could cause the offset gap to move
94      * back and forth affecting the view performance.
95      */

96     private ChildrenUpdateTask childrenUpdateTask;
97     
98     /**
99      * Last allocation assigned to this view.
100      */

101     private int lastAllocationX;
102     private int lastAllocationY;
103     private int lastAllocationWidth;
104     private int lastAllocationHeight;
105     
106     /**
107      * Sub-offset along the major axis inside the first child
108      * from which a repaint request has came.
109      */

110     private double firstRepaintChildYSubOffset;
111     private double firstRepaintChildYSubSpan;
112     private float firstRepaintChildXSubOffset;
113     
114     /**
115      * Depth of the layout lock used to defer the updateLayout() call.
116      */

117     private int layoutLockDepth;
118     
119     /**
120      * Construct a view intended to cover the whole document.
121      *
122      * @param elem the element of the model to represent.
123      * @param majorAxis the axis to tile along. This can be
124      * either X_AXIS or Y_AXIS.
125      * @param baselineLayout whether baseline layout should be used
126      * instead of default layout.
127      */

128     public GapDocumentView(Element JavaDoc elem) {
129         super(elem, View.Y_AXIS);
130     }
131
132     protected GapBoxViewChildren createChildren() {
133         return new GapDocumentViewChildren(this);
134     }
135
136     protected Rectangle JavaDoc reallocate(Shape JavaDoc a) {
137         Rectangle JavaDoc alloc = super.reallocate(a);
138         
139         lastAllocationX = alloc.x;
140         lastAllocationY = alloc.y;
141         lastAllocationWidth = alloc.width;
142         lastAllocationHeight = alloc.height;
143         
144         return alloc;
145     }
146
147     protected void directUpdateLayout() {
148         // assert (layoutLockDepth >= 0);
149
if (layoutLockDepth == 0) {
150             super.directUpdateLayout();
151         }
152     }
153     
154     protected final void layoutLock() {
155         layoutLockDepth++;
156     }
157     
158     protected final void layoutUnlock() {
159         layoutLockDepth--;
160     }
161
162     public void renderWithUpdateLayout(Runnable JavaDoc r) {
163         layoutLockDepth++;
164         try {
165             r.run();
166         } finally {
167             updateLayout();
168             layoutLockDepth--;
169         }
170     }
171
172     public void setParent(View JavaDoc parent) {
173         layoutLockDepth++;
174         try {
175             super.setParent(parent);
176         } finally {
177             updateLayout();
178             layoutLockDepth--;
179         }
180     }
181
182     public void setSize(float width, float height) {
183         layoutLockDepth++;
184         try {
185             super.setSize(width, height);
186         } finally {
187             updateLayout();
188             layoutLockDepth--;
189         }
190     }
191
192     public void insertUpdate(DocumentEvent JavaDoc evt, Shape JavaDoc a, ViewFactory JavaDoc f) {
193         layoutLockDepth++;
194         try {
195             super.insertUpdate(evt, a, f);
196         } finally {
197             updateLayout();
198             layoutLockDepth--;
199         }
200     }
201     
202     public void removeUpdate(DocumentEvent JavaDoc evt, Shape JavaDoc a, ViewFactory JavaDoc f) {
203         layoutLockDepth++;
204         try {
205             super.removeUpdate(evt, a, f);
206         } finally {
207             updateLayout();
208             layoutLockDepth--;
209         }
210     }
211     
212     public void changedUpdate(DocumentEvent JavaDoc e, Shape JavaDoc a, ViewFactory JavaDoc f) {
213         layoutLockDepth++;
214         try {
215             super.changedUpdate(e, a, f);
216         } finally {
217             updateLayout();
218             layoutLockDepth--;
219         }
220     }
221
222     public void paint(Graphics JavaDoc g, Shape JavaDoc a) {
223         if (debugPaint) {
224             System.err.println("VIEW-PAINT: clip=" + g.getClipBounds() + ", alloc=" + a); // NOI18N
225
}
226
227         // During paint the estimated spans of children may be reset to exact measurements
228
// causing the layout to be updated
229
layoutLockDepth++;
230         try {
231             super.paint(g, a);
232         } finally {
233             updateLayout();
234             layoutLockDepth--;
235         }
236     }
237
238     public void repaint(ViewLayoutState child,
239     double majorAxisOffset, double majorAxisSpan,
240     float minorAxisOffset, float minorAxisSpan) {
241
242         int childIndex = getChildIndexNoCheck(child);
243         if (markRepaint(childIndex, false)) { // lower index was marked
244
firstRepaintChildYSubOffset = majorAxisOffset;
245             firstRepaintChildXSubOffset = minorAxisOffset;
246         }
247     }
248
249     protected boolean markRepaint(int childIndex, boolean repaintTillEnd) {
250         boolean lowerIndexMarked = super.markRepaint(childIndex, repaintTillEnd);
251         if (lowerIndexMarked) {
252             firstRepaintChildYSubOffset = 0d;
253             firstRepaintChildXSubOffset = 0f;
254         }
255         return lowerIndexMarked;
256     }
257         
258     protected void processRepaint(ViewLayoutState.Parent lsParent) {
259         int firstRepaintChildIndex = getChildren().getFirstRepaintChildIndex();
260         if (firstRepaintChildIndex >= 0 && firstRepaintChildIndex < getViewCount()) {
261             double repY = getChildren().getMajorAxisOffset(firstRepaintChildIndex);
262             repY += firstRepaintChildYSubOffset;
263             int repaintY = (int)Math.floor(repY);
264
265             int repaintX;
266             int repaintHeight;
267             if (isRepaintTillEnd()) {
268                 repaintX = 0; // till end should always be since begining
269
repaintHeight = lastAllocationHeight;
270             } else { // repaint only inside one child
271
repaintX = (int)Math.floor(firstRepaintChildXSubOffset);
272                 double repYEnd = repY
273                     + getChild(firstRepaintChildIndex).getLayoutMajorAxisPreferredSpan();
274                 repaintHeight = (int)Math.ceil(repYEnd) - repaintY;
275             }
276
277             int repaintWidth = lastAllocationWidth - repaintX;
278             // Shift repaintX by lastAllocationX
279
repaintX += lastAllocationX;
280
281             if (debugRepaint) {
282                 System.err.println("REPAINT(childIndex=" + firstRepaintChildIndex // NOI18N
283
+ ", rect(" + repaintX + ", " + repaintY // NOI18N
284
+ ", " + repaintWidth + ", " + repaintHeight + "))" // NOI18N
285
); // NOI18N
286
}
287
288             Component JavaDoc c = getContainer();
289             if (c != null) {
290                 c.repaint(repaintX, repaintY, repaintWidth, repaintHeight);
291             }
292         }
293     }
294     
295     ChildrenUpdateTask getChildrenUpdateTask() {
296         if (childrenUpdateTask == null) {
297             childrenUpdateTask = new ChildrenUpdateTask();
298         }
299         return childrenUpdateTask;
300     }
301     
302     protected void resetEstimatedSpan(int childIndex, int count) {
303         if (count >= ASYNC_CHILDREN_UPDATE_COUNT) {
304             ChildrenUpdateTask updateTask = getChildrenUpdateTask();
305             updateTask.markResetChildEstimatedSpan();
306             updateTask.setChildIndex(childIndex);
307             if (!updateTask.isRunning()) {
308                 updateTask.start();
309             }
310             
311         } else { // small count => do synchronously
312
super.resetEstimatedSpan(childIndex, count);
313         }
314     }
315
316     protected void markSizeInvalid(int childIndex, int count) {
317         if (count >= ASYNC_CHILDREN_UPDATE_COUNT) {
318             ChildrenUpdateTask updateTask = getChildrenUpdateTask();
319             updateTask.markUpdateChildSize();
320             updateTask.setChildIndex(0);
321             if (!updateTask.isRunning()) {
322                 updateTask.start();
323             }
324             
325         } else { // small count => do synchronously
326
super.markSizeInvalid(childIndex, count);
327         }
328     }
329
330     protected final int getLastAllocationX() {
331         return lastAllocationX;
332     }
333
334     protected final int getLastAllocationY() {
335         return lastAllocationY;
336     }
337
338     protected final int getLastAllocationWidth() {
339         return lastAllocationWidth;
340     }
341
342     protected final int getLastAllocationHeight() {
343         return lastAllocationHeight;
344     }
345
346     /**
347      * Fetch the queue to use for layout.
348      */

349     protected ViewLayoutQueue getLayoutQueue() {
350 // return ViewLayoutQueue.getSynchronousQueue();
351
return ViewLayoutQueue.getDefaultQueue();
352     }
353     
354
355     /**
356      * Task that crawls through children and sets their estimated span
357      * to false.
358      * <br>
359      * It's used when a large replace is done or when the view changes
360      * its estimated span from true to false.
361      * <br>
362      * The task gets initial child index and processes everything
363      * till the last child.
364      */

365     final class ChildrenUpdateTask implements Runnable JavaDoc {
366         
367         private int childIndex = Integer.MAX_VALUE;
368         
369         private boolean running;
370         
371         private boolean updateChildSize;
372         
373         private boolean resetChildEstimatedSpan;
374         
375         ChildrenUpdateTask() {
376         }
377         
378         void markUpdateChildSize() {
379             updateChildSize = true;
380         }
381         
382         void markResetChildEstimatedSpan() {
383             resetChildEstimatedSpan = true;
384         }
385
386         void start() {
387             running = true;
388             getLayoutQueue().addTask(this);
389         }
390
391         boolean isRunning() {
392             return running;
393         }
394         
395         private void finish() {
396             running = false;
397             updateChildSize = false;
398             resetChildEstimatedSpan = false;
399             childIndex = Integer.MAX_VALUE;
400         }
401         
402         void setChildIndex(int childIndex) {
403             if (childIndex < this.childIndex) {
404                 this.childIndex = childIndex;
405             }
406         }
407         
408         public void run() {
409             AbstractDocument JavaDoc doc = (AbstractDocument JavaDoc)getDocument();
410             if (doc!=null){
411                 doc.readLock();
412                 try {
413                     LockView lockView = LockView.get(GapDocumentView.this);
414                     if (lockView != null) {
415                         lockView.lock();
416                         try {
417                             layoutLock();
418                             try {
419                                 updateView(lockView);
420                             } finally {
421                                 updateLayout();
422                                 layoutUnlock();
423                             }
424                         } finally {
425                             lockView.unlock();
426                         }
427                     } // missing lock view => likely disconnected from hierarchy
428
} finally {
429                     doc.readUnlock();
430                 }
431             }
432         }
433
434         private void updateView(LockView lockView) {
435             if (getContainer() == null) { // view disconnected from component
436
finish();
437                 return;
438             }
439
440             int viewCount = getViewCount();
441             int updateCount = Math.max(1,
442                 viewCount / CHILDREN_UPDATE_SUBTASK_COUNT);
443
444             while (updateCount > 0 && childIndex < viewCount
445                 && !lockView.isPriorityThreadWaiting()
446             ) {
447                 ViewLayoutState child = getChild(childIndex);
448                 if (!child.isFlyweight()) {
449                     View JavaDoc childView = child.getView();
450
451                     // Posibly reset child's estimated span
452
if (resetChildEstimatedSpan) {
453                         if (childView instanceof EstimatedSpanView) {
454                             ((EstimatedSpanView)childView).setEstimatedSpan(false);
455                         }
456                     }
457
458                     // Possibly mark the child as invalid
459
if (updateChildSize) {
460                         child.markViewSizeInvalid();
461                     }
462
463                     // Update child's layout
464
child.updateLayout();
465                     // assert (child.isLayoutValid());
466

467                     updateCount--;
468                 }
469
470                 childIndex++;
471             }
472             
473             if (childIndex < viewCount) { // not finished yet
474
// Schedule this runnable again to layout thread
475
// to continue the ongoing work
476
getLayoutQueue().addTask(this);
477
478             } else { // no more children
479
finish();
480             }
481         }
482         
483     }
484     
485 }
486
Popular Tags