1 | /*
|
---|
2 | * 10/08/2011
|
---|
3 | *
|
---|
4 | * FoldManager.java - Manages code folding in an RSyntaxTextArea instance.
|
---|
5 | *
|
---|
6 | * This library is distributed under a modified BSD license. See the included
|
---|
7 | * RSyntaxTextArea.License.txt file for details.
|
---|
8 | */
|
---|
9 | package org.fife.ui.rsyntaxtextarea.folding;
|
---|
10 |
|
---|
11 | import java.beans.PropertyChangeEvent;
|
---|
12 | import java.beans.PropertyChangeListener;
|
---|
13 | import java.beans.PropertyChangeSupport;
|
---|
14 | import java.util.ArrayList;
|
---|
15 | import java.util.Collections;
|
---|
16 | import java.util.List;
|
---|
17 | import javax.swing.event.DocumentEvent;
|
---|
18 | import javax.swing.event.DocumentListener;
|
---|
19 | import javax.swing.text.BadLocationException;
|
---|
20 | import javax.swing.text.Document;
|
---|
21 | import javax.swing.text.Element;
|
---|
22 |
|
---|
23 | import org.fife.ui.rsyntaxtextarea.RSyntaxDocument;
|
---|
24 | import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
---|
25 | import org.fife.ui.rsyntaxtextarea.parser.AbstractParser;
|
---|
26 | import org.fife.ui.rsyntaxtextarea.parser.DefaultParseResult;
|
---|
27 | import org.fife.ui.rsyntaxtextarea.parser.ParseResult;
|
---|
28 | import org.fife.ui.rsyntaxtextarea.parser.Parser;
|
---|
29 |
|
---|
30 |
|
---|
31 | /**
|
---|
32 | * Manages code folding in an instance of RSyntaxTextArea.
|
---|
33 | *
|
---|
34 | * @author Robert Futrell
|
---|
35 | * @version 1.0
|
---|
36 | */
|
---|
37 | public class FoldManager {
|
---|
38 |
|
---|
39 | private RSyntaxTextArea textArea;
|
---|
40 | private FoldParser parser;
|
---|
41 | private List folds;
|
---|
42 | private boolean codeFoldingEnabled;
|
---|
43 | private PropertyChangeSupport support;
|
---|
44 |
|
---|
45 |
|
---|
46 | /**
|
---|
47 | * Property fired when folds have been updated.
|
---|
48 | */
|
---|
49 | public static final String PROPERTY_FOLDS_UPDATED = "FoldsUpdated";
|
---|
50 |
|
---|
51 |
|
---|
52 | /**
|
---|
53 | * Constructor.
|
---|
54 | *
|
---|
55 | * @param textArea The text area whose folds we are managing.
|
---|
56 | */
|
---|
57 | public FoldManager(RSyntaxTextArea textArea) {
|
---|
58 | this.textArea = textArea;
|
---|
59 | support = new PropertyChangeSupport(this);
|
---|
60 | Listener l = new Listener();
|
---|
61 | textArea.getDocument().addDocumentListener(l);
|
---|
62 | textArea.addPropertyChangeListener(RSyntaxTextArea.SYNTAX_STYLE_PROPERTY, l);
|
---|
63 | folds = new ArrayList();
|
---|
64 | updateFoldParser();
|
---|
65 | }
|
---|
66 |
|
---|
67 |
|
---|
68 | /**
|
---|
69 | * Adds a property change listener to this fold manager.
|
---|
70 | *
|
---|
71 | * @param l The new listener.
|
---|
72 | * @see #removePropertyChangeListener(PropertyChangeListener)
|
---|
73 | */
|
---|
74 | public void addPropertyChangeListener(PropertyChangeListener l) {
|
---|
75 | support.addPropertyChangeListener(l);
|
---|
76 | }
|
---|
77 |
|
---|
78 |
|
---|
79 | /**
|
---|
80 | * Removes all folds.
|
---|
81 | */
|
---|
82 | public void clear() {
|
---|
83 | folds.clear();
|
---|
84 | }
|
---|
85 |
|
---|
86 |
|
---|
87 | /**
|
---|
88 | * Ensures that the specified offset is not hidden in a collapsed fold.
|
---|
89 | * Any folds containing this offset that are collapsed will be expanded.
|
---|
90 | *
|
---|
91 | * @param offs The offset.
|
---|
92 | * @return Whether any folds had to be opened.
|
---|
93 | * @see #getDeepestFoldContaining(int)
|
---|
94 | */
|
---|
95 | public boolean ensureOffsetNotInClosedFold(int offs) {
|
---|
96 | boolean foldsOpened = false;
|
---|
97 | Fold fold = getDeepestFoldContaining(offs);
|
---|
98 | while (fold!=null) {
|
---|
99 | if (fold.isCollapsed()) {
|
---|
100 | fold.setCollapsed(false);
|
---|
101 | foldsOpened = true;
|
---|
102 | }
|
---|
103 | fold = fold.getParent();
|
---|
104 | }
|
---|
105 | return foldsOpened;
|
---|
106 | }
|
---|
107 |
|
---|
108 |
|
---|
109 | /**
|
---|
110 | * Returns the "deepest" nested fold containing the specified offset.
|
---|
111 | *
|
---|
112 | * @param offs The offset.
|
---|
113 | * @return The deepest fold containing the offset, or <code>null</code> if
|
---|
114 | * no fold contains the offset.
|
---|
115 | */
|
---|
116 | public Fold getDeepestFoldContaining(int offs) {
|
---|
117 | Fold deepestFold = null;
|
---|
118 | if (offs>-1) {
|
---|
119 | for (int i=0; i<folds.size(); i++) {
|
---|
120 | Fold fold = getFold(i);
|
---|
121 | if (fold.containsOffset(offs)) {
|
---|
122 | deepestFold = fold.getDeepestFoldContaining(offs);
|
---|
123 | break;
|
---|
124 | }
|
---|
125 | }
|
---|
126 | }
|
---|
127 | return deepestFold;
|
---|
128 | }
|
---|
129 |
|
---|
130 |
|
---|
131 | /**
|
---|
132 | * Returns the "deepest" open fold containing the specified offset.
|
---|
133 | *
|
---|
134 | * @param offs The offset.
|
---|
135 | * @return The fold, or <code>null</code> if no open fold contains the
|
---|
136 | * offset.
|
---|
137 | */
|
---|
138 | public Fold getDeepestOpenFoldContaining(int offs) {
|
---|
139 |
|
---|
140 | Fold deepestFold = null;
|
---|
141 |
|
---|
142 | if (offs>-1) {
|
---|
143 | for (int i=0; i<folds.size(); i++) {
|
---|
144 | Fold fold = getFold(i);
|
---|
145 | if (fold.containsOffset(offs)) {
|
---|
146 | if (fold.isCollapsed()) {
|
---|
147 | return null;
|
---|
148 | }
|
---|
149 | deepestFold = fold.getDeepestOpenFoldContaining(offs);
|
---|
150 | break;
|
---|
151 | }
|
---|
152 | }
|
---|
153 | }
|
---|
154 |
|
---|
155 | return deepestFold;
|
---|
156 |
|
---|
157 | }
|
---|
158 |
|
---|
159 |
|
---|
160 | /**
|
---|
161 | * Returns a specific top-level fold, which may have child folds.
|
---|
162 | *
|
---|
163 | * @param index The index of the fold.
|
---|
164 | * @return The fold.
|
---|
165 | * @see #getFoldCount()
|
---|
166 | */
|
---|
167 | public Fold getFold(int index) {
|
---|
168 | return (Fold)folds.get(index);
|
---|
169 | }
|
---|
170 |
|
---|
171 |
|
---|
172 | /**
|
---|
173 | * Returns the number of top-level folds.
|
---|
174 | *
|
---|
175 | * @return The number of top-level folds.
|
---|
176 | * @see #getFold(int)
|
---|
177 | */
|
---|
178 | public int getFoldCount() {
|
---|
179 | return folds.size();
|
---|
180 | }
|
---|
181 |
|
---|
182 |
|
---|
183 | /**
|
---|
184 | * Returns the fold region that starts at the specified line.
|
---|
185 | *
|
---|
186 | * @param line The line number.
|
---|
187 | * @return The fold, or <code>null</code> if the line is not the start
|
---|
188 | * of a fold region.
|
---|
189 | * @see #isFoldStartLine(int)
|
---|
190 | */
|
---|
191 | public Fold getFoldForLine(int line) {
|
---|
192 | return getFoldForLineImpl(null, folds, line);
|
---|
193 | }
|
---|
194 |
|
---|
195 |
|
---|
196 | private Fold getFoldForLineImpl(Fold parent, List folds, int line) {
|
---|
197 |
|
---|
198 | int low = 0;
|
---|
199 | int high = folds.size() - 1;
|
---|
200 |
|
---|
201 | while (low <= high) {
|
---|
202 | int mid = (low + high) >> 1;
|
---|
203 | Fold midFold = (Fold)folds.get(mid);
|
---|
204 | int startLine = midFold.getStartLine();
|
---|
205 | if (line==startLine) {
|
---|
206 | return midFold;
|
---|
207 | }
|
---|
208 | else if (line<startLine) {
|
---|
209 | high = mid - 1;
|
---|
210 | }
|
---|
211 | else {
|
---|
212 | int endLine = midFold.getEndLine();
|
---|
213 | if (line>=endLine) {
|
---|
214 | low = mid + 1;
|
---|
215 | }
|
---|
216 | else { // line>startLine && line<=endLine
|
---|
217 | List children = midFold.getChildren();
|
---|
218 | return children!=null ? getFoldForLineImpl(midFold, children, line) : null;
|
---|
219 | }
|
---|
220 | }
|
---|
221 | }
|
---|
222 |
|
---|
223 | return null; // No fold for this line
|
---|
224 | }
|
---|
225 |
|
---|
226 |
|
---|
227 | /**
|
---|
228 | * Returns the total number of hidden (folded) lines.
|
---|
229 | *
|
---|
230 | * @return The total number of hidden (folded) lines.
|
---|
231 | * @see #getHiddenLineCountAbove(int)
|
---|
232 | */
|
---|
233 | public int getHiddenLineCount() {
|
---|
234 | int count = 0;
|
---|
235 | for (int i=0; i<folds.size(); i++) {
|
---|
236 | count += ((Fold)folds.get(i)).getCollapsedLineCount();
|
---|
237 | }
|
---|
238 | return count;
|
---|
239 | }
|
---|
240 |
|
---|
241 |
|
---|
242 | /**
|
---|
243 | * Returns the number of lines "hidden" by collapsed folds above the
|
---|
244 | * specified line.
|
---|
245 | *
|
---|
246 | * @param line The line. This is the line number for a logical line.
|
---|
247 | * For the line number of a physical line (i.e. visible, not folded),
|
---|
248 | * use <code>getHiddenLineCountAbove(int, true)</code>.
|
---|
249 | * @return The number of lines hidden in folds above <code>line</code>.
|
---|
250 | * @see #getHiddenLineCountAbove(int, boolean)
|
---|
251 | */
|
---|
252 | public int getHiddenLineCountAbove(int line) {
|
---|
253 | return getHiddenLineCountAbove(line, false);
|
---|
254 | }
|
---|
255 |
|
---|
256 |
|
---|
257 | /**
|
---|
258 | * Returns the number of lines "hidden" by collapsed folds above the
|
---|
259 | * specified line.
|
---|
260 | *
|
---|
261 | * @param line The line.
|
---|
262 | * @param physical Whether <code>line</code> is the number of a physical
|
---|
263 | * line (i.e. visible, not code-folded), or a logical one (i.e. any
|
---|
264 | * line from the model). If <code>line</code> was determined by a
|
---|
265 | * raw line calculation (i.e. <code>(visibleTopY / lineHeight)</code>),
|
---|
266 | * this value should be <code>true</code>. It should be
|
---|
267 | * <code>false</code> when it was calculated from an offset in the
|
---|
268 | * document (for example).
|
---|
269 | * @return The number of lines hidden in folds above <code>line</code>.
|
---|
270 | */
|
---|
271 | public int getHiddenLineCountAbove(int line, boolean physical) {
|
---|
272 |
|
---|
273 | int count = 0;
|
---|
274 |
|
---|
275 | for (int i=0; i<folds.size(); i++) {
|
---|
276 | Fold fold = (Fold)folds.get(i);
|
---|
277 | int comp = physical ? line+count : line;
|
---|
278 | if (fold.getStartLine()>=comp) {
|
---|
279 | break;
|
---|
280 | }
|
---|
281 | count += getHiddenLineCountAboveImpl(fold, comp, physical);
|
---|
282 | }
|
---|
283 |
|
---|
284 | return count;
|
---|
285 |
|
---|
286 | }
|
---|
287 |
|
---|
288 |
|
---|
289 | /**
|
---|
290 | * Returns the number of lines "hidden" by collapsed folds above the
|
---|
291 | * specified line.
|
---|
292 | *
|
---|
293 | * @param fold The current fold in the recursive algorithm. It and its
|
---|
294 | * children are examined.
|
---|
295 | * @param line The line.
|
---|
296 | * @param physical Whether <code>line</code> is the number of a physical
|
---|
297 | * line (i.e. visible, not code-folded), or a logical one (i.e. any
|
---|
298 | * line from the model). If <code>line</code> was determined by a
|
---|
299 | * raw line calculation (i.e. <code>(visibleTopY / lineHeight)</code>),
|
---|
300 | * this value should be <code>true</code>. It should be
|
---|
301 | * <code>false</code> when it was calculated from an offset in the
|
---|
302 | * document (for example).
|
---|
303 | * @return The number of lines hidden in folds that are descendants of
|
---|
304 | * <code>fold</code>, or <code>fold</code> itself, above
|
---|
305 | * <code>line</code>.
|
---|
306 | */
|
---|
307 | private int getHiddenLineCountAboveImpl(Fold fold, int line, boolean physical) {
|
---|
308 |
|
---|
309 | int count = 0;
|
---|
310 |
|
---|
311 | if (fold.getEndLine()<line ||
|
---|
312 | (fold.isCollapsed() && fold.getStartLine()<line)) {
|
---|
313 | count = fold.getCollapsedLineCount();
|
---|
314 | }
|
---|
315 | else {
|
---|
316 | int childCount = fold.getChildCount();
|
---|
317 | for (int i=0; i<childCount; i++) {
|
---|
318 | Fold child = fold.getChild(i);
|
---|
319 | int comp = physical ? line+count : line;
|
---|
320 | if (child.getStartLine()>=comp) {
|
---|
321 | break;
|
---|
322 | }
|
---|
323 | count += getHiddenLineCountAboveImpl(child, comp, physical);
|
---|
324 | }
|
---|
325 | }
|
---|
326 |
|
---|
327 | return count;
|
---|
328 |
|
---|
329 | }
|
---|
330 |
|
---|
331 |
|
---|
332 | /**
|
---|
333 | * Returns the last visible line in the text area, taking into account
|
---|
334 | * folds.
|
---|
335 | *
|
---|
336 | * @return The last visible line.
|
---|
337 | */
|
---|
338 | public int getLastVisibleLine() {
|
---|
339 |
|
---|
340 | int lastLine = textArea.getLineCount() - 1;
|
---|
341 |
|
---|
342 | if (isCodeFoldingSupportedAndEnabled()) {
|
---|
343 | int foldCount = getFoldCount();
|
---|
344 | if (foldCount>0) {
|
---|
345 | Fold lastFold = getFold(foldCount-1);
|
---|
346 | if (lastFold.containsLine(lastLine)) {
|
---|
347 | if (lastFold.isCollapsed()) {
|
---|
348 | lastLine = lastFold.getStartLine();
|
---|
349 | }
|
---|
350 | else { // Child fold may end on the same line as parent
|
---|
351 | while (lastFold.getHasChildFolds()) {
|
---|
352 | lastFold = lastFold.getLastChild();
|
---|
353 | if (lastFold.containsLine(lastLine)) {
|
---|
354 | if (lastFold.isCollapsed()) {
|
---|
355 | lastLine = lastFold.getStartLine();
|
---|
356 | break;
|
---|
357 | }
|
---|
358 | }
|
---|
359 | else { // Higher up
|
---|
360 | break;
|
---|
361 | }
|
---|
362 | }
|
---|
363 | }
|
---|
364 | }
|
---|
365 | }
|
---|
366 | }
|
---|
367 |
|
---|
368 | return lastLine;
|
---|
369 |
|
---|
370 | }
|
---|
371 |
|
---|
372 |
|
---|
373 | public int getVisibleLineAbove(int line) {
|
---|
374 |
|
---|
375 | if (line<=0 || line>=textArea.getLineCount()) {
|
---|
376 | return -1;
|
---|
377 | }
|
---|
378 |
|
---|
379 | do {
|
---|
380 | line--;
|
---|
381 | } while (line>=0 && isLineHidden(line));
|
---|
382 |
|
---|
383 | return line;
|
---|
384 |
|
---|
385 | }
|
---|
386 |
|
---|
387 |
|
---|
388 | public int getVisibleLineBelow(int line) {
|
---|
389 |
|
---|
390 | int lineCount = textArea.getLineCount();
|
---|
391 | if (line<0 || line>=lineCount-1) {
|
---|
392 | return -1;
|
---|
393 | }
|
---|
394 |
|
---|
395 | do {
|
---|
396 | line++;
|
---|
397 | } while (line<lineCount && isLineHidden(line));
|
---|
398 |
|
---|
399 | return line==lineCount ? -1 : line;
|
---|
400 |
|
---|
401 | }
|
---|
402 |
|
---|
403 |
|
---|
404 | // private static int binaryFindFoldContainingLine(int line) {
|
---|
405 | //
|
---|
406 | //List allFolds;
|
---|
407 | //
|
---|
408 | // int low = 0;
|
---|
409 | // int high = allFolds.size() - 1;
|
---|
410 | //
|
---|
411 | // while (low <= high) {
|
---|
412 | // int mid = (low + high) >> 1;
|
---|
413 | // Fold midVal = (Fold)allFolds.get(mid);
|
---|
414 | // if (midVal.containsLine(line)) {
|
---|
415 | // return mid;
|
---|
416 | // }
|
---|
417 | // if (line<=midVal.getStartLine()) {
|
---|
418 | // high = mid - 1;
|
---|
419 | // }
|
---|
420 | // else { // line > midVal.getEndLine()
|
---|
421 | // low = mid + 1;
|
---|
422 | // }
|
---|
423 | // }
|
---|
424 | //
|
---|
425 | // return -(low + 1); // key not found
|
---|
426 | //
|
---|
427 | // }
|
---|
428 |
|
---|
429 |
|
---|
430 | /**
|
---|
431 | * Returns whether code folding is enabled. Note that only certain
|
---|
432 | * languages support code folding; those that do not will ignore this
|
---|
433 | * property.
|
---|
434 | *
|
---|
435 | * @return Whether code folding is enabled.
|
---|
436 | * @see #setCodeFoldingEnabled(boolean)
|
---|
437 | */
|
---|
438 | public boolean isCodeFoldingEnabled() {
|
---|
439 | return codeFoldingEnabled;
|
---|
440 | }
|
---|
441 |
|
---|
442 |
|
---|
443 | /**
|
---|
444 | * Returns <code>true</code> if and only if code folding is enabled for
|
---|
445 | * this text area, AND folding is supported for the language it is editing.
|
---|
446 | * Whether or not folding is supported for a language depends on whether
|
---|
447 | * a fold parser is registered for that language with the
|
---|
448 | * <code>FoldParserManager</code>.
|
---|
449 | *
|
---|
450 | * @return Whether folding is supported and enabled for this text area.
|
---|
451 | * @see FoldParserManager
|
---|
452 | */
|
---|
453 | public boolean isCodeFoldingSupportedAndEnabled() {
|
---|
454 | return codeFoldingEnabled && parser!=null;
|
---|
455 | }
|
---|
456 |
|
---|
457 |
|
---|
458 | /**
|
---|
459 | * Returns whether the specified line contains the start of a fold region.
|
---|
460 | *
|
---|
461 | * @param line The line.
|
---|
462 | * @return Whether the line contains the start of a fold region.
|
---|
463 | * @see #getFoldForLine(int)
|
---|
464 | */
|
---|
465 | public boolean isFoldStartLine(int line) {
|
---|
466 | return getFoldForLine(line)!=null;
|
---|
467 | }
|
---|
468 |
|
---|
469 |
|
---|
470 | /**
|
---|
471 | * Returns whether a line is hidden in a collapsed fold.
|
---|
472 | *
|
---|
473 | * @param line The line to check.
|
---|
474 | * @return Whether the line is hidden in a collapsed fold.
|
---|
475 | */
|
---|
476 | public boolean isLineHidden(int line) {
|
---|
477 | for (int i=0; i<folds.size(); i++) {
|
---|
478 | Fold fold = (Fold)folds.get(i);
|
---|
479 | if (fold.containsLine(line)) {
|
---|
480 | if (fold.isCollapsed()) {
|
---|
481 | return true;
|
---|
482 | }
|
---|
483 | else {
|
---|
484 | return isLineHiddenImpl(fold, line);
|
---|
485 | }
|
---|
486 | }
|
---|
487 | }
|
---|
488 | return false;
|
---|
489 | }
|
---|
490 |
|
---|
491 |
|
---|
492 | private boolean isLineHiddenImpl(Fold parent, int line) {
|
---|
493 | for (int i=0; i<parent.getChildCount(); i++) {
|
---|
494 | Fold child = parent.getChild(i);
|
---|
495 | if (child.containsLine(line)) {
|
---|
496 | if (child.isCollapsed()) {
|
---|
497 | return true;
|
---|
498 | }
|
---|
499 | else {
|
---|
500 | return isLineHiddenImpl(child, line);
|
---|
501 | }
|
---|
502 | }
|
---|
503 | }
|
---|
504 | return false;
|
---|
505 | }
|
---|
506 |
|
---|
507 |
|
---|
508 | private void keepFoldState(Fold newFold, List oldFolds) {
|
---|
509 | int previousLoc = Collections.binarySearch(oldFolds, newFold);
|
---|
510 | //System.out.println(newFold + " => " + previousLoc);
|
---|
511 | if (previousLoc>=0) {
|
---|
512 | Fold prevFold = (Fold)oldFolds.get(previousLoc);
|
---|
513 | newFold.setCollapsed(prevFold.isCollapsed());
|
---|
514 | }
|
---|
515 | else {
|
---|
516 | //previousLoc = -(insertion point) - 1;
|
---|
517 | int insertionPoint = -(previousLoc + 1);
|
---|
518 | if (insertionPoint>0) {
|
---|
519 | Fold possibleParentFold = (Fold)oldFolds.get(insertionPoint-1);
|
---|
520 | List children = possibleParentFold.getChildren();
|
---|
521 | if (children!=null) {
|
---|
522 | keepFoldState(newFold, children);
|
---|
523 | }
|
---|
524 | }
|
---|
525 | }
|
---|
526 | }
|
---|
527 |
|
---|
528 |
|
---|
529 | /**
|
---|
530 | * Removes a property change listener from this fold manager.
|
---|
531 | *
|
---|
532 | * @param l The listener to remove.
|
---|
533 | * @see #addPropertyChangeListener(PropertyChangeListener)
|
---|
534 | */
|
---|
535 | public void removePropertyChangeListener(PropertyChangeListener l) {
|
---|
536 | support.removePropertyChangeListener(l);
|
---|
537 | }
|
---|
538 |
|
---|
539 |
|
---|
540 | /**
|
---|
541 | * Forces an immediate reparsing for folds, if folding is enabled. This
|
---|
542 | * usually does not need to be called by the programmer, since fold
|
---|
543 | * parsing is done automatically by RSTA.
|
---|
544 | */
|
---|
545 | public void reparse() {
|
---|
546 |
|
---|
547 | if (codeFoldingEnabled && parser!=null) {
|
---|
548 |
|
---|
549 | // Re-calculate folds. Keep the fold state of folds that are
|
---|
550 | // still around.
|
---|
551 | List newFolds = parser.getFolds(textArea);
|
---|
552 | if (newFolds==null) {
|
---|
553 | newFolds = Collections.EMPTY_LIST;
|
---|
554 | }
|
---|
555 | else {
|
---|
556 | for (int i=0; i<newFolds.size(); i++) {
|
---|
557 | Fold newFold = (Fold)newFolds.get(i);
|
---|
558 | keepFoldState(newFold, folds);
|
---|
559 | for (int j=0; j<newFold.getChildCount(); j++) {
|
---|
560 | keepFoldState(newFold.getChild(j), folds);
|
---|
561 | }
|
---|
562 | }
|
---|
563 | }
|
---|
564 | folds = newFolds;
|
---|
565 |
|
---|
566 | // Let folks (gutter, etc.) know that folds have been updated.
|
---|
567 | support.firePropertyChange(PROPERTY_FOLDS_UPDATED, null, folds);
|
---|
568 | textArea.repaint();
|
---|
569 |
|
---|
570 | }
|
---|
571 | else {
|
---|
572 | folds.clear();
|
---|
573 | }
|
---|
574 |
|
---|
575 | }
|
---|
576 |
|
---|
577 | /**
|
---|
578 | * Sets whether code folding is enabled. Note that only certain
|
---|
579 | * languages will support code folding out of the box. Those languages
|
---|
580 | * which do not support folding will ignore this property.
|
---|
581 | *
|
---|
582 | * @param enabled Whether code folding should be enabled.
|
---|
583 | * @see #isCodeFoldingEnabled()
|
---|
584 | */
|
---|
585 | public void setCodeFoldingEnabled(boolean enabled) {
|
---|
586 | if (enabled!=codeFoldingEnabled) {
|
---|
587 | codeFoldingEnabled = enabled;
|
---|
588 | if (tempParser!=null) {
|
---|
589 | textArea.removeParser(tempParser);
|
---|
590 | }
|
---|
591 | if (enabled) {
|
---|
592 | tempParser = new AbstractParser() {
|
---|
593 | public ParseResult parse(RSyntaxDocument doc, String style) {
|
---|
594 | reparse();
|
---|
595 | return new DefaultParseResult(this);
|
---|
596 | }
|
---|
597 | };
|
---|
598 | textArea.addParser(tempParser);
|
---|
599 | support.firePropertyChange(PROPERTY_FOLDS_UPDATED, null, null);
|
---|
600 | //reparse();
|
---|
601 | }
|
---|
602 | else {
|
---|
603 | folds = Collections.EMPTY_LIST;
|
---|
604 | textArea.repaint();
|
---|
605 | support.firePropertyChange(PROPERTY_FOLDS_UPDATED, null, null);
|
---|
606 | }
|
---|
607 | }
|
---|
608 | }
|
---|
609 | private Parser tempParser;
|
---|
610 |
|
---|
611 |
|
---|
612 | /**
|
---|
613 | * Sets the folds for this fold manager.
|
---|
614 | *
|
---|
615 | * @param folds The new folds. This should not be <code>null</code>.
|
---|
616 | */
|
---|
617 | public void setFolds(List folds) {
|
---|
618 | this.folds = folds;
|
---|
619 | }
|
---|
620 |
|
---|
621 |
|
---|
622 | /**
|
---|
623 | * Updates the fold parser to be the one appropriate for the language
|
---|
624 | * currently being highlighted.
|
---|
625 | */
|
---|
626 | private void updateFoldParser() {
|
---|
627 | parser = FoldParserManager.get().getFoldParser(
|
---|
628 | textArea.getSyntaxEditingStyle());
|
---|
629 | }
|
---|
630 |
|
---|
631 |
|
---|
632 | /**
|
---|
633 | * Listens for events in the text editor.
|
---|
634 | */
|
---|
635 | private class Listener implements DocumentListener, PropertyChangeListener {
|
---|
636 |
|
---|
637 | public void changedUpdate(DocumentEvent e) {
|
---|
638 | }
|
---|
639 |
|
---|
640 | public void insertUpdate(DocumentEvent e) {
|
---|
641 | // Adding text containing a newline to the visible line of a folded
|
---|
642 | // Fold causes that Fold to unfold. Check only start offset of
|
---|
643 | // insertion since that's the line that was "modified".
|
---|
644 | int startOffs = e.getOffset();
|
---|
645 | int endOffs = startOffs + e.getLength();
|
---|
646 | Document doc = e.getDocument();
|
---|
647 | Element root = doc.getDefaultRootElement();
|
---|
648 | int startLine = root.getElementIndex(startOffs);
|
---|
649 | int endLine = root.getElementIndex(endOffs);
|
---|
650 | if (startLine!=endLine) { // Inserted text covering > 1 line...
|
---|
651 | Fold fold = getFoldForLine(startLine);
|
---|
652 | if (fold!=null && fold.isCollapsed()) {
|
---|
653 | fold.toggleCollapsedState();
|
---|
654 | }
|
---|
655 | }
|
---|
656 | }
|
---|
657 |
|
---|
658 | public void propertyChange(PropertyChangeEvent e) {
|
---|
659 | // Syntax style changed in editor.
|
---|
660 | updateFoldParser();
|
---|
661 | reparse(); // Even if no fold parser change, highlighting did
|
---|
662 | }
|
---|
663 | public void removeUpdate(DocumentEvent e) {
|
---|
664 | // Removing text from the visible line of a folded Fold causes that
|
---|
665 | // Fold to unfold. We only need to check the removal offset since
|
---|
666 | // that's the new caret position.
|
---|
667 | int offs = e.getOffset();
|
---|
668 | try {
|
---|
669 | int lastLineModified = textArea.getLineOfOffset(offs);
|
---|
670 | //System.out.println(">>> " + lastLineModified);
|
---|
671 | Fold fold = getFoldForLine(lastLineModified);
|
---|
672 | //System.out.println("&&& " + fold);
|
---|
673 | if (fold!=null && fold.isCollapsed()) {
|
---|
674 | fold.toggleCollapsedState();
|
---|
675 | }
|
---|
676 | } catch (BadLocationException ble) {
|
---|
677 | ble.printStackTrace(); // Never happens
|
---|
678 | }
|
---|
679 | }
|
---|
680 |
|
---|
681 | }
|
---|
682 |
|
---|
683 |
|
---|
684 | } |
---|