~ubuntu-branches/ubuntu/trusty/netbeans/trusty

« back to all changes in this revision

Viewing changes to openide/text/src/org/openide/text/NbDocument.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
package org.openide.text;
 
42
 
 
43
import java.awt.Color;
 
44
import java.awt.Component;
 
45
 
 
46
import java.beans.*;
 
47
 
 
48
import javax.swing.JEditorPane;
 
49
import javax.swing.JToolBar;
 
50
import javax.swing.SwingUtilities;
 
51
import javax.swing.text.*;
 
52
 
 
53
 
 
54
/** Dummy class holding utility methods for working with NetBeans document conventions.
 
55
*
 
56
* @author Jaroslav Tulach
 
57
*/
 
58
public final class NbDocument extends Object {
 
59
    /** Attribute that signals that a given character is guarded (cannot
 
60
    * be modified). Implements {@link javax.swing.text.AttributeSet.CharacterAttribute} to signal that
 
61
    * this attribute applies to characters, not paragraphs.
 
62
    */
 
63
    public static final Object GUARDED = new AttributeSet.CharacterAttribute() {
 
64
        };
 
65
 
 
66
    /** Attribute set that adds to a part of document guarded flag
 
67
    */
 
68
    private static final SimpleAttributeSet ATTR_ADD = new SimpleAttributeSet();
 
69
 
 
70
    /** Attribute set to remove the guarded flag.
 
71
    */
 
72
    private static final SimpleAttributeSet ATTR_REMOVE = new SimpleAttributeSet();
 
73
 
 
74
    static {
 
75
        ATTR_ADD.addAttribute(GUARDED, Boolean.TRUE);
 
76
        ATTR_REMOVE.addAttribute(GUARDED, Boolean.FALSE);
 
77
    }
 
78
 
 
79
    /** Name of style attached to documents to mark a paragraph (line)
 
80
    * as a (debugger) breakpoint.
 
81
    */
 
82
    public static final String BREAKPOINT_STYLE_NAME = "NbBreakpointStyle"; // NOI18N
 
83
 
 
84
    /** Name of style attached to documents to mark a paragraph (line)
 
85
    * as erroneous.
 
86
    */
 
87
    public static final String ERROR_STYLE_NAME = "NbErrorStyle"; // NOI18N
 
88
 
 
89
    /** Name of style attached to documents to mark a paragraph (line)
 
90
    * as current (in a debugger).
 
91
    */
 
92
    public static final String CURRENT_STYLE_NAME = "NbCurrentStyle"; // NOI18N
 
93
 
 
94
    /** Name of style attached to documents to unmark a paragraph (line)
 
95
    * as anything special.
 
96
    */
 
97
    public static final String NORMAL_STYLE_NAME = "NbNormalStyle"; // NOI18N
 
98
    
 
99
    private NbDocument() {
 
100
    }
 
101
 
 
102
    /** Find the root element of all lines.
 
103
    * All conforming NetBeans documents
 
104
    * should return a valid element.
 
105
    *
 
106
    * @param doc styled document (expecting NetBeans document)
 
107
    * @return the root element
 
108
     * @exception NullPointerException If the <code>doc</code> parameter
 
109
     *                                 is <code>null</code>.
 
110
    */
 
111
    public static Element findLineRootElement(StyledDocument doc) {
 
112
        checkDocParameter(doc);
 
113
 
 
114
        Element e = doc.getParagraphElement(0).getParentElement();
 
115
 
 
116
        if (e == null) {
 
117
            // try default root (should work for text/plain)
 
118
            e = doc.getDefaultRootElement();
 
119
        }
 
120
 
 
121
        return e;
 
122
    }
 
123
 
 
124
    /** For given document and an offset, find the line number.
 
125
    * @param doc the document
 
126
    * @param offset offset in the document
 
127
    * @return the line number for that offset
 
128
     * @exception NullPointerException If the <code>doc</code> parameter
 
129
     *                                 is <code>null</code>.
 
130
    */
 
131
    public static int findLineNumber(StyledDocument doc, int offset) {
 
132
        /* pre-33165
 
133
        Element paragraphsParent = findLineRootElement (doc);
 
134
        return paragraphsParent.getElementIndex (offset);
 
135
         */
 
136
        return new DocumentRenderer(DocumentRenderer.FIND_LINE_NUMBER, doc, offset).renderToInt();
 
137
    }
 
138
 
 
139
    /** Finds column number given an offset.
 
140
    * @param doc the document
 
141
    * @param offset offset in the document
 
142
    * @return column within the line of that offset (counting starts from zero)
 
143
     * @exception NullPointerException If the <code>doc</code> parameter
 
144
     *                                 is <code>null</code>.
 
145
    */
 
146
    public static int findLineColumn(StyledDocument doc, int offset) {
 
147
        /*
 
148
        Element paragraphsParent = findLineRootElement (doc);
 
149
        int indx = paragraphsParent.getElementIndex (offset);
 
150
        return offset - paragraphsParent.getElement (indx).getStartOffset ();
 
151
         */
 
152
        return new DocumentRenderer(DocumentRenderer.FIND_LINE_COLUMN, doc, offset).renderToInt();
 
153
    }
 
154
 
 
155
    /** Finds offset of the beginning of a line.
 
156
    * @param doc the document
 
157
    * @param lineNumber number of the line to find the start of (zero-based)
 
158
    * @return offset
 
159
     * @exception NullPointerException If the <code>doc</code> parameter
 
160
     *                                 is <code>null</code>.
 
161
     * @exception IndexOutOfBoundsException when incorrect
 
162
     * <code>lineNumber</code> value is inserted
 
163
    */
 
164
    public static int findLineOffset(StyledDocument doc, int lineNumber) {
 
165
        /*
 
166
        Element paragraphsParent = findLineRootElement (doc);
 
167
        Element line = paragraphsParent.getElement (lineNumber);
 
168
 
 
169
        if(line == null) {
 
170
            throw new IndexOutOfBoundsException(
 
171
                "Index=" + lineNumber + " is out of bounds."); // NOI18N
 
172
        }
 
173
 
 
174
        return line.getStartOffset ();
 
175
         */
 
176
        return new DocumentRenderer(DocumentRenderer.FIND_LINE_OFFSET, doc, lineNumber).renderToInt();
 
177
    }
 
178
 
 
179
    /** Creates position with a bias. If the bias is {@link javax.swing.text.Position.Bias#Backward}
 
180
    * then if an insert occures at the position, the text is inserted
 
181
    * after the position. If the bias is {@link javax.swing.text.Position.Bias#Forward <code>Forward</code>}, then the text is
 
182
    * inserted before the position.
 
183
    * <P>
 
184
    * The method checks if the document implements {@link PositionBiasable},
 
185
    * and if so, {@link PositionBiasable#createPosition <code>createPosition</code>} is called.
 
186
    * Otherwise an attempt is made to provide a <code>Position</code> with the correct behavior.
 
187
    *
 
188
    * @param doc document to create position in
 
189
    * @param offset the current offset for the position
 
190
    * @param bias the bias to use for the position
 
191
     * @exception NullPointerException If the <code>doc</code> parameter
 
192
     *                                 is <code>null</code>.
 
193
    * @exception BadLocationException if the offset is invalid
 
194
    */
 
195
    public static Position createPosition(Document doc, int offset, Position.Bias bias)
 
196
    throws BadLocationException {
 
197
        checkDocParameter(doc);
 
198
 
 
199
        if (doc instanceof PositionBiasable) {
 
200
            return ((PositionBiasable) doc).createPosition(offset, bias);
 
201
        } else {
 
202
            if (bias == Position.Bias.Forward) {
 
203
                // default behaviour
 
204
                return doc.createPosition(offset);
 
205
            } else {
 
206
                // use our special position
 
207
                return BackwardPosition.create(doc, offset);
 
208
            }
 
209
        }
 
210
    }
 
211
 
 
212
    /** Mark part of a document as guarded (immutable to the user).
 
213
    * @param doc styled document
 
214
    * @param offset offset to start at
 
215
    * @param len length of text to mark as guarded
 
216
     * @exception NullPointerException If the <code>doc</code> parameter
 
217
     *                                 is <code>null</code>.
 
218
    */
 
219
    public static void markGuarded(StyledDocument doc, int offset, int len) {
 
220
        checkDocParameter(doc);
 
221
        doc.setCharacterAttributes(offset, len, ATTR_ADD, false);
 
222
    }
 
223
 
 
224
    /** Remove guarded mark on a block of a document.
 
225
    * @param doc styled document
 
226
    * @param offset offset to start at
 
227
    * @param len length of text to mark as unguarded
 
228
     * @exception NullPointerException If the <code>doc</code> parameter
 
229
     *                                 is <code>null</code>.
 
230
    */
 
231
    public static void unmarkGuarded(StyledDocument doc, int offset, int len) {
 
232
        checkDocParameter(doc);
 
233
        doc.setCharacterAttributes(offset, len, ATTR_REMOVE, false);
 
234
    }
 
235
 
 
236
    /** Inserts a text into given offset and marks it guarded.
 
237
    * @param doc document to insert to
 
238
    * @param offset offset of insertion
 
239
    * @param txt string text to insert
 
240
     * @exception NullPointerException If the <code>doc</code> parameter
 
241
     *                                 is <code>null</code>.
 
242
    */
 
243
    public static void insertGuarded(StyledDocument doc, int offset, String txt)
 
244
    throws BadLocationException {
 
245
        checkDocParameter(doc);
 
246
        doc.insertString(offset, txt, ATTR_ADD);
 
247
    }
 
248
 
 
249
    /** Attach a breakpoint to a line in the document.
 
250
    * If the document has a defined style named {@link #BREAKPOINT_STYLE_NAME}, it is used.
 
251
    * Otherwise, a new style is defined.
 
252
    *
 
253
    * @param doc the document
 
254
    * @param offset identifies the line to set breakpoint to
 
255
     * @exception NullPointerException If the <code>doc</code> parameter
 
256
     *                                 is <code>null</code>.
 
257
    *
 
258
    * @deprecated since 1.20. Use addAnnotation() instead
 
259
    */
 
260
    @Deprecated
 
261
    public static void markBreakpoint(StyledDocument doc, int offset) {
 
262
        checkDocParameter(doc);
 
263
 
 
264
        Style bp = doc.getStyle(BREAKPOINT_STYLE_NAME);
 
265
 
 
266
        if (bp == null) {
 
267
            // create the style
 
268
            bp = doc.addStyle(BREAKPOINT_STYLE_NAME, null);
 
269
 
 
270
            if (bp == null) {
 
271
                return;
 
272
            }
 
273
 
 
274
            bp.addAttribute(StyleConstants.ColorConstants.Background, Color.red);
 
275
            bp.addAttribute(StyleConstants.ColorConstants.Foreground, Color.white);
 
276
        }
 
277
 
 
278
        doc.setLogicalStyle(offset, bp);
 
279
    }
 
280
 
 
281
    /** Mark a line as erroneous (e.g.&nbsp;by the compiler).
 
282
    * If the document has a defined style named {@link #ERROR_STYLE_NAME}, it is used.
 
283
    * Otherwise, a new style is defined.
 
284
    *
 
285
    * @param doc the document
 
286
    * @param offset identifies the line to mark
 
287
     * @exception NullPointerException If the <code>doc</code> parameter
 
288
     *                                 is <code>null</code>.
 
289
    *
 
290
    * @deprecated since 1.20. Use addAnnotation() instead
 
291
    */
 
292
    @Deprecated
 
293
    public static void markError(StyledDocument doc, int offset) {
 
294
        checkDocParameter(doc);
 
295
 
 
296
        Style bp = doc.getStyle(ERROR_STYLE_NAME);
 
297
 
 
298
        if (bp == null) {
 
299
            // create the style
 
300
            bp = doc.addStyle(ERROR_STYLE_NAME, null);
 
301
 
 
302
            if (bp == null) {
 
303
                return;
 
304
            }
 
305
 
 
306
            bp.addAttribute(StyleConstants.ColorConstants.Background, Color.green);
 
307
            bp.addAttribute(StyleConstants.ColorConstants.Foreground, Color.black);
 
308
        }
 
309
 
 
310
        doc.setLogicalStyle(offset, bp);
 
311
    }
 
312
 
 
313
    /** Marks a line as current (e.g.&nbsp;for the debugger).
 
314
    * If the document has a defined style named {@link #CURRENT_STYLE_NAME}, it is used.
 
315
    * Otherwise, a new style is defined.
 
316
    *
 
317
    * @param doc the document
 
318
    * @param offset identifies the line to mark
 
319
     * @exception NullPointerException If the <code>doc</code> parameter
 
320
     *                                 is <code>null</code>.
 
321
    *
 
322
    * @deprecated since 1.20. Use addAnnotation() instead
 
323
    */
 
324
    @Deprecated
 
325
    public static void markCurrent(StyledDocument doc, int offset) {
 
326
        checkDocParameter(doc);
 
327
 
 
328
        Style bp = doc.getStyle(CURRENT_STYLE_NAME);
 
329
 
 
330
        if (bp == null) {
 
331
            // create the style
 
332
            bp = doc.addStyle(CURRENT_STYLE_NAME, null);
 
333
 
 
334
            if (bp == null) {
 
335
                return;
 
336
            }
 
337
 
 
338
            bp.addAttribute(StyleConstants.ColorConstants.Background, Color.blue);
 
339
            bp.addAttribute(StyleConstants.ColorConstants.Foreground, Color.white);
 
340
        }
 
341
 
 
342
        doc.setLogicalStyle(offset, bp);
 
343
    }
 
344
 
 
345
    /**
 
346
     * Mark a line as normal (no special attributes).
 
347
     * This uses the dummy style named {@link #NORMAL_STYLE_NAME}.
 
348
     * This method should be used to undo the effect of {@link #markBreakpoint}, {@link #markError} and {@link #markCurrent}.
 
349
     * @param doc the document
 
350
     * @param offset identified the line to unmark
 
351
     * @exception NullPointerException If the <code>doc</code> parameter
 
352
     *                                 is <code>null</code>.
 
353
     *
 
354
     * @deprecated since 1.20. Use addAnnotation() instead
 
355
     */
 
356
    @Deprecated
 
357
    public static void markNormal(StyledDocument doc, int offset) {
 
358
        checkDocParameter(doc);
 
359
 
 
360
        Style st = doc.getStyle(NORMAL_STYLE_NAME);
 
361
 
 
362
        if (st == null) {
 
363
            st = doc.addStyle(NORMAL_STYLE_NAME, null);
 
364
        }
 
365
 
 
366
        if (st != null) {
 
367
            doc.setLogicalStyle(offset, st);
 
368
        }
 
369
    }
 
370
 
 
371
    /** Locks the document to have exclusive access to it.
 
372
     * Documents implementing {@link Lockable} can specify exactly how to do this.
 
373
    *
 
374
    * @param doc document to lock
 
375
    * @param run the action to run
 
376
     * @exception NullPointerException If the <code>doc</code> parameter
 
377
     *                                 is <code>null</code>.
 
378
    */
 
379
    public static void runAtomic(StyledDocument doc, Runnable run) {
 
380
        checkDocParameter(doc);
 
381
 
 
382
        if (doc instanceof WriteLockable) {
 
383
            // use the method
 
384
            ((WriteLockable) doc).runAtomic(run);
 
385
        } else {
 
386
            // transfer the runnable to event dispatch thread
 
387
            synchronized (doc) {
 
388
                run.run();
 
389
            }
 
390
        }
 
391
    }
 
392
 
 
393
    /** Executes given runnable in "user mode" does not allowing any modifications
 
394
    * to parts of text marked as guarded. The actions should be run as "atomic" so
 
395
    * either happen all at once or none at all (if a guarded block should be modified).
 
396
    *
 
397
    * @param doc document to modify
 
398
    * @param run runnable to run in user mode that will have exclusive access to modify the document
 
399
     * @exception NullPointerException If the <code>doc</code> parameter
 
400
     *                                 is <code>null</code>.
 
401
    * @exception BadLocationException if a modification of guarded text occured
 
402
    *   and that is why no changes to the document has been done.
 
403
    */
 
404
    public static void runAtomicAsUser(StyledDocument doc, Runnable run)
 
405
    throws BadLocationException {
 
406
        checkDocParameter(doc);
 
407
 
 
408
        if (doc instanceof WriteLockable) {
 
409
            // use the method
 
410
            ((WriteLockable) doc).runAtomicAsUser(run);
 
411
        } else {
 
412
            // transfer the runnable to event dispatch thread
 
413
            synchronized (doc) {
 
414
                run.run();
 
415
            }
 
416
        }
 
417
    }
 
418
 
 
419
    /** Helper method for checking document parameter validity.
 
420
     * @exception NullPointerException If the <code>doc</code> parameter
 
421
     *                                 is <code>null</code>. */
 
422
    private static void checkDocParameter(Document doc) {
 
423
        if (doc == null) {
 
424
            throw new NullPointerException("Invalid doc parameter. Document may not be null!"); // NOI18N
 
425
        }
 
426
    }
 
427
 
 
428
    /** Find a way to print a given document.
 
429
     * If the document implements the correct interface(s) then the document is returned,
 
430
     * else a default {@link java.awt.print.Printable} is used as a wrapper. In this last case it is useful
 
431
     * to implement {@link NbDocument.Printable} to describe how to print in terms of
 
432
     * attributed characters, rather than specifying the complete page layout from scratch.
 
433
     *
 
434
     * @param doc the document to find printing support for
 
435
     * @return an object that is instance of eith {@link java.awt.print.Printable} or {@link java.awt.print.Pageable}
 
436
     */
 
437
    public static Object findPageable(StyledDocument doc) {
 
438
        if (doc instanceof java.awt.print.Pageable) {
 
439
            return doc;
 
440
        } else if (doc instanceof java.awt.print.Printable) {
 
441
            return doc;
 
442
        } else {
 
443
            return new DefaultPrintable(doc);
 
444
        }
 
445
    }
 
446
 
 
447
    /** Add annotation to the document. For annotation of whole line
 
448
     * the length parameter can be ignored (specify value -1).
 
449
     * @param doc the document which will be annotated
 
450
     * @param startPos position which represent begining
 
451
     * of the annotated text
 
452
     * @param length length of the annotated text. If -1 is specified
 
453
     * the whole line will be annotated
 
454
     * @param annotation annotation which is attached to this text
 
455
     * @since 1.20
 
456
     */
 
457
    public static void addAnnotation(
 
458
        final StyledDocument doc, final Position startPos, final int length, final Annotation annotation
 
459
    ) {
 
460
        if (!(doc instanceof Annotatable)) {
 
461
            return;
 
462
        }
 
463
 
 
464
        if (SwingUtilities.isEventDispatchThread()) {
 
465
            ((Annotatable) doc).addAnnotation(startPos, length, annotation);
 
466
        } else {
 
467
            SwingUtilities.invokeLater(
 
468
                new Runnable() {
 
469
                    public void run() {
 
470
                        ((Annotatable) doc).addAnnotation(startPos, length, annotation);
 
471
                    }
 
472
                }
 
473
            );
 
474
        }
 
475
    }
 
476
 
 
477
    /** Removal of added annotation.
 
478
     * @param doc the annotated document
 
479
     * @param annotation annotation which is going to be removed
 
480
     * @since 1.20
 
481
     */
 
482
    public static void removeAnnotation(final StyledDocument doc, final Annotation annotation) {
 
483
        if (!(doc instanceof Annotatable)) {
 
484
            return;
 
485
        }
 
486
 
 
487
        if (SwingUtilities.isEventDispatchThread()) {
 
488
            ((Annotatable) doc).removeAnnotation(annotation);
 
489
        } else {
 
490
            SwingUtilities.invokeLater(
 
491
                new Runnable() {
 
492
                    public void run() {
 
493
                        ((Annotatable) doc).removeAnnotation(annotation);
 
494
                    }
 
495
                }
 
496
            );
 
497
        }
 
498
    }
 
499
 
 
500
    /** Specialized version of document that knows how to lock the document
 
501
    * for complex modifications.
 
502
    */
 
503
    public interface WriteLockable extends Document {
 
504
        /** Executes given runnable in lock mode of the document.
 
505
        * In this mode, all redrawing is stopped and the caller has exclusive
 
506
        * access to all modifications to the document.
 
507
        * <P>
 
508
        * By definition there should be only one locker at a time. Sample implementation, if you are extending {@link AbstractDocument}:
 
509
        *
 
510
        * <p><code>
 
511
        * writeLock();<br>
 
512
        * try {<br>
 
513
        * &nbsp;&nbsp;r.run();<br>
 
514
        * } finally {<br>
 
515
        * &nbsp;&nbsp;writeUnlock();<br>
 
516
        * }
 
517
        * </code>
 
518
        *
 
519
        * @param r runnable to run while locked
 
520
        *
 
521
        * @see NbDocument#runAtomic
 
522
        */
 
523
        public void runAtomic(Runnable r);
 
524
 
 
525
        /** Executes given runnable in "user mode" does not allowing any modifications
 
526
        * to parts of text marked as guarded. The actions should be run as "atomic" so
 
527
        * either happen all at once or none at all (if a guarded block should be modified).
 
528
        *
 
529
        * @param r runnable to run in user mode that will have exclusive access to modify the document
 
530
        * @exception BadLocationException if a modification of guarded text occured
 
531
        *   and that is why no changes to the document has been done.
 
532
        */
 
533
        public void runAtomicAsUser(Runnable r) throws BadLocationException;
 
534
    }
 
535
 
 
536
    /** Document which may support styled text printing.
 
537
    * Any document that wishes to support special formatting while printing
 
538
    * can implement this interface and provide a <code>AttributedCharacterIterator</code>
 
539
    * specifying colors, fonts, etc.
 
540
    */
 
541
    public interface Printable extends Document {
 
542
        /** Get an attributed character iterator for the document, so that it may be printed.
 
543
         * <p>For a convenient way to do this, you may use {@link AttributedCharacters#iterator a simple implementation}
 
544
         * of an
 
545
         * attributed character list.
 
546
        * @return list of <code>AttributedCharacterIterator</code>s to be used for printing
 
547
        *
 
548
        * @see NbDocument#findPageable */
 
549
        public java.text.AttributedCharacterIterator[] createPrintIterators();
 
550
    }
 
551
 
 
552
    /** Enhanced version of document that provides better support for
 
553
    * holding and working with biased positions. It adds one new method
 
554
    * {@link #createPosition} that creates
 
555
    * a position that moves either to the left or to the right when an insertion
 
556
    * is performed at it.
 
557
    * <P>
 
558
    * If a document implements this interface, the new method is
 
559
    * used in {@link NbDocument#createPosition}.
 
560
    * If not, special support for the position is created.
 
561
    */
 
562
    public interface PositionBiasable extends Document {
 
563
        /** Creates position with a bias. If the bias is {@link javax.swing.text.Position.Bias#Backward}
 
564
        * then if an insert occures at the position, the text is inserted
 
565
        * after the position. If the bias is {@link javax.swing.text.Position.Bias#Forward <code>Forward</code>}, then the text is
 
566
        * inserted before the position.
 
567
        *
 
568
        * @param offset the offset for the position
 
569
        * @param bias the bias to use for the position
 
570
        * @exception BadLocationException if the offset is invalid
 
571
        *
 
572
        * @see NbDocument#createPosition
 
573
        */
 
574
        public Position createPosition(int offset, Position.Bias bias)
 
575
        throws BadLocationException;
 
576
    }
 
577
 
 
578
    /** Enabled documents to add special UI components to their Editor pane.
 
579
    * If this interface is implemented by the Editor document, it can be used
 
580
    * to add other components (such as toolbars) to the pane.
 
581
    */
 
582
    public interface CustomEditor extends Document {
 
583
        /** Create a whole editor component over the given <code>JEditorPane</code>.
 
584
         * The implementation should generally add some kind of scrolling
 
585
         * support to the given <code>JEditorPane</code> (typically with scrollbars),
 
586
         * possibly some other components
 
587
         * according to the desired layout,
 
588
         * and return the resulting container.
 
589
         * @param j editor pane over which the resulting component
 
590
         *   will be built
 
591
         * @return component encapsulating the pane and all other
 
592
         *   custom UI components
 
593
         */
 
594
        public Component createEditor(JEditorPane j);
 
595
    }
 
596
 
 
597
    /**
 
598
     * Enabled documents to add special UI toolbar components to their Editor pane.
 
599
     */
 
600
    public interface CustomToolbar extends Document {
 
601
        /**
 
602
         * Implementation shall return a toolbar for the document. Preferably non-floatable.
 
603
         */
 
604
        public JToolBar createToolbar(JEditorPane j);
 
605
    }
 
606
 
 
607
    /** Enhanced version of document which is capable of
 
608
     * attaching/detaching of annotations. It is guaranteed that
 
609
     * annotations are added/removed to document only in AWT thread.
 
610
     * @since 1.20
 
611
     */
 
612
    public interface Annotatable extends Document {
 
613
        /** Add annotation to the document. For annotation of whole line
 
614
         * the length parameter can be ignored (specify value -1).
 
615
         * @param startPos position which represent begining
 
616
         * of the annotated text
 
617
         * @param length length of the annotated text. If -1 is specified
 
618
         * the whole line will be annotated
 
619
         * @param annotation annotation which is attached to this text */
 
620
        public void addAnnotation(Position startPos, int length, Annotation annotation);
 
621
 
 
622
        /** Removal of added annotation.
 
623
         * @param annotation annotation which is going to be removed */
 
624
        public void removeAnnotation(Annotation annotation);
 
625
    }
 
626
    
 
627
    private static final class DocumentRenderer implements Runnable {
 
628
        private static final int FIND_LINE_NUMBER = 0;
 
629
        private static final int FIND_LINE_COLUMN = 1;
 
630
        private static final int FIND_LINE_OFFSET = 2;
 
631
        private StyledDocument doc;
 
632
        private int opCode;
 
633
        private int argInt;
 
634
        private int retInt;
 
635
 
 
636
        DocumentRenderer(int opCode, StyledDocument doc, int argInt) {
 
637
            this.opCode = opCode;
 
638
            this.doc = doc;
 
639
            this.argInt = argInt;
 
640
        }
 
641
 
 
642
        int renderToInt() {
 
643
            if (doc != null) {
 
644
                doc.render(this);
 
645
            }
 
646
 
 
647
            return retInt;
 
648
        }
 
649
 
 
650
        public void run() {
 
651
            switch (opCode) {
 
652
            case FIND_LINE_NUMBER: {
 
653
                Element paragraphsParent = findLineRootElement(doc);
 
654
                retInt = paragraphsParent.getElementIndex(argInt); // argInt is offset
 
655
 
 
656
                break;
 
657
            }
 
658
 
 
659
            case FIND_LINE_COLUMN: {
 
660
                Element paragraphsParent = findLineRootElement(doc);
 
661
                int indx = paragraphsParent.getElementIndex(argInt); // argInt is offset
 
662
                retInt = argInt - paragraphsParent.getElement(indx).getStartOffset();
 
663
 
 
664
                break;
 
665
            }
 
666
 
 
667
            case FIND_LINE_OFFSET: {
 
668
                Element paragraphsParent = findLineRootElement(doc);
 
669
                Element line = paragraphsParent.getElement(argInt); // argInt is lineNumber
 
670
 
 
671
                if (line == null) {
 
672
                    throw new IndexOutOfBoundsException("Index=" + argInt + " is out of bounds."); // NOI18N
 
673
                }
 
674
 
 
675
                retInt = line.getStartOffset();
 
676
 
 
677
                break;
 
678
            }
 
679
 
 
680
            default:
 
681
                throw new IllegalStateException();
 
682
            }
 
683
        }
 
684
    }
 
685
}