001: /*
002: * BufferHandler.java
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2001, 2005 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.textarea;
024:
025: import java.util.Iterator;
026: import org.gjt.sp.jedit.buffer.*;
027: import org.gjt.sp.jedit.Debug;
028:
029: /**
030: * Note that in this class we take great care to defer complicated
031: * calculations to the end of the current transaction if the buffer
032: * informs us a compound edit is in progress
033: * (<code>isTransactionInProgress()</code>).
034: *
035: * This greatly speeds up replace all for example, by only doing certain
036: * things once, particularly in <code>moveCaretPosition()</code>.
037: *
038: * Try doing a replace all in a large file, for example. It is very slow
039: * in 3.2, faster in 4.0 (where the transaction optimization was
040: * introduced) and faster still in 4.1 (where it was further improved).
041: *
042: * There is still work to do; see TODO.txt.
043: */
044: class BufferHandler implements BufferListener {
045: private DisplayManager displayManager;
046: private TextArea textArea;
047: private JEditBuffer buffer;
048: boolean delayedUpdate;
049: boolean delayedMultilineUpdate;
050: int delayedUpdateStart;
051: int delayedUpdateEnd;
052:
053: //{{{ BufferChangeHandler constructor
054: BufferHandler(DisplayManager displayManager, TextArea textArea,
055: JEditBuffer buffer) {
056: this .displayManager = displayManager;
057: this .textArea = textArea;
058: this .buffer = buffer;
059: } //}}}
060:
061: //{{{ bufferLoaded() method
062: public void bufferLoaded(JEditBuffer buffer) {
063: displayManager.bufferLoaded();
064: } //}}}
065:
066: //{{{ foldHandlerChanged() method
067: public void foldHandlerChanged(JEditBuffer buffer) {
068: displayManager.foldHandlerChanged();
069: } //}}}
070:
071: //{{{ foldLevelChanged() method
072: public void foldLevelChanged(JEditBuffer buffer, int start, int end) {
073: //System.err.println("foldLevelChanged " + (start-1) + " to " + textArea.getLastPhysicalLine() + "," + end);
074:
075: if (textArea.getDisplayManager() == displayManager && end != 0
076: && !buffer.isLoading()) {
077: textArea.invalidateLineRange(start - 1, textArea
078: .getLastPhysicalLine());
079: }
080: } //}}}
081:
082: //{{{ contentInserted() method
083: public void contentInserted(JEditBuffer buffer, int startLine,
084: int offset, int numLines, int length) {
085: if (buffer.isLoading())
086: return;
087:
088: displayManager.screenLineMgr.contentInserted(startLine,
089: numLines);
090:
091: int endLine = startLine + numLines;
092:
093: if (numLines != 0)
094: delayedMultilineUpdate = true;
095:
096: displayManager.folds.contentInserted(startLine, numLines);
097:
098: FirstLine firstLine = displayManager.firstLine;
099: ScrollLineCount scrollLineCount = displayManager.scrollLineCount;
100:
101: if (textArea.getDisplayManager() == displayManager) {
102: if (numLines != 0) {
103: firstLine.contentInserted(startLine, numLines);
104: scrollLineCount.contentInserted(startLine, numLines);
105: }
106:
107: if (delayedUpdateEnd >= startLine)
108: delayedUpdateEnd += numLines;
109: delayUpdate(startLine, endLine);
110:
111: //{{{ resize selections if necessary
112:
113: Iterator<Selection> iter = textArea.getSelectionIterator();
114: while (iter.hasNext()) {
115: Selection s = iter.next();
116:
117: if (s.contentInserted(buffer, startLine, offset,
118: numLines, length)) {
119: delayUpdate(s.startLine, s.endLine);
120: }
121: } //}}}
122:
123: int caret = textArea.getCaretPosition();
124: if (caret >= offset) {
125: int scrollMode = textArea.caretAutoScroll() ? TextArea.ELECTRIC_SCROLL
126: : TextArea.NO_SCROLL;
127: textArea.moveCaretPosition(caret + length, scrollMode);
128: } else {
129: int scrollMode = textArea.caretAutoScroll() ? TextArea.NORMAL_SCROLL
130: : TextArea.NO_SCROLL;
131: textArea.moveCaretPosition(caret, scrollMode);
132: }
133: } else {
134: firstLine.callReset = true;
135: scrollLineCount.callReset = true;
136: }
137: } //}}}
138:
139: /**
140: * Called when text is about to be removed from the buffer, but is
141: * still present.
142: *
143: * @param buffer The buffer in question
144: * @param startLine The first line
145: * @param offset The start offset, from the beginning of the buffer
146: * @param numLines The number of lines to be removed
147: * @param length The number of characters to be removed
148: * @since jEdit 4.3pre11
149: */
150: public void preContentInserted(JEditBuffer buffer, int startLine,
151: int offset, int numLines, int length) {
152: if (textArea.getDisplayManager() == displayManager
153: && numLines != 0) {
154: //{{{ fix for black hole bug
155: // if you remove the {{{ at a fold start, the fold is removed so it must be expanded otherwise }}}
156: // the text remains invisible
157: if (buffer.isFoldStart(startLine)) {
158: displayManager.expandFold(startLine, false);
159: }
160: // }}}
161: }
162: }
163:
164: //{{{ preContentRemoved() method
165: /**
166: * Method called before some content is removed.
167: *
168: * @param buffer the buffer
169: * @param startLine the first removed line
170: * @param offset the offset where starts the removed content
171: * @param numLines the number of removed lines
172: * @param length the removed length
173: */
174: public void preContentRemoved(JEditBuffer buffer, int startLine,
175: int offset, int numLines, int length) {
176: if (buffer.isLoading())
177: return;
178:
179: FirstLine firstLine = displayManager.firstLine;
180: ScrollLineCount scrollLineCount = displayManager.scrollLineCount;
181:
182: if (textArea.getDisplayManager() == displayManager) {
183: //{{{ fix for black hole bug
184: // if you remove the {{{ at a fold start, the fold is removed so it must be expanded otherwise }}}
185: // the text remains invisible
186: int endLine = startLine + numLines;
187: if (buffer.isFoldStart(endLine)) {
188: if (numLines == 0) {
189: String endLineText = buffer.getLineText(endLine);
190: int i = endLineText.indexOf("{{{"); // }}}
191: if (i != -1) {
192: int lineStartOffset = buffer
193: .getLineStartOffset(endLine);
194: if (offset < lineStartOffset + i + 3
195: && offset + length > lineStartOffset
196: + i) {
197: displayManager.expandFold(endLine, false);
198: }
199: }
200: } else {
201: displayManager.expandFold(endLine, false);
202: }
203: }
204: // }}}
205:
206: if (numLines != 0) {
207: firstLine
208: .preContentRemoved(startLine, offset, numLines);
209: scrollLineCount.preContentRemoved(startLine, offset,
210: numLines);
211: }
212:
213: if (delayedUpdateEnd >= startLine)
214: delayedUpdateEnd -= numLines;
215: delayUpdate(startLine, startLine);
216: } else {
217: firstLine.callReset = true;
218: scrollLineCount.callReset = true;
219: }
220:
221: displayManager.screenLineMgr
222: .contentRemoved(startLine, numLines);
223:
224: if (numLines == 0)
225: return;
226:
227: delayedMultilineUpdate = true;
228:
229: if (displayManager.folds.preContentRemoved(startLine, numLines)) {
230: displayManager.folds.reset(buffer.getLineCount());
231: firstLine.callReset = true;
232: scrollLineCount.callReset = true;
233: }
234:
235: if (firstLine.physicalLine > displayManager
236: .getLastVisibleLine()
237: || firstLine.physicalLine < displayManager
238: .getFirstVisibleLine()) {
239: // will be handled later.
240: // see comments at the end of
241: // transactionComplete().
242: }
243: // very subtle... if we leave this for
244: // ensurePhysicalLineIsVisible(), an
245: // extra line will be added to the
246: // scroll line count.
247: else if (!displayManager.isLineVisible(firstLine.physicalLine)) {
248: firstLine.physicalLine = displayManager
249: .getNextVisibleLine(firstLine.physicalLine);
250: }
251: } //}}}
252:
253: //{{{ contentRemoved() method
254: public void contentRemoved(JEditBuffer buffer, int startLine,
255: int start, int numLines, int length) {
256: if (buffer.isLoading())
257: return;
258:
259: if (textArea.getDisplayManager() == displayManager) {
260: //{{{ resize selections if necessary
261: int nSel = textArea.getSelectionCount();
262: Iterator<Selection> iter = textArea.getSelectionIterator();
263: while (iter.hasNext()) {
264: Selection s = iter.next();
265:
266: if (s.contentRemoved(buffer, startLine, start,
267: numLines, length)) {
268: delayUpdate(s.startLine, s.endLine);
269: if (nSel == 1 && s.start == s.end)
270: iter.remove();
271: }
272: } //}}}
273:
274: int caret = textArea.getCaretPosition();
275:
276: if (caret >= start + length) {
277: int scrollMode = textArea.caretAutoScroll() ? TextArea.ELECTRIC_SCROLL
278: : TextArea.NO_SCROLL;
279: textArea.moveCaretPosition(caret - length, scrollMode);
280: } else if (caret >= start) {
281: int scrollMode = textArea.caretAutoScroll() ? TextArea.ELECTRIC_SCROLL
282: : TextArea.NO_SCROLL;
283: textArea.moveCaretPosition(start, scrollMode);
284: } else {
285: int scrollMode = textArea.caretAutoScroll() ? TextArea.NORMAL_SCROLL
286: : TextArea.NO_SCROLL;
287: textArea.moveCaretPosition(caret, scrollMode);
288: }
289: }
290: }
291:
292: //}}}
293:
294: //{{{ transactionComplete() method
295: public void transactionComplete(JEditBuffer buffer) {
296: if (textArea.getDisplayManager() != displayManager) {
297: delayedUpdate = false;
298: return;
299: }
300:
301: if (delayedUpdate)
302: doDelayedUpdate();
303:
304: textArea._finishCaretUpdate();
305:
306: delayedUpdate = false;
307:
308: //{{{ Debug code
309: if (Debug.SCROLL_VERIFY) {
310: int line = delayedUpdateStart;
311: if (!displayManager.isLineVisible(line))
312: line = displayManager.getNextVisibleLine(line);
313: System.err.println(delayedUpdateStart + ":"
314: + delayedUpdateEnd + ':' + textArea.getLineCount());
315: int scrollLineCount = 0;
316: while (line != -1 && line <= delayedUpdateEnd) {
317: scrollLineCount += displayManager
318: .getScreenLineCount(line);
319: line = displayManager.getNextVisibleLine(line);
320: }
321:
322: if (scrollLineCount != displayManager.getScrollLineCount()) {
323: throw new InternalError(scrollLineCount + " != "
324: + displayManager.getScrollLineCount());
325: }
326: } //}}}
327: } //}}}
328:
329: //{{{ doDelayedUpdate() method
330: private void doDelayedUpdate() {
331: // must update screen line counts before we call
332: // notifyScreenLineChanges() since that calls
333: // updateScrollBar() which needs valid info
334: int line = delayedUpdateStart;
335: if (!displayManager.isLineVisible(line))
336: line = displayManager.getNextVisibleLine(line);
337: while (line != -1 && line <= delayedUpdateEnd) {
338: displayManager.updateScreenLineCount(line);
339: line = displayManager.getNextVisibleLine(line);
340: }
341:
342: // must be before the below call
343: // so that the chunk cache is not
344: // updated with an invisible first
345: // line (see above)
346: displayManager.notifyScreenLineChanges();
347:
348: if (delayedMultilineUpdate) {
349: textArea.invalidateScreenLineRange(textArea.chunkCache
350: .getScreenLineOfOffset(delayedUpdateStart, 0),
351: textArea.getVisibleLines());
352: delayedMultilineUpdate = false;
353: } else {
354: textArea.invalidateLineRange(delayedUpdateStart,
355: delayedUpdateEnd);
356: }
357:
358: // update visible lines
359: int visibleLines = textArea.getVisibleLines();
360: if (visibleLines != 0) {
361: textArea.chunkCache.getLineInfo(visibleLines - 1);
362: }
363:
364: // force the fold levels to be
365: // updated.
366:
367: // when painting the last line of
368: // a buffer, Buffer.isFoldStart()
369: // doesn't call getFoldLevel(),
370: // hence the foldLevelChanged()
371: // event might not be sent for the
372: // previous line.
373:
374: buffer.getFoldLevel(delayedUpdateEnd);
375: } //}}}
376:
377: //{{{ delayUpdate() method
378: private void delayUpdate(int startLine, int endLine) {
379: textArea.chunkCache.invalidateChunksFromPhys(startLine);
380: textArea.repaintMgr.setFastScroll(false);
381:
382: if (!delayedUpdate) {
383: delayedUpdateStart = startLine;
384: delayedUpdateEnd = endLine;
385: delayedUpdate = true;
386: } else {
387: delayedUpdateStart = Math
388: .min(delayedUpdateStart, startLine);
389: delayedUpdateEnd = Math.max(delayedUpdateEnd, endLine);
390: }
391: } //}}}
392: }
|