PageRenderTime 227ms CodeModel.GetById 212ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/textarea/BufferHandler.java

#
Java | 458 lines | 304 code | 52 blank | 102 comment | 65 complexity | 909a972206c4f3a8e57dbdb5f9cf13b3 MD5 | raw file
  1/*
  2 * BufferHandler.java
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2001, 2005 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit.textarea;
 24
 25import java.util.Iterator;
 26import org.gjt.sp.jedit.buffer.*;
 27import org.gjt.sp.jedit.Debug;
 28
 29/**
 30 * Note that in this class we take great care to defer complicated
 31 * calculations to the end of the current transaction if the buffer
 32 * informs us a compound edit is in progress
 33 * (<code>isTransactionInProgress()</code>).
 34 *
 35 * This greatly speeds up replace all for example, by only doing certain
 36 * things once, particularly in <code>moveCaretPosition()</code>.
 37 *
 38 * Try doing a replace all in a large file, for example. It is very slow
 39 * in 3.2, faster in 4.0 (where the transaction optimization was
 40 * introduced) and faster still in 4.1 (where it was further improved).
 41 *
 42 * There is still work to do; see TODO.txt.
 43 */
 44class BufferHandler implements BufferListener
 45{
 46	private final DisplayManager displayManager;
 47	private final TextArea textArea;
 48	private final JEditBuffer buffer;
 49	private boolean delayedUpdate;
 50	private boolean delayedMultilineUpdate;
 51	private int delayedUpdateStart;
 52	private int delayedUpdateEnd;
 53
 54	//{{{ BufferChangeHandler constructor
 55	BufferHandler(DisplayManager displayManager,
 56		TextArea textArea,
 57		JEditBuffer buffer)
 58	{
 59		this.displayManager = displayManager;
 60		this.textArea = textArea;
 61		this.buffer = buffer;
 62	} //}}}
 63
 64	//{{{ bufferLoaded() method
 65	public void bufferLoaded(JEditBuffer buffer)
 66	{
 67		displayManager.bufferLoaded();
 68		textArea._finishCaretUpdate();
 69	} //}}}
 70
 71	//{{{ foldHandlerChanged() method
 72	public void foldHandlerChanged(JEditBuffer buffer)
 73	{
 74		displayManager.foldHandlerChanged();
 75	} //}}}
 76
 77	//{{{ foldLevelChanged() method
 78	public void foldLevelChanged(JEditBuffer buffer, int start, int end)
 79	{
 80		//System.err.println("foldLevelChanged " + (start-1) + " to " + textArea.getLastPhysicalLine() + "," + end);
 81
 82		if(textArea.getDisplayManager() == displayManager
 83			&& end != 0 && !buffer.isLoading())
 84		{
 85			textArea.invalidateLineRange(start - 1,
 86				textArea.getLastPhysicalLine());
 87		}
 88	} //}}}
 89
 90	//{{{ contentInserted() method
 91	public void contentInserted(JEditBuffer buffer, int startLine,
 92		int offset, int numLines, int length)
 93	{
 94		if(buffer.isLoading())
 95			return;
 96
 97		displayManager.screenLineMgr.contentInserted(startLine,numLines);
 98
 99		int endLine = startLine + numLines;
100
101		if(numLines != 0)
102			delayedMultilineUpdate = true;
103
104		displayManager.folds.contentInserted(startLine,numLines);
105
106		FirstLine firstLine = displayManager.firstLine;
107		ScrollLineCount scrollLineCount = displayManager.scrollLineCount;
108
109		if(textArea.getDisplayManager() == displayManager)
110		{
111			if(numLines != 0)
112			{
113				firstLine.contentInserted(startLine,numLines);
114				scrollLineCount.contentInserted(startLine,numLines);
115			}
116
117			if(delayedUpdateEnd >= startLine)
118				delayedUpdateEnd += numLines;
119			delayUpdate(startLine,endLine);
120
121			//{{{ resize selections if necessary
122			
123			Iterator<Selection> iter = textArea.getSelectionIterator();
124			while(iter.hasNext())
125			{
126				Selection s = iter.next();
127
128				if(s.contentInserted(buffer,startLine,offset,
129					numLines,length))
130				{
131					delayUpdate(s.startLine,s.endLine);
132				}
133			} //}}}
134
135			int caret = textArea.getCaretPosition();
136			if(caret >= offset)
137			{
138				int scrollMode = textArea.caretAutoScroll()
139					? TextArea.ELECTRIC_SCROLL
140					: TextArea.NO_SCROLL;
141				textArea.moveCaretPosition(
142					caret + length,scrollMode);
143			}
144			else
145			{
146				int scrollMode = textArea.caretAutoScroll()
147					? TextArea.NORMAL_SCROLL
148					: TextArea.NO_SCROLL;
149				textArea.moveCaretPosition(
150					caret,scrollMode);
151			}
152		}
153		else
154		{
155			firstLine.callReset = true;
156			scrollLineCount.callReset = true;
157		}
158	} //}}}
159
160	//{{{ preContentInserted() method
161	/**
162	 * Called when text is about to be inserted in the buffer.
163	 * @param buffer The buffer in question
164	 * @param startLine The first line
165	 * @param offset The start offset, from the beginning of the buffer
166	 * @param numLines The number of lines inserted
167	 * @param length The number of characters inserted
168	 * @since jEdit 4.3pre11
169	 */
170	public void preContentInserted(JEditBuffer buffer, int startLine, int offset, int numLines, int length)
171	{
172		if(textArea.getDisplayManager() == displayManager)
173		{
174			getReadyToBreakFold(startLine);
175		}
176	} //}}}
177
178	//{{{ preContentRemoved() method
179	/**
180	 * Called when text is about to be removed from the buffer, but is
181	 * still present.
182	 * @param buffer The buffer in question
183	 * @param startLine The first line
184	 * @param offset The start offset, from the beginning of the buffer
185	 * @param numLines The number of lines to be removed
186	 * @param length The number of characters to be removed
187	 * @since jEdit 4.3pre3
188	 */
189	public void preContentRemoved(JEditBuffer buffer, int startLine,
190		int offset, int numLines, int length)
191	{
192		if(buffer.isLoading())
193			return;
194
195		FirstLine firstLine = displayManager.firstLine;
196		ScrollLineCount scrollLineCount = displayManager.scrollLineCount;
197
198		if(textArea.getDisplayManager() == displayManager)
199		{
200			if(numLines == 0)
201			{
202				getReadyToBreakFold(startLine);
203			}
204			else
205			{
206				int lastLine = startLine + numLines;
207				if(!displayManager.isLineVisible(startLine)
208				 || !displayManager.isLineVisible(lastLine)
209			 	 || offset != buffer.getLineStartOffset(startLine)
210				 || offset + length != buffer.getLineStartOffset(lastLine))
211				{
212					getReadyToBreakFold(startLine);
213					getReadyToBreakFold(lastLine);
214				}
215				else
216				{
217					// The removal will not touch
218					// inside of folds and wll not
219					// modify any remaining lines.
220				}
221			}
222
223			if(numLines != 0)
224			{
225				firstLine.preContentRemoved(startLine,offset, numLines);
226				scrollLineCount.preContentRemoved(startLine, offset, numLines);
227			}
228
229			if(delayedUpdateEnd >= startLine)
230				delayedUpdateEnd -= numLines;
231			delayUpdate(startLine,startLine);
232		}
233		else
234		{
235			firstLine.callReset = true;
236			scrollLineCount.callReset = true;
237		}
238
239		displayManager.screenLineMgr.contentRemoved(startLine,numLines);
240
241		if(numLines == 0)
242			return;
243
244		delayedMultilineUpdate = true;
245
246		if(displayManager.folds.preContentRemoved(startLine,numLines))
247		{
248			displayManager.folds.reset(buffer.getLineCount());
249			firstLine.callReset = true;
250			scrollLineCount.callReset = true;
251		}
252
253		if(firstLine.physicalLine
254			> displayManager.getLastVisibleLine()
255			|| firstLine.physicalLine
256			< displayManager.getFirstVisibleLine())
257		{
258			// will be handled later.
259			// see comments at the end of
260			// transactionComplete().
261		}
262		// very subtle... if we leave this for
263		// ensurePhysicalLineIsVisible(), an
264		// extra line will be added to the
265		// scroll line count.
266		else if(!displayManager.isLineVisible(
267			firstLine.physicalLine))
268		{
269			firstLine.physicalLine =
270				displayManager.getNextVisibleLine(
271				firstLine.physicalLine);
272		}
273	} //}}}
274
275	//{{{ contentRemoved() method
276	public void contentRemoved(JEditBuffer buffer, int startLine,
277		int start, int numLines, int length)
278	{
279		if(buffer.isLoading())
280			return;
281
282		if(textArea.getDisplayManager() == displayManager)
283		{
284			//{{{ resize selections if necessary
285			int nSel = textArea.getSelectionCount();
286			Iterator<Selection> iter = textArea.getSelectionIterator();
287			while(iter.hasNext())
288			{
289				Selection s = iter.next();
290
291				if(s.contentRemoved(buffer,startLine,
292					start,numLines,length))
293				{
294					delayUpdate(s.startLine,s.endLine);
295					if(nSel == 1 && s.start == s.end)
296						iter.remove();
297				}
298			} //}}}
299
300			int caret = textArea.getCaretPosition();
301
302			if(caret >= start + length)
303			{
304				int scrollMode = textArea.caretAutoScroll()
305					? TextArea.ELECTRIC_SCROLL
306					: TextArea.NO_SCROLL;
307				textArea.moveCaretPosition(
308					caret - length,
309					scrollMode);
310			}
311			else if(caret >= start)
312			{
313				int scrollMode = textArea.caretAutoScroll()
314					? TextArea.ELECTRIC_SCROLL
315					: TextArea.NO_SCROLL;
316				textArea.moveCaretPosition(
317					start,scrollMode);
318			}
319			else
320			{
321				int scrollMode = textArea.caretAutoScroll()
322					? TextArea.NORMAL_SCROLL
323					: TextArea.NO_SCROLL;
324				textArea.moveCaretPosition(caret,scrollMode);
325			}
326		}
327	}
328	//}}}
329
330	//{{{ transactionComplete() method
331	public void transactionComplete(JEditBuffer buffer)
332	{
333		if(textArea.getDisplayManager() != displayManager)
334		{
335			delayedUpdate = false;
336			return;
337		}
338
339		if(delayedUpdate)
340			doDelayedUpdate();
341
342		textArea._finishCaretUpdate();
343
344		delayedUpdate = false;
345
346		//{{{ Debug code
347		if(Debug.SCROLL_VERIFY)
348		{
349			int line = delayedUpdateStart;
350			if(!displayManager.isLineVisible(line))
351				line = displayManager.getNextVisibleLine(line);
352			System.err.println(delayedUpdateStart + ":" + delayedUpdateEnd + ':' + textArea.getLineCount());
353			int scrollLineCount = 0;
354			while(line != -1 && line <= delayedUpdateEnd)
355			{
356				scrollLineCount += displayManager.getScreenLineCount(line);
357				line = displayManager.getNextVisibleLine(line);
358			}
359
360			if(scrollLineCount != displayManager.getScrollLineCount())
361			{
362				throw new InternalError(scrollLineCount
363					+ " != "
364					+ displayManager.getScrollLineCount());
365			}
366		} //}}}
367	} //}}}
368
369	//{{{ doDelayedUpdate() method
370	private void doDelayedUpdate()
371	{
372		// must update screen line counts before we call
373		// notifyScreenLineChanges() since that calls
374		// updateScrollBar() which needs valid info
375		int line = delayedUpdateStart;
376		if(!displayManager.isLineVisible(line))
377			line = displayManager.getNextVisibleLine(line);
378		while(line != -1 && line <= delayedUpdateEnd)
379		{
380			displayManager.updateScreenLineCount(line);
381			line = displayManager.getNextVisibleLine(line);
382		}
383
384		// must be before the below call
385		// so that the chunk cache is not
386		// updated with an invisible first
387		// line (see above)
388		displayManager.notifyScreenLineChanges();
389
390		if(delayedMultilineUpdate)
391		{
392			textArea.invalidateScreenLineRange(
393				textArea.chunkCache
394				.getScreenLineOfOffset(
395				delayedUpdateStart,0),
396				textArea.getVisibleLines());
397			delayedMultilineUpdate = false;
398		}
399		else
400		{
401			textArea.invalidateLineRange(
402				delayedUpdateStart,
403				delayedUpdateEnd);
404		}
405
406		// update visible lines
407		int visibleLines = textArea.getVisibleLines();
408		if(visibleLines != 0)
409		{
410			textArea.chunkCache.getLineInfo(
411				visibleLines - 1);
412		}
413
414		// force the fold levels to be
415		// updated.
416
417		// when painting the last line of
418		// a buffer, Buffer.isFoldStart()
419		// doesn't call getFoldLevel(),
420		// hence the foldLevelChanged()
421		// event might not be sent for the
422		// previous line.
423
424		buffer.getFoldLevel(delayedUpdateEnd);
425	} //}}}
426
427	//{{{ delayUpdate() method
428	private void delayUpdate(int startLine, int endLine)
429	{
430		textArea.chunkCache.invalidateChunksFromPhys(startLine);
431
432		if(!delayedUpdate)
433		{
434			delayedUpdateStart = startLine;
435			delayedUpdateEnd = endLine;
436			delayedUpdate = true;
437		}
438		else
439		{
440			delayedUpdateStart = Math.min(
441				delayedUpdateStart,
442				startLine);
443			delayedUpdateEnd = Math.max(
444				delayedUpdateEnd,
445				endLine);
446		}
447	} //}}}
448
449	//{{{ getReadyToBreakFold() method
450	// This is a fix for black hole bug.
451	// If you modify a part of folded lines, like {{{ (followed by }}}),
452	// the fold is removed so it must be expanded otherwise the text
453	// remains invisible.
454	private void getReadyToBreakFold(int line)
455	{
456		displayManager.expandFold(line, false);
457	} //}}}
458}