/******************************************************************************* * Copyright (c) 2007, 2009 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Michael Scharf (Wind River) - initial API and implementation * Martin Oberhuber (Wind River) - [261486][api][cleanup] Mark @noimplement interfaces as @noextend *******************************************************************************/ package org.eclipse.tm.terminal.model; /** * This class maintains a snapshot of an instance of {@link ITerminalTextData}. * While the {@link ITerminalTextData} continues changing, the snapshot remains * unchanged until the next snapshot is taken by calling * {@link #updateSnapshot(boolean)}. This is important, because the * {@link ITerminalTextData} might get modified by another thread. Suppose you * would want to draw the content of the {@link ITerminalTextData} using the * following loop: * *
 * for (int line = 0; line < term.getHeight(); line++)
 * 	for (int column = 0; column < term.getWidth(); column++)
 * 		drawCharacter(column, line, term.getChar(column, line), term.getStyle(column, line));
 * 
* * This might fail because the background thread could change the dimensions of * the {@link ITerminalTextData} while you iterate the loop. One solution would * be to put a synchronized(term){} statement around the code. This * has two problems: 1. you would have to know about the internals of the * synchronisation of {@link ITerminalTextData}. 2. The other thread that * changes {@link ITerminalTextData} is blocked while the potentially slow * drawing is done. *

* Solution: Take a snapshot of the terminal and use the snapshot to draw * the content. There is no danger that the data structure get changed while you * draw. There are also methods to find out what has changed to minimize the * number of lines that get redrawn. *

* *

* Drawing optimization: To optimize redrawing of changed lines, this * class keeps track of lines that have changed since the previous snapshot. *

* *
 * // iterate over the potentially changed lines
 * for (int line = snap.getFirstChangedLine(); line <= snap.getLastChangedLine(); line++)
 * 	// redraw only if the line has changed
 * 	if (snap.hasLineChanged(line))
 * 		for (int column = 0; column < snap.getWidth(); column++)
 * 			drawCharacter(column, line, snap.getChar(column, line), snap.getStyle(column, line));
 * 
* *

* Scroll optimization: Often new lines are appended at the bottom of the * terminal and the rest of the lines are scrolled up. In this case all lines * would be marked as changed. To optimize for this case, * {@link #updateSnapshot(boolean)} can be called with true for the * detectScrolling parameter. The object will keep track of * scrolling. The UI must first handle the scrolling and then use the * {@link #hasLineChanged(int)} method to determine scrolling: * *

 * // scroll the visible region of the UI <b>before</b> drawing the changed lines.
 * doUIScrolling(snap.getScrollChangeY(), snap.getScrollChangeN(), snap.getScrollChangeShift());
 * // iterate over the potentially changed lines
 * for (int line = snap.getFirstChangedLine(); line <= snap.getFirstChangedLine(); line++)
 * 	// redraw only if the line has changed
 * 	if (snap.hasLineChanged(line))
 * 		for (int column = 0; column < snap.getWidth(); column++)
 * 			drawCharacter(column, line, snap.getChar(column, line), snap.getStyle(column, line));
 * 
* *

*

* Threading Note: This class is not thread safe! All methods have to be * called by the a same thread, that created the instance by calling * {@link ITerminalTextDataReadOnly#makeSnapshot()}. *

* * @noimplement This interface is not intended to be implemented by clients. * @noextend This interface is not intended to be extended by clients. */ public interface ITerminalTextDataSnapshot extends ITerminalTextDataReadOnly { /** * This listener gets called when the current snapshot * is out of date. Calling {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} * will have an effect. Once the {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} method is called, * it will not be called until {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)} * is called and a new snapshot needs to be updated again. *

* A typical terminal view would not update the snapshot immediately * after the {@link #snapshotOutOfDate(ITerminalTextDataSnapshot)} has been called. It would introduce a * delay to update the UI (and the snapshot} 10 or 20 times per second. * *

Make sure you don't spend too much time in this method. */ interface SnapshotOutOfDateListener { /** * Gets called when the snapshot is out of date. To get the snapshot up to date, * call {@link ITerminalTextDataSnapshot#updateSnapshot(boolean)}. * @param snapshot The snapshot that is out of date */ void snapshotOutOfDate(ITerminalTextDataSnapshot snapshot); } void addListener(SnapshotOutOfDateListener listener); void removeListener(SnapshotOutOfDateListener listener); /** * Ends the listening to the {@link ITerminalTextData}. After this * has been called no new snapshot data is collected. */ void detach(); /** * @return true if the data has changed since the previous snapshot. */ boolean isOutOfDate(); /** * The window of interest is the region the snapshot should track. * Changes outside this region are ignored. The change takes effect after * an update! * @param startLine -1 means track the end of the data * @param size number of lines to track. A size of -1 means track all. */ void setInterestWindow(int startLine, int size); int getInterestWindowStartLine(); int getInterestWindowSize(); /** * Create a new snapshot of the {@link ITerminalTextData}. It will efficiently * copy the data of the {@link ITerminalTextData} into an internal representation. * The snapshot also keeps track of the changes since the previous snapshot. *

With the methods {@link #getFirstChangedLine()}, {@link #getLastChangedLine()} and * {@link #hasLineChanged(int)} * you can find out what has changed in the current snapshot since the previous snapshot. * @param detectScrolling if true the snapshot tries to identify scroll * changes since the last snapshot. In this case the information about scrolling * can be retrieved using the following methods: * {@link #getScrollWindowStartLine()}, {@link #getScrollWindowSize()} and {@link #getScrollWindowShift()} *
Note: The method {@link #hasLineChanged(int)} returns changes after the * scrolling has been applied. */ void updateSnapshot(boolean detectScrolling); /** * @return The first line changed in this snapshot compared * to the previous snapshot. * *

Note: If no line has changed, this * returns {@link Integer#MAX_VALUE} * *

Note: if {@link #updateSnapshot(boolean)} has been called with true, * then this does not include lines that only have been scrolled. This is the * first line that has changed after the scroll has been applied. */ int getFirstChangedLine(); /** * @return The last line changed in this snapshot compared * to the previous snapshot. If the height has changed since the * last update of the snapshot, then the returned value is within * the new dimensions. * *

Note: If no line has changed, this returns -1 * *

Note: if {@link #updateSnapshot(boolean)} has been called with true, * then this does not include lines that only have been scrolled. This is the * last line that has changed after the scroll has been applied. * *

A typical for loop using this method would look like this (note the <= in the for loop): *

	 * for(int line=snap.{@link #getFirstChangedLine()}; line <= snap.getLastChangedLine(); line++)
	 *    if(snap.{@link #hasLineChanged(int) hasLineChanged(line)})
	 *       doSomething(line);
	 * 
*/ int getLastChangedLine(); /** * @param line * @return true if the line has changed since the previous snapshot */ boolean hasLineChanged(int line); boolean hasDimensionsChanged(); /** * @return true if the terminal has changed (and not just the * window of interest) */ boolean hasTerminalChanged(); /** * If {@link #updateSnapshot(boolean)} was called with true, then this method * returns the top of the scroll region. * @return The first line scrolled in this snapshot compared * to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)}. */ int getScrollWindowStartLine(); /** * If {@link #updateSnapshot(boolean)} was called with true, then this method * returns the size of the scroll region. * @return The number of lines scrolled in this snapshot compared * to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)} * If nothing has changed, 0 is returned. */ int getScrollWindowSize(); /** * If {@link #updateSnapshot(boolean)} was called with true, then this method * returns number of lines moved by the scroll region. * @return The the scroll shift of this snapshot compared * to the previous snapshot. See also {@link ITerminalTextData#scroll(int, int, int)} */ int getScrollWindowShift(); /** * @return The {@link ITerminalTextData} on that this instance is observing. */ ITerminalTextData getTerminalTextData(); }