package com.jediterm.terminal.display; import com.jediterm.terminal.TextStyle; import org.jetbrains.annotations.NotNull; import java.util.*; /** * @author traff */ public class TerminalLine { private TextEntries myTextEntries = new TextEntries(); private boolean wrapped = false; public TerminalLine(@NotNull TextEntry entry) { myTextEntries.add(entry); } public static TerminalLine createEmpty() { return new TerminalLine(TextEntry.EMPTY); } public String getText() { final StringBuilder sb = new StringBuilder(); for (TerminalLine.TextEntry textEntry : myTextEntries) { sb.append(textEntry.getText()); } return sb.toString(); } public boolean isWrapped() { return wrapped; } public void setWrapped(boolean wrapped) { this.wrapped = wrapped; } public Iterator<TextEntry> entriesIterator() { return myTextEntries.iterator(); } public void clear() { myTextEntries.clear(); setWrapped(false); } public void writeString(int x, @NotNull String str, @NotNull TextStyle style) { writeCharacters(x, style, new CharBuffer(str)); } private void writeCharacters(int x, @NotNull TextStyle style, @NotNull CharBuffer characters) { int len = myTextEntries.length(); if (x >= len) { if (x - len > 0) { myTextEntries.add(new TextEntry(TextStyle.EMPTY, new CharBuffer(' ', x - len))); } myTextEntries.add(new TextEntry(style, characters)); } else { len = Math.max(len, x + characters.length()); myTextEntries = merge(x, characters, style, myTextEntries, len); } } private static TextEntries merge(int x, @NotNull CharBuffer str, @NotNull TextStyle style, @NotNull TextEntries entries, int lineLength) { char[] buf = new char[lineLength]; TextStyle[] styles = new TextStyle[lineLength]; int p = 0; for (TextEntry entry : entries) { for (int i = 0; i < entry.getLength(); i++) { buf[p + i] = entry.getText().charAt(i); styles[p + i] = entry.getStyle(); } p += entry.getLength(); } for (int i = 0; i < str.length(); i++) { buf[i + x] = str.charAt(i); styles[i + x] = style; } return collectFromBuffer(buf, styles); } private static TextEntries collectFromBuffer(@NotNull char[] buf, @NotNull TextStyle[] styles) { TextEntries result = new TextEntries(); TextStyle curStyle = styles[0]; int start = 0; for (int i = 1; i < buf.length; i++) { if (styles[i] != curStyle) { result.add(new TextEntry(curStyle, new CharBuffer(buf, start, i - start))); curStyle = styles[i]; start = i; } } result.add(new TextEntry(curStyle, new CharBuffer(buf, start, buf.length - start))); return result; } public void deleteCharacters(int x) { deleteCharacters(x, myTextEntries.length() - x); // delete to the end of line : line is no more wrapped setWrapped(false); } public void deleteCharacters(int x, int count) { int p = 0; TextEntries newEntries = new TextEntries(); for (TextEntry entry : myTextEntries) { if (count == 0) { newEntries.add(entry); continue; } int len = entry.getLength(); if (p + len <= x) { p += len; newEntries.add(entry); continue; } int dx = x - p; //>=0 if (dx>0) { //part of entry before x newEntries.add(new TextEntry(entry.getStyle(), entry.getText().subBuffer(0, dx))); p = x; } if (dx + count < len) { //part that left after deleting count newEntries.add(new TextEntry(entry.getStyle(), entry.getText().subBuffer(dx + count, len - (dx + count)))); count = 0; } else { count -= (len-dx); p = x; } } myTextEntries = newEntries; } public void insertBlankCharacters(int x, int count, int maxLen) { int len = myTextEntries.length(); len = Math.min(len + count, maxLen); char[] buf = new char[len]; TextStyle[] styles = new TextStyle[len]; int p = 0; for (TextEntry entry : myTextEntries) { for (int i = 0; i < entry.getLength() && p < len; i++) { if (p == x) { for(int j = 0; j < count; j++) { buf[p] = ' '; styles[p] = TextStyle.EMPTY; p++; } } if (p < len) { buf[p] = entry.getText().charAt(i); styles[p] = entry.getStyle(); p++; } } if (p >= len) { break; } } myTextEntries = collectFromBuffer(buf, styles); } public void clearArea(int leftX, int rightX, @NotNull TextStyle style) { if (leftX < myTextEntries.length()) { writeCharacters(leftX, style, new CharBuffer(' ', Math.min(myTextEntries.length(), rightX) - leftX)); } } static class TextEntry { public static final TextEntry EMPTY = new TextEntry(TextStyle.EMPTY, CharBuffer.EMPTY); private final TextStyle myStyle; private final CharBuffer myText; public TextEntry(@NotNull TextStyle style, @NotNull CharBuffer text) { myStyle = style; myText = text.clone(); } public TextStyle getStyle() { return myStyle; } public CharBuffer getText() { return myText; } public int getLength() { return myText.getLength(); } } private static class TextEntries implements Iterable<TextEntry> { private Deque<TextEntry> myTextEntries = new ArrayDeque<TextEntry>(); private int myLength = 0; public void add(TextEntry entry) { myTextEntries.add(entry); myLength+=entry.getLength(); } private Collection<TextEntry> entries() { return Collections.unmodifiableCollection(myTextEntries); } public Iterator<TextEntry> iterator() { return entries().iterator(); } public int length() { return myLength; } public void clear() { myTextEntries.clear(); myLength = 0; } } }