KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > TextPresentation


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jface.text;
12
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.NoSuchElementException JavaDoc;
17
18 import org.eclipse.swt.custom.StyleRange;
19 import org.eclipse.swt.custom.StyledText;
20
21 import org.eclipse.core.runtime.Assert;
22
23
24 /**
25  * Describes the presentation styles for a section of an indexed text such as a
26  * document or string. A text presentation defines a default style for the whole
27  * section and in addition style differences for individual subsections. Text
28  * presentations can be narrowed down to a particular result window. All methods
29  * are result window aware, i.e. ranges outside the result window are always
30  * ignored.
31  * <p>
32  * All iterators provided by a text presentation assume that they enumerate non
33  * overlapping, consecutive ranges inside the default range. Thus, all these
34  * iterators do not include the default range. The default style range must be
35  * explicitly asked for using <code>getDefaultStyleRange</code>.
36  */

37 public class TextPresentation {
38
39     /**
40      * Applies the given presentation to the given text widget. Helper method.
41      *
42      * @param presentation the style information
43      * @param text the widget to which to apply the style information
44      * @since 2.0
45      */

46     public static void applyTextPresentation(TextPresentation presentation, StyledText text) {
47
48         StyleRange[] ranges= new StyleRange[presentation.getDenumerableRanges()];
49
50         int i= 0;
51         Iterator JavaDoc e= presentation.getAllStyleRangeIterator();
52         while (e.hasNext())
53             ranges[i++]= (StyleRange) e.next();
54
55         text.setStyleRanges(ranges);
56     }
57
58
59
60
61     /**
62      * Enumerates all the <code>StyleRange</code>s included in the presentation.
63      */

64     class FilterIterator implements Iterator JavaDoc {
65
66         /** The index of the next style range to be enumerated */
67         protected int fIndex;
68         /** The upper bound of the indices of style ranges to be enumerated */
69         protected int fLength;
70         /** Indicates whether ranges similar to the default range should be enumerated */
71         protected boolean fSkipDefaults;
72         /** The result window */
73         protected IRegion fWindow;
74
75         /**
76          * <code>skipDefaults</code> tells the enumeration to skip all those style ranges
77          * which define the same style as the presentation's default style range.
78          *
79          * @param skipDefaults <code>false</code> if ranges similar to the default range should be enumerated
80          */

81         protected FilterIterator(boolean skipDefaults) {
82
83             fSkipDefaults= skipDefaults;
84
85             fWindow= fResultWindow;
86             fIndex= getFirstIndexInWindow(fWindow);
87             fLength= getFirstIndexAfterWindow(fWindow);
88
89             if (fSkipDefaults)
90                 computeIndex();
91         }
92
93         /*
94          * @see Iterator#next()
95          */

96         public Object JavaDoc next() {
97             try {
98                 StyleRange r= (StyleRange) fRanges.get(fIndex++);
99                 return createWindowRelativeRange(fWindow, r);
100             } catch (ArrayIndexOutOfBoundsException JavaDoc x) {
101                 throw new NoSuchElementException JavaDoc();
102             } finally {
103                 if (fSkipDefaults)
104                     computeIndex();
105             }
106         }
107
108         /*
109          * @see Iterator#hasNext()
110          */

111         public boolean hasNext() {
112             return fIndex < fLength;
113         }
114
115         /*
116          * @see Iterator#remove()
117          */

118         public void remove() {
119             throw new UnsupportedOperationException JavaDoc();
120         }
121
122         /**
123          * Returns whether the given object should be skipped.
124          *
125          * @param o the object to be checked
126          * @return <code>true</code> if the object should be skipped by the iterator
127          */

128         protected boolean skip(Object JavaDoc o) {
129             StyleRange r= (StyleRange) o;
130             return r.similarTo(fDefaultRange);
131         }
132
133         /**
134          * Computes the index of the styled range that is the next to be enumerated.
135          */

136         protected void computeIndex() {
137             while (fIndex < fLength && skip(fRanges.get(fIndex)))
138                 ++ fIndex;
139         }
140     }
141
142     /** The style information for the range covered by the whole presentation */
143     private StyleRange fDefaultRange;
144     /** The member ranges of the presentation */
145     private ArrayList JavaDoc fRanges;
146     /** A clipping region against which the presentation can be clipped when asked for results */
147     private IRegion fResultWindow;
148     /**
149      * The optional extent for this presentation.
150      * @since 3.0
151      */

152     private IRegion fExtent;
153
154
155     /**
156      * Creates a new empty text presentation.
157      */

158     public TextPresentation() {
159         fRanges= new ArrayList JavaDoc(50);
160     }
161
162     /**
163      * Creates a new empty text presentation. <code>sizeHint</code> tells the
164      * expected size of this presentation.
165      *
166      * @param sizeHint the expected size of this presentation
167      */

168     public TextPresentation(int sizeHint) {
169         Assert.isTrue(sizeHint > 0);
170         fRanges= new ArrayList JavaDoc(sizeHint);
171     }
172
173     /**
174      * Creates a new empty text presentation with the given extent.
175      * <code>sizeHint</code> tells the expected size of this presentation.
176      *
177      * @param extent the extent of the created <code>TextPresentation</code>
178      * @param sizeHint the expected size of this presentation
179      * @since 3.0
180      */

181     public TextPresentation(IRegion extent, int sizeHint) {
182         this(sizeHint);
183         Assert.isNotNull(extent);
184         fExtent= extent;
185     }
186
187     /**
188      * Sets the result window for this presentation. When dealing with
189      * this presentation all ranges which are outside the result window
190      * are ignored. For example, the size of the presentation is 0
191      * when there is no range inside the window even if there are ranges
192      * outside the window. All methods are aware of the result window.
193      *
194      * @param resultWindow the result window
195      */

196     public void setResultWindow(IRegion resultWindow) {
197         fResultWindow= resultWindow;
198     }
199
200     /**
201      * Set the default style range of this presentation.
202      * The default style range defines the overall area covered
203      * by this presentation and its style information.
204      *
205      * @param range the range describing the default region
206      */

207     public void setDefaultStyleRange(StyleRange range) {
208         fDefaultRange= range;
209     }
210
211     /**
212      * Returns this presentation's default style range. The returned <code>StyleRange</code>
213      * is relative to the start of the result window.
214      *
215      * @return this presentation's default style range
216      */

217     public StyleRange getDefaultStyleRange() {
218         StyleRange range= createWindowRelativeRange(fResultWindow, fDefaultRange);
219         if (range == null)
220             return null;
221         return (StyleRange)range.clone();
222
223     }
224
225     /**
226      * Add the given range to the presentation. The range must be a
227      * subrange of the presentation's default range.
228      *
229      * @param range the range to be added
230      */

231     public void addStyleRange(StyleRange range) {
232         checkConsistency(range);
233         fRanges.add(range);
234     }
235
236     /**
237      * Replaces the given range in this presentation. The range must be a
238      * subrange of the presentation's default range.
239      *
240      * @param range the range to be added
241      * @since 3.0
242      */

243     public void replaceStyleRange(StyleRange range) {
244         applyStyleRange(range, false);
245     }
246
247     /**
248      * Merges the given range into this presentation. The range must be a
249      * subrange of the presentation's default range.
250      *
251      * @param range the range to be added
252      * @since 3.0
253      */

254     public void mergeStyleRange(StyleRange range) {
255         applyStyleRange(range, true);
256     }
257
258     /**
259      * Applies the given range to this presentation. The range must be a
260      * subrange of the presentation's default range.
261      *
262      * @param range the range to be added
263      * @param merge <code>true</code> if the style should be merged instead of replaced
264      * @since 3.0
265      */

266     private void applyStyleRange(StyleRange range, boolean merge) {
267         if (range.length == 0)
268             return;
269
270         checkConsistency(range);
271
272         int start= range.start;
273         int length= range.length;
274         int end= start + length;
275
276         if (fRanges.size() == 0) {
277             StyleRange defaultRange= getDefaultStyleRange();
278             if (defaultRange == null)
279                 defaultRange= range;
280
281             defaultRange.start= start;
282             defaultRange.length= length;
283             applyStyle(range, defaultRange, merge);
284             fRanges.add(defaultRange);
285         } else {
286             IRegion rangeRegion= new Region(start, length);
287             int first= getFirstIndexInWindow(rangeRegion);
288
289             if (first == fRanges.size()) {
290                 StyleRange defaultRange= getDefaultStyleRange();
291                 if (defaultRange == null)
292                     defaultRange= range;
293                 defaultRange.start= start;
294                 defaultRange.length= length;
295                 applyStyle(range, defaultRange, merge);
296                 fRanges.add(defaultRange);
297                 return;
298             }
299
300             int last= getFirstIndexAfterWindow(rangeRegion);
301             for (int i= first; i < last && length > 0; i++) {
302
303                 StyleRange current= (StyleRange)fRanges.get(i);
304                 int currentStart= current.start;
305                 int currentEnd= currentStart + current.length;
306
307                 if (end <= currentStart) {
308                     fRanges.add(i, range);
309                     return;
310                 }
311
312                 if (start >= currentEnd)
313                     continue;
314
315                 StyleRange currentCopy= null;
316                 if (end < currentEnd)
317                     currentCopy= (StyleRange)current.clone();
318
319                 if (start < currentStart) {
320                     // Apply background to new default range and add it
321
StyleRange defaultRange= getDefaultStyleRange();
322                     if (defaultRange == null)
323                         defaultRange= new StyleRange();
324
325                     defaultRange.start= start;
326                     defaultRange.length= currentStart - start;
327                     applyStyle(range, defaultRange, merge);
328                     fRanges.add(i, defaultRange);
329                     i++; last++;
330
331
332                     // Apply background to first part of current range
333
current.length= Math.min(end, currentEnd) - currentStart;
334                     applyStyle(range, current, merge);
335                 }
336
337                 if (start >= currentStart) {
338                     // Shorten the current range
339
current.length= start - currentStart;
340
341                     // Apply the background to the rest of the current range and add it
342
if (current.length > 0) {
343                         current= (StyleRange)current.clone();
344                         i++; last++;
345                         fRanges.add(i, current);
346                     }
347                     applyStyle(range, current, merge);
348                     current.start= start;
349                     current.length= Math.min(end, currentEnd) - start;
350                 }
351
352                 if (end < currentEnd) {
353                     // Add rest of current range
354
currentCopy.start= end;
355                     currentCopy.length= currentEnd - end;
356                     i++; last++;
357                     fRanges.add(i, currentCopy);
358                 }
359
360                 // Update range
361
range.start= currentEnd;
362                 range.length= Math.max(end - currentEnd, 0);
363                 start= range.start;
364                 length= range.length;
365             }
366             if (length > 0) {
367                 // Apply background to new default range and add it
368
StyleRange defaultRange= getDefaultStyleRange();
369                 if (defaultRange == null)
370                     defaultRange= range;
371                 defaultRange.start= start;
372                 defaultRange.length= end - start;
373                 defaultRange.background= range.background;
374                 fRanges.add(last, defaultRange);
375             }
376         }
377     }
378
379     /**
380      * Replaces the given ranges in this presentation. Each range must be a
381      * subrange of the presentation's default range. The ranges must be ordered
382      * by increasing offset and must not overlap (but may be adjacent).
383      *
384      * @param ranges the ranges to be added
385      * @since 3.0
386      */

387     public void replaceStyleRanges(StyleRange[] ranges) {
388         applyStyleRanges(ranges, false);
389     }
390
391     /**
392      * Merges the given ranges into this presentation. Each range must be a
393      * subrange of the presentation's default range. The ranges must be ordered
394      * by increasing offset and must not overlap (but may be adjacent).
395      *
396      * @param ranges the ranges to be added
397      * @since 3.0
398      */

399     public void mergeStyleRanges(StyleRange[] ranges) {
400         applyStyleRanges(ranges, true);
401     }
402
403     /**
404      * Applies the given ranges to this presentation. Each range must be a
405      * subrange of the presentation's default range. The ranges must be ordered
406      * by increasing offset and must not overlap (but may be adjacent).
407      *
408      * @param ranges the ranges to be added
409      * @param merge <code>true</code> if the style should be merged instead of replaced
410      * @since 3.0
411      */

412     private void applyStyleRanges(StyleRange[] ranges, boolean merge) {
413         int j= 0;
414         ArrayList JavaDoc oldRanges= fRanges;
415         ArrayList JavaDoc newRanges= new ArrayList JavaDoc(2*ranges.length + oldRanges.size());
416         for (int i= 0, n= ranges.length; i < n; i++) {
417             StyleRange range= ranges[i];
418             fRanges= oldRanges; // for getFirstIndexAfterWindow(...)
419
for (int m= getFirstIndexAfterWindow(new Region(range.start, range.length)); j < m; j++)
420                 newRanges.add(oldRanges.get(j));
421             fRanges= newRanges; // for mergeStyleRange(...)
422
applyStyleRange(range, merge);
423         }
424         for (int m= oldRanges.size(); j < m; j++)
425             newRanges.add(oldRanges.get(j));
426         fRanges= newRanges;
427     }
428
429     /**
430      * Applies the template's style to the target.
431      *
432      * @param template the style range to be used as template
433      * @param target the style range to which to apply the template
434      * @param merge <code>true</code> if the style should be merged instead of replaced
435      * @since 3.0
436      */

437     private void applyStyle(StyleRange template, StyleRange target, boolean merge) {
438         if (merge) {
439             if (template.font != null)
440                 target.font= template.font;
441             if (template.metrics != null)
442                 target.metrics= template.metrics;
443             if (template.foreground != null)
444                 target.foreground= template.foreground;
445             if (template.background != null)
446                 target.background= template.background;
447             target.fontStyle |= template.fontStyle;
448             target.strikeout= template.strikeout || target.strikeout;
449             target.underline= template.underline || target.underline;
450         } else {
451             target.foreground= template.foreground;
452             target.background= template.background;
453             target.fontStyle= template.fontStyle;
454             target.strikeout= template.strikeout;
455             target.underline= template.underline;
456             target.font= template.font;
457             target.metrics= template.metrics;
458         }
459     }
460
461     /**
462      * Checks whether the given range is a subrange of the presentation's
463      * default style range.
464      *
465      * @param range the range to be checked
466      * @exception IllegalArgumentException if range is not a subrange of the presentation's default range
467      */

468     private void checkConsistency(StyleRange range) {
469
470         if (range == null)
471             throw new IllegalArgumentException JavaDoc();
472
473         if (fDefaultRange != null) {
474
475             if (range.start < fDefaultRange.start)
476                 range.start= fDefaultRange.start;
477
478             int defaultEnd= fDefaultRange.start + fDefaultRange.length;
479             int end= range.start + range.length;
480             if (end > defaultEnd)
481                 range.length -= (end - defaultEnd);
482         }
483     }
484
485     /**
486      * Returns the index of the first range which overlaps with the
487      * specified window.
488      *
489      * @param window the window to be used for searching
490      * @return the index of the first range overlapping with the window
491      */

492     private int getFirstIndexInWindow(IRegion window) {
493         if (window != null) {
494             int start= window.getOffset();
495             int i= -1, j= fRanges.size();
496             while (j - i > 1) {
497                 int k= (i + j) >> 1;
498                 StyleRange r= (StyleRange) fRanges.get(k);
499                 if (r.start + r.length > start)
500                     j= k;
501                 else
502                     i= k;
503             }
504             return j;
505         }
506         return 0;
507     }
508
509     /**
510      * Returns the index of the first range which comes after the specified window and does
511      * not overlap with this window.
512      *
513      * @param window the window to be used for searching
514      * @return the index of the first range behind the window and not overlapping with the window
515      */

516     private int getFirstIndexAfterWindow(IRegion window) {
517         if (window != null) {
518             int end= window.getOffset() + window.getLength();
519             int i= -1, j= fRanges.size();
520             while (j - i > 1) {
521                 int k= (i + j) >> 1;
522                 StyleRange r= (StyleRange) fRanges.get(k);
523                 if (r.start < end)
524                     i= k;
525                 else
526                     j= k;
527             }
528             return j;
529         }
530         return fRanges.size();
531     }
532
533     /**
534      * Returns a style range which is relative to the specified window and
535      * appropriately clipped if necessary. The original style range is not
536      * modified.
537      *
538      * @param window the reference window
539      * @param range the absolute range
540      * @return the window relative range based on the absolute range
541      */

542     private StyleRange createWindowRelativeRange(IRegion window, StyleRange range) {
543         if (window == null || range == null)
544             return range;
545
546         int start= range.start - window.getOffset();
547         if (start < 0)
548             start= 0;
549
550         int rangeEnd= range.start + range.length;
551         int windowEnd= window.getOffset() + window.getLength();
552         int end= (rangeEnd > windowEnd ? windowEnd : rangeEnd);
553         end -= window.getOffset();
554
555         StyleRange newRange= (StyleRange) range.clone();
556         newRange.start= start;
557         newRange.length= end - start;
558         return newRange;
559     }
560
561     /**
562      * Returns the region which is relative to the specified window and
563      * appropriately clipped if necessary.
564      *
565      * @param coverage the absolute coverage
566      * @return the window relative region based on the absolute coverage
567      * @since 3.0
568      */

569     private IRegion createWindowRelativeRegion(IRegion coverage) {
570         if (fResultWindow == null || coverage == null)
571             return coverage;
572
573         int start= coverage.getOffset() - fResultWindow.getOffset();
574         if (start < 0)
575             start= 0;
576
577         int rangeEnd= coverage.getOffset() + coverage.getLength();
578         int windowEnd= fResultWindow.getOffset() + fResultWindow.getLength();
579         int end= (rangeEnd > windowEnd ? windowEnd : rangeEnd);
580         end -= fResultWindow.getOffset();
581
582         return new Region(start, end - start);
583     }
584
585     /**
586      * Returns an iterator which enumerates all style ranged which define a style
587      * different from the presentation's default style range. The default style range
588      * is not enumerated.
589      *
590      * @return a style range iterator
591      */

592     public Iterator JavaDoc getNonDefaultStyleRangeIterator() {
593         return new FilterIterator(fDefaultRange != null);
594     }
595
596     /**
597      * Returns an iterator which enumerates all style ranges of this presentation
598      * except the default style range. The returned <code>StyleRange</code>s
599      * are relative to the start of the presentation's result window.
600      *
601      * @return a style range iterator
602      */

603     public Iterator JavaDoc getAllStyleRangeIterator() {
604         return new FilterIterator(false);
605     }
606
607     /**
608      * Returns whether this collection contains any style range including
609      * the default style range.
610      *
611      * @return <code>true</code> if there is no style range in this presentation
612      */

613     public boolean isEmpty() {
614         return (fDefaultRange == null && getDenumerableRanges() == 0);
615     }
616
617     /**
618      * Returns the number of style ranges in the presentation not counting the default
619      * style range.
620      *
621      * @return the number of style ranges in the presentation excluding the default style range
622      */

623     public int getDenumerableRanges() {
624         int size= getFirstIndexAfterWindow(fResultWindow) - getFirstIndexInWindow(fResultWindow);
625         return (size < 0 ? 0 : size);
626     }
627
628     /**
629      * Returns the style range with the smallest offset ignoring the default style range or null
630      * if the presentation is empty.
631      *
632      * @return the style range with the smallest offset different from the default style range
633      */

634     public StyleRange getFirstStyleRange() {
635         try {
636
637             StyleRange range= (StyleRange) fRanges.get(getFirstIndexInWindow(fResultWindow));
638             return createWindowRelativeRange(fResultWindow, range);
639
640         } catch (NoSuchElementException JavaDoc x) {
641         } catch (IndexOutOfBoundsException JavaDoc x) {
642         }
643
644         return null;
645     }
646
647     /**
648      * Returns the style range with the highest offset ignoring the default style range.
649      *
650      * @return the style range with the highest offset different from the default style range
651      */

652     public StyleRange getLastStyleRange() {
653         try {
654
655             StyleRange range= (StyleRange) fRanges.get(getFirstIndexAfterWindow(fResultWindow) - 1);
656             return createWindowRelativeRange(fResultWindow, range);
657
658         } catch (NoSuchElementException JavaDoc x) {
659             return null;
660         } catch (IndexOutOfBoundsException JavaDoc x) {
661             return null;
662         }
663     }
664
665     /**
666      * Returns the coverage of this presentation as clipped by the presentation's
667      * result window.
668      *
669      * @return the coverage of this presentation
670      */

671     public IRegion getCoverage() {
672
673         if (fDefaultRange != null) {
674             StyleRange range= getDefaultStyleRange();
675             return new Region(range.start, range.length);
676         }
677
678         StyleRange first= getFirstStyleRange();
679         StyleRange last= getLastStyleRange();
680
681         if (first == null || last == null)
682             return null;
683
684         return new Region(first.start, last.start - first. start + last.length);
685     }
686
687     /**
688      * Returns the extent of this presentation clipped by the
689      * presentation's result window.
690      *
691      * @return the clipped extent
692      * @since 3.0
693      */

694     public IRegion getExtent() {
695         if (fExtent != null)
696             return createWindowRelativeRegion(fExtent);
697         return getCoverage();
698     }
699
700     /**
701      * Clears this presentation by resetting all applied changes.
702      * @since 2.0
703      */

704     public void clear() {
705         fDefaultRange= null;
706         fResultWindow= null;
707         fRanges.clear();
708     }
709
710
711 }
712
Popular Tags