package com.googlecode.lanterna.terminal.swing;
import com.googlecode.lanterna.SGR;
import com.googlecode.lanterna.TerminalPosition;
import com.googlecode.lanterna.graphics.TextGraphics;
import com.googlecode.lanterna.input.KeyStroke;
import com.googlecode.lanterna.terminal.IOSafeTerminal;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.terminal.TerminalResizeListener;
import java.awt.*;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
@SuppressWarnings("serial")
public class ScrollingSwingTerminal extends JComponent implements IOSafeTerminal {
private final SwingTerminal swingTerminal;
private final JScrollBar scrollBar;
private volatile boolean scrollModelUpdateBySystem;
public ScrollingSwingTerminal() {
this(TerminalEmulatorDeviceConfiguration.getDefault(),
SwingTerminalFontConfiguration.getDefault(),
TerminalEmulatorColorConfiguration.getDefault());
}
@SuppressWarnings({"SameParameterValue", "WeakerAccess"})
public ScrollingSwingTerminal(
TerminalEmulatorDeviceConfiguration deviceConfiguration,
SwingTerminalFontConfiguration fontConfiguration,
TerminalEmulatorColorConfiguration colorConfiguration) {
this.scrollBar = new JScrollBar(JScrollBar.VERTICAL);
this.swingTerminal = new SwingTerminal(
deviceConfiguration,
fontConfiguration,
colorConfiguration,
new ScrollController());
setLayout(new BorderLayout());
add(swingTerminal, BorderLayout.CENTER);
add(scrollBar, BorderLayout.EAST);
this.scrollBar.setMinimum(0);
this.scrollBar.setMaximum(20);
this.scrollBar.setValue(0);
this.scrollBar.setVisibleAmount(20);
this.scrollBar.addAdjustmentListener(new ScrollbarListener());
this.scrollModelUpdateBySystem = false;
}
private class ScrollController implements TerminalScrollController {
private int scrollValue;
@Override
public void updateModel(final int totalSize, final int screenHeight) {
if(!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(() -> updateModel(totalSize, screenHeight));
return;
}
try {
scrollModelUpdateBySystem = true;
int value = scrollBar.getValue();
int maximum = scrollBar.getMaximum();
int visibleAmount = scrollBar.getVisibleAmount();
if(maximum != totalSize) {
int lastMaximum = maximum;
maximum = totalSize > screenHeight ? totalSize : screenHeight;
if(lastMaximum < maximum &&
lastMaximum - visibleAmount - value == 0) {
value = scrollBar.getValue() + (maximum - lastMaximum);
}
}
if(value + screenHeight > maximum) {
value = maximum - screenHeight;
}
if(visibleAmount != screenHeight) {
if(visibleAmount > screenHeight) {
value += visibleAmount - screenHeight;
}
visibleAmount = screenHeight;
}
if(value > maximum - visibleAmount) {
value = maximum - visibleAmount;
}
if(value < 0) {
value = 0;
}
this.scrollValue = value;
if(scrollBar.getMaximum() != maximum) {
scrollBar.setMaximum(maximum);
}
if(scrollBar.getVisibleAmount() != visibleAmount) {
scrollBar.setVisibleAmount(visibleAmount);
}
if(scrollBar.getValue() != value) {
scrollBar.setValue(value);
}
}
finally {
scrollModelUpdateBySystem = false;
}
}
@Override
public int getScrollingOffset() {
return scrollValue;
}
}
private class ScrollbarListener implements AdjustmentListener {
@Override
public synchronized void adjustmentValueChanged(AdjustmentEvent e) {
if(!scrollModelUpdateBySystem) {
swingTerminal.repaint();
}
}
}
public void addInput(KeyStroke keyStroke) {
swingTerminal.addInput(keyStroke);
}
@Override
public KeyStroke pollInput() {
return swingTerminal.pollInput();
}
@Override
public KeyStroke readInput() {
return swingTerminal.readInput();
}
@Override
public void enterPrivateMode() {
swingTerminal.enterPrivateMode();
}
@Override
public void exitPrivateMode() {
swingTerminal.exitPrivateMode();
}
@Override
public void clearScreen() {
swingTerminal.clearScreen();
}
@Override
public void setCursorPosition(int x, int y) {
swingTerminal.setCursorPosition(x, y);
}
@Override
public void setCursorPosition(TerminalPosition position) {
swingTerminal.setCursorPosition(position);
}
@Override
public TerminalPosition getCursorPosition() {
return swingTerminal.getCursorPosition();
}
@Override
public void setCursorVisible(boolean visible) {
swingTerminal.setCursorVisible(visible);
}
@Override
public void putCharacter(char c) {
swingTerminal.putCharacter(c);
}
@Override
public void putString(String string) {
swingTerminal.putString(string);
}
@Override
public TextGraphics newTextGraphics() {
return swingTerminal.newTextGraphics();
}
@Override
public void enableSGR(SGR sgr) {
swingTerminal.enableSGR(sgr);
}
@Override
public void disableSGR(SGR sgr) {
swingTerminal.disableSGR(sgr);
}
@Override
public void resetColorAndSGR() {
swingTerminal.resetColorAndSGR();
}
@Override
public void setForegroundColor(TextColor color) {
swingTerminal.setForegroundColor(color);
}
@Override
public void setBackgroundColor(TextColor color) {
swingTerminal.setBackgroundColor(color);
}
@Override
public TerminalSize getTerminalSize() {
return swingTerminal.getTerminalSize();
}
@Override
public byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) {
return swingTerminal.enquireTerminal(timeout, timeoutUnit);
}
@Override
public void bell() {
swingTerminal.bell();
}
@Override
public void flush() {
swingTerminal.flush();
}
@Override
public void close() {
swingTerminal.close();
}
@Override
public void addResizeListener(TerminalResizeListener listener) {
swingTerminal.addResizeListener(listener);
}
@Override
public void removeResizeListener(TerminalResizeListener listener) {
swingTerminal.removeResizeListener(listener);
}
}