import java.util.List;
import java.util.ListIterator;
-import be.nikiroo.jvcard.resources.StringUtils;
+import be.nikiroo.utils.StringUtils;
/**
* This class is basically a List with a parent and a "dirty" state check. It
* </p>
*
* @author niki
- *
+ *
* @param <E>
* the type of the child elements
*/
public String getContentState(boolean self) {
StringBuilder builder = new StringBuilder();
buildContentStateRaw(builder, self);
- return StringUtils.getHash(builder.toString());
+ return StringUtils.getMd5Hash(builder.toString());
}
/**
import be.nikiroo.jvcard.parsers.Format;
import be.nikiroo.jvcard.parsers.Parser;
-import be.nikiroo.jvcard.resources.StringUtils;
+import be.nikiroo.utils.StringUtils;
/**
* A contact is the information that represent a contact person or organisation.
package be.nikiroo.jvcard.launcher;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.Socket;
import java.nio.charset.Charset;
import be.nikiroo.jvcard.parsers.Format;
import be.nikiroo.jvcard.remote.Command;
import be.nikiroo.jvcard.remote.SimpleSocket;
-import be.nikiroo.jvcard.resources.Bundles;
-import be.nikiroo.jvcard.resources.StringUtils;
-import be.nikiroo.jvcard.resources.bundles.ColorBundle;
-import be.nikiroo.jvcard.resources.bundles.DisplayBundle;
-import be.nikiroo.jvcard.resources.bundles.RemoteBundle;
-import be.nikiroo.jvcard.resources.bundles.TransBundle;
-import be.nikiroo.jvcard.resources.enums.DisplayOption;
-import be.nikiroo.jvcard.resources.enums.StringId;
+import be.nikiroo.jvcard.resources.DisplayBundle;
+import be.nikiroo.jvcard.resources.DisplayOption;
+import be.nikiroo.jvcard.resources.RemoteBundle;
+import be.nikiroo.jvcard.resources.StringId;
+import be.nikiroo.jvcard.resources.TransBundle;
+import be.nikiroo.utils.ImageUtils;
+import be.nikiroo.utils.StringUtils;
+import be.nikiroo.utils.Version;
+import be.nikiroo.utils.resources.Bundles;
/**
* This class contains the runnable Main method. It will parse the user supplied
* a MainWindow.
*
* @author niki
- *
+ *
*/
public class Main {
static public final String APPLICATION_TITLE = "jVcard";
- static public final String APPLICATION_VERSION = "1.1-dev";
static private final int ERR_NO_FILE = 1;
static private final int ERR_SYNTAX = 2;
}
new TransBundle().updateFile(dir); // default locale
- for (String lang : TransBundle.getKnownLanguages()) {
+ for (String lang : new TransBundle().getKnownLanguages()) {
new TransBundle(lang).updateFile(dir);
}
- new ColorBundle().updateFile(dir);
+ // new UIColors().updateFile(dir);
new DisplayBundle().updateFile(dir);
new RemoteBundle().updateFile(dir);
} catch (IOException e) {
.toLowerCase();
}
- String b64 = StringUtils.fromImage(f);
+ String b64;
+ InputStream in = null;
+ try {
+ in = new FileInputStream(f);
+ b64 = ImageUtils.toBase64(in);
+ } finally {
+ if (in != null) {
+ in.close();
+ }
+ }
// remove previous photos:
for (Data photo = contact
System.out.println("Saving " + f);
try {
ImageIO.write(
- StringUtils.toImage(photo.getValue()),
+ ImageUtils.fromBase64(photo.getValue()),
"png", f);
} catch (IOException e) {
System.err.println(trans(
break;
}
case HELP: {
- System.out.println(APPLICATION_TITLE + " " + APPLICATION_VERSION);
+ System.out.println(APPLICATION_TITLE + " "
+ + Version.getCurrentVersion());
System.out.println();
System.out.println(trans(StringId.CLI_HELP));
import be.nikiroo.jvcard.parsers.Format;
import be.nikiroo.jvcard.parsers.Vcard21Parser;
import be.nikiroo.jvcard.remote.SimpleSocket.BlockAppendable;
-import be.nikiroo.jvcard.resources.StringUtils;
-import be.nikiroo.jvcard.resources.bundles.RemoteBundle;
-import be.nikiroo.jvcard.resources.enums.RemotingOption;
+import be.nikiroo.jvcard.resources.RemoteBundle;
+import be.nikiroo.jvcard.resources.RemotingOption;
+import be.nikiroo.utils.StringUtils;
/**
* This class implements a small server that can listen for requests to
* </p>
*
* @author niki
- *
+ *
*/
public class Server implements Runnable {
private ServerSocket ss;
import be.nikiroo.jvcard.parsers.Format;
import be.nikiroo.jvcard.parsers.Vcard21Parser;
import be.nikiroo.jvcard.remote.SimpleSocket.BlockAppendable;
-import be.nikiroo.jvcard.resources.StringUtils;
-import be.nikiroo.jvcard.resources.bundles.RemoteBundle;
-import be.nikiroo.jvcard.resources.enums.RemotingOption;
+import be.nikiroo.jvcard.resources.RemoteBundle;
+import be.nikiroo.jvcard.resources.RemotingOption;
+import be.nikiroo.utils.StringUtils;
/**
* This class will synchronise {@link Card}s between a local instance an a
* remote jVCard server.
*
* @author niki
- *
+ *
*/
public class Sync {
/** The time in ms after which we declare that 2 timestamps are different */
*
* @param dir
* the cache to use
- *
+ *
* @return the cached {@link File}
*/
private File getCache(File dir) {
+++ /dev/null
-package be.nikiroo.jvcard.resources;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
-import java.lang.reflect.Field;
-import java.util.Locale;
-import java.util.ResourceBundle;
-
-/**
- * This class help you get UTF-8 bundles for this application.
- *
- * @author niki
- *
- */
-public class Bundles {
- /**
- * The configuration directory where we try to get the <tt>.properties</tt>
- * in priority, or NULL to get the information from the compiled resources.
- */
- static private String confDir = getConfDir();
-
- /**
- * The type of configuration information the associated {@link Bundle} will
- * convey.
- *
- * @author niki
- *
- */
- public enum Target {
- colors, display, jvcard, remote, resources
- }
-
- /**
- * Return the configuration directory where to try to find the
- * <tt>.properties</tt> files in priority.
- *
- * @return the configuration directory
- */
- static private String getConfDir() {
- // Do not override user-supplied config directory (see --help)
- if (Bundles.confDir != null)
- return Bundles.confDir;
-
- try {
- ResourceBundle bundle = ResourceBundle.getBundle(Bundles.class
- .getPackage().getName() + "." + "jvcard",
- Locale.getDefault(), new FixedResourceBundleControl(null));
-
- String configDir = bundle.getString("CONFIG_DIR");
- if (configDir != null && configDir.trim().length() > 0)
- return configDir;
- } catch (Exception e) {
- }
-
- return null;
- }
-
- /**
- * Set the primary configuration directory to look for <tt>.properties</tt>
- * files in.
- *
- * All {@link ResourceBundle}s returned by this class after that point will
- * respect this new directory.
- *
- * @param confDir
- * the new directory
- */
- static public void setDirectory(String confDir) {
- Bundles.confDir = confDir;
- }
-
- /**
- * Get the primary configuration directory to look for <tt>.properties</tt>
- * files in.
- *
- * @return the directory
- */
- static public String getDirectory() {
- return Bundles.confDir;
- }
-
- /**
- * This class encapsulate a {@link ResourceBundle} in UTF-8. It only allows
- * to retrieve values associated to an enumeration, and allows some
- * additional methods.
- *
- * @author niki
- *
- * @param <E>
- * the enum to use to get values out of this class
- */
- public class Bundle<E extends Enum<E>> {
- private Class<E> type;
- protected Target name;
- protected ResourceBundle map;
-
- /**
- * Create a new {@link Bundles} of the given name.
- *
- * @param type
- * a runtime instance of the class of E
- *
- * @param name
- * the name of the {@link Bundles}
- */
- protected Bundle(Class<E> type, Target name) {
- this.type = type;
- this.name = name;
- this.map = getBundle(name);
- }
-
- /**
- * Return the value associated to the given id as a {@link String}.
- *
- * @param mame
- * the id of the value to get
- *
- * @return the associated value
- */
- public String getString(E id) {
- if (map.containsKey(id.name())) {
- return map.getString(id.name()).trim();
- }
-
- return "";
- }
-
- /**
- * Return the value associated to the given id as a {@link Boolean}.
- *
- * @param mame
- * the id of the value to get
- *
- * @return the associated value
- */
- public Boolean getBoolean(E id) {
- String str = getString(id);
- if (str != null && str.length() > 0) {
- if (str.equalsIgnoreCase("true") || str.equalsIgnoreCase("on")
- || str.equalsIgnoreCase("yes"))
- return true;
- if (str.equalsIgnoreCase("false")
- || str.equalsIgnoreCase("off")
- || str.equalsIgnoreCase("no"))
- return false;
-
- }
-
- return null;
- }
-
- /**
- * Return the value associated to the given id as a {@link boolean}.
- *
- * @param mame
- * the id of the value to get
- * @param def
- * the default value when it is not present in the config
- * file or if it is not a boolean value
- *
- * @return the associated value
- */
- public boolean getBoolean(E id, boolean def) {
- Boolean b = getBoolean(id);
- if (b != null)
- return b;
-
- return def;
- }
-
- /**
- * Return the value associated to the given id as an {@link Integer}.
- *
- * @param mame
- * the id of the value to get
- *
- * @return the associated value
- */
- public Integer getInteger(E id) {
- try {
- return Integer.parseInt(getString(id));
- } catch (Exception e) {
- }
-
- return null;
- }
-
- /**
- * Return the value associated to the given id as a {@link int}.
- *
- * @param mame
- * the id of the value to get
- * @param def
- * the default value when it is not present in the config
- * file or if it is not a int value
- *
- * @return the associated value
- */
- public int getInteger(E id, int def) {
- Integer i = getInteger(id);
- if (i != null)
- return i;
-
- return def;
- }
-
- /**
- * Create/update the .properties file. Will use the most likely
- * candidate as base if the file does not already exists and this
- * resource is translatable (for instance, "en_US" will use "en" as a
- * base if the resource is a translation file).
- *
- * @param path
- * the path where the .properties files are
- *
- * @throws IOException
- * in case of IO errors
- */
- public void updateFile(String path) throws IOException {
- File file = getUpdateFile(path);
-
- BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
- new FileOutputStream(file), "UTF-8"));
-
- writeHeader(writer);
- writer.write("\n");
- writer.write("\n");
-
- for (Field field : type.getDeclaredFields()) {
- Meta meta = field.getAnnotation(Meta.class);
- if (meta != null) {
- E id = E.valueOf(type, field.getName());
- String info = getMetaInfo(meta);
-
- if (info != null) {
- writer.write(info);
- writer.write("\n");
- }
-
- writeValue(writer, id);
- }
- }
-
- writer.close();
- }
-
- /**
- * Return formated, display-able information from the {@link Meta} field
- * given. Each line will always starts with a "#" character.
- *
- * @param meta
- * the {@link Meta} field
- *
- * @return the information to display or NULL if none
- */
- protected String getMetaInfo(Meta meta) {
- String what = meta.what();
- String where = meta.where();
- String format = meta.format();
- String info = meta.info();
-
- int opt = what.length() + where.length() + format.length();
- if (opt + info.length() == 0)
- return null;
-
- StringBuilder builder = new StringBuilder();
- builder.append("# ");
-
- if (opt > 0) {
- builder.append("(");
- if (what.length() > 0) {
- builder.append("WHAT: " + what);
- if (where.length() + format.length() > 0)
- builder.append(", ");
- }
-
- if (where.length() > 0) {
- builder.append("WHERE: " + where);
- if (format.length() > 0)
- builder.append(", ");
- }
-
- if (format.length() > 0) {
- builder.append("FORMAT: " + format);
- }
-
- builder.append(")");
- if (info.length() > 0) {
- builder.append("\n# ");
- }
- }
-
- builder.append(info);
-
- return builder.toString();
- }
-
- /**
- * Write the header found in the configuration <tt>.properties</tt> file
- * of this {@link Bundles}.
- *
- * @param writer
- * the {@link Writer} to write the header in
- *
- * @throws IOException
- * in case of IO error
- */
- protected void writeHeader(Writer writer) throws IOException {
- writer.write("# " + name + "\n");
- writer.write("#\n");
- }
-
- /**
- * Write the given id to the config file, i.e.,
- * "MY_ID = my_curent_value" followed by a new line
- *
- * @param writer
- * the {@link Writer} to write into
- * @param id
- * the id to write
- *
- * @throws IOException
- * in case of IO error
- */
- protected void writeValue(Writer writer, E id) throws IOException {
- writeValue(writer, id.name(), getString(id));
- }
-
- /**
- * Write the given data to the config file, i.e.,
- * "MY_ID = my_curent_value" followed by a new line
- *
- * @param writer
- * the {@link Writer} to write into
- * @param id
- * the id to write
- * @param value
- * the id's value
- *
- * @throws IOException
- * in case of IO error
- */
- protected void writeValue(Writer writer, String id, String value)
- throws IOException {
- writer.write(id);
- writer.write(" = ");
-
- String[] lines = value.replaceAll("\\\t", "\\\\\\t").split("\n");
- for (int i = 0; i < lines.length; i++) {
- writer.write(lines[i]);
- if (i < lines.length - 1) {
- writer.write("\\n\\");
- }
- writer.write("\n");
- }
- }
-
- /**
- * Return the non-localised bundle of the given name.
- *
- * @param name
- * the name of the bundle to load
- *
- * @return the bundle
- */
- protected ResourceBundle getBundle(Target name) {
- return ResourceBundle.getBundle(Bundles.class.getPackage()
- .getName() + "." + name.name(),
- new FixedResourceBundleControl(confDir));
- }
-
- /**
- * Return the localised bundle of the given name and {@link Locale}.
- *
- * @param name
- * the name of the bundle to load
- * @param locale
- * the {@link Locale} to use
- *
- * @return the localised bundle
- */
- protected ResourceBundle getBundle(Target name, Locale locale) {
- return ResourceBundle.getBundle(Bundles.class.getPackage()
- .getName() + "." + name.name(), locale,
- new FixedResourceBundleControl(confDir));
- }
-
- /**
- * Return the source file for this {@link Bundles} from the given path.
- *
- * @param path
- * the path where the .properties files are
- *
- * @return the source {@link File}
- *
- * @throws IOException
- * in case of IO errors
- */
- protected File getUpdateFile(String path) {
- return new File(path, name.name() + ".properties");
- }
- }
-}
--- /dev/null
+package be.nikiroo.jvcard.resources;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import be.nikiroo.utils.resources.Bundle;
+
+/**
+ * All colour information must come from here.
+ * <p>
+ * TODO: delete this class, and think about a better way to get BG/FG colours...
+ *
+ * @author niki
+ */
+public class ColorBundle extends Bundle<ColorOption> {
+ public ColorBundle() {
+ super(ColorOption.class, Target.colors, null);
+ }
+
+ @Override
+ protected void writeHeader(Writer writer) throws IOException {
+ ColorOption.writeHeader(writer);
+ }
+
+ @Override
+ protected void writeValue(Writer writer, ColorOption id) throws IOException {
+ String name = id.name() + "_FG";
+ String value = "";
+ if (containsKey(name))
+ value = getString(name).trim();
+
+ writeValue(writer, name, value);
+
+ name = id.name() + "_BG";
+ value = "";
+ if (containsKey(name))
+ value = getString(name).trim();
+
+ writeValue(writer, name, value);
+ }
+
+ @Override
+ protected void resetMap(ResourceBundle bundle) {
+ // this.map.clear();
+
+ if (bundle != null) {
+ for (ColorOption field : type.getEnumConstants()) {
+ try {
+ // String value = bundle.getString(field.name());
+ // this.map.put(field.name(), value == null ? null :
+ // value.trim());
+ setString(field.name() + "_FG",
+ bundle.getString(field.name() + "_FG"));
+ setString(field.name() + "_BG",
+ bundle.getString(field.name() + "_BG"));
+ } catch (MissingResourceException e) {
+ }
+ }
+ }
+ }
+
+ @Override
+ public String getStringX(ColorOption id, String suffix) {
+ String key = id.name()
+ + (suffix == null ? "" : "_" + suffix.toUpperCase());
+
+ return getString(key);
+ }
+}
-package be.nikiroo.jvcard.resources.enums;
+package be.nikiroo.jvcard.resources;
import java.io.IOException;
import java.io.Writer;
-import be.nikiroo.jvcard.resources.Meta;
-import be.nikiroo.jvcard.resources.Bundles.Bundle;
+import be.nikiroo.utils.resources.Meta;
+import be.nikiroo.utils.resources.Meta.Format;
/**
* Represent an element that can be coloured (foreground/background colours).
*
* @author niki
- *
+ *
*/
public enum ColorOption {
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
DEFAULT, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
TITLE_MAIN, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
TITLE_VARIABLE, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
TITLE_COUNT, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
ACTION_KEY, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
ACTION_DESC, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
LINE_MESSAGE, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
LINE_MESSAGE_ERR, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
LINE_MESSAGE_QUESTION, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
LINE_MESSAGE_ANS, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
CONTACT_LINE, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
CONTACT_LINE_SEPARATOR, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
CONTACT_LINE_SELECTED, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
CONTACT_LINE_SEPARATOR_SELECTED, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
CONTACT_LINE_DIRTY, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
CONTACT_LINE_DIRTY_SELECTED, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
VIEW_CONTACT_NAME, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
VIEW_CONTACT_NORMAL, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
VIEW_CONTACT_HIGHLIGHT, //
- @Meta(what = "", where = "", format = "colour", info = "")
+ @Meta(format = Format.COLOR)
VIEW_CONTACT_NOTES_TITLE, //
;
-package be.nikiroo.jvcard.resources.bundles;
+package be.nikiroo.jvcard.resources;
import java.io.IOException;
import java.io.Writer;
-import be.nikiroo.jvcard.resources.Bundles;
-import be.nikiroo.jvcard.resources.Bundles.Bundle;
-import be.nikiroo.jvcard.resources.Bundles.Target;
-import be.nikiroo.jvcard.resources.enums.DisplayOption;
+import be.nikiroo.utils.resources.Bundle;
/**
* This class manages the display configuration of the application.
*/
public class DisplayBundle extends Bundle<DisplayOption> {
public DisplayBundle() {
- new Bundles().super(DisplayOption.class, Target.display);
+ super(DisplayOption.class, Target.display, null);
}
@Override
-package be.nikiroo.jvcard.resources.enums;
+package be.nikiroo.jvcard.resources;
import java.io.IOException;
import java.io.Writer;
-import be.nikiroo.jvcard.resources.Bundles.Bundle;
-import be.nikiroo.jvcard.resources.Meta;
+import be.nikiroo.utils.resources.Meta;
+
public enum DisplayOption {
- @Meta(what = "", where = "", format = "coma-separated list of CLF", info = "The format of each line in the contact list")
+ @Meta( info = "comma-separated list of CLF", description = "The format of each line in the contact list")
CONTACT_LIST_FORMAT, //
- @Meta(what = "", where = "", format = "CDIF", info = "The list of details to show in View Contact mode")
+ @Meta( info = "CDIF", description = "The list of details to show in View Contact mode")
CONTACT_DETAILS_INFO, //
- @Meta(what = "", where = "", format = "Integer or nothing for auto", info = "The size of the details' labels")
+ @Meta( info = "Integer or nothing for auto", description = "The size of the details' labels")
CONTACT_DETAILS_LABEL_WIDTH, //
- @Meta(what = "", where = "", format = "CLF", info = "The default value of FN if it is not present")
+ @Meta( info = "CLF", description = "The default value of FN if it is not present")
CONTACT_DETAILS_DEFAULT_FN, //
- @Meta(what = "", where = "", format = "TRUE or FALSE", info = "TRUE to force all FNs to be recreated from CONTACT_DETAILS_DEFAULT_FN")
+ @Meta(info = "TRUE or FALSE", description = "TRUE to force all FNs to be recreated from CONTACT_DETAILS_DEFAULT_FN")
CONTACT_DETAILS_SHOW_COMPUTED_FN, //
;
+++ /dev/null
-package be.nikiroo.jvcard.resources;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.net.URLConnection;
-import java.util.Locale;
-import java.util.PropertyResourceBundle;
-import java.util.ResourceBundle;
-import java.util.ResourceBundle.Control;
-
-/**
- * Fixed ResourceBundle.Control class. It will use UTF-8 for the files to load.
- *
- * Also support an option to first check into the given path before looking into
- * the resources.
- *
- * @author niki
- *
- */
-class FixedResourceBundleControl extends Control {
- private String outsideWorld = null;
-
- /**
- * Create a new {@link FixedResourceBundleControl}.
- *
- * @param outsideWorld
- * NULL if you are only interested into the resources, a path to
- * first check into it before looking at the actual resources
- */
- public FixedResourceBundleControl(String outsideWorld) {
- this.outsideWorld = outsideWorld;
- }
-
- @Override
- public ResourceBundle newBundle(String baseName, Locale locale,
- String format, ClassLoader loader, boolean reload)
- throws IllegalAccessException, InstantiationException, IOException {
- // The below is a copy of the default implementation.
- String bundleName = toBundleName(baseName, locale);
- String resourceName = toResourceName(bundleName, "properties");
-
- ResourceBundle bundle = null;
- InputStream stream = null;
- if (reload) {
- URL url = loader.getResource(resourceName);
- if (url != null) {
- URLConnection connection = url.openConnection();
- if (connection != null) {
- connection.setUseCaches(false);
- stream = connection.getInputStream();
- }
- }
- } else {
- // New code to support outside resources:
- if (outsideWorld != null) {
- String pkg = this.getClass().getPackage().getName();
- pkg = pkg.replaceAll("\\.", File.separator) + File.separator;
-
- if (resourceName.startsWith(pkg)) {
- try {
- String file = outsideWorld + File.separator
- + resourceName.substring(pkg.length());
- stream = new FileInputStream(file);
- } catch (Exception e) {
- // file not in priority directory,
- // fallback to default resource
- }
- }
- }
-
- if (stream == null)
- stream = loader.getResourceAsStream(resourceName);
- //
- }
- if (stream != null) {
- try {
- // This line is changed to make it to read properties files
- // as UTF-8.
- // How can someone use an archaic encoding such as ISO 8859-1 by
- // *DEFAULT* is beyond me...
- bundle = new PropertyResourceBundle(new InputStreamReader(
- stream, "UTF-8"));
- } finally {
- stream.close();
- }
- }
- return bundle;
- }
-}
\ No newline at end of file
+++ /dev/null
-package be.nikiroo.jvcard.resources;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Annotation used to give some information about the translation keys, so the
- * translation .properties file can be created programmatically.
- *
- * @author niki
- *
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.FIELD)
-public @interface Meta {
- /**
- * What kind of item this key represent (a Key, a Label text, a format to
- * use for something else...).
- *
- * @return what it is
- */
- String what();
-
- /**
- * Where in the application will this key appear (in the action keys, in a
- * menu, in a message...).
- *
- * @return where it is
- */
- String where();
-
- /**
- * What format should/must this key be in.
- *
- * @return the format it is in
- */
- String format();
-
- /**
- * Free info text to help translate.
- *
- * @return some info
- */
- String info();
-}
-package be.nikiroo.jvcard.resources.bundles;
+package be.nikiroo.jvcard.resources;
import java.io.IOException;
import java.io.Writer;
-import be.nikiroo.jvcard.resources.Bundles;
-import be.nikiroo.jvcard.resources.Bundles.Bundle;
-import be.nikiroo.jvcard.resources.Bundles.Target;
-import be.nikiroo.jvcard.resources.enums.RemotingOption;
+import be.nikiroo.utils.resources.Bundle;
/**
* This class manages the display configuration of the application.
*/
public class RemoteBundle extends Bundle<RemotingOption> {
public RemoteBundle() {
- new Bundles().super(RemotingOption.class, Target.remote);
+ super(RemotingOption.class, Target.remote, null);
}
@Override
-package be.nikiroo.jvcard.resources.enums;
+package be.nikiroo.jvcard.resources;
import java.io.IOException;
import java.io.Writer;
-import be.nikiroo.jvcard.resources.Meta;
-import be.nikiroo.jvcard.resources.Bundles.Bundle;
+import be.nikiroo.utils.resources.Meta;
+import be.nikiroo.utils.resources.Meta.Format;
public enum RemotingOption {
- @Meta(what = "", where = "Server", format = "directory", info = "when starting as a jVCard remote server, where to look for data")
+ @Meta(format = Format.DIRECTORY, description = "when starting as a jVCard remote server, where to look for data")
SERVER_DATA_PATH, //
- @Meta(what = "", where = "Client", format = "directory", info = "when loading \"jvcard://\" links, where to save cache files")
+ @Meta(format = Format.DIRECTORY, description = "when loading \"jvcard://\" links, where to save cache files")
CLIENT_CACHE_DIR, //
- @Meta(what = "", where = "Client", format = "TRUE or FALSE", info = "Automatically synchronise remote cards")
+ @Meta(format = Format.BOOLEAN, description = "Automatically synchronise remote cards")
CLIENT_AUTO_SYNC, //
;
+++ /dev/null
-package be.nikiroo.jvcard.resources;
-
-// code copied from from:
-// http://forums.devx.com/showthread.php?t=153784,
-// via:
-// http://stackoverflow.com/questions/3923129/get-a-list-of-resources-from-classpath-directory
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipException;
-import java.util.zip.ZipFile;
-
-/**
- * list resources available from the classpath @ *
- */
-public class ResourceList {
-
- /**
- * for all elements of java.class.path get a Collection of resources Pattern
- * pattern = Pattern.compile(".*"); gets all resources
- *
- * @param pattern
- * the pattern to match
- * @return the resources in the order they are found
- */
- public static Collection<String> getResources(final Pattern pattern) {
- final ArrayList<String> retval = new ArrayList<String>();
- final String classPath = System.getProperty("java.class.path", ".");
- final String[] classPathElements = classPath.split(System
- .getProperty("path.separator"));
- for (final String element : classPathElements) {
- retval.addAll(getResources(element, pattern));
- }
- return retval;
- }
-
- private static Collection<String> getResources(final String element,
- final Pattern pattern) {
- final ArrayList<String> retval = new ArrayList<String>();
- final File file = new File(element);
- if (file.isDirectory()) {
- retval.addAll(getResourcesFromDirectory(file, pattern));
- } else {
- retval.addAll(getResourcesFromJarFile(file, pattern));
- }
- return retval;
- }
-
- private static Collection<String> getResourcesFromJarFile(final File file,
- final Pattern pattern) {
- final ArrayList<String> retval = new ArrayList<String>();
- ZipFile zf;
- try {
- zf = new ZipFile(file);
- } catch (final ZipException e) {
- throw new Error(e);
- } catch (final IOException e) {
- throw new Error(e);
- }
- final Enumeration<? extends ZipEntry> e = zf.entries();
- while (e.hasMoreElements()) {
- final ZipEntry ze = (ZipEntry) e.nextElement();
- final String fileName = ze.getName();
- final boolean accept = pattern.matcher(fileName).matches();
- if (accept) {
- retval.add(fileName);
- }
- }
- try {
- zf.close();
- } catch (final IOException e1) {
- throw new Error(e1);
- }
- return retval;
- }
-
- private static Collection<String> getResourcesFromDirectory(
- final File directory, final Pattern pattern) {
- final ArrayList<String> retval = new ArrayList<String>();
- final File[] fileList = directory.listFiles();
- for (final File file : fileList) {
- if (file.isDirectory()) {
- retval.addAll(getResourcesFromDirectory(file, pattern));
- } else {
- try {
- final String fileName = file.getCanonicalPath();
- final boolean accept = pattern.matcher(fileName).matches();
- if (accept) {
- retval.add(fileName);
- }
- } catch (final IOException e) {
- throw new Error(e);
- }
- }
- }
- return retval;
- }
-
- /**
- * list the resources that match args[0]
- *
- * @param args
- * args[0] is the pattern to match, or list all resources if
- * there are no args
- */
- public static void main(final String[] args) {
- Pattern pattern;
- if (args.length < 1) {
- pattern = Pattern.compile(".*");
- } else {
- pattern = Pattern.compile(args[0]);
- }
- final Collection<String> list = ResourceList.getResources(pattern);
- for (final String name : list) {
- System.out.println(name);
- }
- }
-}
--- /dev/null
+package be.nikiroo.jvcard.resources;
+
+import be.nikiroo.utils.resources.Meta;
+
+
+/**
+ * The enum representing textual information to be translated to the user as a
+ * key.
+ *
+ * Note that each key that should be translated MUST be annotated with a
+ * {@link Meta} annotation.
+ *
+ * @author niki
+ *
+ */
+public enum StringId {
+ DUMMY, // <-- TODO : remove
+ NULL, // Special usage, no annotations so it is not visible in
+ // .properties files
+ @Meta( info = "MUST BE 3 chars long", description = "Tab key")
+ KEY_TAB, // keys
+ @Meta( info = "MUST BE 3 chars long", description = "Enter key")
+ KEY_ENTER, //
+ @Meta( description = "Go back to previous screen")
+ KEY_ACTION_BACK, //
+ @Meta( description = "Get help text")
+ KEY_ACTION_HELP, //
+ @Meta( description = "View the selected card")
+ KEY_ACTION_VIEW_CARD, //
+ @Meta( description = "View the selected contact")
+ KEY_ACTION_VIEW_CONTACT, //
+ @Meta( description = "Edit the contact")
+ KEY_ACTION_EDIT_CONTACT, //
+ @Meta( description = "Edit the contact in RAW mode")
+ KEY_ACTION_EDIT_CONTACT_RAW, //
+ @Meta( description = "Edit the RAW field")
+ KEY_ACTION_EDIT_FIELD, //
+ @Meta( description = "Save the whole card")
+ KEY_ACTION_SAVE_CARD, //
+ @Meta( description = "Delete the selected element")
+ KEY_ACTION_DELETE, //
+ @Meta( description = "Filter the displayed contacts")
+ KEY_ACTION_SEARCH, //
+ @Meta( info = "we could use: ' ', ┃, │...", description = "Field separator")
+ DEAULT_FIELD_SEPARATOR, // MainContentList
+ @Meta( description = "Invert the photo's colours")
+ KEY_ACTION_INVERT, //
+ @Meta( description = "Show the photo in 'fullscreen'")
+ KEY_ACTION_FULLSCREEN, //
+ @Meta( description = "Switch between the available display formats")
+ KEY_ACTION_SWITCH_FORMAT, // multi-usage
+ @Meta( description = "Add a new contact/field")
+ KEY_ACTION_ADD, //
+ @Meta( description = "New contact")
+ ASK_USER_CONTACT_NAME, //
+ @Meta( info = "%s = contact name", description = "Delete contact")
+ CONFIRM_USER_DELETE_CONTACT, //
+ @Meta( info = "%s = contact name", description = "cannot delete a contact")
+ ERR_CANNOT_DELETE_CONTACT, //
+ @Meta( description = "The Help message header line")
+ CLI_HELP, //
+ @Meta( description = "The Help message line before explaining the different modes")
+ CLI_HELP_MODES, //
+ @Meta( description = "The Help message line for help usage")
+ CLI_HELP_MODE_HELP, //
+ @Meta( description = "The Help message line for contact manager usage")
+ CLI_HELP_MODE_CONTACT_MANAGER, //
+ @Meta( description = "The Help message line for contact manager usage")
+ CLI_HELP_MODE_I18N, //
+ @Meta( description = "The Help message line for jVCard server usage")
+ CLI_HELP_MODE_SERVER, //
+ @Meta( description = "The Help message line for --load-photo usage")
+ CLI_HELP_MODE_LOAD_PHOTO, //
+ @Meta( description = "The Help message line for --save-photo usage")
+ CLI_HELP_MODE_SAVE_PHOTO, //
+ @Meta( description = "The Help message line for config save usage")
+ CLI_HELP_MODE_SAVE_CONFIG, //
+ @Meta( description = "The Help message line before the list of options")
+ CLI_HELP_OPTIONS, //
+ @Meta( description = "The Help message line for: --")
+ CLI_HELP_DD, //
+ @Meta( description = "The Help message line for: --")
+ CLI_HELP_LANG, //
+ @Meta( description = "The Help message line for: --")
+ CLI_HELP_GUI, //
+ @Meta( description = "The Help message line for: --")
+ CLI_HELP_TUI, //
+ @Meta( description = "The Help message line for: --")
+ CLI_HELP_NOUTF_OPTION, //
+ @Meta( description = "The Help message line for: --")
+ CLI_HELP_CONFIG, //
+ @Meta( description = "The Help message footer about files and jvcard:// links")
+ CLI_HELP_FOOTER, //
+ @Meta( info = "%s = the error", description = "Syntax error: SOME TEXT")
+ CLI_SERR, //
+ @Meta( description = "More than one mode given")
+ CLI_SERR_MODES, //
+ @Meta( description = "--lang is required")
+ CLI_SERR_NOLANG, //
+ @Meta( description = "The dir is required")
+ CLI_SERR_NODIR, //
+ @Meta( description = "The port is required")
+ CLI_SERR_NOPORT, //
+ @Meta( description = "The format is required")
+ CLI_SERR_NOFORMAT, //
+ @Meta( info = "%s = bad port", description = "The port is not valid")
+ CLI_SERR_BADPORT, //
+ @Meta( info = "%s = mode", description = "Card files are not supported in mode %s")
+ CLI_SERR_CANNOT_CARDS, //
+ @Meta( info = "%s = the error", description = "Error: SOME TEXT")
+ CLI_ERR, //
+ @Meta( description = "No files given")
+ CLI_ERR_NOFILES, //
+ @Meta( info = "%s = dir", description = "Cannot create conf dir %s")
+ CLI_ERR_CANNOT_CREATE_CONFDIR, //
+ @Meta( description = "Remoting not available")
+ CLI_ERR_NO_REMOTING, //
+ @Meta( description = "TUI not available")
+ CLI_ERR_NO_TUI, //
+ @Meta( info = "%s = dir", description = "Cannot create/update language in dir %s")
+ CLI_ERR_CANNOT_CREATE_LANG, //
+ @Meta( info = "%s = card", description = "Cannot open card %s")
+ CLI_ERR_CANNOT_OPEN, //
+ @Meta(info = "%s = contact FN", description = "Cannot save photo of contact %s")
+ CLI_ERR_CANNOT_SAVE_PHOTO, //
+ @Meta( description = "Cannot start the program with the given cards")
+ CLI_ERR_CANNOT_START, //
+};
+++ /dev/null
-package be.nikiroo.jvcard.resources;
-
-import java.awt.Image;
-import java.awt.geom.AffineTransform;
-import java.awt.image.AffineTransformOp;
-import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.text.Normalizer;
-import java.text.Normalizer.Form;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.regex.Pattern;
-
-import javax.imageio.ImageIO;
-import javax.xml.bind.DatatypeConverter;
-
-import com.googlecode.lanterna.gui2.LinearLayout.Alignment;
-
-public class StringUtils {
- static private Pattern marks = Pattern
- .compile("[\\p{InCombiningDiacriticalMarks}\\p{IsLm}\\p{IsSk}]+");
-
- /**
- * Fix the size of the given {@link String} either with space-padding or by
- * shortening it.
- *
- * @param text
- * the {@link String} to fix
- * @param width
- * the size of the resulting {@link String} or -1 for a noop
- *
- * @return the resulting {@link String} of size <i>size</i>
- */
- static public String padString(String text, int width) {
- return padString(text, width, true, Alignment.Beginning);
- }
-
- /**
- * Fix the size of the given {@link String} either with space-padding or by
- * optionally shortening it.
- *
- * @param text
- * the {@link String} to fix
- * @param width
- * the size of the resulting {@link String} if the text fits or
- * if cut is TRUE or -1 for a noop
- * @param cut
- * cut the {@link String} shorter if needed
- * @param align
- * align the {@link String} in this position if we have enough
- * space
- *
- * @return the resulting {@link String} of size <i>size</i> minimum
- */
- static public String padString(String text, int width, boolean cut,
- Alignment align) {
-
- if (width >= 0) {
- if (text == null)
- text = "";
-
- int diff = width - text.length();
-
- if (diff < 0) {
- if (cut)
- text = text.substring(0, width);
- } else if (diff > 0) {
- if (diff < 2 && align != Alignment.End)
- align = Alignment.Beginning;
-
- switch (align) {
- case Beginning:
- text = text + new String(new char[diff]).replace('\0', ' ');
- break;
- case End:
- text = new String(new char[diff]).replace('\0', ' ') + text;
- break;
- case Center:
- case Fill:
- default:
- int pad1 = (diff) / 2;
- int pad2 = (diff + 1) / 2;
- text = new String(new char[pad1]).replace('\0', ' ') + text
- + new String(new char[pad2]).replace('\0', ' ');
- break;
- }
- }
- }
-
- return text;
- }
-
- /**
- * Sanitise the given input to make it more Terminal-friendly by removing
- * combining characters.
- *
- * @param input
- * the input to sanitise
- * @param allowUnicode
- * allow Unicode or only allow ASCII Latin characters
- *
- * @return the sanitised {@link String}
- */
- static public String sanitize(String input, boolean allowUnicode) {
- return sanitize(input, allowUnicode, !allowUnicode);
- }
-
- /**
- * Sanitise the given input to make it more Terminal-friendly by removing
- * combining characters.
- *
- * @param input
- * the input to sanitise
- * @param allowUnicode
- * allow Unicode or only allow ASCII Latin characters
- * @param removeAllAccents
- * TRUE to replace all accentuated characters by their non
- * accentuated counter-parts
- *
- * @return the sanitised {@link String}
- */
- static public String sanitize(String input, boolean allowUnicode,
- boolean removeAllAccents) {
-
- if (removeAllAccents) {
- input = Normalizer.normalize(input, Form.NFKD);
- input = marks.matcher(input).replaceAll("");
- }
-
- input = Normalizer.normalize(input, Form.NFKC);
-
- if (!allowUnicode) {
- StringBuilder builder = new StringBuilder();
- for (int index = 0; index < input.length(); index++) {
- char car = input.charAt(index);
- // displayable chars in ASCII are in the range 32<->255,
- // except DEL (127)
- if (car >= 32 && car <= 255 && car != 127) {
- builder.append(car);
- }
- }
- input = builder.toString();
- }
-
- return input;
- }
-
- /**
- * Convert between time in milliseconds to {@link String} in a "static" way
- * (to exchange data over the wire, for instance).
- *
- * @param time
- * the time in milliseconds
- *
- * @return the time as a {@link String}
- */
- static public String fromTime(long time) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- return sdf.format(new Date(time));
- }
-
- /**
- * Convert between time as a {@link String} to milliseconds in a "static"
- * way (to exchange data over the wire, for instance).
- *
- * @param time
- * the time as a {@link String}
- *
- * @return the time in milliseconds
- */
- static public long toTime(String display) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- try {
- return sdf.parse(display).getTime();
- } catch (ParseException e) {
- return -1;
- }
- }
-
- /**
- * Convert the given {@link Image} object into a Base64 representation of
- * the same {@link Image}. object.
- *
- * @param image
- * the {@link Image} object to convert
- *
- * @return the Base64 representation
- *
- * @throws IOException
- * in case of IO error
- */
- static public String fromImage(BufferedImage image) throws IOException {
- String imageString = null;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- ImageIO.write(image, "jpeg", out);
- byte[] imageBytes = out.toByteArray();
-
- imageString = DatatypeConverter.printBase64Binary(imageBytes);
-
- out.close();
-
- return imageString;
- }
-
- /**
- * Convert the given {@link File} image into a Base64 representation of the
- * same {@link File}.
- *
- * @param file
- * the {@link File} image to convert
- *
- * @return the Base64 representation
- *
- * @throws IOException
- * in case of IO error
- */
- static public String fromImage(File file) throws IOException {
- String fileString = null;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- byte[] buf = new byte[8192];
- InputStream in = new FileInputStream(file);
-
- int c = 0;
- while ((c = in.read(buf, 0, buf.length)) > 0) {
- out.write(buf, 0, c);
- }
- out.flush();
- in.close();
-
- fileString = DatatypeConverter.printBase64Binary(out.toByteArray());
- out.close();
-
- return fileString;
- }
-
- /**
- * Convert the given Base64 representation of an image into an {@link Image}
- * object.
- *
- * @param b64data
- * the {@link Image} in Base64 format
- *
- * @return the {@link Image} object
- *
- * @throws IOException
- * in case of IO error
- */
- static public BufferedImage toImage(String b64data) throws IOException {
- ByteArrayInputStream in = new ByteArrayInputStream(
- DatatypeConverter.parseBase64Binary(b64data));
-
- int orientation;
- try {
- orientation = getExifTransorm(in);
- } catch (Exception e) {
- // no EXIF transform, ok
- orientation = -1;
- }
-
- in.reset();
- BufferedImage image = ImageIO.read(in);
-
- // Note: this code has been found on internet;
- // thank you anonymous coder.
- int width = image.getWidth();
- int height = image.getHeight();
- AffineTransform affineTransform = new AffineTransform();
-
- switch (orientation) {
- case 1:
- break;
- case 2: // Flip X
- affineTransform.scale(-1.0, 1.0);
- affineTransform.translate(-width, 0);
- break;
- case 3: // PI rotation
- affineTransform.translate(width, height);
- affineTransform.rotate(Math.PI);
- break;
- case 4: // Flip Y
- affineTransform.scale(1.0, -1.0);
- affineTransform.translate(0, -height);
- break;
- case 5: // - PI/2 and Flip X
- affineTransform.rotate(-Math.PI / 2);
- affineTransform.scale(-1.0, 1.0);
- break;
- case 6: // -PI/2 and -width
- affineTransform.translate(height, 0);
- affineTransform.rotate(Math.PI / 2);
- break;
- case 7: // PI/2 and Flip
- affineTransform.scale(-1.0, 1.0);
- affineTransform.translate(-height, 0);
- affineTransform.translate(0, width);
- affineTransform.rotate(3 * Math.PI / 2);
- break;
- case 8: // PI / 2
- affineTransform.translate(0, width);
- affineTransform.rotate(3 * Math.PI / 2);
- break;
- default:
- affineTransform = null;
- break;
- }
-
- if (affineTransform != null) {
- AffineTransformOp affineTransformOp = new AffineTransformOp(
- affineTransform, AffineTransformOp.TYPE_BILINEAR);
-
- BufferedImage transformedImage = new BufferedImage(height, width,
- image.getType());
- transformedImage = affineTransformOp
- .filter(image, transformedImage);
-
- image = transformedImage;
- }
- //
-
- return image;
- }
-
- /**
- * Return a hash of the given {@link String}.
- *
- * @param input
- * the input data
- *
- * @return the hash
- */
- static public String getHash(String input) {
- try {
- MessageDigest md = MessageDigest.getInstance("MD5");
- md.update(input.getBytes());
- byte byteData[] = md.digest();
-
- StringBuffer hexString = new StringBuffer();
- for (int i = 0; i < byteData.length; i++) {
- String hex = Integer.toHexString(0xff & byteData[i]);
- if (hex.length() == 1)
- hexString.append('0');
- hexString.append(hex);
- }
-
- return hexString.toString();
- } catch (NoSuchAlgorithmException e) {
- // all JVM most probably have an MD5 implementation, but even if
- // not, returning the input is "correct", if inefficient and
- // unsecure
- return input;
- }
- }
-
- /**
- * Return the EXIF transformation flag of this image if any.
- *
- * <p>
- * Note: this code has been found on internet; thank you anonymous coder.
- * </p>
- *
- * @param in
- * the data {@link InputStream}
- *
- * @return the transformation flag if any
- *
- * @throws IOException
- * in case of IO error
- */
- static private int getExifTransorm(InputStream in) throws IOException {
- int[] exif_data = new int[100];
- int set_flag = 0;
- int is_motorola = 0;
-
- /* Read File head, check for JPEG SOI + Exif APP1 */
- for (int i = 0; i < 4; i++)
- exif_data[i] = in.read();
-
- if (exif_data[0] != 0xFF || exif_data[1] != 0xD8
- || exif_data[2] != 0xFF || exif_data[3] != 0xE1)
- return -2;
-
- /* Get the marker parameter length count */
- int length = (in.read() << 8 | in.read());
-
- /* Length includes itself, so must be at least 2 */
- /* Following Exif data length must be at least 6 */
- if (length < 8)
- return -1;
- length -= 8;
- /* Read Exif head, check for "Exif" */
- for (int i = 0; i < 6; i++)
- exif_data[i] = in.read();
-
- if (exif_data[0] != 0x45 || exif_data[1] != 0x78
- || exif_data[2] != 0x69 || exif_data[3] != 0x66
- || exif_data[4] != 0 || exif_data[5] != 0)
- return -1;
-
- /* Read Exif body */
- length = length > exif_data.length ? exif_data.length : length;
- for (int i = 0; i < length; i++)
- exif_data[i] = in.read();
-
- if (length < 12)
- return -1; /* Length of an IFD entry */
-
- /* Discover byte order */
- if (exif_data[0] == 0x49 && exif_data[1] == 0x49)
- is_motorola = 0;
- else if (exif_data[0] == 0x4D && exif_data[1] == 0x4D)
- is_motorola = 1;
- else
- return -1;
-
- /* Check Tag Mark */
- if (is_motorola == 1) {
- if (exif_data[2] != 0)
- return -1;
- if (exif_data[3] != 0x2A)
- return -1;
- } else {
- if (exif_data[3] != 0)
- return -1;
- if (exif_data[2] != 0x2A)
- return -1;
- }
-
- /* Get first IFD offset (offset to IFD0) */
- int offset;
- if (is_motorola == 1) {
- if (exif_data[4] != 0)
- return -1;
- if (exif_data[5] != 0)
- return -1;
- offset = exif_data[6];
- offset <<= 8;
- offset += exif_data[7];
- } else {
- if (exif_data[7] != 0)
- return -1;
- if (exif_data[6] != 0)
- return -1;
- offset = exif_data[5];
- offset <<= 8;
- offset += exif_data[4];
- }
- if (offset > length - 2)
- return -1; /* check end of data segment */
-
- /* Get the number of directory entries contained in this IFD */
- int number_of_tags;
- if (is_motorola == 1) {
- number_of_tags = exif_data[offset];
- number_of_tags <<= 8;
- number_of_tags += exif_data[offset + 1];
- } else {
- number_of_tags = exif_data[offset + 1];
- number_of_tags <<= 8;
- number_of_tags += exif_data[offset];
- }
- if (number_of_tags == 0)
- return -1;
- offset += 2;
-
- /* Search for Orientation Tag in IFD0 */
- for (;;) {
- if (offset > length - 12)
- return -1; /* check end of data segment */
- /* Get Tag number */
- int tagnum;
- if (is_motorola == 1) {
- tagnum = exif_data[offset];
- tagnum <<= 8;
- tagnum += exif_data[offset + 1];
- } else {
- tagnum = exif_data[offset + 1];
- tagnum <<= 8;
- tagnum += exif_data[offset];
- }
- if (tagnum == 0x0112)
- break; /* found Orientation Tag */
- if (--number_of_tags == 0)
- return -1;
- offset += 12;
- }
-
- /* Get the Orientation value */
- if (is_motorola == 1) {
- if (exif_data[offset + 8] != 0)
- return -1;
- set_flag = exif_data[offset + 9];
- } else {
- if (exif_data[offset + 9] != 0)
- return -1;
- set_flag = exif_data[offset + 8];
- }
- if (set_flag > 8)
- return -1;
-
- return set_flag;
- }
-}
--- /dev/null
+package be.nikiroo.jvcard.resources;
+
+/**
+ * The type of configuration information the associated {@link Bundle} will
+ * convey.
+ *
+ * @author niki
+ *
+ */
+public enum Target {
+ colors, display, jvcard, remote, resources
+}
\ No newline at end of file
--- /dev/null
+package be.nikiroo.jvcard.resources;
+
+
+/**
+ * This class manages the translation of {@link TransBundle.StringId}s into
+ * user-understandable text.
+ *
+ * @author niki
+ *
+ */
+public class TransBundle extends
+ be.nikiroo.utils.resources.TransBundle<StringId> {
+
+ /**
+ * Create a translation service with the default language.
+ */
+ public TransBundle() {
+ super(StringId.class, Target.resources);
+ }
+
+ /**
+ * Create a translation service for the given language. (Will fall back to
+ * the default one i not found.)
+ *
+ * @param language
+ * the language to use
+ */
+ public TransBundle(String language) {
+ super(StringId.class, Target.resources, language);
+ }
+}
+++ /dev/null
-package be.nikiroo.jvcard.resources.bundles;
-
-import java.io.IOException;
-import java.io.Writer;
-
-import be.nikiroo.jvcard.resources.Bundles;
-import be.nikiroo.jvcard.resources.Bundles.Bundle;
-import be.nikiroo.jvcard.resources.Bundles.Target;
-import be.nikiroo.jvcard.resources.enums.ColorOption;
-
-/**
- * All colour information must come from here.
- *
- * @author niki
- *
- */
-public class ColorBundle extends Bundle<ColorOption> {
- public ColorBundle() {
- new Bundles().super(ColorOption.class, Target.colors);
- }
-
- @Override
- protected void writeHeader(Writer writer) throws IOException {
- ColorOption.writeHeader(writer);
- }
-
- @Override
- protected void writeValue(Writer writer, ColorOption id) throws IOException {
- String name = id.name() + "_FG";
- String value = "";
- if (map.containsKey(name))
- value = map.getString(name).trim();
-
- writeValue(writer, name, value);
-
- name = id.name() + "_BG";
- value = "";
- if (map.containsKey(name))
- value = map.getString(name).trim();
-
- writeValue(writer, name, value);
- }
-}
+++ /dev/null
-package be.nikiroo.jvcard.resources.bundles;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Pattern;
-
-import be.nikiroo.jvcard.resources.Bundles;
-import be.nikiroo.jvcard.resources.Bundles.Bundle;
-import be.nikiroo.jvcard.resources.Bundles.Target;
-import be.nikiroo.jvcard.resources.ResourceList;
-import be.nikiroo.jvcard.resources.enums.StringId;
-
-/**
- * This class manages the translation of {@link TransBundle.StringId}s into
- * user-understandable text.
- *
- * @author niki
- *
- */
-public class TransBundle extends Bundle<StringId> {
- private boolean utf = true;
- private Locale locale;
- private boolean defaultLocale = false;
-
- /**
- * Create a translation service with the default language.
- */
- public TransBundle() {
- new Bundles().super(StringId.class, Target.resources);
- setLanguage(null);
- }
-
- /**
- * Create a translation service for the given language. (Will fall back to
- * the default one i not found.)
- *
- * @param language
- * the language to use
- */
- public TransBundle(String language) {
- new Bundles().super(StringId.class, Target.resources);
-
- setLanguage(language);
- }
-
- /**
- * Translate the given {@link StringId} into user text.
- *
- * @param stringId
- * the ID to translate
- * @param values
- * the values to insert instead of the place holders in the
- * translation
- *
- * @return the translated text with the given value where required
- */
- public String getString(StringId stringId, Object... values) {
- StringId id = stringId;
- String result = "";
-
- if (!isUnicode()) {
- try {
- id = StringId.valueOf(stringId.name() + "_NOUTF");
- } catch (IllegalArgumentException iae) {
- // no special _NOUTF version found
- }
- }
-
- if (id == StringId.NULL) {
- result = "";
- } else if (id == StringId.DUMMY) {
- result = "[dummy]";
- } else if (map.containsKey(id.name())) {
- result = map.getString(id.name());
- } else {
- result = id.toString();
- }
-
- if (values != null && values.length > 0)
- return String.format(locale, result, values);
- else
- return result;
- }
-
- /**
- * Check if unicode characters should be used.
- *
- * @return TRUE to allow unicode
- */
- public boolean isUnicode() {
- return utf;
- }
-
- /**
- * Allow or disallow unicode characters in the program.
- *
- * @param utf
- * TRUE to allow unuciode, FALSE to only allow ASCII characters
- */
- public void setUnicode(boolean utf) {
- this.utf = utf;
- }
-
- /**
- * Initialise the translation mappings for the given language.
- *
- * @param language
- * the language to initialise, in the form "en-GB" or "fr" for
- * instance
- */
- private void setLanguage(String language) {
- defaultLocale = (language == null || language.length() == 0);
- locale = getLocaleFor(language);
- map = getBundle(Target.resources, locale);
- }
-
- @Override
- public String getString(StringId id) {
- return getString(id, (Object[]) null);
- }
-
- @Override
- protected File getUpdateFile(String path) {
- String code = locale.toString();
- File file = null;
- if (!defaultLocale && code.length() > 0) {
- file = new File(path, name.name() + "_" + code + ".properties");
- } else {
- // Default properties file:
- file = new File(path, name.name() + ".properties");
- }
-
- return file;
- }
-
- @Override
- protected void writeHeader(Writer writer) throws IOException {
- String code = locale.toString();
- String name = locale.getDisplayCountry(locale);
-
- if (name.length() == 0)
- name = locale.getDisplayLanguage(locale);
- if (name.length() == 0)
- name = "default";
-
- if (code.length() > 0) {
- name = name + " (" + code + ")";
- }
-
- StringId.writeHeader(writer, name);
- }
-
- @Override
- protected void writeValue(Writer writer, StringId id) throws IOException {
- super.writeValue(writer, id);
-
- String name = id.name() + "_NOUTF";
- if (map.containsKey(name)) {
- String value = map.getString(name).trim();
- writeValue(writer, name, value);
- }
- }
-
- /**
- * Return the {@link Locale} representing the given language.
- *
- * @param language
- * the language to initialise, in the form "en-GB" or "fr" for
- * instance
- *
- * @return the corresponding {@link Locale} or the default {@link Locale} if
- * it is not known
- */
- static private Locale getLocaleFor(String language) {
- Locale locale;
-
- if (language == null) {
- locale = Locale.getDefault();
- } else {
- language = language.replaceAll("_", "-");
- String lang = language;
- String country = null;
- if (language.contains("-")) {
- lang = language.split("-")[0];
- country = language.split("-")[1];
- }
-
- if (country != null)
- locale = new Locale(lang, country);
- else
- locale = new Locale(lang);
- }
-
- return locale;
- }
-
- /**
- * Return all the languages known by the program.
- *
- * @return the known language codes
- */
- static public List<String> getKnownLanguages() {
- List<String> resources = new LinkedList<String>();
-
- String regex = ".*" + Target.resources.name()
- + "[_a-zA-Za]*\\.properties$";
-
- for (String res : ResourceList.getResources(Pattern.compile(regex))) {
- String resource = res;
- int index = resource.lastIndexOf('/');
- if (index >= 0 && index < (resource.length() - 1))
- resource = resource.substring(index + 1);
- if (resource.startsWith(Target.resources.name())) {
- resource = resource.substring(0, resource.length()
- - ".properties".length());
- resource = resource.substring(Target.resources.name().length());
- if (resource.startsWith("_")) {
- resource = resource.substring(1);
- resources.add(resource);
- }
- }
- }
-
- return resources;
- }
-}
#
-# (FORMAT: coma-separated list of CLF)
+# (FORMAT: comma-separated list of CLF)
# The format of each line in the contact list
CONTACT_LIST_FORMAT = NICKNAME@10|FN@+|EMAIL@30|PHOTO@x,N@[0]@20|N@[1]@+|EMAIL@40
# (FORMAT: CDIF)
+++ /dev/null
-package be.nikiroo.jvcard.resources.enums;
-
-import java.io.IOException;
-import java.io.Writer;
-
-import be.nikiroo.jvcard.resources.Bundles.Bundle;
-import be.nikiroo.jvcard.resources.Meta;
-
-/**
- * The enum representing textual information to be translated to the user as a
- * key.
- *
- * Note that each key that should be translated MUST be annotated with a
- * {@link Meta} annotation.
- *
- * @author niki
- *
- */
-public enum StringId {
- DUMMY, // <-- TODO : remove
- NULL, // Special usage, no annotations so it is not visible in
- // .properties files
- @Meta(what = "a key to press", where = "action keys", format = "MUST BE 3 chars long", info = "Tab key")
- KEY_TAB, // keys
- @Meta(what = "a key to press", where = "action keys", format = "MUST BE 3 chars long", info = "Enter key")
- KEY_ENTER, //
- @Meta(what = "Action key", where = "All screens except the first (KEY_ACTION_QUIT)", format = "", info = "Go back to previous screen")
- KEY_ACTION_BACK, //
- @Meta(what = "Action key", where = "MainWindow", format = "", info = "Get help text")
- KEY_ACTION_HELP, //
- @Meta(what = "Action key", where = "FileList", format = "", info = "View the selected card")
- KEY_ACTION_VIEW_CARD, //
- @Meta(what = "Action key", where = "ContactList", format = "", info = "View the selected contact")
- KEY_ACTION_VIEW_CONTACT, //
- @Meta(what = "Action key", where = "ContactDetails", format = "", info = "Edit the contact")
- KEY_ACTION_EDIT_CONTACT, //
- @Meta(what = "Action key", where = "ContactDetails", format = "", info = "Edit the contact in RAW mode")
- KEY_ACTION_EDIT_CONTACT_RAW, //
- @Meta(what = "Action key", where = "ContactDetailsRaw", format = "", info = "Edit the RAW field")
- KEY_ACTION_EDIT_FIELD, //
- @Meta(what = "Action key", where = "ContactList", format = "", info = "Save the whole card")
- KEY_ACTION_SAVE_CARD, //
- @Meta(what = "", where = "ContactList/ContactDetailsRaw", format = "", info = "Delete the selected element")
- KEY_ACTION_DELETE, //
- @Meta(what = "Action key", where = "ContactList", format = "", info = "Filter the displayed contacts")
- KEY_ACTION_SEARCH, //
- @Meta(what = "", where = "", format = "we could use: ' ', ┃, │...", info = "Field separator")
- DEAULT_FIELD_SEPARATOR, // MainContentList
- @Meta(what = "Action key", where = "ContactDetails", format = "", info = "Invert the photo's colours")
- KEY_ACTION_INVERT, //
- @Meta(what = "Action key", where = "ContactDetails", format = "", info = "Show the photo in 'fullscreen'")
- KEY_ACTION_FULLSCREEN, //
- @Meta(what = "Action key", where = "ContactList, ContactDetails, ContactDetailsRaw", format = "", info = "Switch between the available display formats")
- KEY_ACTION_SWITCH_FORMAT, // multi-usage
- @Meta(what = "Action key", where = "Contact list, Edit Contact", format = "", info = "Add a new contact/field")
- KEY_ACTION_ADD, //
- @Meta(what = "User question: TEXT", where = "Contact list", format = "", info = "New contact")
- ASK_USER_CONTACT_NAME, //
- @Meta(what = "User question: [Y|N]", where = "Contact list", format = "%s = contact name", info = "Delete contact")
- CONFIRM_USER_DELETE_CONTACT, //
- @Meta(what = "Error", where = "Contact list", format = "%s = contact name", info = "cannot delete a contact")
- ERR_CANNOT_DELETE_CONTACT, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message header line")
- CLI_HELP, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line before explaining the different modes")
- CLI_HELP_MODES, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for help usage")
- CLI_HELP_MODE_HELP, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for contact manager usage")
- CLI_HELP_MODE_CONTACT_MANAGER, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for contact manager usage")
- CLI_HELP_MODE_I18N, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for jVCard server usage")
- CLI_HELP_MODE_SERVER, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for --load-photo usage")
- CLI_HELP_MODE_LOAD_PHOTO, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for --save-photo usage")
- CLI_HELP_MODE_SAVE_PHOTO, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for config save usage")
- CLI_HELP_MODE_SAVE_CONFIG, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line before the list of options")
- CLI_HELP_OPTIONS, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for: --")
- CLI_HELP_DD, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for: --")
- CLI_HELP_LANG, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for: --")
- CLI_HELP_GUI, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for: --")
- CLI_HELP_TUI, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for: --")
- CLI_HELP_NOUTF_OPTION, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message line for: --")
- CLI_HELP_CONFIG, //
- @Meta(what = "CLI --help", where = "", format = "", info = "The Help message footer about files and jvcard:// links")
- CLI_HELP_FOOTER, //
- @Meta(what = "CLI ERROR", where = "", format = "%s = the error", info = "Syntax error: SOME TEXT")
- CLI_SERR, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "More than one mode given")
- CLI_SERR_MODES, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "--lang is required")
- CLI_SERR_NOLANG, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "The dir is required")
- CLI_SERR_NODIR, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "The port is required")
- CLI_SERR_NOPORT, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "The format is required")
- CLI_SERR_NOFORMAT, //
- @Meta(what = "CLI ERROR", where = "", format = "%s = bad port", info = "The port is not valid")
- CLI_SERR_BADPORT, //
- @Meta(what = "CLI ERROR", where = "", format = "%s = mode", info = "Card files are not supported in mode %s")
- CLI_SERR_CANNOT_CARDS, //
- @Meta(what = "CLI ERROR", where = "", format = "%s = the error", info = "Error: SOME TEXT")
- CLI_ERR, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "No files given")
- CLI_ERR_NOFILES, //
- @Meta(what = "CLI ERROR", where = "", format = "%s = dir", info = "Cannot create conf dir %s")
- CLI_ERR_CANNOT_CREATE_CONFDIR, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "Remoting not available")
- CLI_ERR_NO_REMOTING, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "TUI not available")
- CLI_ERR_NO_TUI, //
- @Meta(what = "CLI ERROR", where = "", format = "%s = dir", info = "Cannot create/update language in dir %s")
- CLI_ERR_CANNOT_CREATE_LANG, //
- @Meta(what = "CLI ERROR", where = "", format = "%s = card", info = "Cannot open card %s")
- CLI_ERR_CANNOT_OPEN, //
- @Meta(what = "CLI ERROR", where = "", format = "%s = contact FN", info = "Cannot save photo of contact %s")
- CLI_ERR_CANNOT_SAVE_PHOTO, //
- @Meta(what = "CLI ERROR", where = "", format = "", info = "Cannot start the program with the given cards")
- CLI_ERR_CANNOT_START, //
-
- ;
-
- /**
- * Write the header found in the configuration <tt>.properties</tt> file of
- * this {@link Bundle}.
- *
- * @param writer
- * the {@link Writer} to write the header in
- * @param name
- * the file name
- *
- * @throws IOException
- * in case of IO error
- */
- static public void writeHeader(Writer writer, String name)
- throws IOException {
- writer.write("# " + name + " translation file (UTF-8)\n");
- writer.write("# \n");
- writer.write("# Note that any key can be doubled with a _NOUTF suffix\n");
- writer.write("# to use when the flag --noutf is passed\n");
- writer.write("# \n");
- writer.write("# Also, the comments always refer to the key below them.\n");
- writer.write("# \n");
- }
-};
+++ /dev/null
-package be.nikiroo.jvcard.tui;
-
-import java.awt.Color;
-import java.awt.Graphics;
-import java.awt.Image;
-import java.awt.image.BufferedImage;
-import java.awt.image.ImageObserver;
-
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * This class converts an {@link Image} into a textual representation that can
- * be displayed to the user in a TUI.
- *
- * @author niki
- *
- */
-public class ImageText {
- private Image image;
- private TerminalSize size;
- private String text;
- private boolean ready;
- private Mode mode;
- private boolean invert;
-
- /**
- * Th rendering modes supported by this {@link ImageText} to convert
- * {@link Image}s into text.
- *
- * @author niki
- *
- */
- public enum Mode {
- /**
- * Use 5 different "colours" which are actually Unicode
- * {@link Character}s representing
- * <ul>
- * <li>space (blank)</li>
- * <li>low shade (░)</li>
- * <li>medium shade (▒)</li>
- * <li>high shade (▓)</li>
- * <li>full block (█)</li>
- * </ul>
- */
- DITHERING,
- /**
- * Use "block" Unicode {@link Character}s up to quarter blocks, thus in
- * effect doubling the resolution both in vertical and horizontal space.
- * Note that since 2 {@link Character}s next to each other are square,
- * we will use 4 blocks per 2 blocks for w/h resolution.
- */
- DOUBLE_RESOLUTION,
- /**
- * Use {@link Character}s from both {@link Mode#DOUBLE_RESOLUTION} and
- * {@link Mode#DITHERING}.
- */
- DOUBLE_DITHERING,
- /**
- * Only use ASCII {@link Character}s.
- */
- ASCII,
- }
-
- /**
- * Create a new {@link ImageText} with the given parameters. Defaults to
- * {@link Mode#DOUBLE_DITHERING} and no colour inversion.
- *
- * @param image
- * the source {@link Image}
- * @param size
- * the final text size to target
- */
- public ImageText(Image image, TerminalSize size) {
- this(image, size, Mode.DOUBLE_DITHERING, false);
- }
-
- /**
- * Create a new {@link ImageText} with the given parameters.
- *
- * @param image
- * the source {@link Image}
- * @param size
- * the final text size to target
- * @param mode
- * the mode of conversion
- * @param invert
- * TRUE to invert colours rendering
- */
- public ImageText(Image image, TerminalSize size, Mode mode, boolean invert) {
- setImage(image);
- setSize(size);
- setMode(mode);
- setColorInvert(invert);
- }
-
- /**
- * Change the source {@link Image}.
- *
- * @param image
- * the new {@link Image}
- */
- public void setImage(Image image) {
- this.text = null;
- this.ready = false;
- this.image = image;
- }
-
- /**
- * Change the target size of this {@link ImageText}.
- *
- * @param size
- * the new size
- */
- public void setSize(TerminalSize size) {
- this.text = null;
- this.ready = false;
- this.size = size;
- }
-
- /**
- * Change the image-to-text mode.
- *
- * @param mode
- * the new {@link Mode}
- */
- public void setMode(Mode mode) {
- this.mode = mode;
- this.text = null;
- this.ready = false;
- }
-
- /**
- * Set the colour-invert mode.
- *
- * @param invert
- * TRUE to inverse the colours
- */
- public void setColorInvert(boolean invert) {
- this.invert = invert;
- this.text = null;
- this.ready = false;
- }
-
- /**
- * Check if the colours are inverted.
- *
- * @return TRUE if the colours are inverted
- */
- public boolean isColorInvert() {
- return invert;
- }
-
- /**
- * Return the textual representation of the included {@link Image}.
- *
- * @return the {@link String} representation
- */
- public String getText() {
- if (text == null) {
- if (image == null || size == null || size.getColumns() == 0
- || size.getRows() == 0)
- return "";
-
- int mult = 1;
- if (mode == Mode.DOUBLE_RESOLUTION || mode == Mode.DOUBLE_DITHERING)
- mult = 2;
-
- int w = size.getColumns() * mult;
- int h = size.getRows() * mult;
-
- BufferedImage buff = new BufferedImage(w, h,
- BufferedImage.TYPE_INT_ARGB);
-
- Graphics gfx = buff.getGraphics();
-
- TerminalSize srcSize = getSize(image);
- srcSize = new TerminalSize(srcSize.getColumns() * 2,
- srcSize.getRows());
- int x = 0;
- int y = 0;
-
- if (srcSize.getColumns() < srcSize.getRows()) {
- double ratio = (double) size.getColumns()
- / (double) size.getRows();
- ratio *= (double) srcSize.getRows()
- / (double) srcSize.getColumns();
-
- h = (int) Math.round(ratio * h);
- y = (buff.getHeight() - h) / 2;
- } else {
- double ratio = (double) size.getRows()
- / (double) size.getColumns();
- ratio *= (double) srcSize.getColumns()
- / (double) srcSize.getRows();
-
- w = (int) Math.round(ratio * w);
- x = (buff.getWidth() - w) / 2;
- }
-
- if (gfx.drawImage(image, x, y, w, h, new ImageObserver() {
- @Override
- public boolean imageUpdate(Image img, int infoflags, int x,
- int y, int width, int height) {
- ImageText.this.ready = true;
- return true;
- }
- })) {
- ready = true;
- }
-
- while (!ready) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- }
-
- gfx.dispose();
-
- StringBuilder builder = new StringBuilder();
-
- for (int row = 0; row < buff.getHeight(); row += mult) {
- if (row > 0)
- builder.append('\n');
-
- for (int col = 0; col < buff.getWidth(); col += mult) {
- if (mult == 1) {
- char car = ' ';
- float brightness = getBrightness(buff.getRGB(col, row));
- if (mode == Mode.DITHERING)
- car = getDitheringChar(brightness, " ░▒▓█");
- if (mode == Mode.ASCII)
- car = getDitheringChar(brightness, " .-+=o8#");
-
- builder.append(car);
- } else if (mult == 2) {
- builder.append(getBlockChar( //
- buff.getRGB(col, row),//
- buff.getRGB(col + 1, row),//
- buff.getRGB(col, row + 1),//
- buff.getRGB(col + 1, row + 1),//
- mode == Mode.DOUBLE_DITHERING//
- ));
- }
- }
- }
-
- text = builder.toString();
- }
-
- return text;
- }
-
- @Override
- public String toString() {
- return getText();
- }
-
- /**
- * Return the size of the given {@link Image}.
- *
- * @param img
- * the image to measure
- *
- * @return the size
- */
- static private TerminalSize getSize(Image img) {
- TerminalSize size = null;
- while (size == null) {
- int w = img.getWidth(null);
- int h = img.getHeight(null);
- if (w > -1 && h > -1) {
- size = new TerminalSize(w, h);
- } else {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- }
- }
- }
-
- return size;
- }
-
- /**
- * Return the {@link Character} corresponding to the given brightness level
- * from the evenly-separated given {@link Character}s.
- *
- * @param brightness
- * the brightness level
- * @param cars
- * the {@link Character}s to choose from, from less bright to
- * most bright; <b>MUST</b> contain at least one
- * {@link Character}
- *
- * @return the {@link Character} to use
- */
- private char getDitheringChar(float brightness, String cars) {
- int index = Math.round(brightness * (cars.length() - 1));
- return cars.charAt(index);
- }
-
- /**
- * Return the {@link Character} corresponding to the 4 given colours in
- * {@link Mode#DOUBLE_RESOLUTION} or {@link Mode#DOUBLE_DITHERING} mode.
- *
- * @param upperleft
- * the upper left colour
- * @param upperright
- * the upper right colour
- * @param lowerleft
- * the lower left colour
- * @param lowerright
- * the lower right colour
- * @param dithering
- * TRUE to use {@link Mode#DOUBLE_DITHERING}, FALSE for
- * {@link Mode#DOUBLE_RESOLUTION}
- *
- * @return the {@link Character} to use
- */
- private char getBlockChar(int upperleft, int upperright, int lowerleft,
- int lowerright, boolean dithering) {
- int choice = 0;
- if (getBrightness(upperleft) > 0.5f)
- choice += 1;
- if (getBrightness(upperright) > 0.5f)
- choice += 2;
- if (getBrightness(lowerleft) > 0.5f)
- choice += 4;
- if (getBrightness(lowerright) > 0.5f)
- choice += 8;
-
- switch (choice) {
- case 0:
- return ' ';
- case 1:
- return '▘';
- case 2:
- return '▝';
- case 3:
- return '▀';
- case 4:
- return '▖';
- case 5:
- return '▌';
- case 6:
- return '▞';
- case 7:
- return '▛';
- case 8:
- return '▗';
- case 9:
- return '▚';
- case 10:
- return '▐';
- case 11:
- return '▜';
- case 12:
- return '▄';
- case 13:
- return '▙';
- case 14:
- return '▟';
- case 15:
- if (dithering) {
- float avg = 0;
- avg += getBrightness(upperleft);
- avg += getBrightness(upperright);
- avg += getBrightness(lowerleft);
- avg += getBrightness(lowerright);
- avg /= 4;
-
- return getDitheringChar(avg, " ░▒▓█");
- } else {
- return '█';
- }
- }
-
- return ' ';
- }
-
- /**
- * Temporary array used so not to create a lot of new ones.
- */
- private float[] tmp = new float[4];
-
- /**
- * Return the brightness value to use from the given ARGB colour.
- *
- * @param argb
- * the argb colour
- *
- * @return the brightness to sue for computations
- */
- private float getBrightness(int argb) {
- if (invert)
- return 1 - rgb2hsb(argb, tmp)[2];
- return rgb2hsb(argb, tmp)[2];
- }
-
- /**
- * Convert the given ARGB colour in HSL/HSB, either into the supplied array
- * or into a new one if array is NULL.
- *
- * <p>
- * ARGB pixels are given in 0xAARRGGBB format, while the returned array will
- * contain Hue, Saturation, Lightness/Brightness, Alpha, in this order. H,
- * S, L and A are all ranging from 0 to 1 (indeed, H is in 1/360th).
- * </p>
- * pixel
- *
- * @param argb
- * the ARGB colour pixel to convert
- * @param array
- * the array to convert into or NULL to create a new one
- *
- * @return the array containing the HSL/HSB converted colour
- */
- static float[] rgb2hsb(int argb, float[] array) {
- int a, r, g, b;
- a = ((argb & 0xff000000) >> 24);
- r = ((argb & 0x00ff0000) >> 16);
- g = ((argb & 0x0000ff00) >> 8);
- b = ((argb & 0x000000ff));
-
- if (array == null)
- array = new float[4];
- Color.RGBtoHSB(r, g, b, array);
-
- array[3] = a;
-
- return array;
-
- // // other implementation:
- //
- // float a, r, g, b;
- // a = ((argb & 0xff000000) >> 24) / 255.0f;
- // r = ((argb & 0x00ff0000) >> 16) / 255.0f;
- // g = ((argb & 0x0000ff00) >> 8) / 255.0f;
- // b = ((argb & 0x000000ff)) / 255.0f;
- //
- // float rgbMin, rgbMax;
- // rgbMin = Math.min(r, Math.min(g, b));
- // rgbMax = Math.max(r, Math.max(g, b));
- //
- // float l;
- // l = (rgbMin + rgbMax) / 2;
- //
- // float s;
- // if (rgbMin == rgbMax) {
- // s = 0;
- // } else {
- // if (l <= 0.5) {
- // s = (rgbMax - rgbMin) / (rgbMax + rgbMin);
- // } else {
- // s = (rgbMax - rgbMin) / (2.0f - rgbMax - rgbMin);
- // }
- // }
- //
- // float h;
- // if (r > g && r > b) {
- // h = (g - b) / (rgbMax - rgbMin);
- // } else if (g > b) {
- // h = 2.0f + (b - r) / (rgbMax - rgbMin);
- // } else {
- // h = 4.0f + (r - g) / (rgbMax - rgbMin);
- // }
- // h /= 6; // from 0 to 1
- //
- // return new float[] { h, s, l, a };
- //
- // // // natural mode:
- // //
- // // int aa = (int) Math.round(100 * a);
- // // int hh = (int) (360 * h);
- // // if (hh < 0)
- // // hh += 360;
- // // int ss = (int) Math.round(100 * s);
- // // int ll = (int) Math.round(100 * l);
- // //
- // // return new int[] { hh, ss, ll, aa };
- }
-}
package be.nikiroo.jvcard.tui;
+import java.awt.Dimension;
import java.awt.Image;
import be.nikiroo.jvcard.launcher.Main;
-import be.nikiroo.jvcard.tui.ImageText.Mode;
+import be.nikiroo.utils.ImageText;
+import be.nikiroo.utils.ImageText.Mode;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.gui2.BorderLayout;
* A {@link Panel} containing an {@link ImageText} rendering.
*
* @author niki
- *
+ *
*/
public class ImageTextControl extends Panel {
private ImageText image;
this.setLayoutManager(new BorderLayout());
setSize(size);
- setImage(new ImageText(image, size, mode, false));
+ setImage(new ImageText(image, new Dimension(size.getColumns(),
+ size.getRows()), mode, false));
}
/**
@Override
public synchronized Panel setSize(TerminalSize size) {
if (image != null)
- image.setSize(size);
+ image.setSize(new Dimension(size.getColumns(), size.getRows()));
super.setSize(size);
import be.nikiroo.jvcard.Contact;
import be.nikiroo.jvcard.Data;
import be.nikiroo.jvcard.launcher.Main;
-import be.nikiroo.jvcard.resources.enums.StringId;
+import be.nikiroo.jvcard.resources.StringId;
import com.googlecode.lanterna.input.KeyStroke;
import com.googlecode.lanterna.input.KeyType;
import java.util.List;
import be.nikiroo.jvcard.launcher.Main;
-import be.nikiroo.jvcard.resources.StringUtils;
-import be.nikiroo.jvcard.resources.enums.ColorOption;
-import be.nikiroo.jvcard.resources.enums.StringId;
+import be.nikiroo.jvcard.resources.ColorOption;
+import be.nikiroo.jvcard.resources.StringId;
import be.nikiroo.jvcard.tui.KeyAction.Mode;
import be.nikiroo.jvcard.tui.panes.ContactDetails;
import be.nikiroo.jvcard.tui.panes.ContactDetailsRaw;
import be.nikiroo.jvcard.tui.panes.ContactList;
import be.nikiroo.jvcard.tui.panes.MainContent;
+import be.nikiroo.utils.StringUtils;
+import be.nikiroo.utils.Version;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.gui2.BasicWindow;
* Information about a question to ask the user and its answer.
*
* @author niki
- *
+ *
*/
private class UserQuestion {
private boolean oneKeyAnswer;
*/
private void setTitle() {
String prefix = " " + Main.APPLICATION_TITLE + " (version "
- + Main.APPLICATION_VERSION + ")";
+ + Version.getCurrentVersion() + ")";
String title = null;
int count = -1;
*
* @param key
* the key that was pressed
- * @param answer
- * the answer given for this key
*
* @return if the window handled the input
*/
/**
* Handle the input in case of "normal" (not "ask for answer") mode.
*
- * @param key
- * the key that was pressed
+ * @param action
+ * the key that was pressed and the action to take
* @param answer
* the answer given for this key
*
- * @return if the window handled the input
*/
private void handleAction(KeyAction action, String answer) {
MainContent content = getContent();
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
-import be.nikiroo.jvcard.resources.bundles.ColorBundle;
-import be.nikiroo.jvcard.resources.enums.ColorOption;
+import be.nikiroo.jvcard.resources.ColorBundle;
+import be.nikiroo.jvcard.resources.ColorOption;
+import be.nikiroo.jvcard.resources.Target;
+import be.nikiroo.utils.resources.Bundle;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.graphics.PropertiesTheme;
* @author niki
*
*/
-public class UiColors extends ColorBundle {
+public class UiColors {
static private Object lock = new Object();
static private UiColors instance = null;
private Map<String, TextColor> colorMap = null;
+ private ColorBundle bundle;
private UiColors() {
- super();
+ bundle = new ColorBundle();
colorMap = new HashMap<String, TextColor>();
}
if (!getInstance().colorMap.containsKey(el.name() + "_BG")) {
String value = null;
try {
- value = getInstance().map.getString(el.name() + "_BG");
+ value = getInstance().bundle.getStringX(el, "BG");
} catch (MissingResourceException mre) {
value = null;
}
if (!getInstance().colorMap.containsKey(el.name() + "_FG")) {
String value = null;
try {
- value = getInstance().map.getString(el.name() + "_FG");
+ value = getInstance().bundle.getStringX(el, "FG");
} catch (MissingResourceException mre) {
value = null;
}
import be.nikiroo.jvcard.Contact;
import be.nikiroo.jvcard.Data;
import be.nikiroo.jvcard.TypeInfo;
-import be.nikiroo.jvcard.resources.StringUtils;
-import be.nikiroo.jvcard.resources.bundles.DisplayBundle;
-import be.nikiroo.jvcard.resources.enums.ColorOption;
-import be.nikiroo.jvcard.resources.enums.DisplayOption;
-import be.nikiroo.jvcard.resources.enums.StringId;
+import be.nikiroo.jvcard.resources.ColorOption;
+import be.nikiroo.jvcard.resources.DisplayBundle;
+import be.nikiroo.jvcard.resources.DisplayOption;
+import be.nikiroo.jvcard.resources.StringId;
import be.nikiroo.jvcard.tui.ImageTextControl;
import be.nikiroo.jvcard.tui.KeyAction;
import be.nikiroo.jvcard.tui.KeyAction.DataType;
import be.nikiroo.jvcard.tui.KeyAction.Mode;
import be.nikiroo.jvcard.tui.UiColors;
+import be.nikiroo.utils.ImageUtils;
+import be.nikiroo.utils.StringUtils;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.gui2.BorderLayout;
&& encoding.getValue().equalsIgnoreCase("b")) {
try {
- image = StringUtils.toImage(photo.getValue());
+ image = ImageUtils.fromBase64(photo.getValue());
} catch (Exception e) {
System.err.println("Cannot parse image for contact: "
+ contact.getPreferredDataValue("UID"));
import be.nikiroo.jvcard.Data;
import be.nikiroo.jvcard.TypeInfo;
import be.nikiroo.jvcard.launcher.Main;
-import be.nikiroo.jvcard.resources.StringUtils;
-import be.nikiroo.jvcard.resources.enums.ColorOption;
-import be.nikiroo.jvcard.resources.enums.StringId;
+import be.nikiroo.jvcard.resources.ColorOption;
+import be.nikiroo.jvcard.resources.StringId;
import be.nikiroo.jvcard.tui.KeyAction;
import be.nikiroo.jvcard.tui.KeyAction.DataType;
import be.nikiroo.jvcard.tui.KeyAction.Mode;
import be.nikiroo.jvcard.tui.TuiLauncher;
+import be.nikiroo.utils.StringUtils;
import com.googlecode.lanterna.gui2.MultiWindowTextGUI;
import com.googlecode.lanterna.gui2.dialogs.ActionListDialogBuilder;
import be.nikiroo.jvcard.Contact;
import be.nikiroo.jvcard.Data;
import be.nikiroo.jvcard.launcher.Main;
-import be.nikiroo.jvcard.resources.bundles.DisplayBundle;
-import be.nikiroo.jvcard.resources.enums.DisplayOption;
-import be.nikiroo.jvcard.resources.enums.ColorOption;
-import be.nikiroo.jvcard.resources.enums.StringId;
+import be.nikiroo.jvcard.resources.ColorOption;
+import be.nikiroo.jvcard.resources.DisplayBundle;
+import be.nikiroo.jvcard.resources.DisplayOption;
+import be.nikiroo.jvcard.resources.StringId;
import be.nikiroo.jvcard.tui.KeyAction;
import be.nikiroo.jvcard.tui.KeyAction.DataType;
import be.nikiroo.jvcard.tui.KeyAction.Mode;
import be.nikiroo.jvcard.launcher.CardResult.MergeCallback;
import be.nikiroo.jvcard.launcher.Main;
import be.nikiroo.jvcard.parsers.Format;
-import be.nikiroo.jvcard.resources.StringUtils;
-import be.nikiroo.jvcard.resources.enums.ColorOption;
-import be.nikiroo.jvcard.resources.enums.StringId;
+import be.nikiroo.jvcard.resources.ColorOption;
+import be.nikiroo.jvcard.resources.StringId;
import be.nikiroo.jvcard.tui.KeyAction;
import be.nikiroo.jvcard.tui.KeyAction.DataType;
import be.nikiroo.jvcard.tui.KeyAction.Mode;
+import be.nikiroo.utils.StringUtils;
import com.googlecode.lanterna.input.KeyType;
import java.util.List;
import be.nikiroo.jvcard.launcher.Main;
-import be.nikiroo.jvcard.resources.StringUtils;
-import be.nikiroo.jvcard.resources.enums.ColorOption;
-import be.nikiroo.jvcard.resources.enums.StringId;
+import be.nikiroo.jvcard.resources.ColorOption;
+import be.nikiroo.jvcard.resources.StringId;
import be.nikiroo.jvcard.tui.UiColors;
+import be.nikiroo.utils.StringUtils;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.gui2.AbstractListBox.ListItemRenderer;
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna;
-
-/**
- * Utilities class for analyzing and working with CJK (Chinese, Japanese, Korean) characters. The main purpose of this
- * class is to assist in figuring out how many terminal columns a character (and in extension, a String) takes up. The
- * main issue is that while most latin (and latin-related) character can be trusted to consume one column in the
- * terminal, CJK characters tends to take two, partly due to the square nature of the characters but mostly due to the
- * fact that they require most space to distinguish.
- *
- * @author Martin
- * @see TerminalTextUtils
- * @deprecated Use {@code TerminalTextUtils} instead
- */
-public class CJKUtils {
- private CJKUtils() {
- }
-
- /**
- * Given a character, is this character considered to be a CJK character?
- * Shamelessly stolen from
- * <a href="http://stackoverflow.com/questions/1499804/how-can-i-detect-japanese-text-in-a-java-string">StackOverflow</a>
- * where it was contributed by user Rakesh N
- * @param c Character to test
- * @return {@code true} if the character is a CJK character
- * @deprecated Use {@code TerminalTextUtils.isCharJCK(c)} instead
- * @see TerminalTextUtils#isCharCJK(char)
- */
- @Deprecated
- public static boolean isCharCJK(final char c) {
- return TerminalTextUtils.isCharCJK(c);
- }
-
- /**
- * @deprecated Call {@code getColumnWidth(s)} instead
- */
- @Deprecated
- public static int getTrueWidth(String s) {
- return TerminalTextUtils.getColumnWidth(s);
- }
-
- /**
- * Given a string, returns how many columns this string would need to occupy in a terminal, taking into account that
- * CJK characters takes up two columns.
- * @param s String to check length
- * @return Number of actual terminal columns the string would occupy
- * @deprecated Use {@code TerminalTextUtils.getColumnWidth(s)} instead
- * @see TerminalTextUtils#getColumnWidth(String)
- */
- @Deprecated
- public static int getColumnWidth(String s) {
- return TerminalTextUtils.getColumnIndex(s, s.length());
- }
-
- /**
- * Given a string and a character index inside that string, find out what the column index of that character would
- * be if printed in a terminal. If the string only contains non-CJK characters then the returned value will be same
- * as {@code stringCharacterIndex}, but if there are CJK characters the value will be different due to CJK
- * characters taking up two columns in width. If the character at the index in the string is a CJK character itself,
- * the returned value will be the index of the left-side of character.
- * @param s String to translate the index from
- * @param stringCharacterIndex Index within the string to get the terminal column index of
- * @return Index of the character inside the String at {@code stringCharacterIndex} when it has been writted to a
- * terminal
- * @throws StringIndexOutOfBoundsException if the index given is outside the String length or negative
- * @deprecated Use {@code TerminalTextUtils.getColumnIndex(s, stringCharacterIndex)} instead
- * @see TerminalTextUtils#getColumnIndex(String, int)
- */
- @Deprecated
- public static int getColumnIndex(String s, int stringCharacterIndex) throws StringIndexOutOfBoundsException {
- return TerminalTextUtils.getColumnIndex(s, stringCharacterIndex);
- }
-
- /**
- * This method does the reverse of getColumnIndex, given a String and imagining it has been printed out to the
- * top-left corner of a terminal, in the column specified by {@code columnIndex}, what is the index of that
- * character in the string. If the string contains no CJK characters, this will always be the same as
- * {@code columnIndex}. If the index specified is the right column of a CJK character, the index is the same as if
- * the column was the left column. So calling {@code getStringCharacterIndex("英", 0)} and
- * {@code getStringCharacterIndex("英", 1)} will both return 0.
- * @param s String to translate the index to
- * @param columnIndex Column index of the string written to a terminal
- * @return The index in the string of the character in terminal column {@code columnIndex}
- * @deprecated Use {@code TerminalTextUtils.getStringCharacterIndex(s, columnIndex} instead
- * @see TerminalTextUtils#getStringCharacterIndex(String, int)
- */
- @Deprecated
- public static int getStringCharacterIndex(String s, int columnIndex) {
- return TerminalTextUtils.getStringCharacterIndex(s, columnIndex);
- }
-
- /**
- * Given a string that may or may not contain CJK characters, returns the substring which will fit inside
- * <code>availableColumnSpace</code> columns. This method does not handle special cases like tab or new-line.
- * <p>
- * Calling this method is the same as calling {@code fitString(string, 0, availableColumnSpace)}.
- * @param string The string to fit inside the availableColumnSpace
- * @param availableColumnSpace Number of columns to fit the string inside
- * @return The whole or part of the input string which will fit inside the supplied availableColumnSpace
- * @deprecated Use {@code TerminalTextUtils.fitString(string, availableColumnSpace)} instead
- * @see TerminalTextUtils#fitString(String, int)
- */
- @Deprecated
- public static String fitString(String string, int availableColumnSpace) {
- return TerminalTextUtils.fitString(string, availableColumnSpace);
- }
-
- /**
- * Given a string that may or may not contain CJK characters, returns the substring which will fit inside
- * <code>availableColumnSpace</code> columns. This method does not handle special cases like tab or new-line.
- * <p>
- * This overload has a {@code fromColumn} parameter that specified where inside the string to start fitting. Please
- * notice that {@code fromColumn} is not a character index inside the string, but a column index as if the string
- * has been printed from the left-most side of the terminal. So if the string is "日本語", fromColumn set to 1 will
- * not starting counting from the second character ("本") in the string but from the CJK filler character belonging
- * to "日". If you want to count from a particular character index inside the string, please pass in a substring
- * and use fromColumn set to 0.
- * @param string The string to fit inside the availableColumnSpace
- * @param fromColumn From what column of the input string to start fitting (see description above!)
- * @param availableColumnSpace Number of columns to fit the string inside
- * @return The whole or part of the input string which will fit inside the supplied availableColumnSpace
- * @deprecated Use {@code TerminalTextUtils.fitString(string, fromColumn, availableColumnSpace)} instead
- * @see TerminalTextUtils#fitString(String, int, int)
- */
- @Deprecated
- public static String fitString(String string, int fromColumn, int availableColumnSpace) {
- return TerminalTextUtils.fitString(string, fromColumn, availableColumnSpace);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna;
-
-/**
- * SGR - Select Graphic Rendition, changes the state of the terminal as to what kind of text to print after this
- * command. When working with the Terminal interface, its keeping a state of which SGR codes are active, so activating
- * one of these codes will make it apply to all text until you explicitly deactivate it. When you work with Screen and
- * GUI systems, usually the SGR is a property of an independent character and won't affect others.
- */
-public enum SGR {
- /**
- * Bold text mode. Please note that on some terminal implementations, instead of (or in addition to) making the text
- * bold, it will draw the text in a slightly different color
- */
- BOLD,
-
- /**
- * Reverse text mode, will flip the foreground and background colors while active
- */
- REVERSE,
-
- /**
- * Draws a horizontal line under the text. Not widely supported.
- */
- UNDERLINE,
-
- /**
- * Text will blink on the screen by alternating the foreground color between the real foreground color and the
- * background color. Not widely supported.
- */
- BLINK,
-
- /**
- * Draws a border around the text. Rarely supported.
- */
- BORDERED,
-
- /**
- * I have no idea, exotic extension, please send me a reference screen shots!
- */
- FRAKTUR,
-
- /**
- * Draws a horizontal line through the text. Rarely supported.
- */
- CROSSED_OUT,
-
- /**
- * Draws a circle around the text. Rarely supported.
- */
- CIRCLED,
- ;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-
-package com.googlecode.lanterna;
-
-/**
- * Some text graphics, taken from http://en.wikipedia.org/wiki/Codepage_437 but converted to its UTF-8 counterpart.
- * This class it mostly here to help out with building text GUIs when you don't have a handy Unicode chart available.
- * Previously this class was known as ACS, which was taken from ncurses (meaning "Alternative Character Set").
- * @author martin
- */
-public class Symbols {
- private Symbols() {}
-
- /**
- * ☺
- */
- public static final char FACE_WHITE = 0x263A;
- /**
- * ☻
- */
- public static final char FACE_BLACK = 0x263B;
- /**
- * ♥
- */
- public static final char HEART = 0x2665;
- /**
- * ♣
- */
- public static final char CLUB = 0x2663;
- /**
- * ♦
- */
- public static final char DIAMOND = 0x2666;
- /**
- * ♠
- */
- public static final char SPADES = 0x2660;
- /**
- * •
- */
- public static final char BULLET = 0x2022;
- /**
- * ◘
- */
- public static final char INVERSE_BULLET = 0x25d8;
- /**
- * ○
- */
- public static final char WHITE_CIRCLE = 0x25cb;
- /**
- * ◙
- */
- public static final char INVERSE_WHITE_CIRCLE = 0x25d9;
-
- /**
- * ■
- */
- public static final char SOLID_SQUARE = 0x25A0;
- /**
- * ▪
- */
- public static final char SOLID_SQUARE_SMALL = 0x25AA;
- /**
- * □
- */
- public static final char OUTLINED_SQUARE = 0x25A1;
- /**
- * ▫
- */
- public static final char OUTLINED_SQUARE_SMALL = 0x25AB;
-
- /**
- * ♀
- */
- public static final char FEMALE = 0x2640;
- /**
- * ♂
- */
- public static final char MALE = 0x2642;
-
- /**
- * ↑
- */
- public static final char ARROW_UP = 0x2191;
- /**
- * ↓
- */
- public static final char ARROW_DOWN = 0x2193;
- /**
- * →
- */
- public static final char ARROW_RIGHT = 0x2192;
- /**
- * ←
- */
- public static final char ARROW_LEFT = 0x2190;
-
- /**
- * █
- */
- public static final char BLOCK_SOLID = 0x2588;
- /**
- * ▓
- */
- public static final char BLOCK_DENSE = 0x2593;
- /**
- * ▒
- */
- public static final char BLOCK_MIDDLE = 0x2592;
- /**
- * ░
- */
- public static final char BLOCK_SPARSE = 0x2591;
-
- /**
- * ⏴
- */
- public static final char TRIANGLE_RIGHT_POINTING_MEDIUM_BLACK = 0x23F4;
- /**
- * ⏵
- */
- public static final char TRIANGLE_LEFT_POINTING_MEDIUM_BLACK = 0x23F5;
- /**
- * ⏶
- */
- public static final char TRIANGLE_UP_POINTING_MEDIUM_BLACK = 0x23F6;
- /**
- * ⏷
- */
- public static final char TRIANGLE_DOWN_POINTING_MEDIUM_BLACK = 0x23F7;
-
-
- /**
- * ─
- */
- public static final char SINGLE_LINE_HORIZONTAL = 0x2500;
- /**
- * ━
- */
- public static final char BOLD_SINGLE_LINE_HORIZONTAL = 0x2501;
- /**
- * ╾
- */
- public static final char BOLD_TO_NORMAL_SINGLE_LINE_HORIZONTAL = 0x257E;
- /**
- * ╼
- */
- public static final char BOLD_FROM_NORMAL_SINGLE_LINE_HORIZONTAL = 0x257C;
- /**
- * ═
- */
- public static final char DOUBLE_LINE_HORIZONTAL = 0x2550;
- /**
- * │
- */
- public static final char SINGLE_LINE_VERTICAL = 0x2502;
- /**
- * ┃
- */
- public static final char BOLD_SINGLE_LINE_VERTICAL = 0x2503;
- /**
- * ╿
- */
- public static final char BOLD_TO_NORMAL_SINGLE_LINE_VERTICAL = 0x257F;
- /**
- * ╽
- */
- public static final char BOLD_FROM_NORMAL_SINGLE_LINE_VERTICAL = 0x257D;
- /**
- * ║
- */
- public static final char DOUBLE_LINE_VERTICAL = 0x2551;
-
- /**
- * ┌
- */
- public static final char SINGLE_LINE_TOP_LEFT_CORNER = 0x250C;
- /**
- * ╔
- */
- public static final char DOUBLE_LINE_TOP_LEFT_CORNER = 0x2554;
- /**
- * ┐
- */
- public static final char SINGLE_LINE_TOP_RIGHT_CORNER = 0x2510;
- /**
- * ╗
- */
- public static final char DOUBLE_LINE_TOP_RIGHT_CORNER = 0x2557;
-
- /**
- * └
- */
- public static final char SINGLE_LINE_BOTTOM_LEFT_CORNER = 0x2514;
- /**
- * ╚
- */
- public static final char DOUBLE_LINE_BOTTOM_LEFT_CORNER = 0x255A;
- /**
- * ┘
- */
- public static final char SINGLE_LINE_BOTTOM_RIGHT_CORNER = 0x2518;
- /**
- * ╝
- */
- public static final char DOUBLE_LINE_BOTTOM_RIGHT_CORNER = 0x255D;
-
- /**
- * ┼
- */
- public static final char SINGLE_LINE_CROSS = 0x253C;
- /**
- * ╬
- */
- public static final char DOUBLE_LINE_CROSS = 0x256C;
- /**
- * ╪
- */
- public static final char DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS = 0x256A;
- /**
- * ╫
- */
- public static final char DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS = 0x256B;
-
- /**
- * ┴
- */
- public static final char SINGLE_LINE_T_UP = 0x2534;
- /**
- * ┬
- */
- public static final char SINGLE_LINE_T_DOWN = 0x252C;
- /**
- * ├
- */
- public static final char SINGLE_LINE_T_RIGHT = 0x251c;
- /**
- * ┤
- */
- public static final char SINGLE_LINE_T_LEFT = 0x2524;
-
- /**
- * ╨
- */
- public static final char SINGLE_LINE_T_DOUBLE_UP = 0x2568;
- /**
- * ╥
- */
- public static final char SINGLE_LINE_T_DOUBLE_DOWN = 0x2565;
- /**
- * ╞
- */
- public static final char SINGLE_LINE_T_DOUBLE_RIGHT = 0x255E;
- /**
- * ╡
- */
- public static final char SINGLE_LINE_T_DOUBLE_LEFT = 0x2561;
-
- /**
- * ╩
- */
- public static final char DOUBLE_LINE_T_UP = 0x2569;
- /**
- * ╦
- */
- public static final char DOUBLE_LINE_T_DOWN = 0x2566;
- /**
- * ╠
- */
- public static final char DOUBLE_LINE_T_RIGHT = 0x2560;
- /**
- * ╣
- */
- public static final char DOUBLE_LINE_T_LEFT = 0x2563;
-
- /**
- * ╧
- */
- public static final char DOUBLE_LINE_T_SINGLE_UP = 0x2567;
- /**
- * ╤
- */
- public static final char DOUBLE_LINE_T_SINGLE_DOWN = 0x2564;
- /**
- * ╟
- */
- public static final char DOUBLE_LINE_T_SINGLE_RIGHT = 0x255F;
- /**
- * ╢
- */
- public static final char DOUBLE_LINE_T_SINGLE_LEFT = 0x2562;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna;
-
-/**
- * A 2-d position in 'terminal space'. Please note that the coordinates are 0-indexed, meaning 0x0 is the top left
- * corner of the terminal. This object is immutable so you cannot change it after it has been created. Instead, you
- * can easily create modified 'clones' by using the 'with' methods.
- *
- * @author Martin
- */
-public class TerminalPosition {
-
- /**
- * Constant for the top-left corner (0x0)
- */
- public static final TerminalPosition TOP_LEFT_CORNER = new TerminalPosition(0, 0);
- /**
- * Constant for the 1x1 position (one offset in both directions from top-left)
- */
- public static final TerminalPosition OFFSET_1x1 = new TerminalPosition(1, 1);
-
- private final int row;
- private final int column;
-
- /**
- * Creates a new TerminalPosition object, which represents a location on the screen. There is no check to verify
- * that the position you specified is within the size of the current terminal and you can specify negative positions
- * as well.
- *
- * @param column Column of the location, or the "x" coordinate, zero indexed (the first column is 0)
- * @param row Row of the location, or the "y" coordinate, zero indexed (the first row is 0)
- */
- public TerminalPosition(int column, int row) {
- this.row = row;
- this.column = column;
- }
-
- /**
- * Returns the index of the column this position is representing, zero indexed (the first column has index 0).
- * @return Index of the column this position has
- */
- public int getColumn() {
- return column;
- }
-
- /**
- * Returns the index of the row this position is representing, zero indexed (the first row has index 0)
- * @return Index of the row this position has
- */
- public int getRow() {
- return row;
- }
-
- /**
- * Creates a new TerminalPosition object representing a position with the same column index as this but with a
- * supplied row index.
- * @param row Index of the row for the new position
- * @return A TerminalPosition object with the same column as this but with a specified row index
- */
- public TerminalPosition withRow(int row) {
- if(row == 0 && this.column == 0) {
- return TOP_LEFT_CORNER;
- }
- return new TerminalPosition(this.column, row);
- }
-
- /**
- * Creates a new TerminalPosition object representing a position with the same row index as this but with a
- * supplied column index.
- * @param column Index of the column for the new position
- * @return A TerminalPosition object with the same row as this but with a specified column index
- */
- public TerminalPosition withColumn(int column) {
- if(column == 0 && this.row == 0) {
- return TOP_LEFT_CORNER;
- }
- return new TerminalPosition(column, this.row);
- }
-
- /**
- * Creates a new TerminalPosition object representing a position on the same row, but with a column offset by a
- * supplied value. Calling this method with delta 0 will return this, calling it with a positive delta will return
- * a terminal position <i>delta</i> number of columns to the right and for negative numbers the same to the left.
- * @param delta Column offset
- * @return New terminal position based off this one but with an applied offset
- */
- public TerminalPosition withRelativeColumn(int delta) {
- if(delta == 0) {
- return this;
- }
- return withColumn(column + delta);
- }
-
- /**
- * Creates a new TerminalPosition object representing a position on the same column, but with a row offset by a
- * supplied value. Calling this method with delta 0 will return this, calling it with a positive delta will return
- * a terminal position <i>delta</i> number of rows to the down and for negative numbers the same up.
- * @param delta Row offset
- * @return New terminal position based off this one but with an applied offset
- */
- public TerminalPosition withRelativeRow(int delta) {
- if(delta == 0) {
- return this;
- }
- return withRow(row + delta);
- }
-
- /**
- * Creates a new TerminalPosition object that is 'translated' by an amount of rows and columns specified by another
- * TerminalPosition. Same as calling
- * <code>withRelativeRow(translate.getRow()).withRelativeColumn(translate.getColumn())</code>
- * @param translate How many columns and rows to translate
- * @return New TerminalPosition that is the result of the original with added translation
- */
- public TerminalPosition withRelative(TerminalPosition translate) {
- return withRelative(translate.getColumn(), translate.getRow());
- }
-
- /**
- * Creates a new TerminalPosition object that is 'translated' by an amount of rows and columns specified by the two
- * parameters. Same as calling
- * <code>withRelativeRow(deltaRow).withRelativeColumn(deltaColumn)</code>
- * @param deltaColumn How many columns to move from the current position in the new TerminalPosition
- * @param deltaRow How many rows to move from the current position in the new TerminalPosition
- * @return New TerminalPosition that is the result of the original position with added translation
- */
- public TerminalPosition withRelative(int deltaColumn, int deltaRow) {
- return withRelativeRow(deltaRow).withRelativeColumn(deltaColumn);
- }
-
- @Override
- public String toString() {
- return "[" + column + ":" + row + "]";
- }
-
- @Override
- public int hashCode() {
- int hash = 3;
- hash = 23 * hash + this.row;
- hash = 23 * hash + this.column;
- return hash;
- }
-
- public boolean equals(int columnIndex, int rowIndex) {
- return this.column == columnIndex &&
- this.row == rowIndex;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final TerminalPosition other = (TerminalPosition) obj;
- return this.row == other.row && this.column == other.column;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna;
-
-/**
- * Terminal dimensions in 2-d space, measured in number of rows and columns. This class is immutable and cannot change
- * its internal state after creation.
- *
- * @author Martin
- */
-public class TerminalSize {
- public static final TerminalSize ZERO = new TerminalSize(0, 0);
- public static final TerminalSize ONE = new TerminalSize(1, 1);
-
- private final int columns;
- private final int rows;
-
- /**
- * Creates a new terminal size representation with a given width (columns) and height (rows)
- * @param columns Width, in number of columns
- * @param rows Height, in number of columns
- */
- public TerminalSize(int columns, int rows) {
- if (columns < 0) {
- throw new IllegalArgumentException("TerminalSize.columns cannot be less than 0!");
- }
- if (rows < 0) {
- throw new IllegalArgumentException("TerminalSize.rows cannot be less than 0!");
- }
- this.columns = columns;
- this.rows = rows;
- }
-
- /**
- * @return Returns the width of this size representation, in number of columns
- */
- public int getColumns() {
- return columns;
- }
-
- /**
- * Creates a new size based on this size, but with a different width
- * @param columns Width of the new size, in columns
- * @return New size based on this one, but with a new width
- */
- public TerminalSize withColumns(int columns) {
- if(this.columns == columns) {
- return this;
- }
- if(columns == 0 && this.rows == 0) {
- return ZERO;
- }
- return new TerminalSize(columns, this.rows);
- }
-
-
- /**
- * @return Returns the height of this size representation, in number of rows
- */
- public int getRows() {
- return rows;
- }
-
- /**
- * Creates a new size based on this size, but with a different height
- * @param rows Height of the new size, in rows
- * @return New size based on this one, but with a new height
- */
- public TerminalSize withRows(int rows) {
- if(this.rows == rows) {
- return this;
- }
- if(rows == 0 && this.columns == 0) {
- return ZERO;
- }
- return new TerminalSize(this.columns, rows);
- }
-
- /**
- * Creates a new TerminalSize object representing a size with the same number of rows, but with a column size offset by a
- * supplied value. Calling this method with delta 0 will return this, calling it with a positive delta will return
- * a terminal size <i>delta</i> number of columns wider and for negative numbers shorter.
- * @param delta Column offset
- * @return New terminal size based off this one but with an applied transformation
- */
- public TerminalSize withRelativeColumns(int delta) {
- if(delta == 0) {
- return this;
- }
- return withColumns(columns + delta);
- }
-
- /**
- * Creates a new TerminalSize object representing a size with the same number of columns, but with a row size offset by a
- * supplied value. Calling this method with delta 0 will return this, calling it with a positive delta will return
- * a terminal size <i>delta</i> number of rows longer and for negative numbers shorter.
- * @param delta Row offset
- * @return New terminal size based off this one but with an applied transformation
- */
- public TerminalSize withRelativeRows(int delta) {
- if(delta == 0) {
- return this;
- }
- return withRows(rows + delta);
- }
-
- /**
- * Creates a new TerminalSize object representing a size based on this object's size but with a delta applied.
- * This is the same as calling
- * <code>withRelativeColumns(delta.getColumns()).withRelativeRows(delta.getRows())</code>
- * @param delta Column and row offset
- * @return New terminal size based off this one but with an applied resize
- */
- public TerminalSize withRelative(TerminalSize delta) {
- return withRelative(delta.getColumns(), delta.getRows());
- }
-
- /**
- * Creates a new TerminalSize object representing a size based on this object's size but with a delta applied.
- * This is the same as calling
- * <code>withRelativeColumns(deltaColumns).withRelativeRows(deltaRows)</code>
- * @param deltaColumns How many extra columns the new TerminalSize will have (negative values are allowed)
- * @param deltaRows How many extra rows the new TerminalSize will have (negative values are allowed)
- * @return New terminal size based off this one but with an applied resize
- */
- public TerminalSize withRelative(int deltaColumns, int deltaRows) {
- return withRelativeRows(deltaRows).withRelativeColumns(deltaColumns);
- }
-
- /**
- * Takes a different TerminalSize and returns a new TerminalSize that has the largest dimensions of the two,
- * measured separately. So calling 3x5 on a 5x3 will return 5x5.
- * @param other Other TerminalSize to compare with
- * @return TerminalSize that combines the maximum width between the two and the maximum height
- */
- public TerminalSize max(TerminalSize other) {
- return withColumns(Math.max(columns, other.columns))
- .withRows(Math.max(rows, other.rows));
- }
-
- /**
- * Takes a different TerminalSize and returns a new TerminalSize that has the smallest dimensions of the two,
- * measured separately. So calling 3x5 on a 5x3 will return 3x3.
- * @param other Other TerminalSize to compare with
- * @return TerminalSize that combines the minimum width between the two and the minimum height
- */
- public TerminalSize min(TerminalSize other) {
- return withColumns(Math.min(columns, other.columns))
- .withRows(Math.min(rows, other.rows));
- }
-
- /**
- * Returns itself if it is equal to the supplied size, otherwise the supplied size. You can use this if you have a
- * size field which is frequently recalculated but often resolves to the same size; it will keep the same object
- * in memory instead of swapping it out every cycle.
- * @param size Size you want to return
- * @return Itself if this size equals the size passed in, otherwise the size passed in
- */
- public TerminalSize with(TerminalSize size) {
- if(equals(size)) {
- return this;
- }
- return size;
- }
-
- @Override
- public String toString() {
- return "{" + columns + "x" + rows + "}";
- }
-
- @Override
- public boolean equals(Object obj) {
- if(this == obj) {
- return true;
- }
- if (!(obj instanceof TerminalSize)) {
- return false;
- }
-
- TerminalSize other = (TerminalSize) obj;
- return columns == other.columns
- && rows == other.rows;
- }
-
- @Override
- public int hashCode() {
- int hash = 5;
- hash = 53 * hash + this.columns;
- hash = 53 * hash + this.rows;
- return hash;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.List;
-
-/**
- * This class contains a number of utility methods for analyzing characters and strings in a terminal context. The main
- * purpose is to make it easier to work with text that may or may not contain double-width text characters, such as CJK
- * (Chinese, Japanese, Korean) and other special symbols. This class assumes those are all double-width and in case the
- * terminal (-emulator) chooses to draw them (somehow) as single-column then all the calculations in this class will be
- * wrong. It seems safe to assume what this class considers double-width really is taking up two columns though.
- *
- * @author Martin
- */
-public class TerminalTextUtils {
- private TerminalTextUtils() {
- }
-
- /**
- * Given a character, is this character considered to be a CJK character?
- * Shamelessly stolen from
- * <a href="http://stackoverflow.com/questions/1499804/how-can-i-detect-japanese-text-in-a-java-string">StackOverflow</a>
- * where it was contributed by user Rakesh N
- * @param c Character to test
- * @return {@code true} if the character is a CJK character
- *
- */
- public static boolean isCharCJK(final char c) {
- Character.UnicodeBlock unicodeBlock = Character.UnicodeBlock.of(c);
- return (unicodeBlock == Character.UnicodeBlock.HIRAGANA)
- || (unicodeBlock == Character.UnicodeBlock.KATAKANA)
- || (unicodeBlock == Character.UnicodeBlock.KATAKANA_PHONETIC_EXTENSIONS)
- || (unicodeBlock == Character.UnicodeBlock.HANGUL_COMPATIBILITY_JAMO)
- || (unicodeBlock == Character.UnicodeBlock.HANGUL_JAMO)
- || (unicodeBlock == Character.UnicodeBlock.HANGUL_SYLLABLES)
- || (unicodeBlock == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS)
- || (unicodeBlock == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A)
- || (unicodeBlock == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B)
- || (unicodeBlock == Character.UnicodeBlock.CJK_COMPATIBILITY_FORMS)
- || (unicodeBlock == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS)
- || (unicodeBlock == Character.UnicodeBlock.CJK_RADICALS_SUPPLEMENT)
- || (unicodeBlock == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION)
- || (unicodeBlock == Character.UnicodeBlock.ENCLOSED_CJK_LETTERS_AND_MONTHS)
- || (unicodeBlock == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS && c < 0xFF61); //The magic number here is the separating index between full-width and half-width
- }
-
- /**
- * Checks if a character is expected to be taking up two columns if printed to a terminal. This will generally be
- * {@code true} for CJK (Chinese, Japanese and Korean) characters.
- * @param c Character to test if it's double-width when printed to a terminal
- * @return {@code true} if this character is expected to be taking up two columns when printed to the terminal,
- * otherwise {@code false}
- */
- public static boolean isCharDoubleWidth(final char c) {
- return isCharCJK(c);
- }
-
- /**
- * @deprecated Call {@code getColumnWidth(s)} instead
- */
- @Deprecated
- public static int getTrueWidth(String s) {
- return getColumnWidth(s);
- }
-
- /**
- * Given a string, returns how many columns this string would need to occupy in a terminal, taking into account that
- * CJK characters takes up two columns.
- * @param s String to check length
- * @return Number of actual terminal columns the string would occupy
- */
- public static int getColumnWidth(String s) {
- return getColumnIndex(s, s.length());
- }
-
- /**
- * Given a string and a character index inside that string, find out what the column index of that character would
- * be if printed in a terminal. If the string only contains non-CJK characters then the returned value will be same
- * as {@code stringCharacterIndex}, but if there are CJK characters the value will be different due to CJK
- * characters taking up two columns in width. If the character at the index in the string is a CJK character itself,
- * the returned value will be the index of the left-side of character.
- * @param s String to translate the index from
- * @param stringCharacterIndex Index within the string to get the terminal column index of
- * @return Index of the character inside the String at {@code stringCharacterIndex} when it has been writted to a
- * terminal
- * @throws StringIndexOutOfBoundsException if the index given is outside the String length or negative
- */
- public static int getColumnIndex(String s, int stringCharacterIndex) throws StringIndexOutOfBoundsException {
- int index = 0;
- for(int i = 0; i < stringCharacterIndex; i++) {
- if(isCharCJK(s.charAt(i))) {
- index++;
- }
- index++;
- }
- return index;
- }
-
- /**
- * This method does the reverse of getColumnIndex, given a String and imagining it has been printed out to the
- * top-left corner of a terminal, in the column specified by {@code columnIndex}, what is the index of that
- * character in the string. If the string contains no CJK characters, this will always be the same as
- * {@code columnIndex}. If the index specified is the right column of a CJK character, the index is the same as if
- * the column was the left column. So calling {@code getStringCharacterIndex("英", 0)} and
- * {@code getStringCharacterIndex("英", 1)} will both return 0.
- * @param s String to translate the index to
- * @param columnIndex Column index of the string written to a terminal
- * @return The index in the string of the character in terminal column {@code columnIndex}
- */
- public static int getStringCharacterIndex(String s, int columnIndex) {
- int index = 0;
- int counter = 0;
- while(counter < columnIndex) {
- if(isCharCJK(s.charAt(index++))) {
- counter++;
- if(counter == columnIndex) {
- return index - 1;
- }
- }
- counter++;
- }
- return index;
- }
-
- /**
- * Given a string that may or may not contain CJK characters, returns the substring which will fit inside
- * <code>availableColumnSpace</code> columns. This method does not handle special cases like tab or new-line.
- * <p>
- * Calling this method is the same as calling {@code fitString(string, 0, availableColumnSpace)}.
- * @param string The string to fit inside the availableColumnSpace
- * @param availableColumnSpace Number of columns to fit the string inside
- * @return The whole or part of the input string which will fit inside the supplied availableColumnSpace
- */
- public static String fitString(String string, int availableColumnSpace) {
- return fitString(string, 0, availableColumnSpace);
- }
-
- /**
- * Given a string that may or may not contain CJK characters, returns the substring which will fit inside
- * <code>availableColumnSpace</code> columns. This method does not handle special cases like tab or new-line.
- * <p>
- * This overload has a {@code fromColumn} parameter that specified where inside the string to start fitting. Please
- * notice that {@code fromColumn} is not a character index inside the string, but a column index as if the string
- * has been printed from the left-most side of the terminal. So if the string is "日本語", fromColumn set to 1 will
- * not starting counting from the second character ("本") in the string but from the CJK filler character belonging
- * to "日". If you want to count from a particular character index inside the string, please pass in a substring
- * and use fromColumn set to 0.
- * @param string The string to fit inside the availableColumnSpace
- * @param fromColumn From what column of the input string to start fitting (see description above!)
- * @param availableColumnSpace Number of columns to fit the string inside
- * @return The whole or part of the input string which will fit inside the supplied availableColumnSpace
- */
- public static String fitString(String string, int fromColumn, int availableColumnSpace) {
- if(availableColumnSpace <= 0) {
- return "";
- }
-
- StringBuilder bob = new StringBuilder();
- int column = 0;
- int index = 0;
- while(index < string.length() && column < fromColumn) {
- char c = string.charAt(index++);
- column += TerminalTextUtils.isCharCJK(c) ? 2 : 1;
- }
- if(column > fromColumn) {
- bob.append(" ");
- availableColumnSpace--;
- }
-
- while(availableColumnSpace > 0 && index < string.length()) {
- char c = string.charAt(index++);
- availableColumnSpace -= TerminalTextUtils.isCharCJK(c) ? 2 : 1;
- if(availableColumnSpace < 0) {
- bob.append(' ');
- }
- else {
- bob.append(c);
- }
- }
- return bob.toString();
- }
-
- /**
- * This method will calculate word wrappings given a number of lines of text and how wide the text can be printed.
- * The result is a list of new rows where word-wrapping was applied.
- * @param maxWidth Maximum number of columns that can be used before word-wrapping is applied, if <= 0 then the
- * lines will be returned unchanged
- * @param lines Input text
- * @return The input text word-wrapped at {@code maxWidth}; this may contain more rows than the input text
- */
- public static List<String> getWordWrappedText(int maxWidth, String... lines) {
- //Bounds checking
- if(maxWidth <= 0) {
- return Arrays.asList(lines);
- }
-
- List<String> result = new ArrayList<String>();
- LinkedList<String> linesToBeWrapped = new LinkedList<String>(Arrays.asList(lines));
- while(!linesToBeWrapped.isEmpty()) {
- String row = linesToBeWrapped.removeFirst();
- int rowWidth = getColumnWidth(row);
- if(rowWidth <= maxWidth) {
- result.add(row);
- }
- else {
- //Now search in reverse and find the first possible line-break
- final int characterIndexMax = getStringCharacterIndex(row, maxWidth);
- int characterIndex = characterIndexMax;
- while(characterIndex >= 0 &&
- !Character.isSpaceChar(row.charAt(characterIndex)) &&
- !isCharCJK(row.charAt(characterIndex))) {
- characterIndex--;
- }
- // right *after* a CJK is also a "nice" spot to break the line!
- if (characterIndex >= 0 && characterIndex < characterIndexMax &&
- isCharCJK(row.charAt(characterIndex))) {
- characterIndex++; // with these conditions it fits!
- }
-
- if(characterIndex < 0) {
- //Failed! There was no 'nice' place to cut so just cut it at maxWidth
- characterIndex = Math.max(characterIndexMax, 1); // at least 1 char
- result.add(row.substring(0, characterIndex));
- linesToBeWrapped.addFirst(row.substring(characterIndex));
- }
- else {
- // characterIndex == 0 only happens, if either
- // - first char is CJK and maxWidth==1 or
- // - first char is whitespace
- // either way: put it in row before break to prevent infinite loop.
- characterIndex = Math.max( characterIndex, 1); // at least 1 char
-
- //Ok, split the row, add it to the result and continue processing the second half on a new line
- result.add(row.substring(0, characterIndex));
- while(characterIndex < row.length() &&
- Character.isSpaceChar(row.charAt(characterIndex))) {
- characterIndex++;
- };
- if (characterIndex < row.length()) { // only if rest contains non-whitespace
- linesToBeWrapped.addFirst(row.substring(characterIndex));
- }
- }
- }
- }
- return result;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.EnumSet;
-
-/**
- * Represents a single character with additional metadata such as colors and modifiers. This class is immutable and
- * cannot be modified after creation.
- * @author Martin
- */
-public class TextCharacter {
- private static EnumSet<SGR> toEnumSet(SGR... modifiers) {
- if(modifiers.length == 0) {
- return EnumSet.noneOf(SGR.class);
- }
- else {
- return EnumSet.copyOf(Arrays.asList(modifiers));
- }
- }
-
- public static final TextCharacter DEFAULT_CHARACTER = new TextCharacter(' ', TextColor.ANSI.DEFAULT, TextColor.ANSI.DEFAULT);
-
- private final char character;
- private final TextColor foregroundColor;
- private final TextColor backgroundColor;
- private final EnumSet<SGR> modifiers; //This isn't immutable, but we should treat it as such and not expose it!
-
- /**
- * Creates a {@code ScreenCharacter} based on a supplied character, with default colors and no extra modifiers.
- * @param character Physical character to use
- */
- public TextCharacter(char character) {
- this(character, TextColor.ANSI.DEFAULT, TextColor.ANSI.DEFAULT);
- }
-
- /**
- * Copies another {@code ScreenCharacter}
- * @param character screenCharacter to copy from
- */
- public TextCharacter(TextCharacter character) {
- this(character.getCharacter(),
- character.getForegroundColor(),
- character.getBackgroundColor(),
- character.getModifiers().toArray(new SGR[character.getModifiers().size()]));
- }
-
- /**
- * Creates a new {@code ScreenCharacter} based on a physical character, color information and optional modifiers.
- * @param character Physical character to refer to
- * @param foregroundColor Foreground color the character has
- * @param backgroundColor Background color the character has
- * @param styles Optional list of modifiers to apply when drawing the character
- */
- @SuppressWarnings("WeakerAccess")
- public TextCharacter(
- char character,
- TextColor foregroundColor,
- TextColor backgroundColor,
- SGR... styles) {
-
- this(character,
- foregroundColor,
- backgroundColor,
- toEnumSet(styles));
- }
-
- /**
- * Creates a new {@code ScreenCharacter} based on a physical character, color information and a set of modifiers.
- * @param character Physical character to refer to
- * @param foregroundColor Foreground color the character has
- * @param backgroundColor Background color the character has
- * @param modifiers Set of modifiers to apply when drawing the character
- */
- public TextCharacter(
- char character,
- TextColor foregroundColor,
- TextColor backgroundColor,
- EnumSet<SGR> modifiers) {
-
- if(foregroundColor == null) {
- foregroundColor = TextColor.ANSI.DEFAULT;
- }
- if(backgroundColor == null) {
- backgroundColor = TextColor.ANSI.DEFAULT;
- }
-
- this.character = character;
- this.foregroundColor = foregroundColor;
- this.backgroundColor = backgroundColor;
- this.modifiers = EnumSet.copyOf(modifiers);
- }
-
- /**
- * The actual character this TextCharacter represents
- * @return character of the TextCharacter
- */
- public char getCharacter() {
- return character;
- }
-
- /**
- * Foreground color specified for this TextCharacter
- * @return Foreground color of this TextCharacter
- */
- public TextColor getForegroundColor() {
- return foregroundColor;
- }
-
- /**
- * Background color specified for this TextCharacter
- * @return Background color of this TextCharacter
- */
- public TextColor getBackgroundColor() {
- return backgroundColor;
- }
-
- /**
- * Returns a set of all active modifiers on this TextCharacter
- * @return Set of active SGR codes
- */
- public EnumSet<SGR> getModifiers() {
- return EnumSet.copyOf(modifiers);
- }
-
- /**
- * Returns true if this TextCharacter has the bold modifier active
- * @return {@code true} if this TextCharacter has the bold modifier active
- */
- public boolean isBold() {
- return modifiers.contains(SGR.BOLD);
- }
-
- /**
- * Returns true if this TextCharacter has the reverse modifier active
- * @return {@code true} if this TextCharacter has the reverse modifier active
- */
- public boolean isReversed() {
- return modifiers.contains(SGR.REVERSE);
- }
-
- /**
- * Returns true if this TextCharacter has the underline modifier active
- * @return {@code true} if this TextCharacter has the underline modifier active
- */
- public boolean isUnderlined() {
- return modifiers.contains(SGR.UNDERLINE);
- }
-
- /**
- * Returns true if this TextCharacter has the blink modifier active
- * @return {@code true} if this TextCharacter has the blink modifier active
- */
- public boolean isBlinking() {
- return modifiers.contains(SGR.BLINK);
- }
-
- /**
- * Returns true if this TextCharacter has the bordered modifier active
- * @return {@code true} if this TextCharacter has the bordered modifier active
- */
- public boolean isBordered() {
- return modifiers.contains(SGR.BORDERED);
- }
-
- /**
- * Returns true if this TextCharacter has the crossed-out modifier active
- * @return {@code true} if this TextCharacter has the crossed-out modifier active
- */
- public boolean isCrossedOut() {
- return modifiers.contains(SGR.CROSSED_OUT);
- }
-
- /**
- * Returns a new TextCharacter with the same colors and modifiers but a different underlying character
- * @param character Character the copy should have
- * @return Copy of this TextCharacter with different underlying character
- */
- @SuppressWarnings("SameParameterValue")
- public TextCharacter withCharacter(char character) {
- if(this.character == character) {
- return this;
- }
- return new TextCharacter(character, foregroundColor, backgroundColor, modifiers);
- }
-
- /**
- * Returns a copy of this TextCharacter with a specified foreground color
- * @param foregroundColor Foreground color the copy should have
- * @return Copy of the TextCharacter with a different foreground color
- */
- public TextCharacter withForegroundColor(TextColor foregroundColor) {
- if(this.foregroundColor == foregroundColor || this.foregroundColor.equals(foregroundColor)) {
- return this;
- }
- return new TextCharacter(character, foregroundColor, backgroundColor, modifiers);
- }
-
- /**
- * Returns a copy of this TextCharacter with a specified background color
- * @param backgroundColor Background color the copy should have
- * @return Copy of the TextCharacter with a different background color
- */
- public TextCharacter withBackgroundColor(TextColor backgroundColor) {
- if(this.backgroundColor == backgroundColor || this.backgroundColor.equals(backgroundColor)) {
- return this;
- }
- return new TextCharacter(character, foregroundColor, backgroundColor, modifiers);
- }
-
- /**
- * Returns a copy of this TextCharacter with specified list of SGR modifiers. None of the currently active SGR codes
- * will be carried over to the copy, only those in the passed in value.
- * @param modifiers SGR modifiers the copy should have
- * @return Copy of the TextCharacter with a different set of SGR modifiers
- */
- public TextCharacter withModifiers(Collection<SGR> modifiers) {
- EnumSet<SGR> newSet = EnumSet.copyOf(modifiers);
- if(modifiers.equals(newSet)) {
- return this;
- }
- return new TextCharacter(character, foregroundColor, backgroundColor, newSet);
- }
-
- /**
- * Returns a copy of this TextCharacter with an additional SGR modifier. All of the currently active SGR codes
- * will be carried over to the copy, in addition to the one specified.
- * @param modifier SGR modifiers the copy should have in additional to all currently present
- * @return Copy of the TextCharacter with a new SGR modifier
- */
- public TextCharacter withModifier(SGR modifier) {
- if(modifiers.contains(modifier)) {
- return this;
- }
- EnumSet<SGR> newSet = EnumSet.copyOf(this.modifiers);
- newSet.add(modifier);
- return new TextCharacter(character, foregroundColor, backgroundColor, newSet);
- }
-
- /**
- * Returns a copy of this TextCharacter with an SGR modifier removed. All of the currently active SGR codes
- * will be carried over to the copy, except for the one specified. If the current TextCharacter doesn't have the
- * SGR specified, it will return itself.
- * @param modifier SGR modifiers the copy should not have
- * @return Copy of the TextCharacter without the SGR modifier
- */
- public TextCharacter withoutModifier(SGR modifier) {
- if(!modifiers.contains(modifier)) {
- return this;
- }
- EnumSet<SGR> newSet = EnumSet.copyOf(this.modifiers);
- newSet.remove(modifier);
- return new TextCharacter(character, foregroundColor, backgroundColor, newSet);
- }
-
- @SuppressWarnings("SimplifiableIfStatement")
- @Override
- public boolean equals(Object obj) {
- if(obj == null) {
- return false;
- }
- if(getClass() != obj.getClass()) {
- return false;
- }
- final TextCharacter other = (TextCharacter) obj;
- if(this.character != other.character) {
- return false;
- }
- if(this.foregroundColor != other.foregroundColor && (this.foregroundColor == null || !this.foregroundColor.equals(other.foregroundColor))) {
- return false;
- }
- if(this.backgroundColor != other.backgroundColor && (this.backgroundColor == null || !this.backgroundColor.equals(other.backgroundColor))) {
- return false;
- }
- return !(this.modifiers != other.modifiers && (this.modifiers == null || !this.modifiers.equals(other.modifiers)));
- }
-
- @Override
- public int hashCode() {
- int hash = 7;
- hash = 37 * hash + this.character;
- hash = 37 * hash + (this.foregroundColor != null ? this.foregroundColor.hashCode() : 0);
- hash = 37 * hash + (this.backgroundColor != null ? this.backgroundColor.hashCode() : 0);
- hash = 37 * hash + (this.modifiers != null ? this.modifiers.hashCode() : 0);
- return hash;
- }
-
- @Override
- public String toString() {
- return "TextCharacter{" + "character=" + character + ", foregroundColor=" + foregroundColor + ", backgroundColor=" + backgroundColor + ", modifiers=" + modifiers + '}';
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna;
-
-
-import java.awt.*;
-
-/**
- * This is an abstract base class for terminal color definitions. Since there are different ways of specifying terminal
- * colors, all with a different range of adoptions, this makes it possible to program an API against an implementation-
- * agnostic color definition. Please remember when using colors that not all terminals and terminal emulators supports
- * them. The 24-bit color mode is very unsupported, for example, and even the default Linux terminal doesn't support
- * the 256-color indexed mode.
- *
- * @author Martin
- */
-public interface TextColor {
- /**
- * Returns the byte sequence in between CSI and character 'm' that is used to enable this color as the foreground
- * color on an ANSI-compatible terminal.
- * @return Byte array out data to output in between of CSI and 'm'
- */
- byte[] getForegroundSGRSequence();
-
- /**
- * Returns the byte sequence in between CSI and character 'm' that is used to enable this color as the background
- * color on an ANSI-compatible terminal.
- * @return Byte array out data to output in between of CSI and 'm'
- */
- byte[] getBackgroundSGRSequence();
-
- /**
- * Converts this color to an AWT color object, assuming a standard VGA palette.
- * @return TextColor as an AWT Color
- */
- Color toColor();
-
- /**
- * This class represent classic ANSI colors that are likely to be very compatible with most terminal
- * implementations. It is limited to 8 colors (plus the 'default' color) but as a norm, using bold mode (SGR code)
- * will slightly alter the color, giving it a bit brighter tone, so in total this will give you 16 (+1) colors.
- * <p>
- * For more information, see http://en.wikipedia.org/wiki/File:Ansi.png
- */
- enum ANSI implements TextColor {
- BLACK((byte)0, 0, 0, 0),
- RED((byte)1, 170, 0, 0),
- GREEN((byte)2, 0, 170, 0),
- YELLOW((byte)3, 170, 85, 0),
- BLUE((byte)4, 0, 0, 170),
- MAGENTA((byte)5, 170, 0, 170),
- CYAN((byte)6, 0, 170, 170),
- WHITE((byte)7, 170, 170, 170),
- DEFAULT((byte)9, 0, 0, 0);
-
- private final byte index;
- private final Color color;
-
- ANSI(byte index, int red, int green, int blue) {
- this.index = index;
- this.color = new Color(red, green, blue);
- }
-
- @Override
- public byte[] getForegroundSGRSequence() {
- return new byte[] { (byte)'3', (byte)(48 + index)}; //48 is ascii code for '0'
- }
-
- @Override
- public byte[] getBackgroundSGRSequence() {
- return new byte[] { (byte)'4', (byte)(48 + index)}; //48 is ascii code for '0'
- }
-
- @Override
- public Color toColor() {
- return color;
- }
- }
-
- /**
- * This class represents a color expressed in the indexed XTerm 256 color extension, where each color is defined in a
- * lookup-table. All in all, there are 256 codes, but in order to know which one to know you either need to have the
- * table at hand, or you can use the two static helper methods which can help you convert from three 8-bit
- * RGB values to the closest approximate indexed color number. If you are interested, the 256 index values are
- * actually divided like this:<br>
- * 0 .. 15 - System colors, same as ANSI, but the actual rendered color depends on the terminal emulators color scheme<br>
- * 16 .. 231 - Forms a 6x6x6 RGB color cube<br>
- * 232 .. 255 - A gray scale ramp (without black and white endpoints)<br>
- * <p>
- * Support for indexed colors is somewhat widely adopted, not as much as the ANSI colors (TextColor.ANSI) but more
- * than the RGB (TextColor.RGB).
- * <p>
- * For more details on this, please see <a
- * href="https://github.com/robertknight/konsole/blob/master/user-doc/README.moreColors">
- * this</a> commit message to Konsole.
- */
- class Indexed implements TextColor {
- private static final byte[][] COLOR_TABLE = new byte[][] {
- //These are the standard 16-color VGA palette entries
- {(byte)0,(byte)0,(byte)0 },
- {(byte)170,(byte)0,(byte)0 },
- {(byte)0,(byte)170,(byte)0 },
- {(byte)170,(byte)85,(byte)0 },
- {(byte)0,(byte)0,(byte)170 },
- {(byte)170,(byte)0,(byte)170 },
- {(byte)0,(byte)170,(byte)170 },
- {(byte)170,(byte)170,(byte)170 },
- {(byte)85,(byte)85,(byte)85 },
- {(byte)255,(byte)85,(byte)85 },
- {(byte)85,(byte)255,(byte)85 },
- {(byte)255,(byte)255,(byte)85 },
- {(byte)85,(byte)85,(byte)255 },
- {(byte)255,(byte)85,(byte)255 },
- {(byte)85,(byte)255,(byte)255 },
- {(byte)255,(byte)255,(byte)255 },
-
- //Starting 6x6x6 RGB color cube from 16
- {(byte)0x00,(byte)0x00,(byte)0x00 },
- {(byte)0x00,(byte)0x00,(byte)0x5f },
- {(byte)0x00,(byte)0x00,(byte)0x87 },
- {(byte)0x00,(byte)0x00,(byte)0xaf },
- {(byte)0x00,(byte)0x00,(byte)0xd7 },
- {(byte)0x00,(byte)0x00,(byte)0xff },
- {(byte)0x00,(byte)0x5f,(byte)0x00 },
- {(byte)0x00,(byte)0x5f,(byte)0x5f },
- {(byte)0x00,(byte)0x5f,(byte)0x87 },
- {(byte)0x00,(byte)0x5f,(byte)0xaf },
- {(byte)0x00,(byte)0x5f,(byte)0xd7 },
- {(byte)0x00,(byte)0x5f,(byte)0xff },
- {(byte)0x00,(byte)0x87,(byte)0x00 },
- {(byte)0x00,(byte)0x87,(byte)0x5f },
- {(byte)0x00,(byte)0x87,(byte)0x87 },
- {(byte)0x00,(byte)0x87,(byte)0xaf },
- {(byte)0x00,(byte)0x87,(byte)0xd7 },
- {(byte)0x00,(byte)0x87,(byte)0xff },
- {(byte)0x00,(byte)0xaf,(byte)0x00 },
- {(byte)0x00,(byte)0xaf,(byte)0x5f },
- {(byte)0x00,(byte)0xaf,(byte)0x87 },
- {(byte)0x00,(byte)0xaf,(byte)0xaf },
- {(byte)0x00,(byte)0xaf,(byte)0xd7 },
- {(byte)0x00,(byte)0xaf,(byte)0xff },
- {(byte)0x00,(byte)0xd7,(byte)0x00 },
- {(byte)0x00,(byte)0xd7,(byte)0x5f },
- {(byte)0x00,(byte)0xd7,(byte)0x87 },
- {(byte)0x00,(byte)0xd7,(byte)0xaf },
- {(byte)0x00,(byte)0xd7,(byte)0xd7 },
- {(byte)0x00,(byte)0xd7,(byte)0xff },
- {(byte)0x00,(byte)0xff,(byte)0x00 },
- {(byte)0x00,(byte)0xff,(byte)0x5f },
- {(byte)0x00,(byte)0xff,(byte)0x87 },
- {(byte)0x00,(byte)0xff,(byte)0xaf },
- {(byte)0x00,(byte)0xff,(byte)0xd7 },
- {(byte)0x00,(byte)0xff,(byte)0xff },
- {(byte)0x5f,(byte)0x00,(byte)0x00 },
- {(byte)0x5f,(byte)0x00,(byte)0x5f },
- {(byte)0x5f,(byte)0x00,(byte)0x87 },
- {(byte)0x5f,(byte)0x00,(byte)0xaf },
- {(byte)0x5f,(byte)0x00,(byte)0xd7 },
- {(byte)0x5f,(byte)0x00,(byte)0xff },
- {(byte)0x5f,(byte)0x5f,(byte)0x00 },
- {(byte)0x5f,(byte)0x5f,(byte)0x5f },
- {(byte)0x5f,(byte)0x5f,(byte)0x87 },
- {(byte)0x5f,(byte)0x5f,(byte)0xaf },
- {(byte)0x5f,(byte)0x5f,(byte)0xd7 },
- {(byte)0x5f,(byte)0x5f,(byte)0xff },
- {(byte)0x5f,(byte)0x87,(byte)0x00 },
- {(byte)0x5f,(byte)0x87,(byte)0x5f },
- {(byte)0x5f,(byte)0x87,(byte)0x87 },
- {(byte)0x5f,(byte)0x87,(byte)0xaf },
- {(byte)0x5f,(byte)0x87,(byte)0xd7 },
- {(byte)0x5f,(byte)0x87,(byte)0xff },
- {(byte)0x5f,(byte)0xaf,(byte)0x00 },
- {(byte)0x5f,(byte)0xaf,(byte)0x5f },
- {(byte)0x5f,(byte)0xaf,(byte)0x87 },
- {(byte)0x5f,(byte)0xaf,(byte)0xaf },
- {(byte)0x5f,(byte)0xaf,(byte)0xd7 },
- {(byte)0x5f,(byte)0xaf,(byte)0xff },
- {(byte)0x5f,(byte)0xd7,(byte)0x00 },
- {(byte)0x5f,(byte)0xd7,(byte)0x5f },
- {(byte)0x5f,(byte)0xd7,(byte)0x87 },
- {(byte)0x5f,(byte)0xd7,(byte)0xaf },
- {(byte)0x5f,(byte)0xd7,(byte)0xd7 },
- {(byte)0x5f,(byte)0xd7,(byte)0xff },
- {(byte)0x5f,(byte)0xff,(byte)0x00 },
- {(byte)0x5f,(byte)0xff,(byte)0x5f },
- {(byte)0x5f,(byte)0xff,(byte)0x87 },
- {(byte)0x5f,(byte)0xff,(byte)0xaf },
- {(byte)0x5f,(byte)0xff,(byte)0xd7 },
- {(byte)0x5f,(byte)0xff,(byte)0xff },
- {(byte)0x87,(byte)0x00,(byte)0x00 },
- {(byte)0x87,(byte)0x00,(byte)0x5f },
- {(byte)0x87,(byte)0x00,(byte)0x87 },
- {(byte)0x87,(byte)0x00,(byte)0xaf },
- {(byte)0x87,(byte)0x00,(byte)0xd7 },
- {(byte)0x87,(byte)0x00,(byte)0xff },
- {(byte)0x87,(byte)0x5f,(byte)0x00 },
- {(byte)0x87,(byte)0x5f,(byte)0x5f },
- {(byte)0x87,(byte)0x5f,(byte)0x87 },
- {(byte)0x87,(byte)0x5f,(byte)0xaf },
- {(byte)0x87,(byte)0x5f,(byte)0xd7 },
- {(byte)0x87,(byte)0x5f,(byte)0xff },
- {(byte)0x87,(byte)0x87,(byte)0x00 },
- {(byte)0x87,(byte)0x87,(byte)0x5f },
- {(byte)0x87,(byte)0x87,(byte)0x87 },
- {(byte)0x87,(byte)0x87,(byte)0xaf },
- {(byte)0x87,(byte)0x87,(byte)0xd7 },
- {(byte)0x87,(byte)0x87,(byte)0xff },
- {(byte)0x87,(byte)0xaf,(byte)0x00 },
- {(byte)0x87,(byte)0xaf,(byte)0x5f },
- {(byte)0x87,(byte)0xaf,(byte)0x87 },
- {(byte)0x87,(byte)0xaf,(byte)0xaf },
- {(byte)0x87,(byte)0xaf,(byte)0xd7 },
- {(byte)0x87,(byte)0xaf,(byte)0xff },
- {(byte)0x87,(byte)0xd7,(byte)0x00 },
- {(byte)0x87,(byte)0xd7,(byte)0x5f },
- {(byte)0x87,(byte)0xd7,(byte)0x87 },
- {(byte)0x87,(byte)0xd7,(byte)0xaf },
- {(byte)0x87,(byte)0xd7,(byte)0xd7 },
- {(byte)0x87,(byte)0xd7,(byte)0xff },
- {(byte)0x87,(byte)0xff,(byte)0x00 },
- {(byte)0x87,(byte)0xff,(byte)0x5f },
- {(byte)0x87,(byte)0xff,(byte)0x87 },
- {(byte)0x87,(byte)0xff,(byte)0xaf },
- {(byte)0x87,(byte)0xff,(byte)0xd7 },
- {(byte)0x87,(byte)0xff,(byte)0xff },
- {(byte)0xaf,(byte)0x00,(byte)0x00 },
- {(byte)0xaf,(byte)0x00,(byte)0x5f },
- {(byte)0xaf,(byte)0x00,(byte)0x87 },
- {(byte)0xaf,(byte)0x00,(byte)0xaf },
- {(byte)0xaf,(byte)0x00,(byte)0xd7 },
- {(byte)0xaf,(byte)0x00,(byte)0xff },
- {(byte)0xaf,(byte)0x5f,(byte)0x00 },
- {(byte)0xaf,(byte)0x5f,(byte)0x5f },
- {(byte)0xaf,(byte)0x5f,(byte)0x87 },
- {(byte)0xaf,(byte)0x5f,(byte)0xaf },
- {(byte)0xaf,(byte)0x5f,(byte)0xd7 },
- {(byte)0xaf,(byte)0x5f,(byte)0xff },
- {(byte)0xaf,(byte)0x87,(byte)0x00 },
- {(byte)0xaf,(byte)0x87,(byte)0x5f },
- {(byte)0xaf,(byte)0x87,(byte)0x87 },
- {(byte)0xaf,(byte)0x87,(byte)0xaf },
- {(byte)0xaf,(byte)0x87,(byte)0xd7 },
- {(byte)0xaf,(byte)0x87,(byte)0xff },
- {(byte)0xaf,(byte)0xaf,(byte)0x00 },
- {(byte)0xaf,(byte)0xaf,(byte)0x5f },
- {(byte)0xaf,(byte)0xaf,(byte)0x87 },
- {(byte)0xaf,(byte)0xaf,(byte)0xaf },
- {(byte)0xaf,(byte)0xaf,(byte)0xd7 },
- {(byte)0xaf,(byte)0xaf,(byte)0xff },
- {(byte)0xaf,(byte)0xd7,(byte)0x00 },
- {(byte)0xaf,(byte)0xd7,(byte)0x5f },
- {(byte)0xaf,(byte)0xd7,(byte)0x87 },
- {(byte)0xaf,(byte)0xd7,(byte)0xaf },
- {(byte)0xaf,(byte)0xd7,(byte)0xd7 },
- {(byte)0xaf,(byte)0xd7,(byte)0xff },
- {(byte)0xaf,(byte)0xff,(byte)0x00 },
- {(byte)0xaf,(byte)0xff,(byte)0x5f },
- {(byte)0xaf,(byte)0xff,(byte)0x87 },
- {(byte)0xaf,(byte)0xff,(byte)0xaf },
- {(byte)0xaf,(byte)0xff,(byte)0xd7 },
- {(byte)0xaf,(byte)0xff,(byte)0xff },
- {(byte)0xd7,(byte)0x00,(byte)0x00 },
- {(byte)0xd7,(byte)0x00,(byte)0x5f },
- {(byte)0xd7,(byte)0x00,(byte)0x87 },
- {(byte)0xd7,(byte)0x00,(byte)0xaf },
- {(byte)0xd7,(byte)0x00,(byte)0xd7 },
- {(byte)0xd7,(byte)0x00,(byte)0xff },
- {(byte)0xd7,(byte)0x5f,(byte)0x00 },
- {(byte)0xd7,(byte)0x5f,(byte)0x5f },
- {(byte)0xd7,(byte)0x5f,(byte)0x87 },
- {(byte)0xd7,(byte)0x5f,(byte)0xaf },
- {(byte)0xd7,(byte)0x5f,(byte)0xd7 },
- {(byte)0xd7,(byte)0x5f,(byte)0xff },
- {(byte)0xd7,(byte)0x87,(byte)0x00 },
- {(byte)0xd7,(byte)0x87,(byte)0x5f },
- {(byte)0xd7,(byte)0x87,(byte)0x87 },
- {(byte)0xd7,(byte)0x87,(byte)0xaf },
- {(byte)0xd7,(byte)0x87,(byte)0xd7 },
- {(byte)0xd7,(byte)0x87,(byte)0xff },
- {(byte)0xd7,(byte)0xaf,(byte)0x00 },
- {(byte)0xd7,(byte)0xaf,(byte)0x5f },
- {(byte)0xd7,(byte)0xaf,(byte)0x87 },
- {(byte)0xd7,(byte)0xaf,(byte)0xaf },
- {(byte)0xd7,(byte)0xaf,(byte)0xd7 },
- {(byte)0xd7,(byte)0xaf,(byte)0xff },
- {(byte)0xd7,(byte)0xd7,(byte)0x00 },
- {(byte)0xd7,(byte)0xd7,(byte)0x5f },
- {(byte)0xd7,(byte)0xd7,(byte)0x87 },
- {(byte)0xd7,(byte)0xd7,(byte)0xaf },
- {(byte)0xd7,(byte)0xd7,(byte)0xd7 },
- {(byte)0xd7,(byte)0xd7,(byte)0xff },
- {(byte)0xd7,(byte)0xff,(byte)0x00 },
- {(byte)0xd7,(byte)0xff,(byte)0x5f },
- {(byte)0xd7,(byte)0xff,(byte)0x87 },
- {(byte)0xd7,(byte)0xff,(byte)0xaf },
- {(byte)0xd7,(byte)0xff,(byte)0xd7 },
- {(byte)0xd7,(byte)0xff,(byte)0xff },
- {(byte)0xff,(byte)0x00,(byte)0x00 },
- {(byte)0xff,(byte)0x00,(byte)0x5f },
- {(byte)0xff,(byte)0x00,(byte)0x87 },
- {(byte)0xff,(byte)0x00,(byte)0xaf },
- {(byte)0xff,(byte)0x00,(byte)0xd7 },
- {(byte)0xff,(byte)0x00,(byte)0xff },
- {(byte)0xff,(byte)0x5f,(byte)0x00 },
- {(byte)0xff,(byte)0x5f,(byte)0x5f },
- {(byte)0xff,(byte)0x5f,(byte)0x87 },
- {(byte)0xff,(byte)0x5f,(byte)0xaf },
- {(byte)0xff,(byte)0x5f,(byte)0xd7 },
- {(byte)0xff,(byte)0x5f,(byte)0xff },
- {(byte)0xff,(byte)0x87,(byte)0x00 },
- {(byte)0xff,(byte)0x87,(byte)0x5f },
- {(byte)0xff,(byte)0x87,(byte)0x87 },
- {(byte)0xff,(byte)0x87,(byte)0xaf },
- {(byte)0xff,(byte)0x87,(byte)0xd7 },
- {(byte)0xff,(byte)0x87,(byte)0xff },
- {(byte)0xff,(byte)0xaf,(byte)0x00 },
- {(byte)0xff,(byte)0xaf,(byte)0x5f },
- {(byte)0xff,(byte)0xaf,(byte)0x87 },
- {(byte)0xff,(byte)0xaf,(byte)0xaf },
- {(byte)0xff,(byte)0xaf,(byte)0xd7 },
- {(byte)0xff,(byte)0xaf,(byte)0xff },
- {(byte)0xff,(byte)0xd7,(byte)0x00 },
- {(byte)0xff,(byte)0xd7,(byte)0x5f },
- {(byte)0xff,(byte)0xd7,(byte)0x87 },
- {(byte)0xff,(byte)0xd7,(byte)0xaf },
- {(byte)0xff,(byte)0xd7,(byte)0xd7 },
- {(byte)0xff,(byte)0xd7,(byte)0xff },
- {(byte)0xff,(byte)0xff,(byte)0x00 },
- {(byte)0xff,(byte)0xff,(byte)0x5f },
- {(byte)0xff,(byte)0xff,(byte)0x87 },
- {(byte)0xff,(byte)0xff,(byte)0xaf },
- {(byte)0xff,(byte)0xff,(byte)0xd7 },
- {(byte)0xff,(byte)0xff,(byte)0xff },
-
- //Grey-scale ramp from 232
- {(byte)0x08,(byte)0x08,(byte)0x08 },
- {(byte)0x12,(byte)0x12,(byte)0x12 },
- {(byte)0x1c,(byte)0x1c,(byte)0x1c },
- {(byte)0x26,(byte)0x26,(byte)0x26 },
- {(byte)0x30,(byte)0x30,(byte)0x30 },
- {(byte)0x3a,(byte)0x3a,(byte)0x3a },
- {(byte)0x44,(byte)0x44,(byte)0x44 },
- {(byte)0x4e,(byte)0x4e,(byte)0x4e },
- {(byte)0x58,(byte)0x58,(byte)0x58 },
- {(byte)0x62,(byte)0x62,(byte)0x62 },
- {(byte)0x6c,(byte)0x6c,(byte)0x6c },
- {(byte)0x76,(byte)0x76,(byte)0x76 },
- {(byte)0x80,(byte)0x80,(byte)0x80 },
- {(byte)0x8a,(byte)0x8a,(byte)0x8a },
- {(byte)0x94,(byte)0x94,(byte)0x94 },
- {(byte)0x9e,(byte)0x9e,(byte)0x9e },
- {(byte)0xa8,(byte)0xa8,(byte)0xa8 },
- {(byte)0xb2,(byte)0xb2,(byte)0xb2 },
- {(byte)0xbc,(byte)0xbc,(byte)0xbc },
- {(byte)0xc6,(byte)0xc6,(byte)0xc6 },
- {(byte)0xd0,(byte)0xd0,(byte)0xd0 },
- {(byte)0xda,(byte)0xda,(byte)0xda },
- {(byte)0xe4,(byte)0xe4,(byte)0xe4 },
- {(byte)0xee,(byte)0xee,(byte)0xee }
- };
-
- private final int colorIndex;
- private final Color awtColor;
-
- /**
- * Creates a new TextColor using the XTerm 256 color indexed mode, with the specified index value. You must
- * choose a value between 0 and 255.
- * @param colorIndex Index value to use for this color.
- */
- public Indexed(int colorIndex) {
- if(colorIndex > 255 || colorIndex < 0) {
- throw new IllegalArgumentException("Cannot create a Color.Indexed with a color index of " + colorIndex +
- ", must be in the range of 0-255");
- }
- this.colorIndex = colorIndex;
- this.awtColor = new Color(COLOR_TABLE[colorIndex][0] & 0x000000ff,
- COLOR_TABLE[colorIndex][1] & 0x000000ff,
- COLOR_TABLE[colorIndex][2] & 0x000000ff);
- }
-
- @Override
- public byte[] getForegroundSGRSequence() {
- return ("38;5;" + colorIndex).getBytes();
- }
-
- @Override
- public byte[] getBackgroundSGRSequence() {
- return ("48;5;" + colorIndex).getBytes();
- }
-
- @Override
- public Color toColor() {
- return awtColor;
- }
-
- @Override
- public String toString() {
- return "{IndexedColor:" + colorIndex + "}";
- }
-
- @Override
- public int hashCode() {
- int hash = 3;
- hash = 43 * hash + this.colorIndex;
- return hash;
- }
-
- @Override
- public boolean equals(Object obj) {
- if(obj == null) {
- return false;
- }
- if(getClass() != obj.getClass()) {
- return false;
- }
- final Indexed other = (Indexed) obj;
- return this.colorIndex == other.colorIndex;
- }
-
- /**
- * Picks out a color approximated from the supplied RGB components
- * @param red Red intensity, from 0 to 255
- * @param green Red intensity, from 0 to 255
- * @param blue Red intensity, from 0 to 255
- * @return Nearest color from the 6x6x6 RGB color cube or from the 24 entries grey-scale ramp (whichever is closest)
- */
- public static Indexed fromRGB(int red, int green, int blue) {
- if(red < 0 || red > 255) {
- throw new IllegalArgumentException("fromRGB: red is outside of valid range (0-255)");
- }
- if(green < 0 || green > 255) {
- throw new IllegalArgumentException("fromRGB: green is outside of valid range (0-255)");
- }
- if(blue < 0 || blue > 255) {
- throw new IllegalArgumentException("fromRGB: blue is outside of valid range (0-255)");
- }
-
- int rescaledRed = (int)(((double)red / 255.0) * 5.0);
- int rescaledGreen = (int)(((double)green / 255.0) * 5.0);
- int rescaledBlue = (int)(((double)blue / 255.0) * 5.0);
-
- int index = rescaledBlue + (6 * rescaledGreen) + (36 * rescaledRed) + 16;
- Indexed fromColorCube = new Indexed(index);
- Indexed fromGreyRamp = fromGreyRamp((red + green + blue) / 3);
-
- //Now figure out which one is closest
- Color colored = fromColorCube.toColor();
- Color grey = fromGreyRamp.toColor();
- int coloredDistance = ((red - colored.getRed()) * (red - colored.getRed())) +
- ((green - colored.getGreen()) * (green - colored.getGreen())) +
- ((blue - colored.getBlue()) * (blue - colored.getBlue()));
- int greyDistance = ((red - grey.getRed()) * (red - grey.getRed())) +
- ((green - grey.getGreen()) * (green - grey.getGreen())) +
- ((blue - grey.getBlue()) * (blue - grey.getBlue()));
- if(coloredDistance < greyDistance) {
- return fromColorCube;
- }
- else {
- return fromGreyRamp;
- }
- }
-
- /**
- * Picks out a color from the grey-scale ramp area of the color index.
- * @param intensity Intensity, 0 - 255
- * @return Indexed color from the grey-scale ramp which is the best match for the supplied intensity
- */
- private static Indexed fromGreyRamp(int intensity) {
- int rescaled = (int)(((double)intensity / 255.0) * 23.0) + 232;
- return new Indexed(rescaled);
- }
- }
-
- /**
- * This class can be used to specify a color in 24-bit color space (RGB with 8-bit resolution per color). Please be
- * aware that only a few terminal support 24-bit color control codes, please avoid using this class unless you know
- * all users will have compatible terminals. For details, please see
- * <a href="https://github.com/robertknight/konsole/blob/master/user-doc/README.moreColors">
- * this</a> commit log. Behavior on terminals that don't support these codes is undefined.
- */
- class RGB implements TextColor {
- private final Color color;
-
- /**
- * This class can be used to specify a color in 24-bit color space (RGB with 8-bit resolution per color). Please be
- * aware that only a few terminal support 24-bit color control codes, please avoid using this class unless you know
- * all users will have compatible terminals. For details, please see
- * <a href="https://github.com/robertknight/konsole/blob/master/user-doc/README.moreColors">
- * this</a> commit log. Behavior on terminals that don't support these codes is undefined.
- *
- * @param r Red intensity, from 0 to 255
- * @param g Green intensity, from 0 to 255
- * @param b Blue intensity, from 0 to 255
- */
- public RGB(int r, int g, int b) {
- if(r < 0 || r > 255) {
- throw new IllegalArgumentException("RGB: r is outside of valid range (0-255)");
- }
- if(g < 0 || g > 255) {
- throw new IllegalArgumentException("RGB: g is outside of valid range (0-255)");
- }
- if(b < 0 || b > 255) {
- throw new IllegalArgumentException("RGB: b is outside of valid range (0-255)");
- }
- this.color = new Color(r, g, b);
- }
-
- @Override
- public byte[] getForegroundSGRSequence() {
- return ("38;2;" + getRed() + ";" + getGreen() + ";" + getBlue()).getBytes();
- }
-
- @Override
- public byte[] getBackgroundSGRSequence() {
- return ("48;2;" + getRed() + ";" + getGreen() + ";" + getBlue()).getBytes();
- }
-
- @Override
- public Color toColor() {
- return color;
- }
-
- /**
- * @return Red intensity of this color, from 0 to 255
- */
- public int getRed() {
- return color.getRed();
- }
-
- /**
- * @return Green intensity of this color, from 0 to 255
- */
- public int getGreen() {
- return color.getGreen();
- }
-
- /**
- * @return Blue intensity of this color, from 0 to 255
- */
- public int getBlue() {
- return color.getBlue();
- }
-
- @Override
- public String toString() {
- return "{RGB:" + getRed() + "," + getGreen() + "," + getBlue() + "}";
- }
-
- @Override
- public int hashCode() {
- int hash = 7;
- hash = 29 * hash + color.hashCode();
- return hash;
- }
-
- @SuppressWarnings("SimplifiableIfStatement")
- @Override
- public boolean equals(Object obj) {
- if(obj == null) {
- return false;
- }
- if(getClass() != obj.getClass()) {
- return false;
- }
- final RGB other = (RGB) obj;
- return color.equals(other.color);
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.bundle;
-
-import java.security.PrivilegedAction;
-import java.text.MessageFormat;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.ResourceBundle;
-
-/**
- * This class permits to deal easily with bundles.
- * @author silveryocha
- */
-public abstract class BundleLocator {
-
- private final String bundleName;
- private static final ClassLoader loader = BundleLocator.class.getClassLoader();
-
- /**
- * Hidden constructor.
- * @param bundleName the name of the bundle.
- */
- protected BundleLocator(final String bundleName) {
- this.bundleName = bundleName;
- }
-
- /**
- * Method that centralizes the way to get the value associated to a bundle key.
- * @param locale the locale.
- * @param key the key searched for.
- * @param parameters the parameters to apply to the value associated to the key.
- * @return the formatted value associated to the given key. Empty string if no value exists for
- * the given key.
- */
- protected String getBundleKeyValue(Locale locale, String key, Object... parameters) {
- String value = null;
- try {
- value = getBundle(locale).getString(key);
- } catch (Exception ignore) {
- }
- return value != null ? MessageFormat.format(value, parameters) : null;
- }
-
- /**
- * Gets the right bundle.<br/>
- * A cache is handled as well as the concurrent accesses.
- * @param locale the locale.
- * @return the instance of the bundle.
- */
- private ResourceBundle getBundle(Locale locale) {
- return ResourceBundle.getBundle(bundleName, locale, loader);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.bundle;
-
-import java.util.Locale;
-
-/**
- * This class permits to get easily localized strings about the UI.
- * @author silveryocha
- */
-public class LocalizedUIBundle extends BundleLocator {
-
- private static final LocalizedUIBundle MY_BUNDLE = new LocalizedUIBundle("multilang.lanterna-ui");
-
- public static String get(String key, String... parameters) {
- return get(Locale.getDefault(), key, parameters);
- }
-
- public static String get(Locale locale, String key, String... parameters) {
- return MY_BUNDLE.getBundleKeyValue(locale, key, (Object[])parameters);
- }
-
- private LocalizedUIBundle(final String bundleName) {
- super(bundleName);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.screen.TabBehaviour;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.EnumSet;
-
-/**
- * This class hold the default logic for drawing the basic text graphic as exposed by TextGraphic. All implementations
- * rely on a setCharacter method being implemented in subclasses.
- * @author Martin
- */
-public abstract class AbstractTextGraphics implements TextGraphics {
- protected TextColor foregroundColor;
- protected TextColor backgroundColor;
- protected TabBehaviour tabBehaviour;
- protected final EnumSet<SGR> activeModifiers;
- private final ShapeRenderer shapeRenderer;
-
- protected AbstractTextGraphics() {
- this.activeModifiers = EnumSet.noneOf(SGR.class);
- this.tabBehaviour = TabBehaviour.ALIGN_TO_COLUMN_4;
- this.foregroundColor = TextColor.ANSI.DEFAULT;
- this.backgroundColor = TextColor.ANSI.DEFAULT;
- this.shapeRenderer = new DefaultShapeRenderer(new DefaultShapeRenderer.Callback() {
- @Override
- public void onPoint(int column, int row, TextCharacter character) {
- setCharacter(column, row, character);
- }
- });
- }
-
- @Override
- public TextColor getBackgroundColor() {
- return backgroundColor;
- }
-
- @Override
- public TextGraphics setBackgroundColor(final TextColor backgroundColor) {
- this.backgroundColor = backgroundColor;
- return this;
- }
-
- @Override
- public TextColor getForegroundColor() {
- return foregroundColor;
- }
-
- @Override
- public TextGraphics setForegroundColor(final TextColor foregroundColor) {
- this.foregroundColor = foregroundColor;
- return this;
- }
-
- @Override
- public TextGraphics enableModifiers(SGR... modifiers) {
- enableModifiers(Arrays.asList(modifiers));
- return this;
- }
-
- private void enableModifiers(Collection<SGR> modifiers) {
- this.activeModifiers.addAll(modifiers);
- }
-
- @Override
- public TextGraphics disableModifiers(SGR... modifiers) {
- disableModifiers(Arrays.asList(modifiers));
- return this;
- }
-
- private void disableModifiers(Collection<SGR> modifiers) {
- this.activeModifiers.removeAll(modifiers);
- }
-
- @Override
- public synchronized TextGraphics setModifiers(EnumSet<SGR> modifiers) {
- activeModifiers.clear();
- activeModifiers.addAll(modifiers);
- return this;
- }
-
- @Override
- public TextGraphics clearModifiers() {
- this.activeModifiers.clear();
- return this;
- }
-
- @Override
- public EnumSet<SGR> getActiveModifiers() {
- return activeModifiers;
- }
-
- @Override
- public TabBehaviour getTabBehaviour() {
- return tabBehaviour;
- }
-
- @Override
- public TextGraphics setTabBehaviour(TabBehaviour tabBehaviour) {
- if(tabBehaviour != null) {
- this.tabBehaviour = tabBehaviour;
- }
- return this;
- }
-
- @Override
- public TextGraphics fill(char c) {
- fillRectangle(TerminalPosition.TOP_LEFT_CORNER, getSize(), c);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(int column, int row, char character) {
- return setCharacter(column, row, newTextCharacter(character));
- }
-
- @Override
- public TextGraphics setCharacter(TerminalPosition position, TextCharacter textCharacter) {
- setCharacter(position.getColumn(), position.getRow(), textCharacter);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(TerminalPosition position, char character) {
- return setCharacter(position.getColumn(), position.getRow(), character);
- }
-
- @Override
- public TextGraphics drawLine(TerminalPosition fromPosition, TerminalPosition toPoint, char character) {
- return drawLine(fromPosition, toPoint, newTextCharacter(character));
- }
-
- @Override
- public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, TextCharacter character) {
- shapeRenderer.drawLine(fromPoint, toPoint, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, char character) {
- return drawLine(fromX, fromY, toX, toY, newTextCharacter(character));
- }
-
- @Override
- public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, TextCharacter character) {
- return drawLine(new TerminalPosition(fromX, fromY), new TerminalPosition(toX, toY), character);
- }
-
- @Override
- public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- return drawTriangle(p1, p2, p3, newTextCharacter(character));
- }
-
- @Override
- public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- shapeRenderer.drawTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- return fillTriangle(p1, p2, p3, newTextCharacter(character));
- }
-
- @Override
- public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- shapeRenderer.fillTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- return drawRectangle(topLeft, size, newTextCharacter(character));
- }
-
- @Override
- public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- shapeRenderer.drawRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- return fillRectangle(topLeft, size, newTextCharacter(character));
- }
-
- @Override
- public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- shapeRenderer.fillRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics drawImage(TerminalPosition topLeft, TextImage image) {
- return drawImage(topLeft, image, TerminalPosition.TOP_LEFT_CORNER, image.getSize());
- }
-
- @Override
- public TextGraphics drawImage(
- TerminalPosition topLeft,
- TextImage image,
- TerminalPosition sourceImageTopLeft,
- TerminalSize sourceImageSize) {
-
- // If the source image position is negative, offset the whole image
- if(sourceImageTopLeft.getColumn() < 0) {
- topLeft = topLeft.withRelativeColumn(-sourceImageTopLeft.getColumn());
- sourceImageSize = sourceImageSize.withRelativeColumns(sourceImageTopLeft.getColumn());
- sourceImageTopLeft = sourceImageTopLeft.withColumn(0);
- }
- if(sourceImageTopLeft.getRow() < 0) {
- topLeft = topLeft.withRelativeRow(-sourceImageTopLeft.getRow());
- sourceImageSize = sourceImageSize.withRelativeRows(sourceImageTopLeft.getRow());
- sourceImageTopLeft = sourceImageTopLeft.withRow(0);
- }
-
- // cropping specified image-subrectangle to the image itself:
- int fromRow = Math.max(sourceImageTopLeft.getRow(), 0);
- int untilRow = Math.min(sourceImageTopLeft.getRow() + sourceImageSize.getRows(), image.getSize().getRows());
- int fromColumn = Math.max(sourceImageTopLeft.getColumn(), 0);
- int untilColumn = Math.min(sourceImageTopLeft.getColumn() + sourceImageSize.getColumns(), image.getSize().getColumns());
-
- // difference between position in image and position on target:
- int diffRow = topLeft.getRow() - sourceImageTopLeft.getRow();
- int diffColumn = topLeft.getColumn() - sourceImageTopLeft.getColumn();
-
- // top/left-crop at target(TextGraphics) rectangle: (only matters, if topLeft has a negative coordinate)
- fromRow = Math.max(fromRow, -diffRow);
- fromColumn = Math.max(fromColumn, -diffColumn);
-
- // bot/right-crop at target(TextGraphics) rectangle: (only matters, if topLeft has a negative coordinate)
- untilRow = Math.min(untilRow, getSize().getRows() - diffRow);
- untilColumn = Math.min(untilColumn, getSize().getColumns() - diffColumn);
-
- if (fromRow >= untilRow || fromColumn >= untilColumn) {
- return this;
- }
- for (int row = fromRow; row < untilRow; row++) {
- for (int column = fromColumn; column < untilColumn; column++) {
- setCharacter(column + diffColumn, row + diffRow, image.getCharacterAt(column, row));
- }
- }
- return this;
- }
-
- @Override
- public TextGraphics putString(int column, int row, String string) {
- if(string.contains("\n")) {
- string = string.substring(0, string.indexOf("\n"));
- }
- if(string.contains("\r")) {
- string = string.substring(0, string.indexOf("\r"));
- }
- string = tabBehaviour.replaceTabs(string, column);
- int offset = 0;
- for(int i = 0; i < string.length(); i++) {
- char character = string.charAt(i);
- setCharacter(
- column + offset,
- row,
- new TextCharacter(
- character,
- foregroundColor,
- backgroundColor,
- activeModifiers.clone()));
-
- if(TerminalTextUtils.isCharCJK(character)) {
- //CJK characters are twice the normal characters in width, so next character position is two columns forward
- offset += 2;
- }
- else {
- //For "normal" characters we advance to the next column
- offset += 1;
- }
- }
- return this;
- }
-
- @Override
- public TextGraphics putString(TerminalPosition position, String string) {
- putString(position.getColumn(), position.getRow(), string);
- return this;
- }
-
- @Override
- public TextGraphics putString(int column, int row, String string, SGR extraModifier, SGR... optionalExtraModifiers) {clearModifiers();
- return putString(column, row, string, EnumSet.of(extraModifier, optionalExtraModifiers));
- }
-
- @Override
- public TextGraphics putString(int column, int row, String string, Collection<SGR> extraModifiers) {
- extraModifiers.removeAll(activeModifiers);
- enableModifiers(extraModifiers);
- putString(column, row, string);
- disableModifiers(extraModifiers);
- return this;
- }
-
- @Override
- public TextGraphics putString(TerminalPosition position, String string, SGR extraModifier, SGR... optionalExtraModifiers) {
- putString(position.getColumn(), position.getRow(), string, extraModifier, optionalExtraModifiers);
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(TerminalPosition position) {
- return getCharacter(position.getColumn(), position.getRow());
- }
-
- @Override
- public TextGraphics newTextGraphics(TerminalPosition topLeftCorner, TerminalSize size) throws IllegalArgumentException {
- TerminalSize writableArea = getSize();
- if(topLeftCorner.getColumn() + size.getColumns() <= 0 ||
- topLeftCorner.getColumn() >= writableArea.getColumns() ||
- topLeftCorner.getRow() + size.getRows() <= 0 ||
- topLeftCorner.getRow() >= writableArea.getRows()) {
- //The area selected is completely outside of this TextGraphics, so we can return a "null" object that doesn't
- //do anything because it is impossible to change anything anyway
- return new NullTextGraphics(size);
- }
- return new SubTextGraphics(this, topLeftCorner, size);
- }
-
- private TextCharacter newTextCharacter(char character) {
- return new TextCharacter(character, foregroundColor, backgroundColor, activeModifiers);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import java.util.Arrays;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.TextColor;
-
-/**
- * Simple implementation of TextImage that keeps the content as a two-dimensional TextCharacter array. Copy operations
- * between two BasicTextImage classes are semi-optimized by using System.arraycopy instead of iterating over each
- * character and copying them over one by one.
- * @author martin
- */
-public class BasicTextImage implements TextImage {
- private final TerminalSize size;
- private final TextCharacter[][] buffer;
-
- /**
- * Creates a new BasicTextImage with the specified size and fills it initially with space characters using the
- * default foreground and background color
- * @param columns Size of the image in number of columns
- * @param rows Size of the image in number of rows
- */
- public BasicTextImage(int columns, int rows) {
- this(new TerminalSize(columns, rows));
- }
-
- /**
- * Creates a new BasicTextImage with the specified size and fills it initially with space characters using the
- * default foreground and background color
- * @param size Size to make the image
- */
- public BasicTextImage(TerminalSize size) {
- this(size, new TextCharacter(' ', TextColor.ANSI.DEFAULT, TextColor.ANSI.DEFAULT));
- }
-
- /**
- * Creates a new BasicTextImage with a given size and a TextCharacter to initially fill it with
- * @param size Size of the image
- * @param initialContent What character to set as the initial content
- */
- public BasicTextImage(TerminalSize size, TextCharacter initialContent) {
- this(size, new TextCharacter[0][], initialContent);
- }
-
- /**
- * Creates a new BasicTextImage by copying a region of a two-dimensional array of TextCharacter:s. If the area to be
- * copied to larger than the source array, a filler character is used.
- * @param size Size to create the new BasicTextImage as (and size to copy from the array)
- * @param toCopy Array to copy initial data from
- * @param initialContent Filler character to use if the source array is smaller than the requested size
- */
- private BasicTextImage(TerminalSize size, TextCharacter[][] toCopy, TextCharacter initialContent) {
- if(size == null || toCopy == null || initialContent == null) {
- throw new IllegalArgumentException("Cannot create BasicTextImage with null " +
- (size == null ? "size" : (toCopy == null ? "toCopy" : "filler")));
- }
- this.size = size;
-
- int rows = size.getRows();
- int columns = size.getColumns();
- buffer = new TextCharacter[rows][];
- for(int y = 0; y < rows; y++) {
- buffer[y] = new TextCharacter[columns];
- for(int x = 0; x < columns; x++) {
- if(y < toCopy.length && x < toCopy[y].length) {
- buffer[y][x] = toCopy[y][x];
- }
- else {
- buffer[y][x] = initialContent;
- }
- }
- }
- }
-
- @Override
- public TerminalSize getSize() {
- return size;
- }
-
- @Override
- public void setAll(TextCharacter character) {
- if(character == null) {
- throw new IllegalArgumentException("Cannot call BasicTextImage.setAll(..) with null character");
- }
- for(TextCharacter[] line : buffer) {
- Arrays.fill(line, character);
- }
- }
-
- @Override
- public BasicTextImage resize(TerminalSize newSize, TextCharacter filler) {
- if(newSize == null || filler == null) {
- throw new IllegalArgumentException("Cannot resize BasicTextImage with null " +
- (newSize == null ? "newSize" : "filler"));
- }
- if(newSize.getRows() == buffer.length &&
- (buffer.length == 0 || newSize.getColumns() == buffer[0].length)) {
- return this;
- }
- return new BasicTextImage(newSize, buffer, filler);
- }
-
- @Override
- public void setCharacterAt(TerminalPosition position, TextCharacter character) {
- if(position == null) {
- throw new IllegalArgumentException("Cannot call BasicTextImage.setCharacterAt(..) with null position");
- }
- setCharacterAt(position.getColumn(), position.getRow(), character);
- }
-
- @Override
- public void setCharacterAt(int column, int row, TextCharacter character) {
- if(character == null) {
- throw new IllegalArgumentException("Cannot call BasicTextImage.setCharacterAt(..) with null character");
- }
- if(column < 0 || row < 0 || row >= buffer.length || column >= buffer[0].length) {
- return;
- }
-
- buffer[row][column] = character;
- }
-
- @Override
- public TextCharacter getCharacterAt(TerminalPosition position) {
- if(position == null) {
- throw new IllegalArgumentException("Cannot call BasicTextImage.getCharacterAt(..) with null position");
- }
- return getCharacterAt(position.getColumn(), position.getRow());
- }
-
- @Override
- public TextCharacter getCharacterAt(int column, int row) {
- if(column < 0 || row < 0 || row >= buffer.length || column >= buffer[0].length) {
- return null;
- }
-
- return buffer[row][column];
- }
-
- @Override
- public void copyTo(TextImage destination) {
- copyTo(destination, 0, buffer.length, 0, buffer[0].length, 0, 0);
- }
-
- @Override
- public void copyTo(
- TextImage destination,
- int startRowIndex,
- int rows,
- int startColumnIndex,
- int columns,
- int destinationRowOffset,
- int destinationColumnOffset) {
-
- // If the source image position is negative, offset the whole image
- if(startColumnIndex < 0) {
- destinationColumnOffset += -startColumnIndex;
- columns += startColumnIndex;
- startColumnIndex = 0;
- }
- if(startRowIndex < 0) {
- startRowIndex += -startRowIndex;
- rows = startRowIndex;
- startRowIndex = 0;
- }
-
- // If the destination offset is negative, adjust the source start indexes
- if(destinationColumnOffset < 0) {
- startColumnIndex -= destinationColumnOffset;
- columns += destinationColumnOffset;
- destinationColumnOffset = 0;
- }
- if(destinationRowOffset < 0) {
- startRowIndex -= destinationRowOffset;
- rows += destinationRowOffset;
- destinationRowOffset = 0;
- }
-
- //Make sure we can't copy more than is available
- columns = Math.min(buffer[0].length - startColumnIndex, columns);
- rows = Math.min(buffer.length - startRowIndex, rows);
-
- //Adjust target lengths as well
- columns = Math.min(destination.getSize().getColumns() - destinationColumnOffset, columns);
- rows = Math.min(destination.getSize().getRows() - destinationRowOffset, rows);
-
- if(columns <= 0 || rows <= 0) {
- return;
- }
-
- TerminalSize destinationSize = destination.getSize();
- if(destination instanceof BasicTextImage) {
- int targetRow = destinationRowOffset;
- for(int y = startRowIndex; y < startRowIndex + rows && targetRow < destinationSize.getRows(); y++) {
- System.arraycopy(buffer[y], startColumnIndex, ((BasicTextImage)destination).buffer[targetRow++], destinationColumnOffset, columns);
- }
- }
- else {
- //Manually copy character by character
- for(int y = startRowIndex; y < startRowIndex + rows; y++) {
- for(int x = startColumnIndex; x < startColumnIndex + columns; x++) {
- destination.setCharacterAt(
- x - startColumnIndex + destinationColumnOffset,
- y - startRowIndex + destinationRowOffset,
- buffer[y][x]);
- }
- }
- }
- }
-
- @Override
- public TextGraphics newTextGraphics() {
- return new AbstractTextGraphics() {
- @Override
- public TextGraphics setCharacter(int columnIndex, int rowIndex, TextCharacter textCharacter) {
- BasicTextImage.this.setCharacterAt(columnIndex, rowIndex, textCharacter);
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(int column, int row) {
- return BasicTextImage.this.getCharacterAt(column, row);
- }
-
- @Override
- public TerminalSize getSize() {
- return size;
- }
- };
- }
-
- private TextCharacter[] newBlankLine() {
- TextCharacter[] line = new TextCharacter[size.getColumns()];
- Arrays.fill(line, TextCharacter.DEFAULT_CHARACTER);
- return line;
- }
-
- @Override
- public void scrollLines(int firstLine, int lastLine, int distance) {
- if (firstLine < 0) { firstLine = 0; }
- if (lastLine >= size.getRows()) { lastLine = size.getRows() - 1; }
- if (firstLine < lastLine) {
- if (distance > 0) {
- // scrolling up: start with first line as target:
- int curLine = firstLine;
- // copy lines from further "below":
- for (; curLine <= lastLine - distance; curLine++) {
- buffer[curLine] = buffer[curLine+distance];
- }
- // blank out the remaining lines:
- for (; curLine <= lastLine; curLine++) {
- buffer[curLine] = newBlankLine();
- }
- }
- else if (distance < 0) {
- // scrolling down: start with last line as target:
- int curLine = lastLine; distance = -distance;
- // copy lines from further "above":
- for (; curLine >= firstLine + distance; curLine--) {
- buffer[curLine] = buffer[curLine-distance];
- }
- // blank out the remaining lines:
- for (; curLine >= firstLine; curLine--) {
- buffer[curLine] = newBlankLine();
- }
- } /* else: distance == 0 => no-op */
- }
- }
-
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder(size.getRows()*(size.getColumns()+1)+50);
- sb.append('{').append(size.getColumns()).append('x').append(size.getRows()).append('}').append('\n');
- for (TextCharacter[] line : buffer) {
- for (TextCharacter tc : line) {
- sb.append(tc.getCharacter());
- }
- sb.append('\n');
- }
- return sb.toString();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextCharacter;
-
-import java.util.Arrays;
-import java.util.Comparator;
-
-/**
- * Default implementation of ShapeRenderer. This class (and the interface) is mostly here to make the code cleaner in
- * {@code AbstractTextGraphics}.
- * @author Martin
- */
-class DefaultShapeRenderer implements ShapeRenderer {
- interface Callback {
- void onPoint(int column, int row, TextCharacter character);
- }
-
- private final Callback callback;
-
- DefaultShapeRenderer(Callback callback) {
- this.callback = callback;
- }
-
- @Override
- public void drawLine(TerminalPosition p1, TerminalPosition p2, TextCharacter character) {
- //http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
- //Implementation from Graphics Programming Black Book by Michael Abrash
- //Available at http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/graphics-programming-black-book-r1698
- if(p1.getRow() > p2.getRow()) {
- TerminalPosition temp = p1;
- p1 = p2;
- p2 = temp;
- }
- int deltaX = p2.getColumn() - p1.getColumn();
- int deltaY = p2.getRow() - p1.getRow();
- if(deltaX > 0) {
- if(deltaX > deltaY) {
- drawLine0(p1, deltaX, deltaY, true, character);
- }
- else {
- drawLine1(p1, deltaX, deltaY, true, character);
- }
- }
- else {
- deltaX = Math.abs(deltaX);
- if(deltaX > deltaY) {
- drawLine0(p1, deltaX, deltaY, false, character);
- }
- else {
- drawLine1(p1, deltaX, deltaY, false, character);
- }
- }
- }
-
- private void drawLine0(TerminalPosition start, int deltaX, int deltaY, boolean leftToRight, TextCharacter character) {
- int x = start.getColumn();
- int y = start.getRow();
- int deltaYx2 = deltaY * 2;
- int deltaYx2MinusDeltaXx2 = deltaYx2 - (deltaX * 2);
- int errorTerm = deltaYx2 - deltaX;
- callback.onPoint(x, y, character);
- while(deltaX-- > 0) {
- if(errorTerm >= 0) {
- y++;
- errorTerm += deltaYx2MinusDeltaXx2;
- }
- else {
- errorTerm += deltaYx2;
- }
- x += leftToRight ? 1 : -1;
- callback.onPoint(x, y, character);
- }
- }
-
- private void drawLine1(TerminalPosition start, int deltaX, int deltaY, boolean leftToRight, TextCharacter character) {
- int x = start.getColumn();
- int y = start.getRow();
- int deltaXx2 = deltaX * 2;
- int deltaXx2MinusDeltaYx2 = deltaXx2 - (deltaY * 2);
- int errorTerm = deltaXx2 - deltaY;
- callback.onPoint(x, y, character);
- while(deltaY-- > 0) {
- if(errorTerm >= 0) {
- x += leftToRight ? 1 : -1;
- errorTerm += deltaXx2MinusDeltaYx2;
- }
- else {
- errorTerm += deltaXx2;
- }
- y++;
- callback.onPoint(x, y, character);
- }
- }
-
- @Override
- public void drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- drawLine(p1, p2, character);
- drawLine(p2, p3, character);
- drawLine(p3, p1, character);
- }
-
- @Override
- public void drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- TerminalPosition topRight = topLeft.withRelativeColumn(size.getColumns() - 1);
- TerminalPosition bottomRight = topRight.withRelativeRow(size.getRows() - 1);
- TerminalPosition bottomLeft = topLeft.withRelativeRow(size.getRows() - 1);
- drawLine(topLeft, topRight, character);
- drawLine(topRight, bottomRight, character);
- drawLine(bottomRight, bottomLeft, character);
- drawLine(bottomLeft, topLeft, character);
- }
-
- @Override
- public void fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- //I've used the algorithm described here:
- //http://www-users.mat.uni.torun.pl/~wrona/3d_tutor/tri_fillers.html
- TerminalPosition[] points = new TerminalPosition[]{p1, p2, p3};
- Arrays.sort(points, new Comparator<TerminalPosition>() {
- @Override
- public int compare(TerminalPosition o1, TerminalPosition o2) {
- return (o1.getRow() < o2.getRow()) ? -1 : ((o1.getRow() == o2.getRow()) ? 0 : 1);
- }
- });
-
- float dx1, dx2, dx3;
- if (points[1].getRow() - points[0].getRow() > 0) {
- dx1 = (float)(points[1].getColumn() - points[0].getColumn()) / (float)(points[1].getRow() - points[0].getRow());
- }
- else {
- dx1 = 0;
- }
- if (points[2].getRow() - points[0].getRow() > 0) {
- dx2 = (float)(points[2].getColumn() - points[0].getColumn()) / (float)(points[2].getRow() - points[0].getRow());
- }
- else {
- dx2 = 0;
- }
- if (points[2].getRow() - points[1].getRow() > 0) {
- dx3 = (float)(points[2].getColumn() - points[1].getColumn()) / (float)(points[2].getRow() - points[1].getRow());
- }
- else {
- dx3 = 0;
- }
-
- float startX, startY, endX;
- startX = endX = points[0].getColumn();
- startY = points[0].getRow();
- if (dx1 > dx2) {
- for (; startY <= points[1].getRow(); startY++, startX += dx2, endX += dx1) {
- drawLine(new TerminalPosition((int)startX, (int)startY), new TerminalPosition((int)endX, (int)startY), character);
- }
- endX = points[1].getColumn();
- for (; startY <= points[2].getRow(); startY++, startX += dx2, endX += dx3) {
- drawLine(new TerminalPosition((int)startX, (int)startY), new TerminalPosition((int)endX, (int)startY), character);
- }
- } else {
- for (; startY <= points[1].getRow(); startY++, startX += dx1, endX += dx2) {
- drawLine(new TerminalPosition((int)startX, (int)startY), new TerminalPosition((int)endX, (int)startY), character);
- }
- startX = points[1].getColumn();
- startY = points[1].getRow();
- for (; startY <= points[2].getRow(); startY++, startX += dx3, endX += dx2) {
- drawLine(new TerminalPosition((int)startX, (int)startY), new TerminalPosition((int)endX, (int)startY), character);
- }
- }
- }
-
- @Override
- public void fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- for(int y = 0; y < size.getRows(); y++) {
- for(int x = 0; x < size.getColumns(); x++) {
- callback.onPoint(topLeft.getColumn() + x, topLeft.getRow() + y, character);
- }
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * This TextGraphics implementation wraps another TextGraphics and forwards all operations to it, but with a few
- * differences. First of all, each individual character being printed is printed twice. Secondly, if you call
- * {@code getSize()}, it will return a size that has half the width of the underlying TextGraphics. This presents the
- * writable view as somewhat squared, since normally terminal characters are twice as tall as wide. You can see some
- * examples of how this looks by running the Triangle test in {@code com.googlecode.lanterna.screen.ScreenTriangleTest}
- * and compare it when running with the --square parameter and without.
- */
-public class DoublePrintingTextGraphics extends AbstractTextGraphics {
- private final TextGraphics underlyingTextGraphics;
-
- /**
- * Creates a new {@code DoublePrintingTextGraphics} on top of a supplied {@code TextGraphics}
- * @param underlyingTextGraphics backend {@code TextGraphics} to forward all the calls to
- */
- public DoublePrintingTextGraphics(TextGraphics underlyingTextGraphics) {
- this.underlyingTextGraphics = underlyingTextGraphics;
- }
-
- @Override
- public TextGraphics setCharacter(int columnIndex, int rowIndex, TextCharacter textCharacter) {
- columnIndex = columnIndex * 2;
- underlyingTextGraphics.setCharacter(columnIndex, rowIndex, textCharacter);
- underlyingTextGraphics.setCharacter(columnIndex + 1, rowIndex, textCharacter);
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(int columnIndex, int rowIndex) {
- columnIndex = columnIndex * 2;
- return underlyingTextGraphics.getCharacter(columnIndex, rowIndex);
-
- }
-
- @Override
- public TerminalSize getSize() {
- TerminalSize size = underlyingTextGraphics.getSize();
- return size.withColumns(size.getColumns() / 2);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.screen.TabBehaviour;
-
-import java.util.Collection;
-import java.util.EnumSet;
-
-/**
- * Implementation of ThemedTextGraphics that wraps a TextGraphics that all calls are delegated to, except for the
- * method from ThemedTextGraphics which are handled. The theme is set at construction time, but you can create a clone
- * of this object with a different theme.
- * @author Martin
- */
-public class ImmutableThemedTextGraphics implements ThemedTextGraphics {
- private final TextGraphics backend;
- private final Theme theme;
-
- /**
- * Creates a new {@code ImmutableThemedTextGraphics} with a specified backend for all drawing operations and a
- * theme.
- * @param backend Backend to send all drawing operations to
- * @param theme Theme to be associated with this object
- */
- public ImmutableThemedTextGraphics(TextGraphics backend, Theme theme) {
- this.backend = backend;
- this.theme = theme;
- }
-
- /**
- * Returns a new {@code ImmutableThemedTextGraphics} that targets the same backend but with another theme
- * @param theme Theme the new {@code ImmutableThemedTextGraphics} is using
- * @return New {@code ImmutableThemedTextGraphics} object that uses the same backend as this object
- */
- public ImmutableThemedTextGraphics withTheme(Theme theme) {
- return new ImmutableThemedTextGraphics(backend, theme);
- }
-
- /**
- * Returns the underlying {@code TextGraphics} that is handling all drawing operations
- * @return Underlying {@code TextGraphics} that is handling all drawing operations
- */
- public TextGraphics getUnderlyingTextGraphics() {
- return backend;
- }
-
- /**
- * Returns the theme associated with this {@code ImmutableThemedTextGraphics}
- * @return The theme associated with this {@code ImmutableThemedTextGraphics}
- */
- public Theme getTheme() {
- return theme;
- }
-
- @Override
- public ThemeDefinition getThemeDefinition(Class<?> clazz) {
- return theme.getDefinition(clazz);
- }
-
- @Override
- public ImmutableThemedTextGraphics applyThemeStyle(ThemeStyle themeStyle) {
- setForegroundColor(themeStyle.getForeground());
- setBackgroundColor(themeStyle.getBackground());
- setModifiers(themeStyle.getSGRs());
- return this;
- }
-
- @Override
- public TerminalSize getSize() {
- return backend.getSize();
- }
-
- @Override
- public ImmutableThemedTextGraphics newTextGraphics(TerminalPosition topLeftCorner, TerminalSize size) throws IllegalArgumentException {
- return new ImmutableThemedTextGraphics(backend.newTextGraphics(topLeftCorner, size), theme);
- }
-
- @Override
- public TextColor getBackgroundColor() {
- return backend.getBackgroundColor();
- }
-
- @Override
- public ImmutableThemedTextGraphics setBackgroundColor(TextColor backgroundColor) {
- backend.setBackgroundColor(backgroundColor);
- return this;
- }
-
- @Override
- public TextColor getForegroundColor() {
- return backend.getForegroundColor();
- }
-
- @Override
- public ImmutableThemedTextGraphics setForegroundColor(TextColor foregroundColor) {
- backend.setForegroundColor(foregroundColor);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics enableModifiers(SGR... modifiers) {
- backend.enableModifiers(modifiers);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics disableModifiers(SGR... modifiers) {
- backend.disableModifiers(modifiers);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics setModifiers(EnumSet<SGR> modifiers) {
- backend.setModifiers(modifiers);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics clearModifiers() {
- backend.clearModifiers();
- return this;
- }
-
- @Override
- public EnumSet<SGR> getActiveModifiers() {
- return backend.getActiveModifiers();
- }
-
- @Override
- public TabBehaviour getTabBehaviour() {
- return backend.getTabBehaviour();
- }
-
- @Override
- public ImmutableThemedTextGraphics setTabBehaviour(TabBehaviour tabBehaviour) {
- backend.setTabBehaviour(tabBehaviour);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics fill(char c) {
- backend.fill(c);
- return this;
- }
-
- @Override
- public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- backend.fillRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- backend.fillRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- backend.drawRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- backend.drawRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- backend.fillTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- backend.fillTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- backend.drawTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- backend.drawTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, char character) {
- backend.drawLine(fromPoint, toPoint, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, TextCharacter character) {
- backend.drawLine(fromPoint, toPoint, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, char character) {
- backend.drawLine(fromX, fromY, toX, toY, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, TextCharacter character) {
- backend.drawLine(fromX, fromY, toX, toY, character);
- return this;
- }
-
- @Override
- public TextGraphics drawImage(TerminalPosition topLeft, TextImage image) {
- backend.drawImage(topLeft, image);
- return this;
- }
-
- @Override
- public TextGraphics drawImage(TerminalPosition topLeft, TextImage image, TerminalPosition sourceImageTopLeft, TerminalSize sourceImageSize) {
- backend.drawImage(topLeft, image, sourceImageTopLeft, sourceImageSize);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(TerminalPosition position, char character) {
- backend.setCharacter(position, character);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(TerminalPosition position, TextCharacter character) {
- backend.setCharacter(position, character);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(int column, int row, char character) {
- backend.setCharacter(column, row, character);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(int column, int row, TextCharacter character) {
- backend.setCharacter(column, row, character);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics putString(int column, int row, String string) {
- backend.putString(column, row, string);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics putString(TerminalPosition position, String string) {
- backend.putString(position, string);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics putString(int column, int row, String string, SGR extraModifier, SGR... optionalExtraModifiers) {
- backend.putString(column, row, string, extraModifier, optionalExtraModifiers);
- return this;
- }
-
- @Override
- public ImmutableThemedTextGraphics putString(TerminalPosition position, String string, SGR extraModifier, SGR... optionalExtraModifiers) {
- backend.putString(position, string, extraModifier, optionalExtraModifiers);
- return this;
- }
-
- @Override
- public TextGraphics putString(int column, int row, String string, Collection<SGR> extraModifiers) {
- backend.putString(column, row, string, extraModifiers);
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(TerminalPosition position) {
- return backend.getCharacter(position);
- }
-
- @Override
- public TextCharacter getCharacter(int column, int row) {
- return backend.getCharacter(column, row);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.screen.TabBehaviour;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.EnumSet;
-
-/**
- * TextGraphics implementation that does nothing, but has a pre-defined size
- * @author martin
- */
-class NullTextGraphics implements TextGraphics {
- private final TerminalSize size;
- private TextColor foregroundColor;
- private TextColor backgroundColor;
- private TabBehaviour tabBehaviour;
- private final EnumSet<SGR> activeModifiers;
-
- /**
- * Creates a new {@code NullTextGraphics} that will return the specified size value if asked how big it is but other
- * than that ignore all other calls.
- * @param size The size to report
- */
- public NullTextGraphics(TerminalSize size) {
- this.size = size;
- this.foregroundColor = TextColor.ANSI.DEFAULT;
- this.backgroundColor = TextColor.ANSI.DEFAULT;
- this.tabBehaviour = TabBehaviour.ALIGN_TO_COLUMN_4;
- this.activeModifiers = EnumSet.noneOf(SGR.class);
- }
-
- @Override
- public TerminalSize getSize() {
- return size;
- }
-
- @Override
- public TextGraphics newTextGraphics(TerminalPosition topLeftCorner, TerminalSize size) throws IllegalArgumentException {
- return this;
- }
-
- @Override
- public TextColor getBackgroundColor() {
- return backgroundColor;
- }
-
- @Override
- public TextGraphics setBackgroundColor(TextColor backgroundColor) {
- this.backgroundColor = backgroundColor;
- return this;
- }
-
- @Override
- public TextColor getForegroundColor() {
- return foregroundColor;
- }
-
- @Override
- public TextGraphics setForegroundColor(TextColor foregroundColor) {
- this.foregroundColor = foregroundColor;
- return this;
- }
-
- @Override
- public TextGraphics enableModifiers(SGR... modifiers) {
- activeModifiers.addAll(Arrays.asList(modifiers));
- return this;
- }
-
- @Override
- public TextGraphics disableModifiers(SGR... modifiers) {
- activeModifiers.removeAll(Arrays.asList(modifiers));
- return this;
- }
-
- @Override
- public TextGraphics setModifiers(EnumSet<SGR> modifiers) {
- clearModifiers();
- activeModifiers.addAll(modifiers);
- return this;
- }
-
- @Override
- public TextGraphics clearModifiers() {
- activeModifiers.clear();
- return this;
- }
-
- @Override
- public EnumSet<SGR> getActiveModifiers() {
- return EnumSet.copyOf(activeModifiers);
- }
-
- @Override
- public TabBehaviour getTabBehaviour() {
- return tabBehaviour;
- }
-
- @Override
- public TextGraphics setTabBehaviour(TabBehaviour tabBehaviour) {
- this.tabBehaviour = tabBehaviour;
- return this;
- }
-
- @Override
- public TextGraphics fill(char c) {
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(int column, int row, char character) {
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(int column, int row, TextCharacter character) {
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(TerminalPosition position, char character) {
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(TerminalPosition position, TextCharacter character) {
- return this;
- }
-
- @Override
- public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, char character) {
- return this;
- }
-
- @Override
- public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, TextCharacter character) {
- return this;
- }
-
- @Override
- public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, char character) {
- return this;
- }
-
- @Override
- public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, TextCharacter character) {
- return this;
- }
-
- @Override
- public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- return this;
- }
-
- @Override
- public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- return this;
- }
-
- @Override
- public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- return this;
- }
-
- @Override
- public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- return this;
- }
-
- @Override
- public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- return this;
- }
-
- @Override
- public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- return this;
- }
-
- @Override
- public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- return this;
- }
-
- @Override
- public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- return this;
- }
-
- @Override
- public TextGraphics drawImage(TerminalPosition topLeft, TextImage image) {
- return this;
- }
-
- @Override
- public TextGraphics drawImage(TerminalPosition topLeft, TextImage image, TerminalPosition sourceImageTopLeft, TerminalSize sourceImageSize) {
- return this;
- }
-
- @Override
- public TextGraphics putString(int column, int row, String string) {
- return this;
- }
-
- @Override
- public TextGraphics putString(TerminalPosition position, String string) {
- return this;
- }
-
- @Override
- public TextGraphics putString(int column, int row, String string, SGR extraModifier, SGR... optionalExtraModifiers) {
- return this;
- }
-
- @Override
- public TextGraphics putString(TerminalPosition position, String string, SGR extraModifier, SGR... optionalExtraModifiers) {
- return this;
- }
-
- @Override
- public TextGraphics putString(int column, int row, String string, Collection<SGR> extraModifiers) {
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(int column, int row) {
- return null;
- }
-
- @Override
- public TextCharacter getCharacter(TerminalPosition position) {
- return null;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TextColor;
-
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This implementation of Theme reads its definitions from a {@code Properties} object.
- * @author Martin
- */
-public final class PropertiesTheme implements Theme {
- private static final String STYLE_NORMAL = "";
- private static final String STYLE_PRELIGHT = "PRELIGHT";
- private static final String STYLE_SELECTED = "SELECTED";
- private static final String STYLE_ACTIVE = "ACTIVE";
- private static final String STYLE_INSENSITIVE = "INSENSITIVE";
-
- private static final Pattern STYLE_FORMAT = Pattern.compile("([a-zA-Z]+)(\\[([a-zA-Z0-9-_]+)\\])?");
- private static final Pattern INDEXED_COLOR = Pattern.compile("#[0-9]{1,3}");
- private static final Pattern RGB_COLOR = Pattern.compile("#[0-9a-fA-F]{6}");
-
- private final ThemeTreeNode rootNode;
-
- /**
- * Creates a new {@code PropertiesTheme} that is initialized by the properties value
- * @param properties Properties to initialize this theme with
- */
- public PropertiesTheme(Properties properties) {
- rootNode = new ThemeTreeNode();
- rootNode.foregroundMap.put(STYLE_NORMAL, TextColor.ANSI.WHITE);
- rootNode.backgroundMap.put(STYLE_NORMAL, TextColor.ANSI.BLACK);
-
- for(String key: properties.stringPropertyNames()) {
- String definition = getDefinition(key);
- ThemeTreeNode node = getNode(definition);
- node.apply(getStyle(key), properties.getProperty(key));
- }
- }
-
- private ThemeTreeNode getNode(String definition) {
- ThemeTreeNode parentNode;
- if(definition.equals("")) {
- return rootNode;
- }
- else if(definition.contains(".")) {
- String parent = definition.substring(0, definition.lastIndexOf("."));
- parentNode = getNode(parent);
- definition = definition.substring(definition.lastIndexOf(".") + 1);
- }
- else {
- parentNode = rootNode;
- }
- if(!parentNode.childMap.containsKey(definition)) {
- parentNode.childMap.put(definition, new ThemeTreeNode());
- }
- return parentNode.childMap.get(definition);
- }
-
- private String getDefinition(String propertyName) {
- if(!propertyName.contains(".")) {
- return "";
- }
- else {
- return propertyName.substring(0, propertyName.lastIndexOf("."));
- }
- }
-
- private String getStyle(String propertyName) {
- if(!propertyName.contains(".")) {
- return propertyName;
- }
- else {
- return propertyName.substring(propertyName.lastIndexOf(".") + 1);
- }
- }
-
- @Override
- public ThemeDefinition getDefaultDefinition() {
- return new DefinitionImpl(Collections.singletonList(rootNode));
- }
-
- @Override
- public ThemeDefinition getDefinition(Class<?> clazz) {
- String name = clazz.getName();
- List<ThemeTreeNode> path = new ArrayList<ThemeTreeNode>();
- ThemeTreeNode currentNode = rootNode;
- while(!name.equals("")) {
- path.add(currentNode);
- String nextNodeName = name;
- if(nextNodeName.contains(".")) {
- nextNodeName = nextNodeName.substring(0, name.indexOf("."));
- name = name.substring(name.indexOf(".") + 1);
- }
- if(currentNode.childMap.containsKey(nextNodeName)) {
- currentNode = currentNode.childMap.get(nextNodeName);
- }
- else {
- break;
- }
- }
- return new DefinitionImpl(path);
- }
-
-
- private class DefinitionImpl implements ThemeDefinition {
- final List<ThemeTreeNode> path;
-
- DefinitionImpl(List<ThemeTreeNode> path) {
- this.path = path;
- }
-
- @Override
- public ThemeStyle getNormal() {
- return new StyleImpl(path, STYLE_NORMAL);
- }
-
- @Override
- public ThemeStyle getPreLight() {
- return new StyleImpl(path, STYLE_PRELIGHT);
- }
-
- @Override
- public ThemeStyle getSelected() {
- return new StyleImpl(path, STYLE_SELECTED);
- }
-
- @Override
- public ThemeStyle getActive() {
- return new StyleImpl(path, STYLE_ACTIVE);
- }
-
- @Override
- public ThemeStyle getInsensitive() {
- return new StyleImpl(path, STYLE_INSENSITIVE);
- }
-
- @Override
- public ThemeStyle getCustom(String name) {
- ThemeTreeNode lastElement = path.get(path.size() - 1);
- if(lastElement.sgrMap.containsKey(name) ||
- lastElement.foregroundMap.containsKey(name) ||
- lastElement.backgroundMap.containsKey(name)) {
- return new StyleImpl(path, name);
- }
- // If there was no custom style with this name, just return the normal one
- return getNormal();
- }
-
- @Override
- public char getCharacter(String name, char fallback) {
- Character character = path.get(path.size() - 1).characterMap.get(name);
- if(character == null) {
- return fallback;
- }
- return character;
- }
-
- @Override
- public String getRenderer() {
- return path.get(path.size() - 1).renderer;
- }
- }
-
- private class StyleImpl implements ThemeStyle {
- private final List<ThemeTreeNode> path;
- private final String name;
-
- private StyleImpl(List<ThemeTreeNode> path, String name) {
- this.path = path;
- this.name = name;
- }
-
- @Override
- public TextColor getForeground() {
- ListIterator<ThemeTreeNode> iterator = path.listIterator(path.size());
- while(iterator.hasPrevious()) {
- ThemeTreeNode node = iterator.previous();
- if(node.foregroundMap.containsKey(name)) {
- return node.foregroundMap.get(name);
- }
- }
- if(!name.equals(STYLE_NORMAL)) {
- return new StyleImpl(path, STYLE_NORMAL).getForeground();
- }
- return TextColor.ANSI.WHITE;
- }
-
- @Override
- public TextColor getBackground() {
- ListIterator<ThemeTreeNode> iterator = path.listIterator(path.size());
- while(iterator.hasPrevious()) {
- ThemeTreeNode node = iterator.previous();
- if(node.backgroundMap.containsKey(name)) {
- return node.backgroundMap.get(name);
- }
- }
- if(!name.equals(STYLE_NORMAL)) {
- return new StyleImpl(path, STYLE_NORMAL).getBackground();
- }
- return TextColor.ANSI.BLACK;
- }
-
- @Override
- public EnumSet<SGR> getSGRs() {
- ListIterator<ThemeTreeNode> iterator = path.listIterator(path.size());
- while(iterator.hasPrevious()) {
- ThemeTreeNode node = iterator.previous();
- if(node.sgrMap.containsKey(name)) {
- return node.sgrMap.get(name);
- }
- }
- if(!name.equals(STYLE_NORMAL)) {
- return new StyleImpl(path, STYLE_NORMAL).getSGRs();
- }
- return EnumSet.noneOf(SGR.class);
- }
- }
-
- private static class ThemeTreeNode {
- private final Map<String, ThemeTreeNode> childMap;
- private final Map<String, TextColor> foregroundMap;
- private final Map<String, TextColor> backgroundMap;
- private final Map<String, EnumSet<SGR>> sgrMap;
- private final Map<String, Character> characterMap;
- private String renderer;
-
- private ThemeTreeNode() {
- childMap = new HashMap<String, ThemeTreeNode>();
- foregroundMap = new HashMap<String, TextColor>();
- backgroundMap = new HashMap<String, TextColor>();
- sgrMap = new HashMap<String, EnumSet<SGR>>();
- characterMap = new HashMap<String, Character>();
- renderer = null;
- }
-
- public void apply(String style, String value) {
- value = value.trim();
- Matcher matcher = STYLE_FORMAT.matcher(style);
- if(!matcher.matches()) {
- throw new IllegalArgumentException("Unknown style declaration: " + style);
- }
- String styleComponent = matcher.group(1);
- String group = matcher.groupCount() > 2 ? matcher.group(3) : null;
- if(styleComponent.toLowerCase().trim().equals("foreground")) {
- foregroundMap.put(getCategory(group), parseValue(value));
- }
- else if(styleComponent.toLowerCase().trim().equals("background")) {
- backgroundMap.put(getCategory(group), parseValue(value));
- }
- else if(styleComponent.toLowerCase().trim().equals("sgr")) {
- sgrMap.put(getCategory(group), parseSGR(value));
- }
- else if(styleComponent.toLowerCase().trim().equals("char")) {
- characterMap.put(getCategory(group), value.isEmpty() ? null : value.charAt(0));
- }
- else if(styleComponent.toLowerCase().trim().equals("renderer")) {
- renderer = value.trim().isEmpty() ? null : value.trim();
- }
- else {
- throw new IllegalArgumentException("Unknown style component \"" + styleComponent + "\" in style \"" + style + "\"");
- }
- }
-
- private TextColor parseValue(String value) {
- value = value.trim();
- if(RGB_COLOR.matcher(value).matches()) {
- int r = Integer.parseInt(value.substring(1, 3), 16);
- int g = Integer.parseInt(value.substring(3, 5), 16);
- int b = Integer.parseInt(value.substring(5, 7), 16);
- return new TextColor.RGB(r, g, b);
- }
- else if(INDEXED_COLOR.matcher(value).matches()) {
- int index = Integer.parseInt(value.substring(1));
- return new TextColor.Indexed(index);
- }
- try {
- return TextColor.ANSI.valueOf(value.toUpperCase());
- }
- catch(IllegalArgumentException e) {
- throw new IllegalArgumentException("Unknown color definition \"" + value + "\"", e);
- }
- }
-
- private EnumSet<SGR> parseSGR(String value) {
- value = value.trim();
- String[] sgrEntries = value.split(",");
- EnumSet<SGR> sgrSet = EnumSet.noneOf(SGR.class);
- for(String entry: sgrEntries) {
- entry = entry.trim().toUpperCase();
- if(!entry.isEmpty()) {
- try {
- sgrSet.add(SGR.valueOf(entry));
- }
- catch(IllegalArgumentException e) {
- throw new IllegalArgumentException("Unknown SGR code \"" + entry + "\"", e);
- }
- }
- }
- return sgrSet;
- }
-
- private String getCategory(String group) {
- if(group == null) {
- return STYLE_NORMAL;
- }
- for(String style: Arrays.asList(STYLE_ACTIVE, STYLE_INSENSITIVE, STYLE_PRELIGHT, STYLE_NORMAL, STYLE_SELECTED)) {
- if(group.toUpperCase().equals(style)) {
- return style;
- }
- }
- return group;
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.graphics;
-
-import java.io.IOException;
-
-/**
- * Describes an area that can be 'scrolled', by moving a range of lines up or down. Certain terminals will implement
- * this through extensions and are much faster than if lanterna tries to manually erase and re-print the text.
- *
- * @author Andreas
- */
-public interface Scrollable {
- /**
- * Scroll a range of lines of this Scrollable according to given distance.
- *
- * If scroll-range is empty (firstLine > lastLine || distance == 0) then
- * this method does nothing.
- *
- * Lines that are scrolled away from are cleared.
- *
- * If absolute value of distance is equal or greater than number of lines
- * in range, then all lines within the range will be cleared.
- *
- * @param firstLine first line of the range to be scrolled (top line is 0)
- * @param lastLine last (inclusive) line of the range to be scrolled
- * @param distance if > 0: move lines up, else if < 0: move lines down.
- */
- void scrollLines(int firstLine, int lastLine, int distance) throws IOException;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextCharacter;
-
-/**
- * This package private interface exposes methods for translating abstract lines, triangles and rectangles to discreet
- * points on a grid.
- * @author Martin
- */
-interface ShapeRenderer {
- void drawLine(TerminalPosition p1, TerminalPosition p2, TextCharacter character);
- void drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character);
- void drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character);
- void fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character);
- void fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * This implementation of TextGraphics will take a 'proper' object and composite a view on top of it, by using a
- * top-left position and a size. Any attempts to put text outside of this area will be dropped.
- * @author Martin
- */
-class SubTextGraphics extends AbstractTextGraphics {
- private final TextGraphics underlyingTextGraphics;
- private final TerminalPosition topLeft;
- private final TerminalSize writableAreaSize;
-
- SubTextGraphics(TextGraphics underlyingTextGraphics, TerminalPosition topLeft, TerminalSize writableAreaSize) {
- this.underlyingTextGraphics = underlyingTextGraphics;
- this.topLeft = topLeft;
- this.writableAreaSize = writableAreaSize;
- }
-
- private TerminalPosition project(int column, int row) {
- return topLeft.withRelative(column, row);
- }
-
- @Override
- public TextGraphics setCharacter(int columnIndex, int rowIndex, TextCharacter textCharacter) {
- TerminalSize writableArea = getSize();
- if(columnIndex < 0 || columnIndex >= writableArea.getColumns() ||
- rowIndex < 0 || rowIndex >= writableArea.getRows()) {
- return this;
- }
- TerminalPosition projectedPosition = project(columnIndex, rowIndex);
- underlyingTextGraphics.setCharacter(projectedPosition, textCharacter);
- return this;
- }
-
- @Override
- public TerminalSize getSize() {
- return writableAreaSize;
- }
-
- @Override
- public TextCharacter getCharacter(int column, int row) {
- TerminalPosition projectedPosition = project(column, row);
- return underlyingTextGraphics.getCharacter(projectedPosition.getColumn(), projectedPosition.getRow());
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.screen.TabBehaviour;
-
-import java.util.Collection;
-import java.util.EnumSet;
-
-/**
- * This interface exposes functionality to 'draw' text graphics on a section of the terminal. It has several
- * implementation for the different levels, including one for Terminal, one for Screen and one which is used by the
- * TextGUI system to draw components. They are all very similar and has a lot of graphics functionality in
- * AbstractTextGraphics.
- * <p>
- * The basic concept behind a TextGraphics implementation is that it keeps a state on four things:
- * <ul>
- * <li>Foreground color</li>
- * <li>Background color</li>
- * <li>Modifiers</li>
- * <li>Tab-expanding behaviour</li>
- * </ul>
- * These call all be altered through ordinary set* methods, but some will be altered as the result of performing one of
- * the 'drawing' operations. See the documentation to each method for further information (for example, putString).
- * <p>
- * Don't hold on to your TextGraphics objects for too long; ideally create them and let them be GC:ed when you are done
- * with them. The reason is that not all implementations will handle the underlying terminal changing size.
- * @author Martin
- */
-public interface TextGraphics {
- /**
- * Returns the size of the area that this text graphic can write to. Any attempts of placing characters outside of
- * this area will be silently ignored.
- * @return Size of the writable area that this TextGraphics can write too
- */
- TerminalSize getSize();
-
- /**
- * Creates a new TextGraphics of the same type as this one, using the same underlying subsystem. Using this method,
- * you need to specify a section of the current TextGraphics valid area that this new TextGraphic shall be
- * restricted to. If you call <code>newTextGraphics(TerminalPosition.TOP_LEFT_CORNER, textGraphics.getSize())</code>
- * then the resulting object will be identical to this one, but having a separated state for colors, position and
- * modifiers.
- * @param topLeftCorner Position of this TextGraphics's writable area that is to become the top-left corner (0x0) of
- * the new TextGraphics
- * @param size How large area, counted from the topLeftCorner, the new TextGraphics can write to. This cannot be
- * larger than the current TextGraphics's writable area (adjusted by topLeftCorner)
- * @return A new TextGraphics with the same underlying subsystem, that can write to only the specified area
- * @throws java.lang.IllegalArgumentException If the size the of new TextGraphics exceeds the dimensions of this
- * TextGraphics in any way.
- */
- TextGraphics newTextGraphics(TerminalPosition topLeftCorner, TerminalSize size) throws IllegalArgumentException;
-
- /**
- * Returns the current background color
- * @return Current background color
- */
- TextColor getBackgroundColor();
-
- /**
- * Updates the current background color
- * @param backgroundColor New background color
- * @return Itself
- */
- TextGraphics setBackgroundColor(TextColor backgroundColor);
-
- /**
- * Returns the current foreground color
- * @return Current foreground color
- */
- TextColor getForegroundColor();
-
- /**
- * Updates the current foreground color
- * @param foregroundColor New foreground color
- * @return Itself
- */
- TextGraphics setForegroundColor(TextColor foregroundColor);
-
- /**
- * Adds zero or more modifiers to the set of currently active modifiers
- * @param modifiers Modifiers to add to the set of currently active modifiers
- * @return Itself
- */
- TextGraphics enableModifiers(SGR... modifiers);
-
- /**
- * Removes zero or more modifiers from the set of currently active modifiers
- * @param modifiers Modifiers to remove from the set of currently active modifiers
- * @return Itself
- */
- TextGraphics disableModifiers(SGR... modifiers);
-
- /**
- * Sets the active modifiers to exactly the set passed in to this method. Any previous state of which modifiers are
- * enabled doesn't matter.
- * @param modifiers Modifiers to set as active
- * @return Itself
- */
- TextGraphics setModifiers(EnumSet<SGR> modifiers);
-
- /**
- * Removes all active modifiers
- * @return Itself
- */
- TextGraphics clearModifiers();
-
- /**
- * Returns all the SGR codes that are currently active in the TextGraphic
- * @return Currently active SGR modifiers
- */
- EnumSet<SGR> getActiveModifiers();
-
- /**
- * Retrieves the current tab behaviour, which is what the TextGraphics will use when expanding \t characters to
- * spaces.
- * @return Current behaviour in use for expanding tab to spaces
- */
- TabBehaviour getTabBehaviour();
-
- /**
- * Sets the behaviour to use when expanding tab characters (\t) to spaces
- * @param tabBehaviour Behaviour to use when expanding tabs to spaces
- */
- TextGraphics setTabBehaviour(TabBehaviour tabBehaviour);
-
- /**
- * Fills the entire writable area with a single character, using current foreground color, background color and modifiers.
- * @param c Character to fill the writable area with
- */
- TextGraphics fill(char c);
-
- /**
- * Sets the character at the current position to the specified value
- * @param column column of the location to set the character
- * @param row row of the location to set the character
- * @param character Character to set at the current position
- * @return Itself
- */
- TextGraphics setCharacter(int column, int row, char character);
-
- /**
- * Sets the character at the current position to the specified value, without using the current colors and modifiers
- * of this TextGraphics.
- * @param column column of the location to set the character
- * @param row row of the location to set the character
- * @param character Character data to set at the current position
- * @return Itself
- */
- TextGraphics setCharacter(int column, int row, TextCharacter character);
-
- /**
- * Sets the character at the current position to the specified value
- * @param position position of the location to set the character
- * @param character Character to set at the current position
- * @return Itself
- */
- TextGraphics setCharacter(TerminalPosition position, char character);
-
- /**
- * Sets the character at the current position to the specified value, without using the current colors and modifiers
- * of this TextGraphics.
- * @param position position of the location to set the character
- * @param character Character data to set at the current position
- * @return Itself
- */
- TextGraphics setCharacter(TerminalPosition position, TextCharacter character);
-
- /**
- * Draws a line from a specified position to a specified position, using a supplied character. The current
- * foreground color, background color and modifiers will be applied.
- * @param fromPoint From where to draw the line
- * @param toPoint Where to draw the line
- * @param character Character to use for the line
- * @return Itself
- */
- TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, char character);
-
- /**
- * Draws a line from a specified position to a specified position, using a supplied TextCharacter. The current
- * foreground color, background color and modifiers of this TextGraphics will not be used and will not be modified
- * by this call.
- * @param fromPoint From where to draw the line
- * @param toPoint Where to draw the line
- * @param character Character data to use for the line, including character, colors and modifiers
- * @return Itself
- */
- TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, TextCharacter character);
-
- /**
- * Draws a line from a specified position to a specified position, using a supplied character. The current
- * foreground color, background color and modifiers will be applied.
- * @param fromX Column of the starting position to draw the line from (inclusive)
- * @param fromY Row of the starting position to draw the line from (inclusive)
- * @param toX Column of the end position to draw the line to (inclusive)
- * @param toY Row of the end position to draw the line to (inclusive)
- * @param character Character to use for the line
- * @return Itself
- */
- TextGraphics drawLine(int fromX, int fromY, int toX, int toY, char character);
-
- /**
- * Draws a line from a specified position to a specified position, using a supplied character. The current
- * foreground color, background color and modifiers of this TextGraphics will not be used and will not be modified
- * by this call.
- * @param fromX Column of the starting position to draw the line from (inclusive)
- * @param fromY Row of the starting position to draw the line from (inclusive)
- * @param toX Column of the end position to draw the line to (inclusive)
- * @param toY Row of the end position to draw the line to (inclusive)
- * @param character Character data to use for the line, including character, colors and modifiers
- * @return Itself
- */
- TextGraphics drawLine(int fromX, int fromY, int toX, int toY, TextCharacter character);
-
- /**
- * Draws the outline of a triangle on the screen, using a supplied character. The triangle will begin at p1, go
- * through p2 and then p3 and then back to p1. The current foreground color, background color and modifiers will be
- * applied.
- * @param p1 First point on the screen of the triangle
- * @param p2 Second point on the screen of the triangle
- * @param p3 Third point on the screen of the triangle
- * @param character What character to use when drawing the lines of the triangle
- */
- TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character);
-
- /**
- * Draws the outline of a triangle on the screen, using a supplied character. The triangle will begin at p1, go
- * through p2 and then p3 and then back to p1. The current foreground color, background color and modifiers of this
- * TextGraphics will not be used and will not be modified by this call.
- * @param p1 First point on the screen of the triangle
- * @param p2 Second point on the screen of the triangle
- * @param p3 Third point on the screen of the triangle
- * @param character What character data to use when drawing the lines of the triangle
- */
- TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character);
-
- /**
- * Draws a filled triangle, using a supplied character. The triangle will begin at p1, go
- * through p2 and then p3 and then back to p1. The current foreground color, background color and modifiers will be
- * applied.
- * @param p1 First point on the screen of the triangle
- * @param p2 Second point on the screen of the triangle
- * @param p3 Third point on the screen of the triangle
- * @param character What character to use when drawing the triangle
- */
- TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character);
-
- /**
- * Draws a filled triangle, using a supplied character. The triangle will begin at p1, go
- * through p2 and then p3 and then back to p1. The current foreground color, background color and modifiers of this
- * TextGraphics will not be used and will not be modified by this call.
- * @param p1 First point on the screen of the triangle
- * @param p2 Second point on the screen of the triangle
- * @param p3 Third point on the screen of the triangle
- * @param character What character data to use when drawing the triangle
- */
- TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character);
-
- /**
- * Draws the outline of a rectangle with a particular character (and the currently active colors and
- * modifiers). The topLeft coordinate is inclusive.
- * <p>
- * For example, calling drawRectangle with size being the size of the terminal and top-left value being the terminal's
- * top-left (0x0) corner will draw a border around the terminal.
- * <p>
- * The current foreground color, background color and modifiers will be applied.
- * @param topLeft Coordinates of the top-left position of the rectangle
- * @param size Size (in columns and rows) of the area to draw
- * @param character What character to use when drawing the outline of the rectangle
- */
- TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, char character);
-
- /**
- * Draws the outline of a rectangle with a particular TextCharacter, ignoring the current colors and modifiers of
- * this TextGraphics.
- * <p>
- * For example, calling drawRectangle with size being the size of the terminal and top-left value being the terminal's
- * top-left (0x0) corner will draw a border around the terminal.
- * <p>
- * The current foreground color, background color and modifiers will not be modified by this call.
- * @param topLeft Coordinates of the top-left position of the rectangle
- * @param size Size (in columns and rows) of the area to draw
- * @param character What character data to use when drawing the outline of the rectangle
- */
- TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character);
-
- /**
- * Takes a rectangle and fills it with a particular character (and the currently active colors and
- * modifiers). The topLeft coordinate is inclusive.
- * <p>
- * For example, calling fillRectangle with size being the size of the terminal and top-left value being the terminal's
- * top-left (0x0) corner will fill the entire terminal with this character.
- * <p>
- * The current foreground color, background color and modifiers will be applied.
- * @param topLeft Coordinates of the top-left position of the rectangle
- * @param size Size (in columns and rows) of the area to draw
- * @param character What character to use when filling the rectangle
- */
- TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, char character);
-
- /**
- * Takes a rectangle and fills it using a particular TextCharacter, ignoring the current colors and modifiers of
- * this TextGraphics. The topLeft coordinate is inclusive.
- * <p>
- * For example, calling fillRectangle with size being the size of the terminal and top-left value being the terminal's
- * top-left (0x0) corner will fill the entire terminal with this character.
- * <p>
- * The current foreground color, background color and modifiers will not be modified by this call.
- * @param topLeft Coordinates of the top-left position of the rectangle
- * @param size Size (in columns and rows) of the area to draw
- * @param character What character data to use when filling the rectangle
- */
- TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character);
-
- /**
- * Takes a TextImage and draws it on the surface this TextGraphics is targeting, given the coordinates on the target
- * that is specifying where the top-left corner of the image should be drawn. This is equivalent of calling
- * {@code drawImage(topLeft, image, TerminalPosition.TOP_LEFT_CORNER, image.getSize()}.
- * @param topLeft Position of the top-left corner of the image on the target
- * @param image Image to draw
- * @return Itself
- */
- TextGraphics drawImage(TerminalPosition topLeft, TextImage image);
-
- /**
- * Takes a TextImage and draws it on the surface this TextGraphics is targeting, given the coordinates on the target
- * that is specifying where the top-left corner of the image should be drawn. This overload will only draw a portion
- * of the image to the target, as specified by the two last parameters.
- * @param topLeft Position of the top-left corner of the image on the target
- * @param image Image to draw
- * @param sourceImageTopLeft Position of the top-left corner in the source image to draw at the topLeft position on
- * the target
- * @param sourceImageSize How much of the source image to draw on the target, counted from the sourceImageTopLeft
- * position
- * @return Itself
- */
- TextGraphics drawImage(TerminalPosition topLeft, TextImage image, TerminalPosition sourceImageTopLeft, TerminalSize sourceImageSize);
-
- /**
- * Puts a string on the screen at the specified position with the current colors and modifiers. If the string
- * contains newlines (\r and/or \n), the method will stop at the character before that; you have to manage
- * multi-line strings yourself! The current foreground color, background color and modifiers will be applied.
- * @param column What column to put the string at
- * @param row What row to put the string at
- * @param string String to put on the screen
- * @return Itself
- */
- TextGraphics putString(int column, int row, String string);
-
- /**
- * Shortcut to calling:
- * <pre>
- * putString(position.getColumn(), position.getRow(), string);
- * </pre>
- * @param position Position to put the string at
- * @param string String to put on the screen
- * @return Itself
- */
- TextGraphics putString(TerminalPosition position, String string);
-
- /**
- * Puts a string on the screen at the specified position with the current colors and modifiers. If the string
- * contains newlines (\r and/or \n), the method will stop at the character before that; you have to manage
- * multi-line strings yourself! If you supplied any extra modifiers, they will be applied when writing the string
- * as well but not recorded into the state of the TextGraphics object.
- * @param column What column to put the string at
- * @param row What row to put the string at
- * @param string String to put on the screen
- * @param extraModifier Modifier to apply to the string
- * @param optionalExtraModifiers Optional extra modifiers to apply to the string
- * @return Itself
- */
- TextGraphics putString(int column, int row, String string, SGR extraModifier, SGR... optionalExtraModifiers);
-
- /**
- * Shortcut to calling:
- * <pre>
- * putString(position.getColumn(), position.getRow(), string, modifiers, optionalExtraModifiers);
- * </pre>
- * @param position Position to put the string at
- * @param string String to put on the screen
- * @param extraModifier Modifier to apply to the string
- * @param optionalExtraModifiers Optional extra modifiers to apply to the string
- * @return Itself
- */
- TextGraphics putString(TerminalPosition position, String string, SGR extraModifier, SGR... optionalExtraModifiers);
-
- /**
- * Puts a string on the screen at the specified position with the current colors and modifiers. If the string
- * contains newlines (\r and/or \n), the method will stop at the character before that; you have to manage
- * multi-line strings yourself! If you supplied any extra modifiers, they will be applied when writing the string
- * as well but not recorded into the state of the TextGraphics object.
- * @param column What column to put the string at
- * @param row What row to put the string at
- * @param string String to put on the screen
- * @param extraModifiers Modifier to apply to the string
- * @return Itself
- */
- TextGraphics putString(int column, int row, String string, Collection<SGR> extraModifiers);
-
- /**
- * Returns the character at the specific position in the terminal. May return {@code null} if the TextGraphics
- * implementation doesn't support it or doesn't know what the character is.
- * @param position Position to return the character for
- * @return The text character at the specified position or {@code null} if not available
- */
- TextCharacter getCharacter(TerminalPosition position);
-
- /**
- * Returns the character at the specific position in the terminal. May return {@code null} if the TextGraphics
- * implementation doesn't support it or doesn't know what the character is.
- * @param column Column to return the character for
- * @param row Row to return the character for
- * @return The text character at the specified position or {@code null} if not available
- */
- TextCharacter getCharacter(int column, int row);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextCharacter;
-
-/**
- * An 'image' build up of text characters with color and style information. These are completely in memory and not
- * visible anyway, but can be used when drawing with a TextGraphics objects.
- * @author martin
- */
-public interface TextImage extends Scrollable {
- /**
- * Returns the dimensions of this TextImage, in columns and rows
- * @return Size of this TextImage
- */
- TerminalSize getSize();
-
- /**
- * Returns the character stored at a particular position in this image
- * @param position Coordinates of the character
- * @return TextCharacter stored at the specified position
- */
- TextCharacter getCharacterAt(TerminalPosition position);
-
- /**
- * Returns the character stored at a particular position in this image
- * @param column Column coordinate of the character
- * @param row Row coordinate of the character
- * @return TextCharacter stored at the specified position
- */
- TextCharacter getCharacterAt(int column, int row);
-
- /**
- * Sets the character at a specific position in the image to a particular TextCharacter. If the position is outside
- * of the image's size, this method does nothing.
- * @param position Coordinates of the character
- * @param character What TextCharacter to assign at the specified position
- */
- void setCharacterAt(TerminalPosition position, TextCharacter character);
-
- /**
- * Sets the character at a specific position in the image to a particular TextCharacter. If the position is outside
- * of the image's size, this method does nothing.
- * @param column Column coordinate of the character
- * @param row Row coordinate of the character
- * @param character What TextCharacter to assign at the specified position
- */
- void setCharacterAt(int column, int row, TextCharacter character);
-
- /**
- * Sets the text image content to one specified character (including color and style)
- * @param character The character to fill the image with
- */
- void setAll(TextCharacter character);
-
- /**
- * Creates a TextGraphics object that targets this TextImage for all its drawing operations.
- * @return TextGraphics object for this TextImage
- */
- TextGraphics newTextGraphics();
-
- /**
- * Returns a copy of this image resized to a new size and using a specified filler character if the new size is
- * larger than the old and we need to fill in empty areas. The copy will be independent from the one this method is
- * invoked on, so modifying one will not affect the other.
- * @param newSize Size of the new image
- * @param filler Filler character to use on the new areas when enlarging the image (is not used when shrinking)
- * @return Copy of this image, but resized
- */
- TextImage resize(TerminalSize newSize, TextCharacter filler);
-
-
- /**
- * Copies this TextImage's content to another TextImage. If the destination TextImage is larger than this
- * ScreenBuffer, the areas outside of the area that is written to will be untouched.
- * @param destination TextImage to copy to
- */
- void copyTo(TextImage destination);
-
- /**
- * Copies this TextImage's content to another TextImage. If the destination TextImage is larger than this
- * TextImage, the areas outside of the area that is written to will be untouched.
- * @param destination TextImage to copy to
- * @param startRowIndex Which row in this image to copy from
- * @param rows How many rows to copy
- * @param startColumnIndex Which column in this image to copy from
- * @param columns How many columns to copy
- * @param destinationRowOffset Offset (in number of rows) in the target image where we want to first copied row to be
- * @param destinationColumnOffset Offset (in number of columns) in the target image where we want to first copied column to be
- */
- void copyTo(
- TextImage destination,
- int startRowIndex,
- int rows,
- int startColumnIndex,
- int columns,
- int destinationRowOffset,
- int destinationColumnOffset);
-
- /**
- * Scroll a range of lines of this TextImage according to given distance.
- *
- * TextImage implementations of this method do <b>not</b> throw IOException.
- */
- @Override
- void scrollLines(int firstLine, int lastLine, int distance);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-/**
- * The main theme interface, from which you can retrieve theme definitions
- * @author Martin
- */
-public interface Theme {
- /**
- * Returns what this theme considers to be the default definition
- * @return The default theme definition
- */
- ThemeDefinition getDefaultDefinition();
-
- /**
- * Returns the theme definition associated with this class. The implementation of Theme should ensure that this
- * call never returns {@code null}, it should always give back a valid value (falling back to the default is nothing
- * else can be used).
- * @param clazz Class to get the theme definition for
- * @return The ThemeDefinition for the class passed in
- */
- ThemeDefinition getDefinition(Class<?> clazz);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-/**
- * A ThemeDefinition contains a collection of ThemeStyle:s, which defines on a lower level which colors and SGRs to
- * apply if you want to draw according to the theme. The different style names are directly inspired from GTK 2. You can
- * also fetch character definitions which are stored inside of the theme, for example if you want to draw a border and
- * make the characters that make up the border customizable.
- *
- * @author Martin
- */
-public interface ThemeDefinition {
- /**
- * The normal style of the definition, which can be considered the default to be used.
- * @return ThemeStyle representation for the normal style
- */
- ThemeStyle getNormal();
-
- /**
- * The pre-light style of this definition, which can be used when a component has input focus but isn't active or
- * selected, similar to mouse-hoovering in modern GUIs
- * @return ThemeStyle representation for the pre-light style
- */
- ThemeStyle getPreLight();
-
- /**
- * The "selected" style of this definition, which can used when a component has been actively selected in some way.
- * @return ThemeStyle representation for the selected style
- */
- ThemeStyle getSelected();
-
- /**
- * The "active" style of this definition, which can be used when a component is being directly interacted with
- * @return ThemeStyle representation for the active style
- */
- ThemeStyle getActive();
-
- /**
- * The insensitive style of this definition, which can be used when a component has been disabled or in some other
- * way isn't able to be interacted with.
- * @return ThemeStyle representation for the insensitive style
- */
- ThemeStyle getInsensitive();
-
- /**
- * Retrieves a custom ThemeStyle, if one is available by this name. Will return null if no such style could be found
- * within this ThemeDefinition. You can use this if you need more categories than the ones available above.
- * @param name Name of the style to look up
- * @return The ThemeStyle associated with the name, or {@code null} if there was no such style
- */
- ThemeStyle getCustom(String name);
-
- /**
- * Retrieves a character from this theme definition by the specified name. This method cannot return {@code null} so
- * you need to give a fallback in case the definition didn't have any character by this name.
- * @param name Name of the character to look up
- * @param fallback Character to return if there was no character by the name supplied in this definition
- * @return The character from this definition by the name entered, or {@code fallback} if the definition didn't have
- * any character defined with this name
- */
- char getCharacter(String name, char fallback);
-
- /**
- * Returns the class name of the ComponentRenderer attached to this definition. If none is declared, it will return
- * {@code null} instead of going up in the hierarchy, unlike the other methods of this interface.
- * @return Full name of the renderer class or {@code null}
- */
- String getRenderer();
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TextColor;
-
-import java.util.EnumSet;
-
-/**
- * ThemeStyle is the lowest entry in the theme hierarchy, containing the actual colors and SGRs to use.
- * @author Martin
- */
-public interface ThemeStyle {
- TextColor getForeground();
- TextColor getBackground();
- EnumSet<SGR> getSGRs();
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.graphics;
-
-/**
- * Expanded TextGraphics that adds methods to interact with themes
- * @author Martin
- */
-public interface ThemedTextGraphics extends TextGraphics {
- /**
- * Returns the {@code Theme} object active on this {@code ThemedTextGraphics}
- * @return Active {@code Theme} object
- */
- Theme getTheme();
-
- /**
- * Retrieves the ThemeDefinition associated with the class parameter passed in. The implementation should make sure
- * that there is always a fallback available if there's no direct definition for this class; the method should never
- * return null.
- * @param clazz Class to search ThemeDefinition for
- * @return ThemeDefinition that was resolved for this class
- */
- ThemeDefinition getThemeDefinition(Class<?> clazz);
-
- /**
- * Takes a ThemeStyle as applies it to this TextGraphics. This will effectively set the foreground color, the
- * background color and all the SGRs.
- * @param themeStyle ThemeStyle to apply
- * @return Itself
- */
- ThemedTextGraphics applyThemeStyle(ThemeStyle themeStyle);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-import java.util.List;
-
-/**
- * Layout manager that places components where they are manually specified to be and sizes them to the size they are
- * manually assigned to. When using the AbsoluteLayout, please use setPosition(..) and setSize(..) manually on each
- * component to choose where to place them. Components that have not had their position and size explicitly set will
- * not be visible.
- *
- * @author martin
- */
-public class AbsoluteLayout implements LayoutManager {
- @Override
- public TerminalSize getPreferredSize(List<Component> components) {
- TerminalSize size = TerminalSize.ZERO;
- for(Component component: components) {
- size = size.max(
- new TerminalSize(
- component.getPosition().getColumn() + component.getSize().getColumns(),
- component.getPosition().getRow() + component.getSize().getRows()));
-
- }
- return size;
- }
-
- @Override
- public void doLayout(TerminalSize area, List<Component> components) {
- //Do nothing
- }
-
- @Override
- public boolean hasChanged() {
- return false;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-import com.googlecode.lanterna.input.MouseAction;
-
-/**
- * This abstract implementation of {@code BasePane} has the common code shared by all different concrete
- * implementations.
- */
-public abstract class AbstractBasePane implements BasePane {
- protected final ContentHolder contentHolder;
- protected InteractableLookupMap interactableLookupMap;
- private Interactable focusedInteractable;
- private boolean invalid;
- private boolean strictFocusChange;
- private boolean enableDirectionBasedMovements;
-
- protected AbstractBasePane() {
- this.contentHolder = new ContentHolder();
- this.interactableLookupMap = new InteractableLookupMap(new TerminalSize(80, 25));
- this.invalid = false;
- this.strictFocusChange = false;
- this.enableDirectionBasedMovements = true;
- }
-
- @Override
- public boolean isInvalid() {
- return invalid || contentHolder.isInvalid();
- }
-
- @Override
- public void invalidate() {
- invalid = true;
-
- //Propagate
- contentHolder.invalidate();
- }
-
- @Override
- public void draw(TextGUIGraphics graphics) {
- graphics.applyThemeStyle(graphics.getThemeDefinition(Window.class).getNormal());
- graphics.fill(' ');
- contentHolder.draw(graphics);
-
- if(!interactableLookupMap.getSize().equals(graphics.getSize())) {
- interactableLookupMap = new InteractableLookupMap(graphics.getSize());
- } else {
- interactableLookupMap.reset();
- }
- contentHolder.updateLookupMap(interactableLookupMap);
- //interactableLookupMap.debug();
- invalid = false;
- }
-
- @Override
- public boolean handleInput(KeyStroke key) {
- if(key.getKeyType() == KeyType.MouseEvent) {
- MouseAction mouseAction = (MouseAction)key;
- TerminalPosition localCoordinates = fromGlobal(mouseAction.getPosition());
- Interactable interactable = interactableLookupMap.getInteractableAt(localCoordinates);
- interactable.handleInput(key);
- }
- else if(focusedInteractable != null) {
- Interactable next = null;
- Interactable.FocusChangeDirection direction = Interactable.FocusChangeDirection.TELEPORT; //Default
- Interactable.Result result = focusedInteractable.handleInput(key);
- if(!enableDirectionBasedMovements) {
- if(result == Interactable.Result.MOVE_FOCUS_DOWN || result == Interactable.Result.MOVE_FOCUS_RIGHT) {
- result = Interactable.Result.MOVE_FOCUS_NEXT;
- }
- else if(result == Interactable.Result.MOVE_FOCUS_UP || result == Interactable.Result.MOVE_FOCUS_LEFT) {
- result = Interactable.Result.MOVE_FOCUS_PREVIOUS;
- }
- }
- switch (result) {
- case HANDLED:
- return true;
- case UNHANDLED:
- //Filter the event recursively through all parent containers until we hit null; give the containers
- //a chance to absorb the event
- Container parent = focusedInteractable.getParent();
- while(parent != null) {
- if(parent.handleInput(key)) {
- return true;
- }
- parent = parent.getParent();
- }
- return false;
- case MOVE_FOCUS_NEXT:
- next = contentHolder.nextFocus(focusedInteractable);
- if(next == null) {
- next = contentHolder.nextFocus(null);
- }
- direction = Interactable.FocusChangeDirection.NEXT;
- break;
- case MOVE_FOCUS_PREVIOUS:
- next = contentHolder.previousFocus(focusedInteractable);
- if(next == null) {
- next = contentHolder.previousFocus(null);
- }
- direction = Interactable.FocusChangeDirection.PREVIOUS;
- break;
- case MOVE_FOCUS_DOWN:
- next = interactableLookupMap.findNextDown(focusedInteractable);
- direction = Interactable.FocusChangeDirection.DOWN;
- if(next == null && !strictFocusChange) {
- next = contentHolder.nextFocus(focusedInteractable);
- direction = Interactable.FocusChangeDirection.NEXT;
- }
- break;
- case MOVE_FOCUS_LEFT:
- next = interactableLookupMap.findNextLeft(focusedInteractable);
- direction = Interactable.FocusChangeDirection.LEFT;
- break;
- case MOVE_FOCUS_RIGHT:
- next = interactableLookupMap.findNextRight(focusedInteractable);
- direction = Interactable.FocusChangeDirection.RIGHT;
- break;
- case MOVE_FOCUS_UP:
- next = interactableLookupMap.findNextUp(focusedInteractable);
- direction = Interactable.FocusChangeDirection.UP;
- if(next == null && !strictFocusChange) {
- next = contentHolder.previousFocus(focusedInteractable);
- direction = Interactable.FocusChangeDirection.PREVIOUS;
- }
- break;
- }
- if(next != null) {
- setFocusedInteractable(next, direction);
- }
- return true;
- }
- return false;
- }
-
- @Override
- public Component getComponent() {
- return contentHolder.getComponent();
- }
-
- @Override
- public void setComponent(Component component) {
- contentHolder.setComponent(component);
- }
-
- @Override
- public Interactable getFocusedInteractable() {
- return focusedInteractable;
- }
-
- @Override
- public TerminalPosition getCursorPosition() {
- if(focusedInteractable == null) {
- return null;
- }
- TerminalPosition position = focusedInteractable.getCursorLocation();
- if(position == null) {
- return null;
- }
- //Don't allow the component to set the cursor outside of its own boundaries
- if(position.getColumn() < 0 ||
- position.getRow() < 0 ||
- position.getColumn() >= focusedInteractable.getSize().getColumns() ||
- position.getRow() >= focusedInteractable.getSize().getRows()) {
- return null;
- }
- return focusedInteractable.toBasePane(position);
- }
-
- @Override
- public void setFocusedInteractable(Interactable toFocus) {
- setFocusedInteractable(toFocus,
- toFocus != null ?
- Interactable.FocusChangeDirection.TELEPORT : Interactable.FocusChangeDirection.RESET);
- }
-
- protected void setFocusedInteractable(Interactable toFocus, Interactable.FocusChangeDirection direction) {
- if(focusedInteractable == toFocus) {
- return;
- }
- if(focusedInteractable != null) {
- focusedInteractable.onLeaveFocus(direction, focusedInteractable);
- }
- Interactable previous = focusedInteractable;
- focusedInteractable = toFocus;
- if(toFocus != null) {
- toFocus.onEnterFocus(direction, previous);
- }
- invalidate();
- }
-
- @Override
- public void setStrictFocusChange(boolean strictFocusChange) {
- this.strictFocusChange = strictFocusChange;
- }
-
- @Override
- public void setEnableDirectionBasedMovements(boolean enableDirectionBasedMovements) {
- this.enableDirectionBasedMovements = enableDirectionBasedMovements;
- }
-
- protected class ContentHolder extends AbstractComposite<Container> {
- @Override
- public void setComponent(Component component) {
- if(getComponent() == component) {
- return;
- }
- setFocusedInteractable(null);
- super.setComponent(component);
- if(focusedInteractable == null && component instanceof Interactable) {
- setFocusedInteractable((Interactable)component);
- }
- else if(focusedInteractable == null && component instanceof Container) {
- setFocusedInteractable(((Container)component).nextFocus(null));
- }
- }
-
- public boolean removeComponent(Component component) {
- boolean removed = super.removeComponent(component);
- if (removed) {
- focusedInteractable = null;
- }
- return removed;
- }
-
- @Override
- public TextGUI getTextGUI() {
- return AbstractBasePane.this.getTextGUI();
- }
-
- @Override
- protected ComponentRenderer<Container> createDefaultRenderer() {
- return new ComponentRenderer<Container>() {
- @Override
- public TerminalSize getPreferredSize(Container component) {
- Component subComponent = getComponent();
- if(subComponent == null) {
- return TerminalSize.ZERO;
- }
- return subComponent.getPreferredSize();
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, Container component) {
- Component subComponent = getComponent();
- if(subComponent == null) {
- return;
- }
- subComponent.draw(graphics);
- }
- };
- }
-
- @Override
- public TerminalPosition toGlobal(TerminalPosition position) {
- return AbstractBasePane.this.toGlobal(position);
- }
-
- @Override
- public TerminalPosition toBasePane(TerminalPosition position) {
- return position;
- }
-
- @Override
- public BasePane getBasePane() {
- return AbstractBasePane.this;
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * Abstract implementation of {@code Border} interface that has some of the methods filled out. If you want to create
- * your own {@code Border} implementation, should should probably extend from this.
- * @author Martin
- */
-public abstract class AbstractBorder extends AbstractComposite<Border> implements Border {
- @Override
- public void setComponent(Component component) {
- super.setComponent(component);
- if(component != null) {
- component.setPosition(TerminalPosition.TOP_LEFT_CORNER);
- }
- }
-
- @Override
- public BorderRenderer getRenderer() {
- return (BorderRenderer)super.getRenderer();
- }
-
- @Override
- public Border setSize(TerminalSize size) {
- super.setSize(size);
- getComponent().setSize(getWrappedComponentSize(size));
- return self();
- }
-
- @Override
- public LayoutData getLayoutData() {
- return getComponent().getLayoutData();
- }
-
- @Override
- public Border setLayoutData(LayoutData ld) {
- getComponent().setLayoutData(ld);
- return this;
- }
-
- @Override
- public TerminalPosition toBasePane(TerminalPosition position) {
- return super.toBasePane(position).withRelative(getWrappedComponentTopLeftOffset());
- }
-
- @Override
- public TerminalPosition toGlobal(TerminalPosition position) {
- return super.toGlobal(position).withRelative(getWrappedComponentTopLeftOffset());
- }
-
- private TerminalPosition getWrappedComponentTopLeftOffset() {
- return getRenderer().getWrappedComponentTopLeftOffset();
- }
-
- private TerminalSize getWrappedComponentSize(TerminalSize borderSize) {
- return getRenderer().getWrappedComponentSize(borderSize);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * AbstractComponent provides some good default behaviour for a {@code Component}, all components in Lanterna extends
- * from this class in some way. If you want to write your own component that isn't interactable or theme:able, you
- * probably want to extend from this class.
- * <p>
- * The way you want to declare your new {@code Component} is to pass in itself as the generic parameter, like this:
- * <pre>
- * {@code
- * public class MyComponent extends AbstractComponent<MyComponent> {
- * ...
- * }
- * }
- * </pre>
- * This was, the component renderer will be correctly setup type-wise and you will need to do fewer typecastings when
- * you implement the drawing method your new component.
- *
- * @author Martin
- * @param <T> Should always be itself, this value will be used for the {@code ComponentRenderer} declaration
- */
-public abstract class AbstractComponent<T extends Component> implements Component {
- private ComponentRenderer<T> renderer;
- private Container parent;
- private TerminalSize size;
- private TerminalSize explicitPreferredSize; //This is keeping the value set by the user (if setPreferredSize() is used)
- private TerminalPosition position;
- private LayoutData layoutData;
- private boolean invalid;
-
- /**
- * Default constructor
- */
- public AbstractComponent() {
- size = TerminalSize.ZERO;
- position = TerminalPosition.TOP_LEFT_CORNER;
- explicitPreferredSize = null;
- layoutData = null;
- invalid = true;
- parent = null;
- renderer = null; //Will be set on the first call to getRenderer()
- }
-
- /**
- * When you create a custom component, you need to implement this method and return a Renderer which is responsible
- * for taking care of sizing the component, rendering it and choosing where to place the cursor (if Interactable).
- * This value is intended to be overridden by custom themes.
- * @return Renderer to use when sizing and drawing this component
- */
- protected abstract ComponentRenderer<T> createDefaultRenderer();
-
- /**
- * This will attempt to dynamically construct a {@code ComponentRenderer} class from a string, assumed to be passed
- * in from a theme. This makes it possible to create themes that supplies their own {@code ComponentRenderers} that
- * can even replace the ones built into lanterna and used for the bundled components.
- *
- * @param className Fully qualified name of the {@code ComponentRenderer} we want to instatiate
- * @return {@code null} if {@code className} was null, otherwise the {@code ComponentRenderer} instance
- * @throws RuntimeException If there were any problems instatiating the class
- */
- @SuppressWarnings("unchecked")
- protected ComponentRenderer<T> getRendererFromTheme(String className) {
- if(className == null) {
- return null;
- }
- try {
- return (ComponentRenderer<T>)Class.forName(className).newInstance();
- } catch (InstantiationException e) {
- throw new RuntimeException(e);
- } catch (IllegalAccessException e) {
- throw new RuntimeException(e);
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Takes a {@code Runnable} and immediately executes it if this is called on the designated GUI thread, otherwise
- * schedules it for later invocation.
- * @param runnable {@code Runnable} to execute on the GUI thread
- */
- protected void runOnGUIThreadIfExistsOtherwiseRunDirect(Runnable runnable) {
- if(getTextGUI() != null && getTextGUI().getGUIThread() != null) {
- getTextGUI().getGUIThread().invokeLater(runnable);
- }
- else {
- runnable.run();
- }
- }
-
- /**
- * Explicitly sets the {@code ComponentRenderer} to be used when drawing this component.
- * @param renderer {@code ComponentRenderer} to be used when drawing this component
- * @return Itself
- */
- public T setRenderer(ComponentRenderer<T> renderer) {
- this.renderer = renderer;
- return self();
- }
-
- @Override
- public synchronized ComponentRenderer<T> getRenderer() {
- if(renderer == null) {
- renderer = createDefaultRenderer();
- if(renderer == null) {
- throw new IllegalStateException(getClass() + " returns a null default renderer");
- }
- }
- return renderer;
- }
-
- @Override
- public void invalidate() {
- invalid = true;
- }
-
- @Override
- public synchronized T setSize(TerminalSize size) {
- this.size = size;
- return self();
- }
-
- @Override
- public TerminalSize getSize() {
- return size;
- }
-
- @Override
- public final TerminalSize getPreferredSize() {
- if(explicitPreferredSize != null) {
- return explicitPreferredSize;
- }
- else {
- return calculatePreferredSize();
- }
- }
-
- @Override
- public final synchronized T setPreferredSize(TerminalSize explicitPreferredSize) {
- this.explicitPreferredSize = explicitPreferredSize;
- return self();
- }
-
- /**
- * Invokes the component renderer's size calculation logic and returns the result. This value represents the
- * preferred size and isn't necessarily what it will eventually be assigned later on.
- * @return Size that the component renderer believes the component should be
- */
- protected synchronized TerminalSize calculatePreferredSize() {
- return getRenderer().getPreferredSize(self());
- }
-
- @Override
- public synchronized T setPosition(TerminalPosition position) {
- this.position = position;
- return self();
- }
-
- @Override
- public TerminalPosition getPosition() {
- return position;
- }
-
- @Override
- public boolean isInvalid() {
- return invalid;
- }
-
- @Override
- public final synchronized void draw(final TextGUIGraphics graphics) {
- if(getRenderer() == null) {
- ComponentRenderer<T> renderer = getRendererFromTheme(graphics.getThemeDefinition(getClass()).getRenderer());
- if(renderer == null) {
- renderer = createDefaultRenderer();
- if(renderer == null) {
- throw new IllegalStateException(getClass() + " returned a null default renderer");
- }
- }
- setRenderer(renderer);
- }
- //Delegate drawing the component to the renderer
- setSize(graphics.getSize());
- onBeforeDrawing();
- getRenderer().drawComponent(graphics, self());
- onAfterDrawing(graphics);
- invalid = false;
- }
-
- /**
- * This method is called just before the component's renderer is invoked for the drawing operation. You can use this
- * hook to do some last-minute adjustments to the component, as an alternative to coding it into the renderer
- * itself. The component should have the correct size and position at this point, if you call {@code getSize()} and
- * {@code getPosition()}.
- */
- protected void onBeforeDrawing() {
- //No operation by default
- }
-
- /**
- * This method is called immediately after the component's renderer has finished the drawing operation. You can use
- * this hook to do some post-processing if you need, as an alternative to coding it into the renderer. The
- * {@code TextGUIGraphics} supplied is the same that was fed into the renderer.
- * @param graphics Graphics object you can use to manipulate the appearance of the component
- */
- protected void onAfterDrawing(TextGUIGraphics graphics) {
- //No operation by default
- }
-
- @Override
- public synchronized T setLayoutData(LayoutData data) {
- if(layoutData != data) {
- layoutData = data;
- invalidate();
- }
- return self();
- }
-
- @Override
- public LayoutData getLayoutData() {
- return layoutData;
- }
-
- @Override
- public Container getParent() {
- return parent;
- }
-
- @Override
- public boolean hasParent(Container parent) {
- if(this.parent == null) {
- return false;
- }
- Container recursiveParent = this.parent;
- while(recursiveParent != null) {
- if(recursiveParent == parent) {
- return true;
- }
- recursiveParent = recursiveParent.getParent();
- }
- return false;
- }
-
- @Override
- public TextGUI getTextGUI() {
- if(parent == null) {
- return null;
- }
- return parent.getTextGUI();
- }
-
- @Override
- public boolean isInside(Container container) {
- Component test = this;
- while(test.getParent() != null) {
- if(test.getParent() == container) {
- return true;
- }
- test = test.getParent();
- }
- return false;
- }
-
- @Override
- public BasePane getBasePane() {
- if(parent == null) {
- return null;
- }
- return parent.getBasePane();
- }
-
- @Override
- public TerminalPosition toBasePane(TerminalPosition position) {
- Container parent = getParent();
- if(parent == null) {
- return null;
- }
- return parent.toBasePane(getPosition().withRelative(position));
- }
-
- @Override
- public TerminalPosition toGlobal(TerminalPosition position) {
- Container parent = getParent();
- if(parent == null) {
- return null;
- }
- return parent.toGlobal(getPosition().withRelative(position));
- }
-
- @Override
- public synchronized Border withBorder(Border border) {
- border.setComponent(this);
- return border;
- }
-
- @Override
- public synchronized T addTo(Panel panel) {
- panel.addComponent(this);
- return self();
- }
-
- @Override
- public synchronized void onAdded(Container container) {
- parent = container;
- }
-
- @Override
- public synchronized void onRemoved(Container container) {
- parent = null;
- }
-
- /**
- * This is a little hack to avoid doing typecasts all over the place when having to return {@code T}. Credit to
- * avl42 for this one!
- * @return Itself, but as type T
- */
- @SuppressWarnings("unchecked")
- protected T self() {
- return (T)this;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.input.KeyStroke;
-
-import java.util.Collection;
-import java.util.Collections;
-
-/**
- * This abstract implementation contains common code for the different {@code Composite} implementations. A
- * {@code Composite} component is one that encapsulates a single component, like borders. Because of this, a
- * {@code Composite} can be seen as a special case of a {@code Container} and indeed this abstract class does in fact
- * implement the {@code Container} interface as well, to make the composites easier to work with internally.
- * @author martin
- * @param <T> Should always be itself, see {@code AbstractComponent}
- */
-public abstract class AbstractComposite<T extends Container> extends AbstractComponent<T> implements Composite, Container {
-
- private Component component;
-
- /**
- * Default constructor
- */
- public AbstractComposite() {
- component = null;
- }
-
- @Override
- public void setComponent(Component component) {
- Component oldComponent = this.component;
- if(oldComponent == component) {
- return;
- }
- if(oldComponent != null) {
- removeComponent(oldComponent);
- }
- if(component != null) {
- this.component = component;
- component.onAdded(this);
- component.setPosition(TerminalPosition.TOP_LEFT_CORNER);
- invalidate();
- }
- }
-
- @Override
- public Component getComponent() {
- return component;
- }
-
- @Override
- public int getChildCount() {
- return component != null ? 1 : 0;
- }
-
- @Override
- public Collection<Component> getChildren() {
- if(component != null) {
- return Collections.singletonList(component);
- }
- else {
- return Collections.emptyList();
- }
- }
-
- @Override
- public boolean containsComponent(Component component) {
- return component != null && component.hasParent(this);
- }
-
- @Override
- public boolean removeComponent(Component component) {
- if(this.component == component) {
- this.component = null;
- component.onRemoved(this);
- invalidate();
- return true;
- }
- return false;
- }
-
- @Override
- public boolean isInvalid() {
- return component != null && component.isInvalid();
- }
-
- @Override
- public void invalidate() {
- super.invalidate();
-
- //Propagate
- if(component != null) {
- component.invalidate();
- }
- }
-
- @Override
- public Interactable nextFocus(Interactable fromThis) {
- if(fromThis == null && getComponent() instanceof Interactable) {
- return (Interactable)getComponent();
- }
- else if(getComponent() instanceof Container) {
- return ((Container)getComponent()).nextFocus(fromThis);
- }
- return null;
- }
-
- @Override
- public Interactable previousFocus(Interactable fromThis) {
- if(fromThis == null && getComponent() instanceof Interactable) {
- return (Interactable)getComponent();
- }
- else if(getComponent() instanceof Container) {
- return ((Container)getComponent()).previousFocus(fromThis);
- }
- return null;
- }
-
- @Override
- public boolean handleInput(KeyStroke key) {
- return false;
- }
-
- @Override
- public void updateLookupMap(InteractableLookupMap interactableLookupMap) {
- if(getComponent() instanceof Container) {
- ((Container)getComponent()).updateLookupMap(interactableLookupMap);
- }
- else if(getComponent() instanceof Interactable) {
- interactableLookupMap.add((Interactable)getComponent());
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.input.KeyStroke;
-
-/**
- * Default implementation of Interactable that extends from AbstractComponent. If you want to write your own component
- * that is interactable, i.e. can receive keyboard (and mouse) input, you probably want to extend from this class as
- * it contains some common implementations of the methods from {@code Interactable} interface
- * @param <T> Should always be itself, see {@code AbstractComponent}
- * @author Martin
- */
-public abstract class AbstractInteractableComponent<T extends AbstractInteractableComponent<T>> extends AbstractComponent<T> implements Interactable {
-
- private InputFilter inputFilter;
- private boolean inFocus;
-
- /**
- * Default constructor
- */
- protected AbstractInteractableComponent() {
- inputFilter = null;
- inFocus = false;
- }
-
- @Override
- public T takeFocus() {
- BasePane basePane = getBasePane();
- if(basePane != null) {
- basePane.setFocusedInteractable(this);
- }
- return self();
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * This method is final in {@code AbstractInteractableComponent}, please override {@code afterEnterFocus} instead
- */
- @Override
- public final void onEnterFocus(FocusChangeDirection direction, Interactable previouslyInFocus) {
- inFocus = true;
- afterEnterFocus(direction, previouslyInFocus);
- }
-
- /**
- * Called by {@code AbstractInteractableComponent} automatically after this component has received input focus. You
- * can override this method if you need to trigger some action based on this.
- * @param direction How focus was transferred, keep in mind this is from the previous component's point of view so
- * if this parameter has value DOWN, focus came in from above
- * @param previouslyInFocus Which interactable component had focus previously
- */
- @SuppressWarnings("EmptyMethod")
- protected void afterEnterFocus(FocusChangeDirection direction, Interactable previouslyInFocus) {
- //By default no action
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * This method is final in {@code AbstractInteractableComponent}, please override {@code afterLeaveFocus} instead
- */
- @Override
- public final void onLeaveFocus(FocusChangeDirection direction, Interactable nextInFocus) {
- inFocus = false;
- afterLeaveFocus(direction, nextInFocus);
- }
-
- /**
- * Called by {@code AbstractInteractableComponent} automatically after this component has lost input focus. You
- * can override this method if you need to trigger some action based on this.
- * @param direction How focus was transferred, keep in mind this is from the this component's point of view so
- * if this parameter has value DOWN, focus is moving down to a component below
- * @param nextInFocus Which interactable component is going to receive focus
- */
- @SuppressWarnings("EmptyMethod")
- protected void afterLeaveFocus(FocusChangeDirection direction, Interactable nextInFocus) {
- //By default no action
- }
-
- @Override
- protected abstract InteractableRenderer<T> createDefaultRenderer();
-
- @Override
- public InteractableRenderer<T> getRenderer() {
- return (InteractableRenderer<T>)super.getRenderer();
- }
-
- @Override
- public boolean isFocused() {
- return inFocus;
- }
-
- @Override
- public final synchronized Result handleInput(KeyStroke keyStroke) {
- if(inputFilter == null || inputFilter.onInput(this, keyStroke)) {
- return handleKeyStroke(keyStroke);
- }
- else {
- return Result.UNHANDLED;
- }
- }
-
- /**
- * This method can be overridden to handle various user input (mostly from the keyboard) when this component is in
- * focus. The input method from the interface, {@code handleInput(..)} is final in
- * {@code AbstractInteractableComponent} to ensure the input filter is properly handled. If the filter decides that
- * this event should be processed, it will call this method.
- * @param keyStroke What input was entered by the user
- * @return Result of processing the key-stroke
- */
- protected Result handleKeyStroke(KeyStroke keyStroke) {
- // Skip the keystroke if ctrl, alt or shift was down
- if(!keyStroke.isAltDown() && !keyStroke.isCtrlDown() && !keyStroke.isShiftDown()) {
- switch(keyStroke.getKeyType()) {
- case ArrowDown:
- return Result.MOVE_FOCUS_DOWN;
- case ArrowLeft:
- return Result.MOVE_FOCUS_LEFT;
- case ArrowRight:
- return Result.MOVE_FOCUS_RIGHT;
- case ArrowUp:
- return Result.MOVE_FOCUS_UP;
- case Tab:
- return Result.MOVE_FOCUS_NEXT;
- case ReverseTab:
- return Result.MOVE_FOCUS_PREVIOUS;
- case MouseEvent:
- getBasePane().setFocusedInteractable(this);
- return Result.HANDLED;
- default:
- }
- }
- return Result.UNHANDLED;
- }
-
- @Override
- public TerminalPosition getCursorLocation() {
- return getRenderer().getCursorLocation(self());
- }
-
- @Override
- public InputFilter getInputFilter() {
- return inputFilter;
- }
-
- @Override
- public synchronized T setInputFilter(InputFilter inputFilter) {
- this.inputFilter = inputFilter;
- return self();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.Symbols;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyStroke;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Base class for several list box implementations, this will handle things like list of items and the scrollbar.
- * @param <T> Should always be itself, see {@code AbstractComponent}
- * @param <V> Type of items this list box contains
- * @author Martin
- */
-public abstract class AbstractListBox<V, T extends AbstractListBox<V, T>> extends AbstractInteractableComponent<T> {
- private final List<V> items;
- private int selectedIndex;
- private ListItemRenderer<V,T> listItemRenderer;
-
- /**
- * This constructor sets up the component so it has no preferred size but will ask to be as big as the list is. If
- * the GUI cannot accommodate this size, scrolling and a vertical scrollbar will be used.
- */
- protected AbstractListBox() {
- this(null);
- }
-
- /**
- * This constructor sets up the component with a preferred size that is will always request, no matter what items
- * are in the list box. If there are more items than the size can contain, scrolling and a vertical scrollbar will
- * be used. Calling this constructor with a {@code null} value has the same effect as calling the default
- * constructor.
- *
- * @param size Preferred size that the list should be asking for instead of invoking the preferred size calculation,
- * or if set to {@code null} will ask to be big enough to display all items.
- */
- protected AbstractListBox(TerminalSize size) {
- this.items = new ArrayList<V>();
- this.selectedIndex = -1;
- setPreferredSize(size);
- setListItemRenderer(createDefaultListItemRenderer());
- }
-
- @Override
- protected InteractableRenderer<T> createDefaultRenderer() {
- return new DefaultListBoxRenderer<V, T>();
- }
-
- /**
- * Method that constructs the {@code ListItemRenderer} that this list box should use to draw the elements of the
- * list box. This can be overridden to supply a custom renderer. Note that this is not the renderer used for the
- * entire list box but for each item, called one by one.
- * @return {@code ListItemRenderer} to use when drawing the items in the list
- */
- protected ListItemRenderer<V,T> createDefaultListItemRenderer() {
- return new ListItemRenderer<V,T>();
- }
-
- ListItemRenderer<V,T> getListItemRenderer() {
- return listItemRenderer;
- }
-
- /**
- * This method overrides the {@code ListItemRenderer} that is used to draw each element in the list box. Note that
- * this is not the renderer used for the entire list box but for each item, called one by one.
- * @param listItemRenderer New renderer to use when drawing the items in the list box
- * @return Itself
- */
- public synchronized T setListItemRenderer(ListItemRenderer<V,T> listItemRenderer) {
- if(listItemRenderer == null) {
- listItemRenderer = createDefaultListItemRenderer();
- if(listItemRenderer == null) {
- throw new IllegalStateException("createDefaultListItemRenderer returned null");
- }
- }
- this.listItemRenderer = listItemRenderer;
- return self();
- }
-
- @Override
- public synchronized Result handleKeyStroke(KeyStroke keyStroke) {
- try {
- switch(keyStroke.getKeyType()) {
- case Tab:
- return Result.MOVE_FOCUS_NEXT;
-
- case ReverseTab:
- return Result.MOVE_FOCUS_PREVIOUS;
-
- case ArrowRight:
- return Result.MOVE_FOCUS_RIGHT;
-
- case ArrowLeft:
- return Result.MOVE_FOCUS_LEFT;
-
- case ArrowDown:
- if(items.isEmpty() || selectedIndex == items.size() - 1) {
- return Result.MOVE_FOCUS_DOWN;
- }
- selectedIndex++;
- return Result.HANDLED;
-
- case ArrowUp:
- if(items.isEmpty() || selectedIndex == 0) {
- return Result.MOVE_FOCUS_UP;
- }
- selectedIndex--;
- return Result.HANDLED;
-
- case Home:
- selectedIndex = 0;
- return Result.HANDLED;
-
- case End:
- selectedIndex = items.size() - 1;
- return Result.HANDLED;
-
- case PageUp:
- if(getSize() != null) {
- setSelectedIndex(getSelectedIndex() - getSize().getRows());
- }
- return Result.HANDLED;
-
- case PageDown:
- if(getSize() != null) {
- setSelectedIndex(getSelectedIndex() + getSize().getRows());
- }
- return Result.HANDLED;
-
- default:
- }
- return Result.UNHANDLED;
- }
- finally {
- invalidate();
- }
- }
-
- @Override
- protected synchronized void afterEnterFocus(FocusChangeDirection direction, Interactable previouslyInFocus) {
- if(items.isEmpty()) {
- return;
- }
-
- if(direction == FocusChangeDirection.DOWN) {
- selectedIndex = 0;
- }
- else if(direction == FocusChangeDirection.UP) {
- selectedIndex = items.size() - 1;
- }
- }
-
- /**
- * Adds one more item to the list box, at the end.
- * @param item Item to add to the list box
- * @return Itself
- */
- public synchronized T addItem(V item) {
- if(item == null) {
- return self();
- }
-
- items.add(item);
- if(selectedIndex == -1) {
- selectedIndex = 0;
- }
- invalidate();
- return self();
- }
-
- /**
- * Removes all items from the list box
- * @return Itself
- */
- public synchronized T clearItems() {
- items.clear();
- selectedIndex = -1;
- invalidate();
- return self();
- }
-
- /**
- * Looks for the particular item in the list and returns the index within the list (starting from zero) of that item
- * if it is found, or -1 otherwise
- * @param item What item to search for in the list box
- * @return Index of the item in the list box or -1 if the list box does not contain the item
- */
- public synchronized int indexOf(V item) {
- return items.indexOf(item);
- }
-
- /**
- * Retrieves the item at the specified index in the list box
- * @param index Index of the item to fetch
- * @return The item at the specified index
- * @throws IndexOutOfBoundsException If the index is less than zero or equals/greater than the number of items in
- * the list box
- */
- public synchronized V getItemAt(int index) {
- return items.get(index);
- }
-
- /**
- * Checks if the list box has no items
- * @return {@code true} if the list box has no items, {@code false} otherwise
- */
- public synchronized boolean isEmpty() {
- return items.isEmpty();
- }
-
- /**
- * Returns the number of items currently in the list box
- * @return Number of items in the list box
- */
- public synchronized int getItemCount() {
- return items.size();
- }
-
- /**
- * Returns a copy of the items in the list box as a {@code List}
- * @return Copy of all the items in this list box
- */
- public synchronized List<V> getItems() {
- return new ArrayList<V>(items);
- }
-
- /**
- * Sets which item in the list box that is currently selected. Please note that in this context, selected simply
- * means it is the item that currently has input focus. This is not to be confused with list box implementations
- * such as {@code CheckBoxList} where individual items have a certain checked/unchecked state.
- * @param index Index of the item that should be currently selected
- * @return Itself
- */
- public synchronized T setSelectedIndex(int index) {
- selectedIndex = index;
- if(selectedIndex < 0) {
- selectedIndex = 0;
- }
- if(selectedIndex > items.size() - 1) {
- selectedIndex = items.size() - 1;
- }
- invalidate();
- return self();
- }
-
- /**
- * Returns the index of the currently selected item in the list box. Please note that in this context, selected
- * simply means it is the item that currently has input focus. This is not to be confused with list box
- * implementations such as {@code CheckBoxList} where individual items have a certain checked/unchecked state.
- * @return The index of the currently selected row in the list box, or -1 if there are no items
- */
- public int getSelectedIndex() {
- return selectedIndex;
- }
-
- /**
- * Returns the currently selected item in the list box. Please note that in this context, selected
- * simply means it is the item that currently has input focus. This is not to be confused with list box
- * implementations such as {@code CheckBoxList} where individual items have a certain checked/unchecked state.
- * @return The currently selected item in the list box, or {@code null} if there are no items
- */
- public synchronized V getSelectedItem() {
- if (selectedIndex == -1) {
- return null;
- } else {
- return items.get(selectedIndex);
- }
- }
-
- /**
- * The default renderer for {@code AbstractListBox} and all its subclasses.
- * @param <V> Type of the items the list box this renderer is for
- * @param <T> Type of list box
- */
- public static class DefaultListBoxRenderer<V, T extends AbstractListBox<V, T>> implements InteractableRenderer<T> {
- private int scrollTopIndex;
-
- /**
- * Default constructor
- */
- public DefaultListBoxRenderer() {
- this.scrollTopIndex = 0;
- }
-
- @Override
- public TerminalPosition getCursorLocation(T listBox) {
- int selectedIndex = listBox.getSelectedIndex();
- int columnAccordingToRenderer = listBox.getListItemRenderer().getHotSpotPositionOnLine(selectedIndex);
- if(columnAccordingToRenderer == -1) {
- return null;
- }
- return new TerminalPosition(columnAccordingToRenderer, selectedIndex - scrollTopIndex);
- }
-
- @Override
- public TerminalSize getPreferredSize(T listBox) {
- int maxWidth = 5; //Set it to something...
- int index = 0;
- for (V item : listBox.getItems()) {
- String itemString = listBox.getListItemRenderer().getLabel(listBox, index++, item);
- int stringLengthInColumns = TerminalTextUtils.getColumnWidth(itemString);
- if (stringLengthInColumns > maxWidth) {
- maxWidth = stringLengthInColumns;
- }
- }
- return new TerminalSize(maxWidth + 1, listBox.getItemCount());
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, T listBox) {
- //update the page size, used for page up and page down keys
- int componentHeight = graphics.getSize().getRows();
- int componentWidth = graphics.getSize().getColumns();
- int selectedIndex = listBox.getSelectedIndex();
- List<V> items = listBox.getItems();
- ListItemRenderer<V,T> listItemRenderer = listBox.getListItemRenderer();
-
- if(selectedIndex != -1) {
- if(selectedIndex < scrollTopIndex)
- scrollTopIndex = selectedIndex;
- else if(selectedIndex >= componentHeight + scrollTopIndex)
- scrollTopIndex = selectedIndex - componentHeight + 1;
- }
-
- //Do we need to recalculate the scroll position?
- //This code would be triggered by resizing the window when the scroll
- //position is at the bottom
- if(items.size() > componentHeight &&
- items.size() - scrollTopIndex < componentHeight) {
- scrollTopIndex = items.size() - componentHeight;
- }
-
- graphics.applyThemeStyle(graphics.getThemeDefinition(AbstractListBox.class).getNormal());
- graphics.fill(' ');
-
- TerminalSize itemSize = graphics.getSize().withRows(1);
- for(int i = scrollTopIndex; i < items.size(); i++) {
- if(i - scrollTopIndex >= componentHeight) {
- break;
- }
- listItemRenderer.drawItem(
- graphics.newTextGraphics(new TerminalPosition(0, i - scrollTopIndex), itemSize),
- listBox,
- i,
- items.get(i),
- selectedIndex == i,
- listBox.isFocused());
- }
-
- graphics.applyThemeStyle(graphics.getThemeDefinition(AbstractListBox.class).getNormal());
- if(items.size() > componentHeight) {
- graphics.putString(componentWidth - 1, 0, Symbols.ARROW_UP + "");
-
- graphics.applyThemeStyle(graphics.getThemeDefinition(AbstractListBox.class).getInsensitive());
- for(int i = 1; i < componentHeight - 1; i++)
- graphics.putString(componentWidth - 1, i, Symbols.BLOCK_MIDDLE + "");
-
- graphics.applyThemeStyle(graphics.getThemeDefinition(AbstractListBox.class).getNormal());
- graphics.putString(componentWidth - 1, componentHeight - 1, Symbols.ARROW_DOWN + "");
-
- //Finally print the 'tick'
- int scrollableSize = items.size() - componentHeight;
- double position = (double)scrollTopIndex / ((double)scrollableSize);
- int tickPosition = (int)(((double) componentHeight - 3.0) * position);
- graphics.applyThemeStyle(graphics.getThemeDefinition(AbstractListBox.class).getInsensitive());
- graphics.putString(componentWidth - 1, 1 + tickPosition, " ");
- }
- }
- }
-
- /**
- * The default list item renderer class, this can be extended and customized it needed. The instance which is
- * assigned to the list box will be called once per item in the list when the list box is drawn.
- * @param <V> Type of the items in the list box
- * @param <T> Type of the list box class itself
- */
- public static class ListItemRenderer<V, T extends AbstractListBox<V, T>> {
- /**
- * Returns where on the line to place the text terminal cursor for a currently selected item. By default this
- * will return 0, meaning the first character of the selected line. If you extend {@code ListItemRenderer} you
- * can change this by returning a different number. Returning -1 will cause lanterna to hide the cursor.
- * @param selectedIndex Which item is currently selected
- * @return Index of the character in the string we want to place the terminal cursor on, or -1 to hide it
- */
- public int getHotSpotPositionOnLine(int selectedIndex) {
- return 0;
- }
-
- /**
- * Given a list box, an index of an item within that list box and what the item is, this method should return
- * what to draw for that item. The default implementation is to return whatever {@code toString()} returns when
- * called on the item.
- * @param listBox List box the item belongs to
- * @param index Index of the item
- * @param item The item itself
- * @return String to draw for this item
- */
- public String getLabel(T listBox, int index, V item) {
- return item != null ? item.toString() : "<null>";
- }
-
- /**
- * This is the main drawing method for a single list box item, it applies the current theme to setup the colors
- * and then calls {@code getLabel(..)} and draws the result using the supplied {@code TextGUIGraphics}. The
- * graphics object is created just for this item and is restricted so that it can only draw on the area this
- * item is occupying. The top-left corner (0x0) should be the starting point when drawing the item.
- * @param graphics Graphics object to draw with
- * @param listBox List box we are drawing an item from
- * @param index Index of the item we are drawing
- * @param item The item we are drawing
- * @param selected Will be set to {@code true} if the item is currently selected, otherwise {@code false}, but
- * please notice what context 'selected' refers to here (see {@code setSelectedIndex})
- * @param focused Will be set to {@code true} if the list box currently has input focus, otherwise {@code false}
- */
- public void drawItem(TextGUIGraphics graphics, T listBox, int index, V item, boolean selected, boolean focused) {
- if(selected && focused) {
- graphics.applyThemeStyle(graphics.getThemeDefinition(AbstractListBox.class).getSelected());
- }
- else {
- graphics.applyThemeStyle(graphics.getThemeDefinition(AbstractListBox.class).getNormal());
- }
- String label = getLabel(listBox, index, item);
- label = TerminalTextUtils.fitString(label, graphics.getSize().getColumns());
- graphics.putString(0, 0, label);
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.graphics.PropertiesTheme;
-import com.googlecode.lanterna.graphics.Theme;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-import com.googlecode.lanterna.screen.Screen;
-
-import java.io.EOFException;
-import java.io.FileInputStream;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
-import java.util.Properties;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * This abstract implementation of TextGUI contains some basic management of the underlying Screen and other common code
- * that can be shared between different implementations.
- * @author Martin
- */
-public abstract class AbstractTextGUI implements TextGUI {
-
- private final Screen screen;
- private final List<Listener> listeners;
- private boolean blockingIO;
- private boolean dirty;
- private TextGUIThread textGUIThread;
- private Theme guiTheme;
-
- /**
- * Constructor for {@code AbstractTextGUI} that requires a {@code Screen} and a factory for creating the GUI thread
- * @param textGUIThreadFactory Factory class to use for creating the {@code TextGUIThread} class
- * @param screen What underlying {@code Screen} to use for this text GUI
- */
- protected AbstractTextGUI(TextGUIThreadFactory textGUIThreadFactory, Screen screen) {
- if(screen == null) {
- throw new IllegalArgumentException("Creating a TextGUI requires an underlying Screen");
- }
- this.screen = screen;
- this.listeners = new CopyOnWriteArrayList<Listener>();
- this.blockingIO = false;
- this.dirty = false;
- this.guiTheme = new PropertiesTheme(loadDefaultThemeProperties());
- this.textGUIThread = textGUIThreadFactory.createTextGUIThread(this);
- }
-
- private static Properties loadDefaultThemeProperties() {
- Properties properties = new Properties();
- try {
- ClassLoader classLoader = AbstractTextGUI.class.getClassLoader();
- InputStream resourceAsStream = classLoader.getResourceAsStream("default-theme.properties");
- if(resourceAsStream == null) {
- resourceAsStream = new FileInputStream("src/main/resources/default-theme.properties");
- }
- properties.load(resourceAsStream);
- resourceAsStream.close();
- return properties;
- }
- catch(IOException e) {
- return properties;
- }
- }
-
- /**
- * Reads one key from the input queue, blocking or non-blocking depending on if blocking I/O has been enabled. To
- * enable blocking I/O (disabled by default), use {@code setBlockingIO(true)}.
- * @return One piece of user input as a {@code KeyStroke} or {@code null} if blocking I/O is disabled and there was
- * no input waiting
- * @throws IOException In case of an I/O error while reading input
- */
- protected KeyStroke readKeyStroke() throws IOException {
- return blockingIO ? screen.readInput() : pollInput();
- }
-
- /**
- * Polls the underlying input queue for user input, returning either a {@code KeyStroke} or {@code null}
- * @return {@code KeyStroke} representing the user input or {@code null} if there was none
- * @throws IOException In case of an I/O error while reading input
- */
- protected KeyStroke pollInput() throws IOException {
- return screen.pollInput();
- }
-
- @Override
- public synchronized boolean processInput() throws IOException {
- boolean gotInput = false;
- KeyStroke keyStroke = readKeyStroke();
- if(keyStroke != null) {
- gotInput = true;
- do {
- if (keyStroke.getKeyType() == KeyType.EOF) {
- throw new EOFException();
- }
- boolean handled = handleInput(keyStroke);
- if(!handled) {
- handled = fireUnhandledKeyStroke(keyStroke);
- }
- dirty = handled || dirty;
- keyStroke = pollInput();
- } while(keyStroke != null);
- }
- return gotInput;
- }
-
- @Override
- public void setTheme(Theme theme) {
- this.guiTheme = theme;
- }
-
- @Override
- public synchronized void updateScreen() throws IOException {
- screen.doResizeIfNecessary();
- drawGUI(new TextGUIGraphics(this, screen.newTextGraphics(), guiTheme));
- screen.setCursorPosition(getCursorPosition());
- screen.refresh();
- dirty = false;
- }
-
- @Override
- public boolean isPendingUpdate() {
- return screen.doResizeIfNecessary() != null || dirty;
- }
-
- @Override
- public TextGUIThread getGUIThread() {
- return textGUIThread;
- }
-
- @Override
- public void addListener(Listener listener) {
- listeners.add(listener);
- }
-
- @Override
- public void removeListener(Listener listener) {
- listeners.remove(listener);
- }
-
- /**
- * Enables blocking I/O, causing calls to {@code readKeyStroke()} to block until there is input available. Notice
- * that you can still poll for input using {@code pollInput()}.
- * @param blockingIO Set this to {@code true} if blocking I/O should be enabled, otherwise {@code false}
- */
- public void setBlockingIO(boolean blockingIO) {
- this.blockingIO = blockingIO;
- }
-
- /**
- * Checks if blocking I/O is enabled or not
- * @return {@code true} if blocking I/O is enabled, otherwise {@code false}
- */
- public boolean isBlockingIO() {
- return blockingIO;
- }
-
- /**
- * This method should be called when there was user input that wasn't handled by the GUI. It will fire the
- * {@code onUnhandledKeyStroke(..)} method on any registered listener.
- * @param keyStroke The {@code KeyStroke} that wasn't handled by the GUI
- * @return {@code true} if at least one of the listeners handled the key stroke, this will signal to the GUI that it
- * needs to be redrawn again.
- */
- protected final boolean fireUnhandledKeyStroke(KeyStroke keyStroke) {
- boolean handled = false;
- for(Listener listener: listeners) {
- handled = listener.onUnhandledKeyStroke(this, keyStroke) || handled;
- }
- return handled;
- }
-
- /**
- * Marks the whole text GUI as invalid and that it needs to be redrawn at next opportunity
- */
- protected void invalidate() {
- dirty = true;
- }
-
- /**
- * Draws the entire GUI using a {@code TextGUIGraphics} object
- * @param graphics Graphics object to draw using
- */
- protected abstract void drawGUI(TextGUIGraphics graphics);
-
- /**
- * Top-level method for drilling in to the GUI and figuring out, in global coordinates, where to place the text
- * cursor on the screen at this time.
- * @return Where to place the text cursor, or {@code null} if the cursor should be hidden
- */
- protected abstract TerminalPosition getCursorPosition();
-
- /**
- * This method should take the user input and feed it to the focused component for handling.
- * @param key {@code KeyStroke} representing the user input
- * @return {@code true} if the input was recognized and handled by the GUI, indicating that the GUI should be redrawn
- */
- protected abstract boolean handleInput(KeyStroke key);
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-import java.io.IOException;
-import java.util.Queue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Created by martin on 20/06/15.
- */
-public abstract class AbstractTextGUIThread implements TextGUIThread {
-
- protected final TextGUI textGUI;
- protected final Queue<Runnable> customTasks;
- protected ExceptionHandler exceptionHandler;
-
- public AbstractTextGUIThread(TextGUI textGUI) {
- this.exceptionHandler = new ExceptionHandler() {
- @Override
- public boolean onIOException(IOException e) {
- e.printStackTrace();
- return true;
- }
-
- @Override
- public boolean onRuntimeException(RuntimeException e) {
- e.printStackTrace();
- return true;
- }
- };
- this.textGUI = textGUI;
- this.customTasks = new LinkedBlockingQueue<Runnable>();
- }
-
- @Override
- public void invokeLater(Runnable runnable) throws IllegalStateException {
- customTasks.add(runnable);
- }
-
- @Override
- public void setExceptionHandler(ExceptionHandler exceptionHandler) {
- if(exceptionHandler == null) {
- throw new IllegalArgumentException("Cannot call setExceptionHandler(null)");
- }
- this.exceptionHandler = exceptionHandler;
- }
-
- @Override
- public synchronized boolean processEventsAndUpdate() throws IOException {
- if(getThread() != Thread.currentThread()) {
- throw new IllegalStateException("Calling processEventAndUpdate outside of GUI thread");
- }
- textGUI.processInput();
- while(!customTasks.isEmpty()) {
- Runnable r = customTasks.poll();
- if(r != null) {
- r.run();
- }
- }
- if(textGUI.isPendingUpdate()) {
- textGUI.updateScreen();
- return true;
- }
- return false;
- }
-
- @Override
- public void invokeAndWait(final Runnable runnable) throws IllegalStateException, InterruptedException {
- if(Thread.currentThread() == getThread()) {
- runnable.run();
- }
- else {
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- invokeLater(new Runnable() {
- @Override
- public void run() {
- try {
- runnable.run();
- }
- finally {
- countDownLatch.countDown();
- }
- }
- });
- countDownLatch.await();
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyType;
-
-import java.util.*;
-
-/**
- * Abstract Window has most of the code requiring for a window to function, all concrete window implementations extends
- * from this in one way or another. You can define your own window by extending from this, as an alternative to building
- * up the GUI externally by constructing a {@code BasicWindow} and adding components to it.
- * @author Martin
- */
-public abstract class AbstractWindow extends AbstractBasePane implements Window {
- private String title;
- private WindowBasedTextGUI textGUI;
- private boolean visible;
- private TerminalSize lastKnownSize;
- private TerminalSize lastKnownDecoratedSize;
- private TerminalPosition lastKnownPosition;
- private TerminalPosition contentOffset;
- private Set<Hint> hints;
- private boolean closeWindowWithEscape;
-
- /**
- * Default constructor, this creates a window with no title
- */
- public AbstractWindow() {
- this("");
- }
-
- /**
- * Creates a window with a specific title that will (probably) be drawn in the window decorations
- * @param title Title of this window
- */
- public AbstractWindow(String title) {
- super();
- this.title = title;
- this.textGUI = null;
- this.visible = true;
- this.lastKnownPosition = null;
- this.lastKnownSize = null;
- this.lastKnownDecoratedSize = null;
- this.closeWindowWithEscape = false;
-
- this.hints = new HashSet<Hint>();
- }
-
- /**
- * Setting this property to {@code true} will cause pressing the ESC key to close the window. This used to be the
- * default behaviour of lanterna 3 during the development cycle but is not longer the case. You are encouraged to
- * put proper buttons or other kind of components to clearly mark to the user how to close the window instead of
- * magically taking ESC, but sometimes it can be useful (when doing testing, for example) to enable this mode.
- * @param closeWindowWithEscape If {@code true}, this window will self-close if you press ESC key
- */
- public void setCloseWindowWithEscape(boolean closeWindowWithEscape) {
- this.closeWindowWithEscape = closeWindowWithEscape;
- }
-
- @Override
- public void setTextGUI(WindowBasedTextGUI textGUI) {
- //This is kind of stupid check, but might cause it to blow up on people using the library incorrectly instead of
- //just causing weird behaviour
- if(this.textGUI != null && textGUI != null) {
- throw new UnsupportedOperationException("Are you calling setTextGUI yourself? Please read the documentation"
- + " in that case (this could also be a bug in Lanterna, please report it if you are sure you are "
- + "not calling Window.setTextGUI(..) from your code)");
- }
- this.textGUI = textGUI;
- }
-
- @Override
- public WindowBasedTextGUI getTextGUI() {
- return textGUI;
- }
-
- /**
- * Alters the title of the window to the supplied string
- * @param title New title of the window
- */
- public void setTitle(String title) {
- this.title = title;
- invalidate();
- }
-
- @Override
- public String getTitle() {
- return title;
- }
-
- @Override
- public boolean isVisible() {
- return visible;
- }
-
- @Override
- public void setVisible(boolean visible) {
- this.visible = visible;
- }
- @Override
- public void draw(TextGUIGraphics graphics) {
- if(!graphics.getSize().equals(lastKnownSize)) {
- getComponent().invalidate();
- }
- setSize(graphics.getSize(), false);
- super.draw(graphics);
- }
-
- @Override
- public boolean handleInput(KeyStroke key) {
- boolean handled = super.handleInput(key);
- if(!handled && closeWindowWithEscape && key.getKeyType() == KeyType.Escape) {
- close();
- return true;
- }
- return handled;
- }
-
- @Override
- public TerminalPosition toGlobal(TerminalPosition localPosition) {
- if(localPosition == null) {
- return null;
- }
- return lastKnownPosition.withRelative(contentOffset.withRelative(localPosition));
- }
-
- @Override
- public TerminalPosition fromGlobal(TerminalPosition globalPosition) {
- if(globalPosition == null) {
- return null;
- }
- return globalPosition.withRelative(
- -lastKnownPosition.getColumn() - contentOffset.getColumn(),
- -lastKnownPosition.getRow() - contentOffset.getRow());
- }
-
- @Override
- public TerminalSize getPreferredSize() {
- return contentHolder.getPreferredSize();
- }
-
- @Override
- public void setHints(Collection<Hint> hints) {
- this.hints = new HashSet<Hint>(hints);
- invalidate();
- }
-
- @Override
- public Set<Hint> getHints() {
- return Collections.unmodifiableSet(hints);
- }
-
- @Override
- public final TerminalPosition getPosition() {
- return lastKnownPosition;
- }
-
- @Override
- public final void setPosition(TerminalPosition topLeft) {
- this.lastKnownPosition = topLeft;
- }
-
- @Override
- public final TerminalSize getSize() {
- return lastKnownSize;
- }
-
- @Override
- public void setSize(TerminalSize size) {
- setSize(size, true);
- }
-
- private void setSize(TerminalSize size, boolean invalidate) {
- this.lastKnownSize = size;
- if(invalidate) {
- invalidate();
- }
- }
-
- @Override
- public final TerminalSize getDecoratedSize() {
- return lastKnownDecoratedSize;
- }
-
- @Override
- public final void setDecoratedSize(TerminalSize decoratedSize) {
- this.lastKnownDecoratedSize = decoratedSize;
- }
-
- @Override
- public void setContentOffset(TerminalPosition offset) {
- this.contentOffset = offset;
- }
-
- @Override
- public void close() {
- if(textGUI != null) {
- textGUI.removeWindow(this);
- }
- setComponent(null);
- }
-
- @Override
- public void waitUntilClosed() {
- WindowBasedTextGUI textGUI = getTextGUI();
- if(textGUI != null) {
- textGUI.waitForWindowToClose(this);
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-
-/**
- * This class is a list box implementation that displays a number of items that has actions associated with them. You
- * can activate this action by pressing the Enter or Space keys on the keyboard and the action associated with the
- * currently selected item will fire.
- * @author Martin
- */
-public class ActionListBox extends AbstractListBox<Runnable, ActionListBox> {
-
- /**
- * Default constructor, creates an {@code ActionListBox} with no pre-defined size that will request to be big enough
- * to display all items
- */
- public ActionListBox() {
- this(null);
- }
-
- /**
- * Creates a new {@code ActionListBox} with a pre-set size. If the items don't fit in within this size, scrollbars
- * will be used to accommodate. Calling {@code new ActionListBox(null)} has the same effect as calling
- * {@code new ActionListBox()}.
- * @param preferredSize
- */
- public ActionListBox(TerminalSize preferredSize) {
- super(preferredSize);
- }
-
- /**
- * {@inheritDoc}
- *
- * The label of the item in the list box will be the result of calling {@code .toString()} on the runnable, which
- * might not be what you want to have unless you explicitly declare it. Consider using
- * {@code addItem(String label, Runnable action} instead, if you want to just set the label easily without having
- * to override {@code .toString()}.
- *
- * @param object Runnable to execute when the action was selected and fired in the list
- * @return Itself
- */
- @Override
- public ActionListBox addItem(Runnable object) {
- return super.addItem(object);
- }
-
- /**
- * Adds a new item to the list, which is displayed in the list using a supplied label.
- * @param label Label to use in the list for the new item
- * @param action Runnable to invoke when this action is selected and then triggered
- * @return Itself
- */
- public ActionListBox addItem(final String label, final Runnable action) {
- return addItem(new Runnable() {
- @Override
- public void run() {
- action.run();
- }
-
- @Override
- public String toString() {
- return label;
- }
- });
- }
-
- @Override
- public TerminalPosition getCursorLocation() {
- return null;
- }
-
- @Override
- public Result handleKeyStroke(KeyStroke keyStroke) {
- Object selectedItem = getSelectedItem();
- if(selectedItem != null &&
- (keyStroke.getKeyType() == KeyType.Enter ||
- (keyStroke.getKeyType() == KeyType.Character && keyStroke.getCharacter() == ' '))) {
-
- ((Runnable)selectedItem).run();
- return Result.HANDLED;
- }
- return super.handleKeyStroke(keyStroke);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-
-import java.lang.ref.WeakReference;
-import java.util.*;
-
-/**
- * This is a special label that contains not just a single text to display but a number of frames that are cycled
- * through. The class will manage a timer on its own and ensure the label is updated and redrawn. There is a static
- * helper method available to create the classic "spinning bar": {@code createClassicSpinningLine()}
- */
-public class AnimatedLabel extends Label {
- private static Timer TIMER = null;
- private static final WeakHashMap<AnimatedLabel, TimerTask> SCHEDULED_TASKS = new WeakHashMap<AnimatedLabel, TimerTask>();
-
- /**
- * Creates a classic spinning bar which can be used to signal to the user that an operation in is process.
- * @return {@code AnimatedLabel} instance which is setup to show a spinning bar
- */
- public static AnimatedLabel createClassicSpinningLine() {
- return createClassicSpinningLine(150);
- }
-
- /**
- * Creates a classic spinning bar which can be used to signal to the user that an operation in is process.
- * @param speed Delay in between each frame
- * @return {@code AnimatedLabel} instance which is setup to show a spinning bar
- */
- public static AnimatedLabel createClassicSpinningLine(int speed) {
- AnimatedLabel animatedLabel = new AnimatedLabel("-");
- animatedLabel.addFrame("\\");
- animatedLabel.addFrame("|");
- animatedLabel.addFrame("/");
- animatedLabel.startAnimation(speed);
- return animatedLabel;
- }
-
- private final List<String[]> frames;
- private TerminalSize combinedMaximumPreferredSize;
- private int currentFrame;
-
- /**
- * Creates a new animated label, initially set to one frame. You will need to add more frames and call
- * {@code startAnimation()} for this to start moving.
- *
- * @param firstFrameText The content of the label at the first frame
- */
- public AnimatedLabel(String firstFrameText) {
- super(firstFrameText);
- frames = new ArrayList<String[]>();
- currentFrame = 0;
- combinedMaximumPreferredSize = TerminalSize.ZERO;
-
- String[] lines = splitIntoMultipleLines(firstFrameText);
- frames.add(lines);
- ensurePreferredSize(lines);
- }
-
- /**
- * Adds one more frame at the end of the list of frames
- * @param text Text to use for the label at this frame
- */
- public synchronized void addFrame(String text) {
- String[] lines = splitIntoMultipleLines(text);
- frames.add(lines);
- ensurePreferredSize(lines);
- }
-
- private void ensurePreferredSize(String[] lines) {
- combinedMaximumPreferredSize = combinedMaximumPreferredSize.max(getBounds(lines, combinedMaximumPreferredSize));
- }
-
- /**
- * Advances the animated label to the next frame. You normally don't need to call this manually as it will be done
- * by the animation thread.
- */
- public synchronized void nextFrame() {
- currentFrame++;
- if(currentFrame >= frames.size()) {
- currentFrame = 0;
- }
- super.setLines(frames.get(currentFrame));
- invalidate();
- }
-
- @Override
- public void onRemoved(Container container) {
- stopAnimation();
- }
-
- /**
- * Starts the animation thread which will periodically call {@code nextFrame()} at the interval specified by the
- * {@code millisecondsPerFrame} parameter. After all frames have been cycled through, it will start over from the
- * first frame again.
- * @param millisecondsPerFrame The interval in between every frame
- */
- public synchronized void startAnimation(long millisecondsPerFrame) {
- if(TIMER == null) {
- TIMER = new Timer("AnimatedLabel");
- }
- AnimationTimerTask animationTimerTask = new AnimationTimerTask(this);
- SCHEDULED_TASKS.put(this, animationTimerTask);
- TIMER.scheduleAtFixedRate(animationTimerTask, millisecondsPerFrame, millisecondsPerFrame);
- }
-
- /**
- * Halts the animation thread and the label will stop at whatever was the current frame at the time when this was
- * called
- */
- public synchronized void stopAnimation() {
- removeTaskFromTimer(this);
- }
-
- private static synchronized void removeTaskFromTimer(AnimatedLabel animatedLabel) {
- SCHEDULED_TASKS.get(animatedLabel).cancel();
- SCHEDULED_TASKS.remove(animatedLabel);
- canCloseTimer();
- }
-
- private static synchronized void canCloseTimer() {
- if(SCHEDULED_TASKS.isEmpty()) {
- TIMER.cancel();
- TIMER = null;
- }
- }
-
- private static class AnimationTimerTask extends TimerTask {
- private final WeakReference<AnimatedLabel> labelRef;
-
- private AnimationTimerTask(AnimatedLabel label) {
- this.labelRef = new WeakReference<AnimatedLabel>(label);
- }
-
- @Override
- public void run() {
- AnimatedLabel animatedLabel = labelRef.get();
- if(animatedLabel == null) {
- cancel();
- canCloseTimer();
- }
- else {
- if(animatedLabel.getBasePane() == null) {
- animatedLabel.stopAnimation();
- }
- else {
- animatedLabel.nextFrame();
- }
- }
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-/**
- * Extended interface of TextGUIThread for implementations that uses a separate thread for all GUI event processing and
- * updating.
- *
- * @author Martin
- */
-public interface AsynchronousTextGUIThread extends TextGUIThread {
- /**
- * Starts the AsynchronousTextGUIThread, typically meaning that the event processing loop will start.
- */
- void start();
-
- /**
- * Requests that the AsynchronousTextGUIThread stops, typically meaning that the event processing loop will exit
- */
- void stop();
-
- /**
- * Blocks until the GUI loop has stopped
- * @throws InterruptedException In case this thread was interrupted while waiting for the GUI thread to exit
- */
- void waitForStop() throws InterruptedException;
-
- /**
- * Returns the current status of this GUI thread
- * @return Current status of the GUI thread
- */
- State getState();
-
- /**
- * Enum representing the states of the GUI thread life-cycle
- */
- enum State {
- /**
- * The instance has been created but not yet started
- */
- CREATED,
- /**
- * The thread has started an is running
- */
- STARTED,
- /**
- * The thread is trying to stop but is still running
- */
- STOPPING,
- /**
- * The thread has stopped
- */
- STOPPED,
- ;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.input.KeyStroke;
-
-/**
- * BasePane is the base container in a Text GUI. A text gui may have several base panes, although they are
- * always independent. One common example of this is a multi-window system where each window is a base pane. Think of
- * the base pane as a root container, the ultimate parent of all components added to a GUI. When you use
- * {@code MultiWindowTextGUI}, the background space behind the windows is a {@code BasePane} too, just like each of the
- * windows. They are all drawn separately and composited together. Every {@code BasePane} has a single component that
- * is drawn over the entire area the {@code BasePane} is occupying, it's very likely you want to set this component to
- * be a container of some sort, probably a {@code Panel}, that can host multiple child components.
- *
- * @see Panel
- * @author Martin
- */
-public interface BasePane extends Composite {
-
- /**
- * Returns the TextGUI this BasePane belongs to or {@code null} if none. One example of when this method returns
- * {@code null} is when calling it on a Window that hasn't been displayed yet.
- * @return The TextGUI this BasePane belongs to
- */
- TextGUI getTextGUI();
-
- /**
- * Called by the GUI system (or something imitating the GUI system) to draw the root container. The TextGUIGraphics
- * object should be used to perform the drawing operations.
- * @param graphics TextGraphics object to draw with
- */
- void draw(TextGUIGraphics graphics);
-
- /**
- * Checks if this root container (i.e. any of its child components) has signaled that what it's currently displaying
- * is out of date and needs re-drawing.
- * @return {@code true} if the container's content is invalid and needs redrawing, {@code false} otherwise
- */
- boolean isInvalid();
-
- /**
- * Invalidates the whole root container (including all of its child components) which will cause them all to be
- * recalculated (for containers) and redrawn.
- */
- void invalidate();
-
- /**
- * Called by the GUI system to delegate a keyboard input event. The root container will decide what to do with this
- * input, usually sending it to one of its sub-components, but if it isn't able to find any handler for this input
- * it should return {@code false} so that the GUI system can take further decisions on what to do with it.
- * @param key Keyboard input
- * @return {@code true} If the root container could handle the input, false otherwise
- */
- boolean handleInput(KeyStroke key);
-
- /**
- * Returns the component that is the content of the BasePane. This is probably the root of a hierarchy of nested
- * Panels but it could also be a single component.
- * @return Component which is the content of this BasePane
- */
- @Override
- Component getComponent();
-
- /**
- * Sets the top-level component inside this BasePane. If you want it to contain only one component, you can set it
- * directly, but for more complicated GUIs you probably want to create a hierarchy of panels and set the first one
- * here.
- * @param component Component which this BasePane is using as it's content
- */
- @Override
- void setComponent(Component component);
-
- /**
- * Returns the component in the root container that currently has input focus. There can only be one component at a
- * time being in focus.
- * @return Interactable component that is currently in receiving input focus
- */
- Interactable getFocusedInteractable();
-
- /**
- * Sets the component currently in focus within this root container, or sets no component in focus if {@code null}
- * is passed in.
- * @param interactable Interactable to focus, or {@code null} to clear focus
- */
- void setFocusedInteractable(Interactable interactable);
-
- /**
- * Returns the position of where to put the terminal cursor according to this root container. This is typically
- * derived from which component has focus, or {@code null} if no component has focus or if the root container doesn't
- * want the cursor to be visible. Note that the coordinates are in local coordinate space, relative to the top-left
- * corner of the root container. You can use your TextGUI implementation to translate these to global coordinates.
- * @return Local position of where to place the cursor, or {@code null} if the cursor shouldn't be visible
- */
- TerminalPosition getCursorPosition();
-
- /**
- * Returns a position in a root container's local coordinate space to global coordinates
- * @param localPosition The local position to translate
- * @return The local position translated to global coordinates
- */
- TerminalPosition toGlobal(TerminalPosition localPosition);
-
- /**
- * Returns a position expressed in global coordinates, i.e. row and column offset from the top-left corner of the
- * terminal into a position relative to the top-left corner of the base pane. Calling
- * {@code fromGlobal(toGlobal(..))} should return the exact same position.
- * @param position Position expressed in global coordinates to translate to local coordinates of this BasePane
- * @return The global coordinates expressed as local coordinates
- */
- TerminalPosition fromGlobal(TerminalPosition position);
-
- /**
- * If set to true, up/down array keys will not translate to next/previous if there are no more components
- * above/below.
- * @param strictFocusChange Will not allow relaxed navigation if set to {@code true}
- */
- void setStrictFocusChange(boolean strictFocusChange);
-
- /**
- * If set to false, using the keyboard arrows keys will have the same effect as using the tab and reverse tab.
- * Lanterna will map arrow down and arrow right to tab, going to the next component, and array up and array left to
- * reverse tab, going to the previous component. If set to true, Lanterna will search for the next component
- * starting at the cursor position in the general direction of the arrow. By default this is enabled.
- * <p>
- * In Lanterna 2, direction based movements were not available.
- * @param enableDirectionBasedMovements Should direction based focus movements be enabled?
- */
- void setEnableDirectionBasedMovements(boolean enableDirectionBasedMovements);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-/**
- * Simple AbstractWindow implementation that you can use as a building block when creating new windows without having
- * to create new classes.
- *
- * @author Martin
- */
-public class BasicWindow extends AbstractWindow {
-
- /**
- * Default constructor, creates a new window with no title
- */
- public BasicWindow() {
- super();
- }
-
- /**
- * This constructor creates a window with a specific title, that is (probably) going to be displayed in the window
- * decoration
- * @param title Title of the window
- */
- public BasicWindow(String title) {
- super(title);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * Main interface for different border classes, with additional methods to help lanterna figure out the size and offset
- * of components wrapped by borders.
- * @author Martin
- */
-public interface Border extends Container, Composite {
- interface BorderRenderer extends ComponentRenderer<Border> {
- /**
- * How large is the offset from the top left corner of the border to the top left corner of the wrapped component?
- * @return Position of the wrapped components top left position, relative to the top left corner of the border
- */
- TerminalPosition getWrappedComponentTopLeftOffset();
-
- /**
- * Given a total size of the border composite and it's wrapped component, how large would the actual wrapped
- * component be?
- * @param borderSize Size to calculate for, this should be the total size of the border and the inner component
- * @return Size of the inner component if the total size of inner + border is borderSize
- */
- TerminalSize getWrappedComponentSize(TerminalSize borderSize);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.*;
-
-/**
- * BorderLayout imitates the BorderLayout class from AWT, allowing you to add a center component with optional
- * components around it in top, bottom, left and right locations. The edge components will be sized at their preferred
- * size and the center component will take up whatever remains.
- * @author martin
- */
-public class BorderLayout implements LayoutManager {
-
- /**
- * This type is what you use as the layout data for components added to a panel using {@code BorderLayout} for its
- * layout manager. This values specified where inside the panel the component should be added.
- */
- public enum Location implements LayoutData {
- /**
- * The component with this value as its layout data will occupy the center space, whatever is remaining after
- * the other components (if any) have allocated their space.
- */
- CENTER,
- /**
- * The component with this value as its layout data will occupy the left side of the container, attempting to
- * allocate the preferred width of the component and at least the preferred height, but could be more depending
- * on the other components added.
- */
- LEFT,
- /**
- * The component with this value as its layout data will occupy the right side of the container, attempting to
- * allocate the preferred width of the component and at least the preferred height, but could be more depending
- * on the other components added.
- */
- RIGHT,
- /**
- * The component with this value as its layout data will occupy the top side of the container, attempting to
- * allocate the preferred height of the component and at least the preferred width, but could be more depending
- * on the other components added.
- */
- TOP,
- /**
- * The component with this value as its layout data will occupy the bottom side of the container, attempting to
- * allocate the preferred height of the component and at least the preferred width, but could be more depending
- * on the other components added.
- */
- BOTTOM,
- ;
- }
-
- //When components don't have a location, we'll assign an available location based on this order
- private static final List<Location> AUTO_ASSIGN_ORDER = Collections.unmodifiableList(Arrays.asList(
- Location.CENTER,
- Location.TOP,
- Location.BOTTOM,
- Location.LEFT,
- Location.RIGHT));
-
- @Override
- public TerminalSize getPreferredSize(List<Component> components) {
- EnumMap<Location, Component> layout = makeLookupMap(components);
- int preferredHeight =
- (layout.containsKey(Location.TOP) ? layout.get(Location.TOP).getPreferredSize().getRows() : 0)
- +
- Math.max(
- layout.containsKey(Location.LEFT) ? layout.get(Location.LEFT).getPreferredSize().getRows() : 0,
- Math.max(
- layout.containsKey(Location.CENTER) ? layout.get(Location.CENTER).getPreferredSize().getRows() : 0,
- layout.containsKey(Location.RIGHT) ? layout.get(Location.RIGHT).getPreferredSize().getRows() : 0))
- +
- (layout.containsKey(Location.BOTTOM) ? layout.get(Location.BOTTOM).getPreferredSize().getRows() : 0);
-
- int preferredWidth =
- Math.max(
- (layout.containsKey(Location.LEFT) ? layout.get(Location.LEFT).getPreferredSize().getColumns() : 0) +
- (layout.containsKey(Location.CENTER) ? layout.get(Location.CENTER).getPreferredSize().getColumns() : 0) +
- (layout.containsKey(Location.RIGHT) ? layout.get(Location.RIGHT).getPreferredSize().getColumns() : 0),
- Math.max(
- layout.containsKey(Location.TOP) ? layout.get(Location.TOP).getPreferredSize().getColumns() : 0,
- layout.containsKey(Location.BOTTOM) ? layout.get(Location.BOTTOM).getPreferredSize().getColumns() : 0));
- return new TerminalSize(preferredWidth, preferredHeight);
- }
-
- @Override
- public void doLayout(TerminalSize area, List<Component> components) {
- EnumMap<Location, Component> layout = makeLookupMap(components);
- int availableHorizontalSpace = area.getColumns();
- int availableVerticalSpace = area.getRows();
-
- //We'll need this later on
- int topComponentHeight = 0;
- int leftComponentWidth = 0;
-
- //First allocate the top
- if(layout.containsKey(Location.TOP)) {
- Component topComponent = layout.get(Location.TOP);
- topComponentHeight = Math.min(topComponent.getPreferredSize().getRows(), availableVerticalSpace);
- topComponent.setPosition(TerminalPosition.TOP_LEFT_CORNER);
- topComponent.setSize(new TerminalSize(availableHorizontalSpace, topComponentHeight));
- availableVerticalSpace -= topComponentHeight;
- }
-
- //Next allocate the bottom
- if(layout.containsKey(Location.BOTTOM)) {
- Component bottomComponent = layout.get(Location.BOTTOM);
- int bottomComponentHeight = Math.min(bottomComponent.getPreferredSize().getRows(), availableVerticalSpace);
- bottomComponent.setPosition(new TerminalPosition(0, area.getRows() - bottomComponentHeight));
- bottomComponent.setSize(new TerminalSize(availableHorizontalSpace, bottomComponentHeight));
- availableVerticalSpace -= bottomComponentHeight;
- }
-
- //Now divide the remaining space between LEFT, CENTER and RIGHT
- if(layout.containsKey(Location.LEFT)) {
- Component leftComponent = layout.get(Location.LEFT);
- leftComponentWidth = Math.min(leftComponent.getPreferredSize().getColumns(), availableHorizontalSpace);
- leftComponent.setPosition(new TerminalPosition(0, topComponentHeight));
- leftComponent.setSize(new TerminalSize(leftComponentWidth, availableVerticalSpace));
- availableHorizontalSpace -= leftComponentWidth;
- }
- if(layout.containsKey(Location.RIGHT)) {
- Component rightComponent = layout.get(Location.RIGHT);
- int rightComponentWidth = Math.min(rightComponent.getPreferredSize().getColumns(), availableHorizontalSpace);
- rightComponent.setPosition(new TerminalPosition(area.getColumns() - rightComponentWidth, topComponentHeight));
- rightComponent.setSize(new TerminalSize(rightComponentWidth, availableVerticalSpace));
- availableHorizontalSpace -= rightComponentWidth;
- }
- if(layout.containsKey(Location.CENTER)) {
- Component centerComponent = layout.get(Location.CENTER);
- centerComponent.setPosition(new TerminalPosition(leftComponentWidth, topComponentHeight));
- centerComponent.setSize(new TerminalSize(availableHorizontalSpace, availableVerticalSpace));
- }
-
- //Set the remaining components to 0x0
- for(Component component: components) {
- if(!layout.values().contains(component)) {
- component.setPosition(TerminalPosition.TOP_LEFT_CORNER);
- component.setSize(TerminalSize.ZERO);
- }
- }
- }
-
- private EnumMap<Location, Component> makeLookupMap(List<Component> components) {
- EnumMap<Location, Component> map = new EnumMap<BorderLayout.Location, Component>(Location.class);
- List<Component> unassignedComponents = new ArrayList<Component>();
- for(Component component: components) {
- if(component.getLayoutData() instanceof Location) {
- map.put((Location)component.getLayoutData(), component);
- }
- else {
- unassignedComponents.add(component);
- }
- }
- //Try to assign components to available locations
- for(Component component: unassignedComponents) {
- for(Location location: AUTO_ASSIGN_ORDER) {
- if(!map.containsKey(location)) {
- map.put(location, component);
- break;
- }
- }
- }
- return map;
- }
-
- @Override
- public boolean hasChanged() {
- //No internal state
- return false;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.graphics.TextGraphics;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * This class containers a couple of border implementation and utility methods for instantiating them. It also contains
- * a utility method for joining border line graphics together with adjacent lines so they blend in together:
- * {@code joinLinesWithFrame(..)}.
- * @author Martin
- */
-public class Borders {
- private Borders() {
- }
-
- //Different ways to draw the border
- private enum BorderStyle {
- Solid,
- Bevel,
- ReverseBevel,
- }
-
- /**
- * Creates a {@code Border} that is drawn as a solid color single line surrounding the wrapped component
- * @return New solid color single line {@code Border}
- */
- public static Border singleLine() {
- return singleLine("");
- }
-
- /**
- * Creates a {@code Border} that is drawn as a solid color single line surrounding the wrapped component with a
- * title string normally drawn at the top-left side
- * @param title The title to draw on the border
- * @return New solid color single line {@code Border} with a title
- */
- public static Border singleLine(String title) {
- return new SingleLine(title, BorderStyle.Solid);
- }
-
- /**
- * Creates a {@code Border} that is drawn as a bevel color single line surrounding the wrapped component
- * @return New bevel color single line {@code Border}
- */
- public static Border singleLineBevel() {
- return singleLineBevel("");
- }
-
- /**
- * Creates a {@code Border} that is drawn as a bevel color single line surrounding the wrapped component with a
- * title string normally drawn at the top-left side
- * @param title The title to draw on the border
- * @return New bevel color single line {@code Border} with a title
- */
- public static Border singleLineBevel(String title) {
- return new SingleLine(title, BorderStyle.Bevel);
- }
-
- /**
- * Creates a {@code Border} that is drawn as a reverse bevel color single line surrounding the wrapped component
- * @return New reverse bevel color single line {@code Border}
- */
- public static Border singleLineReverseBevel() {
- return singleLineReverseBevel("");
- }
-
- /**
- * Creates a {@code Border} that is drawn as a reverse bevel color single line surrounding the wrapped component
- * with a title string normally drawn at the top-left side
- * @param title The title to draw on the border
- * @return New reverse bevel color single line {@code Border} with a title
- */
- public static Border singleLineReverseBevel(String title) {
- return new SingleLine(title, BorderStyle.ReverseBevel);
- }
-
- /**
- * Creates a {@code Border} that is drawn as a solid color double line surrounding the wrapped component
- * @return New solid color double line {@code Border}
- */
- public static Border doubleLine() {
- return doubleLine("");
- }
-
- /**
- * Creates a {@code Border} that is drawn as a solid color double line surrounding the wrapped component with a
- * title string normally drawn at the top-left side
- * @param title The title to draw on the border
- * @return New solid color double line {@code Border} with a title
- */
- public static Border doubleLine(String title) {
- return new DoubleLine(title, BorderStyle.Solid);
- }
-
- /**
- * Creates a {@code Border} that is drawn as a bevel color double line surrounding the wrapped component
- * @return New bevel color double line {@code Border}
- */
- public static Border doubleLineBevel() {
- return doubleLineBevel("");
- }
-
- /**
- * Creates a {@code Border} that is drawn as a bevel color double line surrounding the wrapped component with a
- * title string normally drawn at the top-left side
- * @param title The title to draw on the border
- * @return New bevel color double line {@code Border} with a title
- */
- public static Border doubleLineBevel(String title) {
- return new DoubleLine(title, BorderStyle.Bevel);
- }
-
- /**
- * Creates a {@code Border} that is drawn as a reverse bevel color double line surrounding the wrapped component
- * @return New reverse bevel color double line {@code Border}
- */
- public static Border doubleLineReverseBevel() {
- return doubleLineReverseBevel("");
- }
-
- /**
- * Creates a {@code Border} that is drawn as a reverse bevel color double line surrounding the wrapped component
- * with a title string normally drawn at the top-left side
- * @param title The title to draw on the border
- * @return New reverse bevel color double line {@code Border} with a title
- */
- public static Border doubleLineReverseBevel(String title) {
- return new DoubleLine(title, BorderStyle.ReverseBevel);
- }
-
- private static abstract class StandardBorder extends AbstractBorder {
- private final String title;
- protected final BorderStyle borderStyle;
-
- protected StandardBorder(String title, BorderStyle borderStyle) {
- if (title == null) {
- throw new IllegalArgumentException("Cannot create a border with null title");
- }
- this.borderStyle = borderStyle;
- this.title = title;
- }
-
- public String getTitle() {
- return title;
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "{" + title + "}";
- }
- }
-
- private static abstract class AbstractBorderRenderer implements Border.BorderRenderer {
- private final BorderStyle borderStyle;
-
- protected AbstractBorderRenderer(BorderStyle borderStyle) {
- this.borderStyle = borderStyle;
- }
-
- @Override
- public TerminalSize getPreferredSize(Border component) {
- StandardBorder border = (StandardBorder)component;
- Component wrappedComponent = border.getComponent();
- TerminalSize preferredSize;
- if (wrappedComponent == null) {
- preferredSize = TerminalSize.ZERO;
- } else {
- preferredSize = wrappedComponent.getPreferredSize();
- }
- preferredSize = preferredSize.withRelativeColumns(2).withRelativeRows(2);
- String borderTitle = border.getTitle();
- return preferredSize.max(new TerminalSize((borderTitle.isEmpty() ? 2 : TerminalTextUtils.getColumnWidth(borderTitle) + 4), 2));
- }
-
- @Override
- public TerminalPosition getWrappedComponentTopLeftOffset() {
- return TerminalPosition.OFFSET_1x1;
- }
-
- @Override
- public TerminalSize getWrappedComponentSize(TerminalSize borderSize) {
- return borderSize
- .withRelativeColumns(-Math.min(2, borderSize.getColumns()))
- .withRelativeRows(-Math.min(2, borderSize.getRows()));
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, Border component) {
- StandardBorder border = (StandardBorder)component;
- Component wrappedComponent = border.getComponent();
- if(wrappedComponent == null) {
- return;
- }
- TerminalSize drawableArea = graphics.getSize();
-
- char horizontalLine = getHorizontalLine(graphics);
- char verticalLine = getVerticalLine(graphics);
- char bottomLeftCorner = getBottomLeftCorner(graphics);
- char topLeftCorner = getTopLeftCorner(graphics);
- char bottomRightCorner = getBottomRightCorner(graphics);
- char topRightCorner = getTopRightCorner(graphics);
-
- if(borderStyle == BorderStyle.Bevel) {
- graphics.applyThemeStyle(graphics.getThemeDefinition(StandardBorder.class).getPreLight());
- }
- else {
- graphics.applyThemeStyle(graphics.getThemeDefinition(StandardBorder.class).getNormal());
- }
- graphics.setCharacter(0, drawableArea.getRows() - 1, bottomLeftCorner);
- if(drawableArea.getRows() > 2) {
- graphics.drawLine(new TerminalPosition(0, drawableArea.getRows() - 2), new TerminalPosition(0, 1), verticalLine);
- }
- graphics.setCharacter(0, 0, topLeftCorner);
- if(drawableArea.getColumns() > 2) {
- graphics.drawLine(new TerminalPosition(1, 0), new TerminalPosition(drawableArea.getColumns() - 2, 0), horizontalLine);
- }
-
- if(borderStyle == BorderStyle.ReverseBevel) {
- graphics.applyThemeStyle(graphics.getThemeDefinition(StandardBorder.class).getPreLight());
- }
- else {
- graphics.applyThemeStyle(graphics.getThemeDefinition(StandardBorder.class).getNormal());
- }
- graphics.setCharacter(drawableArea.getColumns() - 1, 0, topRightCorner);
- if(drawableArea.getRows() > 2) {
- graphics.drawLine(new TerminalPosition(drawableArea.getColumns() - 1, 1),
- new TerminalPosition(drawableArea.getColumns() - 1, drawableArea.getRows() - 2),
- verticalLine);
- }
- graphics.setCharacter(drawableArea.getColumns() - 1, drawableArea.getRows() - 1, bottomRightCorner);
- if(drawableArea.getColumns() > 2) {
- graphics.drawLine(new TerminalPosition(1, drawableArea.getRows() - 1),
- new TerminalPosition(drawableArea.getColumns() - 2, drawableArea.getRows() - 1),
- horizontalLine);
- }
-
- if(drawableArea.getColumns() >= TerminalTextUtils.getColumnWidth(border.getTitle()) + 4) {
- graphics.putString(2, 0, border.getTitle());
- }
-
- wrappedComponent.draw(graphics.newTextGraphics(getWrappedComponentTopLeftOffset(), getWrappedComponentSize(drawableArea)));
-
-
- joinLinesWithFrame(graphics);
- }
-
- protected abstract char getHorizontalLine(TextGUIGraphics graphics);
- protected abstract char getVerticalLine(TextGUIGraphics graphics);
- protected abstract char getBottomLeftCorner(TextGUIGraphics graphics);
- protected abstract char getTopLeftCorner(TextGUIGraphics graphics);
- protected abstract char getBottomRightCorner(TextGUIGraphics graphics);
- protected abstract char getTopRightCorner(TextGUIGraphics graphics);
- }
-
- /**
- * This method will attempt to join line drawing characters with the outermost bottom and top rows and left and
- * right columns. For example, if a vertical left border character is ║ and the character immediately to the right
- * of it is ─, then the border character will be updated to ╟ to join the two together. Please note that this method
- * will <b>only</b> join the outer border columns and rows.
- * @param graphics Graphics to use when inspecting and joining characters
- */
- public static void joinLinesWithFrame(TextGraphics graphics) {
- TerminalSize drawableArea = graphics.getSize();
- if(drawableArea.getRows() <= 2 || drawableArea.getColumns() <= 2) {
- //Too small
- return;
- }
-
- int upperRow = 0;
- int lowerRow = drawableArea.getRows() - 1;
- int leftRow = 0;
- int rightRow = drawableArea.getColumns() - 1;
-
- List<Character> junctionFromBelowSingle = Arrays.asList(
- Symbols.SINGLE_LINE_VERTICAL,
- Symbols.BOLD_FROM_NORMAL_SINGLE_LINE_VERTICAL,
- Symbols.SINGLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS,
- Symbols.SINGLE_LINE_BOTTOM_LEFT_CORNER,
- Symbols.SINGLE_LINE_BOTTOM_RIGHT_CORNER,
- Symbols.SINGLE_LINE_T_LEFT,
- Symbols.SINGLE_LINE_T_RIGHT,
- Symbols.SINGLE_LINE_T_UP,
- Symbols.SINGLE_LINE_T_DOUBLE_LEFT,
- Symbols.SINGLE_LINE_T_DOUBLE_RIGHT,
- Symbols.DOUBLE_LINE_T_SINGLE_UP);
- List<Character> junctionFromBelowDouble = Arrays.asList(
- Symbols.DOUBLE_LINE_VERTICAL,
- Symbols.DOUBLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_BOTTOM_LEFT_CORNER,
- Symbols.DOUBLE_LINE_BOTTOM_RIGHT_CORNER,
- Symbols.DOUBLE_LINE_T_LEFT,
- Symbols.DOUBLE_LINE_T_RIGHT,
- Symbols.DOUBLE_LINE_T_UP,
- Symbols.DOUBLE_LINE_T_SINGLE_LEFT,
- Symbols.DOUBLE_LINE_T_SINGLE_RIGHT,
- Symbols.SINGLE_LINE_T_DOUBLE_UP);
- List<Character> junctionFromAboveSingle = Arrays.asList(
- Symbols.SINGLE_LINE_VERTICAL,
- Symbols.BOLD_TO_NORMAL_SINGLE_LINE_VERTICAL,
- Symbols.SINGLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS,
- Symbols.SINGLE_LINE_TOP_LEFT_CORNER,
- Symbols.SINGLE_LINE_TOP_RIGHT_CORNER,
- Symbols.SINGLE_LINE_T_LEFT,
- Symbols.SINGLE_LINE_T_RIGHT,
- Symbols.SINGLE_LINE_T_DOWN,
- Symbols.SINGLE_LINE_T_DOUBLE_LEFT,
- Symbols.SINGLE_LINE_T_DOUBLE_RIGHT,
- Symbols.DOUBLE_LINE_T_SINGLE_DOWN);
- List<Character> junctionFromAboveDouble = Arrays.asList(
- Symbols.DOUBLE_LINE_VERTICAL,
- Symbols.DOUBLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_TOP_LEFT_CORNER,
- Symbols.DOUBLE_LINE_TOP_RIGHT_CORNER,
- Symbols.DOUBLE_LINE_T_LEFT,
- Symbols.DOUBLE_LINE_T_RIGHT,
- Symbols.DOUBLE_LINE_T_DOWN,
- Symbols.DOUBLE_LINE_T_SINGLE_LEFT,
- Symbols.DOUBLE_LINE_T_SINGLE_RIGHT,
- Symbols.SINGLE_LINE_T_DOUBLE_DOWN);
- List<Character> junctionFromLeftSingle = Arrays.asList(
- Symbols.SINGLE_LINE_HORIZONTAL,
- Symbols.BOLD_TO_NORMAL_SINGLE_LINE_HORIZONTAL,
- Symbols.SINGLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS,
- Symbols.SINGLE_LINE_BOTTOM_LEFT_CORNER,
- Symbols.SINGLE_LINE_TOP_LEFT_CORNER,
- Symbols.SINGLE_LINE_T_UP,
- Symbols.SINGLE_LINE_T_DOWN,
- Symbols.SINGLE_LINE_T_RIGHT,
- Symbols.SINGLE_LINE_T_DOUBLE_UP,
- Symbols.SINGLE_LINE_T_DOUBLE_DOWN,
- Symbols.DOUBLE_LINE_T_SINGLE_RIGHT);
- List<Character> junctionFromLeftDouble = Arrays.asList(
- Symbols.DOUBLE_LINE_HORIZONTAL,
- Symbols.DOUBLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_BOTTOM_LEFT_CORNER,
- Symbols.DOUBLE_LINE_TOP_LEFT_CORNER,
- Symbols.DOUBLE_LINE_T_UP,
- Symbols.DOUBLE_LINE_T_DOWN,
- Symbols.DOUBLE_LINE_T_RIGHT,
- Symbols.DOUBLE_LINE_T_SINGLE_UP,
- Symbols.DOUBLE_LINE_T_SINGLE_DOWN,
- Symbols.SINGLE_LINE_T_DOUBLE_RIGHT);
- List<Character> junctionFromRightSingle = Arrays.asList(
- Symbols.SINGLE_LINE_HORIZONTAL,
- Symbols.BOLD_FROM_NORMAL_SINGLE_LINE_HORIZONTAL,
- Symbols.SINGLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS,
- Symbols.SINGLE_LINE_BOTTOM_RIGHT_CORNER,
- Symbols.SINGLE_LINE_TOP_RIGHT_CORNER,
- Symbols.SINGLE_LINE_T_UP,
- Symbols.SINGLE_LINE_T_DOWN,
- Symbols.SINGLE_LINE_T_LEFT,
- Symbols.SINGLE_LINE_T_DOUBLE_UP,
- Symbols.SINGLE_LINE_T_DOUBLE_DOWN,
- Symbols.DOUBLE_LINE_T_SINGLE_LEFT);
- List<Character> junctionFromRightDouble = Arrays.asList(
- Symbols.DOUBLE_LINE_HORIZONTAL,
- Symbols.DOUBLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS,
- Symbols.DOUBLE_LINE_BOTTOM_RIGHT_CORNER,
- Symbols.DOUBLE_LINE_TOP_RIGHT_CORNER,
- Symbols.DOUBLE_LINE_T_UP,
- Symbols.DOUBLE_LINE_T_DOWN,
- Symbols.DOUBLE_LINE_T_LEFT,
- Symbols.DOUBLE_LINE_T_SINGLE_UP,
- Symbols.DOUBLE_LINE_T_SINGLE_DOWN,
- Symbols.SINGLE_LINE_T_DOUBLE_LEFT);
-
- //Go horizontally and check vertical neighbours if it's possible to extend lines into the border
- for(int column = 1; column < drawableArea.getColumns() - 1; column++) {
- //Check first row
- TextCharacter borderCharacter = graphics.getCharacter(column, upperRow);
- if(borderCharacter == null) {
- continue;
- }
- TextCharacter neighbourCharacter = graphics.getCharacter(column, upperRow + 1);
- if(neighbourCharacter != null) {
- char neighbour = neighbourCharacter.getCharacter();
- if(borderCharacter.getCharacter() == Symbols.SINGLE_LINE_HORIZONTAL) {
- if(junctionFromBelowSingle.contains(neighbour)) {
- graphics.setCharacter(column, upperRow, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOWN));
- }
- else if(junctionFromBelowDouble.contains(neighbour)) {
- graphics.setCharacter(column, upperRow, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOUBLE_DOWN));
- }
- }
- else if(borderCharacter.getCharacter() == Symbols.DOUBLE_LINE_HORIZONTAL) {
- if(junctionFromBelowSingle.contains(neighbour)) {
- graphics.setCharacter(column, upperRow, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_SINGLE_DOWN));
- }
- else if(junctionFromBelowDouble.contains(neighbour)) {
- graphics.setCharacter(column, upperRow, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_DOWN));
- }
- }
- }
-
- //Check last row
- borderCharacter = graphics.getCharacter(column, lowerRow);
- if(borderCharacter == null) {
- continue;
- }
- neighbourCharacter = graphics.getCharacter(column, lowerRow - 1);
- if(neighbourCharacter != null) {
- char neighbour = neighbourCharacter.getCharacter();
- if(borderCharacter.getCharacter() == Symbols.SINGLE_LINE_HORIZONTAL) {
- if(junctionFromAboveSingle.contains(neighbour)) {
- graphics.setCharacter(column, lowerRow, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_UP));
- }
- else if(junctionFromAboveDouble.contains(neighbour)) {
- graphics.setCharacter(column, lowerRow, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOUBLE_UP));
- }
- }
- else if(borderCharacter.getCharacter() == Symbols.DOUBLE_LINE_HORIZONTAL) {
- if(junctionFromAboveSingle.contains(neighbour)) {
- graphics.setCharacter(column, lowerRow, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_SINGLE_UP));
- }
- else if(junctionFromAboveDouble.contains(neighbour)) {
- graphics.setCharacter(column, lowerRow, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_UP));
- }
- }
- }
- }
-
- //Go vertically and check horizontal neighbours if it's possible to extend lines into the border
- for(int row = 1; row < drawableArea.getRows() - 1; row++) {
- //Check first column
- TextCharacter borderCharacter = graphics.getCharacter(leftRow, row);
- if(borderCharacter == null) {
- continue;
- }
- TextCharacter neighbourCharacter = graphics.getCharacter(leftRow + 1, row);
- if(neighbourCharacter != null) {
- char neighbour = neighbourCharacter.getCharacter();
- if(borderCharacter.getCharacter() == Symbols.SINGLE_LINE_VERTICAL) {
- if(junctionFromRightSingle.contains(neighbour)) {
- graphics.setCharacter(leftRow, row, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_RIGHT));
- }
- else if(junctionFromRightDouble.contains(neighbour)) {
- graphics.setCharacter(leftRow, row, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOUBLE_RIGHT));
- }
- }
- else if(borderCharacter.getCharacter() == Symbols.DOUBLE_LINE_VERTICAL) {
- if(junctionFromRightSingle.contains(neighbour)) {
- graphics.setCharacter(leftRow, row, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_SINGLE_RIGHT));
- }
- else if(junctionFromRightDouble.contains(neighbour)) {
- graphics.setCharacter(leftRow, row, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_RIGHT));
- }
- }
- }
-
- //Check last column
- borderCharacter = graphics.getCharacter(rightRow, row);
- if(borderCharacter == null) {
- continue;
- }
- neighbourCharacter = graphics.getCharacter(rightRow - 1, row);
- if(neighbourCharacter != null) {
- char neighbour = neighbourCharacter.getCharacter();
- if(borderCharacter.getCharacter() == Symbols.SINGLE_LINE_VERTICAL) {
- if(junctionFromLeftSingle.contains(neighbour)) {
- graphics.setCharacter(rightRow, row, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_LEFT));
- }
- else if(junctionFromLeftDouble.contains(neighbour)) {
- graphics.setCharacter(rightRow, row, borderCharacter.withCharacter(Symbols.SINGLE_LINE_T_DOUBLE_LEFT));
- }
- }
- else if(borderCharacter.getCharacter() == Symbols.DOUBLE_LINE_VERTICAL) {
- if(junctionFromLeftSingle.contains(neighbour)) {
- graphics.setCharacter(rightRow, row, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_SINGLE_LEFT));
- }
- else if(junctionFromLeftDouble.contains(neighbour)) {
- graphics.setCharacter(rightRow, row, borderCharacter.withCharacter(Symbols.DOUBLE_LINE_T_LEFT));
- }
- }
- }
- }
- }
-
- private static class SingleLine extends StandardBorder {
- private SingleLine(String title, BorderStyle borderStyle) {
- super(title, borderStyle);
- }
-
- @Override
- protected BorderRenderer createDefaultRenderer() {
- return new SingleLineRenderer(borderStyle);
- }
- }
-
- private static class SingleLineRenderer extends AbstractBorderRenderer {
- public SingleLineRenderer(BorderStyle borderStyle) {
- super(borderStyle);
- }
-
- @Override
- protected char getTopRightCorner(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(SingleLineRenderer.class).getCharacter("TOP_RIGHT_CORNER", Symbols.SINGLE_LINE_TOP_RIGHT_CORNER);
- }
-
- @Override
- protected char getBottomRightCorner(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(SingleLineRenderer.class).getCharacter("BOTTOM_RIGHT_CORNER", Symbols.SINGLE_LINE_BOTTOM_RIGHT_CORNER);
- }
-
- @Override
- protected char getTopLeftCorner(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(SingleLineRenderer.class).getCharacter("TOP_LEFT_CORNER", Symbols.SINGLE_LINE_TOP_LEFT_CORNER);
- }
-
- @Override
- protected char getBottomLeftCorner(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(SingleLineRenderer.class).getCharacter("BOTTOM_LEFT_CORNER", Symbols.SINGLE_LINE_BOTTOM_LEFT_CORNER);
- }
-
- @Override
- protected char getVerticalLine(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(SingleLineRenderer.class).getCharacter("VERTICAL_LINE", Symbols.SINGLE_LINE_VERTICAL);
- }
-
- @Override
- protected char getHorizontalLine(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(SingleLineRenderer.class).getCharacter("HORIZONTAL_LINE", Symbols.SINGLE_LINE_HORIZONTAL);
- }
- }
-
- private static class DoubleLine extends StandardBorder {
- private DoubleLine(String title, BorderStyle borderStyle) {
- super(title, borderStyle);
- }
-
- @Override
- protected BorderRenderer createDefaultRenderer() {
- return new DoubleLineRenderer(borderStyle);
- }
- }
-
- private static class DoubleLineRenderer extends AbstractBorderRenderer {
- public DoubleLineRenderer(BorderStyle borderStyle) {
- super(borderStyle);
- }
-
- @Override
- protected char getTopRightCorner(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(DoubleLine.class).getCharacter("TOP_RIGHT_CORNER", Symbols.DOUBLE_LINE_TOP_RIGHT_CORNER);
- }
-
- @Override
- protected char getBottomRightCorner(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(DoubleLine.class).getCharacter("BOTTOM_RIGHT_CORNER", Symbols.DOUBLE_LINE_BOTTOM_RIGHT_CORNER);
- }
-
- @Override
- protected char getTopLeftCorner(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(DoubleLine.class).getCharacter("TOP_LEFT_CORNER", Symbols.DOUBLE_LINE_TOP_LEFT_CORNER);
- }
-
- @Override
- protected char getBottomLeftCorner(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(DoubleLine.class).getCharacter("BOTTOM_LEFT_CORNER", Symbols.DOUBLE_LINE_BOTTOM_LEFT_CORNER);
- }
-
- @Override
- protected char getVerticalLine(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(DoubleLine.class).getCharacter("VERTICAL_LINE", Symbols.DOUBLE_LINE_VERTICAL);
- }
-
- @Override
- protected char getHorizontalLine(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(DoubleLine.class).getCharacter("HORIZONTAL_LINE", Symbols.DOUBLE_LINE_HORIZONTAL);
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.ThemeDefinition;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-
-/**
- * Simple labeled button with an optional action attached to it, you trigger the action by pressing the Enter key on the
- * keyboard when the component is in focus.
- * @author Martin
- */
-public class Button extends AbstractInteractableComponent<Button> {
- private final Runnable action;
- private String label;
-
- /**
- * Creates a new button with a specific label and no attached action. Why would you need this? I have no idea.
- * @param label Label to put on the button
- */
- public Button(String label) {
- this(label, new Runnable() {
- @Override
- public void run() {
- }
- });
- }
-
- /**
- * Creates a new button with a label and an associated action to fire when triggered by the user
- * @param label Label to put on the button
- * @param action What action to fire when the user triggers the button by pressing the enter key
- */
- public Button(String label, Runnable action) {
- this.action = action;
- setLabel(label);
- }
-
- @Override
- protected ButtonRenderer createDefaultRenderer() {
- return new DefaultButtonRenderer();
- }
-
- @Override
- public synchronized TerminalPosition getCursorLocation() {
- return getRenderer().getCursorLocation(this);
- }
-
- @Override
- public synchronized Result handleKeyStroke(KeyStroke keyStroke) {
- if(keyStroke.getKeyType() == KeyType.Enter) {
- action.run();
- return Result.HANDLED;
- }
- return super.handleKeyStroke(keyStroke);
- }
-
- /**
- * Updates the label on the button to the specified string
- * @param label New label to use on the button
- */
- public final synchronized void setLabel(String label) {
- if(label == null) {
- throw new IllegalArgumentException("null label to a button is not allowed");
- }
- if(label.isEmpty()) {
- label = " ";
- }
- this.label = label;
- invalidate();
- }
-
- /**
- * Returns the label current assigned to the button
- * @return Label currently used by the button
- */
- public String getLabel() {
- return label;
- }
-
- @Override
- public String toString() {
- return "Button{" + label + "}";
- }
-
- /**
- * Helper interface that doesn't add any new methods but makes coding new button renderers a little bit more clear
- */
- public interface ButtonRenderer extends InteractableRenderer<Button> {
- }
-
- /**
- * This is the default button renderer that is used if you don't override anything. With this renderer, buttons are
- * drawn on a single line, with the label inside of "<" and ">".
- */
- public static class DefaultButtonRenderer implements ButtonRenderer {
- @Override
- public TerminalPosition getCursorLocation(Button button) {
- return new TerminalPosition(1 + getLabelShift(button, button.getSize()), 0);
- }
-
- @Override
- public TerminalSize getPreferredSize(Button button) {
- return new TerminalSize(Math.max(8, TerminalTextUtils.getColumnWidth(button.getLabel()) + 2), 1);
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, Button button) {
- if(button.isFocused()) {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getActive());
- }
- else {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getInsensitive());
- }
- graphics.fill(' ');
- graphics.setCharacter(0, 0, getThemeDefinition(graphics).getCharacter("LEFT_BORDER", '<'));
- graphics.setCharacter(graphics.getSize().getColumns() - 1, 0, getThemeDefinition(graphics).getCharacter("RIGHT_BORDER", '>'));
-
- if(button.isFocused()) {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getActive());
- }
- else {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getPreLight());
- }
- int labelShift = getLabelShift(button, graphics.getSize());
- graphics.setCharacter(1 + labelShift, 0, button.getLabel().charAt(0));
-
- if(TerminalTextUtils.getColumnWidth(button.getLabel()) == 1) {
- return;
- }
- if(button.isFocused()) {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getSelected());
- }
- else {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getNormal());
- }
- graphics.putString(1 + labelShift + 1, 0, button.getLabel().substring(1));
- }
-
- private int getLabelShift(Button button, TerminalSize size) {
- int availableSpace = size.getColumns() - 2;
- if(availableSpace <= 0) {
- return 0;
- }
- int labelShift = 0;
- int widthInColumns = TerminalTextUtils.getColumnWidth(button.getLabel());
- if(availableSpace > widthInColumns) {
- labelShift = (size.getColumns() - 2 - widthInColumns) / 2;
- }
- return labelShift;
- }
- }
-
- /**
- * Alternative button renderer that displays buttons with just the label and minimal decoration
- */
- public static class FlatButtonRenderer implements ButtonRenderer {
- @Override
- public TerminalPosition getCursorLocation(Button component) {
- return null;
- }
-
- @Override
- public TerminalSize getPreferredSize(Button component) {
- return new TerminalSize(TerminalTextUtils.getColumnWidth(component.getLabel()), 1);
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, Button button) {
- if(button.isFocused()) {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getActive());
- }
- else {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getInsensitive());
- }
- graphics.fill(' ');
- if(button.isFocused()) {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getSelected());
- }
- else {
- graphics.applyThemeStyle(getThemeDefinition(graphics).getNormal());
- }
- graphics.putString(0, 0, button.getLabel());
- }
- }
-
- private static ThemeDefinition getThemeDefinition(TextGUIGraphics graphics) {
- return graphics.getThemeDefinition(Button.class);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.ThemeDefinition;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * The checkbox component looks like a regular checkbox that you can find in modern graphics user interfaces, a label
- * and a space that the user can toggle on and off by using enter or space keys.
- *
- * @author Martin
- */
-public class CheckBox extends AbstractInteractableComponent<CheckBox> {
-
- /**
- * Listener interface that can be used to catch user events on the check box
- */
- public interface Listener {
- /**
- * This is fired when the user has altered the checked state of this {@code CheckBox}
- * @param checked If the {@code CheckBox} is now toggled on, this is set to {@code true}, otherwise
- * {@code false}
- */
- void onStatusChanged(boolean checked);
- }
-
- private final List<Listener> listeners;
- private String label;
- private boolean checked;
-
- /**
- * Creates a new checkbox with no label, initially set to un-checked
- */
- public CheckBox() {
- this("");
- }
-
- /**
- * Creates a new checkbox with a specific label, initially set to un-checked
- * @param label Label to assign to the check box
- */
- public CheckBox(String label) {
- if(label == null) {
- throw new IllegalArgumentException("Cannot create a CheckBox with null label");
- }
- else if(label.contains("\n") || label.contains("\r")) {
- throw new IllegalArgumentException("Multiline checkbox labels are not supported");
- }
- this.listeners = new CopyOnWriteArrayList<Listener>();
- this.label = label;
- this.checked = false;
- }
-
- /**
- * Programmatically updated the check box to a particular checked state
- * @param checked If {@code true}, the check box will be set to toggled on, otherwise {@code false}
- * @return Itself
- */
- public synchronized CheckBox setChecked(final boolean checked) {
- this.checked = checked;
- runOnGUIThreadIfExistsOtherwiseRunDirect(new Runnable() {
- @Override
- public void run() {
- for(Listener listener : listeners) {
- listener.onStatusChanged(checked);
- }
- }
- });
- invalidate();
- return this;
- }
-
- /**
- * Returns the checked state of this check box
- * @return {@code true} if the check box is toggled on, otherwise {@code false}
- */
- public boolean isChecked() {
- return checked;
- }
-
- @Override
- public Result handleKeyStroke(KeyStroke keyStroke) {
- if((keyStroke.getKeyType() == KeyType.Character && keyStroke.getCharacter() == ' ') ||
- keyStroke.getKeyType() == KeyType.Enter) {
- setChecked(!isChecked());
- return Result.HANDLED;
- }
- return super.handleKeyStroke(keyStroke);
- }
-
- /**
- * Updates the label of the checkbox
- * @param label New label to assign to the check box
- * @return Itself
- */
- public synchronized CheckBox setLabel(String label) {
- if(label == null) {
- throw new IllegalArgumentException("Cannot set CheckBox label to null");
- }
- this.label = label;
- invalidate();
- return this;
- }
-
- /**
- * Returns the label of check box
- * @return Label currently assigned to the check box
- */
- public String getLabel() {
- return label;
- }
-
- /**
- * Adds a listener to this check box so that it will be notificed on certain user actions
- * @param listener Listener to fire events on
- * @return Itself
- */
- public CheckBox addListener(Listener listener) {
- if(listener != null && !listeners.contains(listener)) {
- listeners.add(listener);
- }
- return this;
- }
-
- /**
- * Removes a listener from this check box so that, if it was previously added, it will no long receive any events
- * @param listener Listener to remove from the check box
- * @return Itself
- */
- public CheckBox removeListener(Listener listener) {
- listeners.remove(listener);
- return this;
- }
-
- @Override
- protected CheckBoxRenderer createDefaultRenderer() {
- return new DefaultCheckBoxRenderer();
- }
-
- /**
- * Helper interface that doesn't add any new methods but makes coding new check box renderers a little bit more clear
- */
- public static abstract class CheckBoxRenderer implements InteractableRenderer<CheckBox> {
- }
-
- /**
- * The default renderer that is used unless overridden. This renderer will draw the checkbox label on the right side
- * of a "[ ]" block which will contain a "X" inside it if the check box has toggle status on
- */
- public static class DefaultCheckBoxRenderer extends CheckBoxRenderer {
- private static final TerminalPosition CURSOR_LOCATION = new TerminalPosition(1, 0);
- @Override
- public TerminalPosition getCursorLocation(CheckBox component) {
- return CURSOR_LOCATION;
- }
-
- @Override
- public TerminalSize getPreferredSize(CheckBox component) {
- int width = 3;
- if(!component.label.isEmpty()) {
- width += 1 + TerminalTextUtils.getColumnWidth(component.label);
- }
- return new TerminalSize(width, 1);
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, CheckBox component) {
- ThemeDefinition themeDefinition = graphics.getThemeDefinition(CheckBox.class);
- if(component.isFocused()) {
- graphics.applyThemeStyle(themeDefinition.getActive());
- }
- else {
- graphics.applyThemeStyle(themeDefinition.getNormal());
- }
-
- graphics.fill(' ');
- graphics.putString(4, 0, component.label);
-
- String head = "[" + (component.isChecked() ? themeDefinition.getCharacter("MARKER", 'x') : " ") + "]";
- if(component.isFocused()) {
- graphics.applyThemeStyle(themeDefinition.getPreLight());
- }
- else {
- graphics.applyThemeStyle(themeDefinition.getNormal());
- }
- graphics.putString(0, 0, head);
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * This is a list box implementation where each item has its own checked state that can be toggled on and off
- * @author Martin
- */
-public class CheckBoxList<V> extends AbstractListBox<V, CheckBoxList<V>> {
- /**
- * Listener interface that can be attached to the {@code CheckBoxList} in order to be notified on user actions
- */
- public interface Listener {
- /**
- * Called by the {@code CheckBoxList} when the user changes the toggle state of one item
- * @param itemIndex Index of the item that was toggled
- * @param checked If the state of the item is now checked, this will be {@code true}, otherwise {@code false}
- */
- void onStatusChanged(int itemIndex, boolean checked);
- }
-
- private final List<Listener> listeners;
- private final List<Boolean> itemStatus;
-
- /**
- * Creates a new {@code CheckBoxList} that is initially empty and has no hardcoded preferred size, so it will
- * attempt to be as big as necessary to draw all items.
- */
- public CheckBoxList() {
- this(null);
- }
-
- /**
- * Creates a new {@code CheckBoxList} that is initially empty and has a pre-defined size that it will request. If
- * there are more items that can fit in this size, the list box will use scrollbars.
- * @param preferredSize Size the list box should request, no matter how many items it contains
- */
- public CheckBoxList(TerminalSize preferredSize) {
- super(preferredSize);
- this.listeners = new CopyOnWriteArrayList<Listener>();
- this.itemStatus = new ArrayList<Boolean>();
- }
-
- @Override
- protected ListItemRenderer<V,CheckBoxList<V>> createDefaultListItemRenderer() {
- return new CheckBoxListItemRenderer<V>();
- }
-
- @Override
- public synchronized CheckBoxList<V> clearItems() {
- itemStatus.clear();
- return super.clearItems();
- }
-
- @Override
- public CheckBoxList<V> addItem(V object) {
- return addItem(object, false);
- }
-
- /**
- * Adds an item to the checkbox list with an explicit checked status
- * @param object Object to add to the list
- * @param checkedState If <code>true</code>, the new item will be initially checked
- * @return Itself
- */
- public synchronized CheckBoxList<V> addItem(V object, boolean checkedState) {
- itemStatus.add(checkedState);
- return super.addItem(object);
- }
-
- /**
- * Checks if a particular item is part of the check box list and returns a boolean value depending on the toggle
- * state of the item.
- * @param object Object to check the status of
- * @return If the item wasn't found in the list box, {@code null} is returned, otherwise {@code true} or
- * {@code false} depending on checked state of the item
- */
- public synchronized Boolean isChecked(V object) {
- if(indexOf(object) == -1)
- return null;
-
- return itemStatus.get(indexOf(object));
- }
-
- /**
- * Checks if a particular item is part of the check box list and returns a boolean value depending on the toggle
- * state of the item.
- * @param index Index of the item to check the status of
- * @return If the index was not valid in the list box, {@code null} is returned, otherwise {@code true} or
- * {@code false} depending on checked state of the item at that index
- */
- public synchronized Boolean isChecked(int index) {
- if(index < 0 || index >= itemStatus.size())
- return null;
-
- return itemStatus.get(index);
- }
-
- /**
- * Programmatically sets the checked state of an item in the list box
- * @param object Object to set the checked state of
- * @param checked If {@code true}, then the item is set to checked, otherwise not
- * @return Itself
- */
- public synchronized CheckBoxList<V> setChecked(V object, boolean checked) {
- int index = indexOf(object);
- if(index != -1) {
- setChecked(index, checked);
- }
- return self();
- }
-
- private void setChecked(final int index, final boolean checked) {
- itemStatus.set(index, checked);
- runOnGUIThreadIfExistsOtherwiseRunDirect(new Runnable() {
- @Override
- public void run() {
- for(Listener listener: listeners) {
- listener.onStatusChanged(index, checked);
- }
- }
- });
- }
-
- /**
- * Returns all the items in the list box that have checked state, as a list
- * @return List of all items in the list box that has checked state on
- */
- public synchronized List<V> getCheckedItems() {
- List<V> result = new ArrayList<V>();
- for(int i = 0; i < itemStatus.size(); i++) {
- if(itemStatus.get(i)) {
- result.add(getItemAt(i));
- }
- }
- return result;
- }
-
- /**
- * Adds a new listener to the {@code CheckBoxList} that will be called on certain user actions
- * @param listener Listener to attach to this {@code CheckBoxList}
- * @return Itself
- */
- public synchronized CheckBoxList<V> addListener(Listener listener) {
- if(listener != null && !listeners.contains(listener)) {
- listeners.add(listener);
- }
- return this;
- }
-
- /**
- * Removes a listener from this {@code CheckBoxList} so that if it had been added earlier, it will no longer be
- * called on user actions
- * @param listener Listener to remove from this {@code CheckBoxList}
- * @return Itself
- */
- public CheckBoxList<V> removeListener(Listener listener) {
- listeners.remove(listener);
- return this;
- }
-
- @Override
- public synchronized Result handleKeyStroke(KeyStroke keyStroke) {
- if(keyStroke.getKeyType() == KeyType.Enter ||
- (keyStroke.getKeyType() == KeyType.Character && keyStroke.getCharacter() == ' ')) {
- if(itemStatus.get(getSelectedIndex()))
- setChecked(getSelectedIndex(), Boolean.FALSE);
- else
- setChecked(getSelectedIndex(), Boolean.TRUE);
- return Result.HANDLED;
- }
- return super.handleKeyStroke(keyStroke);
- }
-
- /**
- * Default renderer for this component which is used unless overridden. The checked state is drawn on the left side
- * of the item label using a "[ ]" block filled with an X if the item has checked state on
- * @param <V>
- */
- public static class CheckBoxListItemRenderer<V> extends ListItemRenderer<V,CheckBoxList<V>> {
- @Override
- public int getHotSpotPositionOnLine(int selectedIndex) {
- return 1;
- }
-
- @Override
- public String getLabel(CheckBoxList<V> listBox, int index, V item) {
- String check = " ";
- List<Boolean> itemStatus = listBox.itemStatus;
- if(itemStatus.get(index))
- check = "x";
-
- String text = item.toString();
- return "[" + check + "] " + text;
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.input.KeyStroke;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * This is a simple combo box implementation that allows the user to select one out of multiple items through a
- * drop-down menu. If the combo box is not in read-only mode, the user can also enter free text in the combo box, much
- * like a {@code TextBox}.
- * @param <V> Type to use for the items in the combo box
- * @author Martin
- */
-public class ComboBox<V> extends AbstractInteractableComponent<ComboBox<V>> {
-
- /**
- * Listener interface that can be used to catch user events on the combo box
- */
- public interface Listener {
- /**
- * This method is called whenever the user changes selection from one item to another in the combo box
- * @param selectedIndex Index of the item which is now selected
- * @param previousSelection Index of the item which was previously selected
- */
- void onSelectionChanged(int selectedIndex, int previousSelection);
- }
-
- private final List<V> items;
- private final List<Listener> listeners;
-
- private PopupWindow popupWindow;
- private String text;
- private int selectedIndex;
-
- private boolean readOnly;
- private boolean dropDownFocused;
- private int textInputPosition;
-
- /**
- * Creates a new {@code ComboBox} initialized with N number of items supplied through the varargs parameter. If at
- * least one item is given, the first one in the array will be initially selected
- * @param items Items to populate the new combo box with
- */
- public ComboBox(V... items) {
- this(Arrays.asList(items));
- }
-
- /**
- * Creates a new {@code ComboBox} initialized with N number of items supplied through the items parameter. If at
- * least one item is given, the first one in the collection will be initially selected
- * @param items Items to populate the new combo box with
- */
- public ComboBox(Collection<V> items) {
- this(items, items.isEmpty() ? -1 : 0);
- }
-
- /**
- * Creates a new {@code ComboBox} initialized with N number of items supplied through the items parameter. The
- * initial text in the combo box is set to a specific value passed in through the {@code initialText} parameter, it
- * can be a text which is not contained within the items and the selection state of the combo box will be
- * "no selection" (so {@code getSelectedIndex()} will return -1) until the user interacts with the combo box and
- * manually changes it
- *
- * @param initialText Text to put in the combo box initially
- * @param items Items to populate the new combo box with
- */
- public ComboBox(String initialText, Collection<V> items) {
- this(items, -1);
- this.text = initialText;
- }
-
- /**
- * Creates a new {@code ComboBox} initialized with N number of items supplied through the items parameter. The
- * initially selected item is specified through the {@code selectedIndex} parameter.
- * @param items Items to populate the new combo box with
- * @param selectedIndex Index of the item which should be initially selected
- */
- public ComboBox(Collection<V> items, int selectedIndex) {
- for(V item: items) {
- if(item == null) {
- throw new IllegalArgumentException("Cannot add null elements to a ComboBox");
- }
- }
- this.items = new ArrayList<V>(items);
- this.listeners = new CopyOnWriteArrayList<Listener>();
- this.popupWindow = null;
- this.selectedIndex = selectedIndex;
- this.readOnly = true;
- this.dropDownFocused = true;
- this.textInputPosition = 0;
- if(selectedIndex != -1) {
- this.text = this.items.get(selectedIndex).toString();
- }
- else {
- this.text = "";
- }
- }
-
- /**
- * Adds a new item to the combo box, at the end
- * @param item Item to add to the combo box
- * @return Itself
- */
- public synchronized ComboBox<V> addItem(V item) {
- if(item == null) {
- throw new IllegalArgumentException("Cannot add null elements to a ComboBox");
- }
- items.add(item);
- if(selectedIndex == -1 && items.size() == 1) {
- setSelectedIndex(0);
- }
- invalidate();
- return this;
- }
-
- /**
- * Adds a new item to the combo box, at a specific index
- * @param index Index to add the item at
- * @param item Item to add
- * @return Itself
- */
- public synchronized ComboBox<V> addItem(int index, V item) {
- if(item == null) {
- throw new IllegalArgumentException("Cannot add null elements to a ComboBox");
- }
- items.add(index, item);
- if(index <= selectedIndex) {
- setSelectedIndex(selectedIndex + 1);
- }
- invalidate();
- return this;
- }
-
- /**
- * Removes all items from the combo box
- * @return Itself
- */
- public synchronized ComboBox<V> clearItems() {
- items.clear();
- setSelectedIndex(-1);
- invalidate();
- return this;
- }
-
- /**
- * Removes a particular item from the combo box, if it is present, otherwise does nothing
- * @param item Item to remove from the combo box
- * @return Itself
- */
- public synchronized ComboBox<V> removeItem(V item) {
- int index = items.indexOf(item);
- if(index == -1) {
- return this;
- }
- return remoteItem(index);
- }
-
- /**
- * Removes an item from the combo box at a particular index
- * @param index Index of the item to remove
- * @return Itself
- * @throws IndexOutOfBoundsException if the index is out of range
- */
- public synchronized ComboBox<V> remoteItem(int index) {
- items.remove(index);
- if(index < selectedIndex) {
- setSelectedIndex(selectedIndex - 1);
- }
- else if(index == selectedIndex) {
- setSelectedIndex(-1);
- }
- invalidate();
- return this;
- }
-
- /**
- * Updates the combo box so the item at the specified index is swapped out with the supplied value in the
- * {@code item} parameter
- * @param index Index of the item to swap out
- * @param item Item to replace with
- * @return Itself
- */
- public synchronized ComboBox<V> setItem(int index, V item) {
- if(item == null) {
- throw new IllegalArgumentException("Cannot add null elements to a ComboBox");
- }
- items.set(index, item);
- invalidate();
- return this;
- }
-
- /**
- * Counts and returns the number of items in this combo box
- * @return Number of items in this combo box
- */
- public synchronized int getItemCount() {
- return items.size();
- }
-
- /**
- * Returns the item at the specific index
- * @param index Index of the item to return
- * @return Item at the specific index
- * @throws IndexOutOfBoundsException if the index is out of range
- */
- public synchronized V getItem(int index) {
- return items.get(index);
- }
-
- /**
- * Returns the text currently displayed in the combo box, this will likely be the label of the selected item but for
- * writable combo boxes it's also what the user has typed in
- * @return String currently displayed in the combo box
- */
- public String getText() {
- return text;
- }
-
- /**
- * Sets the combo box to either read-only or writable. In read-only mode, the user cannot type in any text in the
- * combo box but is forced to pick one of the items, displayed by the drop-down. In writable mode, the user can
- * enter any string in the combo box
- * @param readOnly If the combo box should be in read-only mode, pass in {@code true}, otherwise {@code false} for
- * writable mode
- * @return Itself
- */
- public synchronized ComboBox<V> setReadOnly(boolean readOnly) {
- this.readOnly = readOnly;
- if(readOnly) {
- dropDownFocused = true;
- }
- return this;
- }
-
- /**
- * Returns {@code true} if this combo box is in read-only mode
- * @return {@code true} if this combo box is in read-only mode, {@code false} otherwise
- */
- public boolean isReadOnly() {
- return readOnly;
- }
-
- /**
- * Returns {@code true} if the users input focus is currently on the drop-down button of the combo box, so that
- * pressing enter would trigger the popup window. This is generally used by renderers only and is always true for
- * read-only combo boxes as the component won't allow you to focus on the text in that mode.
- * @return {@code true} if the input focus is on the drop-down "button" of the combo box
- */
- public boolean isDropDownFocused() {
- return dropDownFocused || isReadOnly();
- }
-
- /**
- * For writable combo boxes, this method returns the position where the text input cursor is right now. Meaning, if
- * the user types some character, where are those are going to be inserted in the string that is currently
- * displayed. If the text input position equals the size of the currently displayed text, new characters will be
- * appended at the end. The user can usually move the text input position by using left and right arrow keys on the
- * keyboard.
- * @return Current text input position
- */
- public int getTextInputPosition() {
- return textInputPosition;
- }
-
- /**
- * Programmatically selects one item in the combo box, which causes the displayed text to change to match the label
- * of the selected index
- * @param selectedIndex Index of the item to select
- * @throws IndexOutOfBoundsException if the index is out of range
- */
- public synchronized void setSelectedIndex(final int selectedIndex) {
- if(items.size() <= selectedIndex || selectedIndex < -1) {
- throw new IndexOutOfBoundsException("Illegal argument to ComboBox.setSelectedIndex: " + selectedIndex);
- }
- final int oldSelection = this.selectedIndex;
- this.selectedIndex = selectedIndex;
- if(selectedIndex == -1) {
- text = "";
- }
- else {
- text = items.get(selectedIndex).toString();
- }
- if(textInputPosition > text.length()) {
- textInputPosition = text.length();
- }
- runOnGUIThreadIfExistsOtherwiseRunDirect(new Runnable() {
- @Override
- public void run() {
- for(Listener listener: listeners) {
- listener.onSelectionChanged(selectedIndex, oldSelection);
- }
- }
- });
- invalidate();
- }
-
- /**
- * Returns the index of the currently selected item
- * @return Index of the currently selected item
- */
- public int getSelectedIndex() {
- return selectedIndex;
- }
-
- /**
- * Adds a new listener to the {@code ComboBox} that will be called on certain user actions
- * @param listener Listener to attach to this {@code ComboBox}
- * @return Itself
- */
- public ComboBox<V> addListener(Listener listener) {
- if(listener != null && !listeners.contains(listener)) {
- listeners.add(listener);
- }
- return this;
- }
-
- /**
- * Removes a listener from this {@code ComboBox} so that if it had been added earlier, it will no longer be
- * called on user actions
- * @param listener Listener to remove from this {@code ComboBox}
- * @return Itself
- */
- public ComboBox<V> removeListener(Listener listener) {
- listeners.remove(listener);
- return this;
- }
-
- @Override
- protected void afterEnterFocus(FocusChangeDirection direction, Interactable previouslyInFocus) {
- if(direction == FocusChangeDirection.RIGHT && !isReadOnly()) {
- dropDownFocused = false;
- selectedIndex = 0;
- }
- }
-
- @Override
- protected void afterLeaveFocus(FocusChangeDirection direction, Interactable nextInFocus) {
- if(popupWindow != null) {
- popupWindow.close();
- popupWindow = null;
- }
- }
-
- @Override
- protected InteractableRenderer<ComboBox<V>> createDefaultRenderer() {
- return new DefaultComboBoxRenderer<V>();
- }
-
- @Override
- public synchronized Result handleKeyStroke(KeyStroke keyStroke) {
- if(isReadOnly()) {
- return handleReadOnlyCBKeyStroke(keyStroke);
- }
- else {
- return handleEditableCBKeyStroke(keyStroke);
- }
- }
-
- private Result handleReadOnlyCBKeyStroke(KeyStroke keyStroke) {
- switch(keyStroke.getKeyType()) {
- case ArrowDown:
- if(popupWindow != null) {
- popupWindow.listBox.handleKeyStroke(keyStroke);
- return Result.HANDLED;
- }
- return Result.MOVE_FOCUS_DOWN;
-
- case ArrowUp:
- if(popupWindow != null) {
- popupWindow.listBox.handleKeyStroke(keyStroke);
- return Result.HANDLED;
- }
- return Result.MOVE_FOCUS_UP;
-
- case Enter:
- if(popupWindow != null) {
- popupWindow.listBox.handleKeyStroke(keyStroke);
- popupWindow.close();
- popupWindow = null;
- }
- else {
- popupWindow = new PopupWindow();
- popupWindow.setPosition(toGlobal(getPosition().withRelativeRow(1)));
- ((WindowBasedTextGUI) getTextGUI()).addWindow(popupWindow);
- }
- break;
-
- case Escape:
- if(popupWindow != null) {
- popupWindow.close();
- popupWindow = null;
- return Result.HANDLED;
- }
- break;
-
- default:
- }
- return super.handleKeyStroke(keyStroke);
- }
-
- private Result handleEditableCBKeyStroke(KeyStroke keyStroke) {
- //First check if we are in drop-down focused mode, treat keystrokes a bit differently then
- if(isDropDownFocused()) {
- switch(keyStroke.getKeyType()) {
- case ReverseTab:
- case ArrowLeft:
- dropDownFocused = false;
- textInputPosition = text.length();
- return Result.HANDLED;
-
- //The rest we can process in the same way as with read-only combo boxes when we are in drop-down focused mode
- default:
- return handleReadOnlyCBKeyStroke(keyStroke);
- }
- }
-
- switch(keyStroke.getKeyType()) {
- case Character:
- text = text.substring(0, textInputPosition) + keyStroke.getCharacter() + text.substring(textInputPosition);
- textInputPosition++;
- return Result.HANDLED;
-
- case Tab:
- dropDownFocused = true;
- return Result.HANDLED;
-
- case Backspace:
- if(textInputPosition > 0) {
- text = text.substring(0, textInputPosition - 1) + text.substring(textInputPosition);
- textInputPosition--;
- }
- return Result.HANDLED;
-
- case Delete:
- if(textInputPosition < text.length()) {
- text = text.substring(0, textInputPosition) + text.substring(textInputPosition + 1);
- }
- return Result.HANDLED;
-
- case ArrowLeft:
- if(textInputPosition > 0) {
- textInputPosition--;
- }
- else {
- return Result.MOVE_FOCUS_LEFT;
- }
- return Result.HANDLED;
-
- case ArrowRight:
- if(textInputPosition < text.length()) {
- textInputPosition++;
- }
- else {
- dropDownFocused = true;
- return Result.HANDLED;
- }
- return Result.HANDLED;
-
- case ArrowDown:
- if(selectedIndex < items.size() - 1) {
- setSelectedIndex(selectedIndex + 1);
- }
- return Result.HANDLED;
-
- case ArrowUp:
- if(selectedIndex > 0) {
- setSelectedIndex(selectedIndex - 1);
- }
- return Result.HANDLED;
-
- default:
- }
- return super.handleKeyStroke(keyStroke);
- }
-
- private class PopupWindow extends BasicWindow {
- private final ActionListBox listBox;
-
- public PopupWindow() {
- setHints(Arrays.asList(
- Hint.NO_FOCUS,
- Hint.FIXED_POSITION));
- listBox = new ActionListBox(ComboBox.this.getSize().withRows(getItemCount()));
- for(int i = 0; i < getItemCount(); i++) {
- V item = items.get(i);
- final int index = i;
- listBox.addItem(item.toString(), new Runnable() {
- @Override
- public void run() {
- setSelectedIndex(index);
- close();
- }
- });
- }
- listBox.setSelectedIndex(getSelectedIndex());
- setComponent(listBox);
- }
- }
-
- /**
- * Helper interface that doesn't add any new methods but makes coding new combo box renderers a little bit more clear
- */
- public static abstract class ComboBoxRenderer<V> implements InteractableRenderer<ComboBox<V>> {
- }
-
- /**
- * This class is the default renderer implementation which will be used unless overridden. The combo box is rendered
- * like a text box with an arrow point down to the right of it, which can receive focus and triggers the popup.
- * @param <V> Type of items in the combo box
- */
- public static class DefaultComboBoxRenderer<V> extends ComboBoxRenderer<V> {
-
- private int textVisibleLeftPosition;
-
- /**
- * Default constructor
- */
- public DefaultComboBoxRenderer() {
- this.textVisibleLeftPosition = 0;
- }
-
- @Override
- public TerminalPosition getCursorLocation(ComboBox<V> comboBox) {
- if(comboBox.isDropDownFocused()) {
- return new TerminalPosition(comboBox.getSize().getColumns() - 1, 0);
- }
- else {
- int textInputPosition = comboBox.getTextInputPosition();
- int textInputColumn = TerminalTextUtils.getColumnWidth(comboBox.getText().substring(0, textInputPosition));
- return new TerminalPosition(textInputColumn - textVisibleLeftPosition, 0);
- }
- }
-
- @Override
- public TerminalSize getPreferredSize(final ComboBox<V> comboBox) {
- TerminalSize size = TerminalSize.ONE.withColumns(
- (comboBox.getItemCount() == 0 ? TerminalTextUtils.getColumnWidth(comboBox.getText()) : 0) + 2);
- synchronized(comboBox) {
- for(int i = 0; i < comboBox.getItemCount(); i++) {
- V item = comboBox.getItem(i);
- size = size.max(new TerminalSize(TerminalTextUtils.getColumnWidth(item.toString()) + 2 + 1, 1)); // +1 to add a single column of space
- }
- }
- return size;
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, ComboBox<V> comboBox) {
- graphics.setForegroundColor(TextColor.ANSI.WHITE);
- graphics.setBackgroundColor(TextColor.ANSI.BLUE);
- if(comboBox.isFocused()) {
- graphics.setForegroundColor(TextColor.ANSI.YELLOW);
- graphics.enableModifiers(SGR.BOLD);
- }
- graphics.fill(' ');
- int editableArea = graphics.getSize().getColumns() - 2; //This is exclusing the 'drop-down arrow'
- int textInputPosition = comboBox.getTextInputPosition();
- int columnsToInputPosition = TerminalTextUtils.getColumnWidth(comboBox.getText().substring(0, textInputPosition));
- if(columnsToInputPosition < textVisibleLeftPosition) {
- textVisibleLeftPosition = columnsToInputPosition;
- }
- if(columnsToInputPosition - textVisibleLeftPosition >= editableArea) {
- textVisibleLeftPosition = columnsToInputPosition - editableArea + 1;
- }
- if(columnsToInputPosition - textVisibleLeftPosition + 1 == editableArea &&
- comboBox.getText().length() > textInputPosition &&
- TerminalTextUtils.isCharCJK(comboBox.getText().charAt(textInputPosition))) {
- textVisibleLeftPosition++;
- }
-
- String textToDraw = TerminalTextUtils.fitString(comboBox.getText(), textVisibleLeftPosition, editableArea);
- graphics.putString(0, 0, textToDraw);
- if(comboBox.isFocused()) {
- graphics.disableModifiers(SGR.BOLD);
- }
- graphics.setForegroundColor(TextColor.ANSI.BLACK);
- graphics.setBackgroundColor(TextColor.ANSI.WHITE);
- graphics.putString(editableArea, 0, "|" + Symbols.ARROW_DOWN);
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * This is the main interface defining a component in Lanterna, although you will probably not implement this directly
- * but rather extend the {@code AbstractComponent} or another one of the sub-classes instead to avoid implementing most
- * of the methods in this interface.
- * @author Martin
- */
-public interface Component extends TextGUIElement {
- /**
- * Returns the top-left corner of this component, measured from its parent.
- * @return Position of this component
- */
- TerminalPosition getPosition();
-
- /**
- * This method will be called by the layout manager when it has decided where the component is to be located. If you
- * call this method yourself, prepare for unexpected results.
- * @param position Top-left position of the component, relative to its parent
- * @return Itself
- */
- Component setPosition(TerminalPosition position);
-
- /**
- * Returns how large this component is. If the layout manager has not yet laid this component out, it will return
- * an empty size (0x0)
- * @return How large this component is
- */
- TerminalSize getSize();
-
- /**
- * This method will be called by the layout manager when it has decided how large the component will be. If you call
- * this method yourself, prepare for unexpected results.
- * @param size Current size of the component
- * @return Itself
- */
- Component setSize(TerminalSize size);
-
- /**
- * Returns the ideal size this component would like to have, in order to draw itself properly. There are no
- * guarantees the GUI system will decide to give it this size though.
- * @return Size we would like to be
- */
- TerminalSize getPreferredSize();
-
-
- /**
- * Overrides the components preferred size calculation and makes the {@code getPreferredSize()} always return the
- * value passed in here. If you call this will {@code null}, it will re-enable the preferred size calculation again.
- * Please note that using this method on components that are not designed to work with arbitrary sizes make have
- * unexpected behaviour.
- * @param explicitPreferredSize Preferred size we want to use for this component
- * @return Itself
- */
- Component setPreferredSize(TerminalSize explicitPreferredSize);
-
- /**
- * Sets optional layout data associated with this component. This meaning of this data is up to the layout manager
- * to figure out, see each layout manager for examples of how to use it.
- * @param data Layout data associated with this component
- * @return Itself
- */
- Component setLayoutData(LayoutData data);
-
- /**
- * Returns the layout data associated with this component. This data will optionally be used by the layout manager,
- * see the documentation for each layout manager for more details on valid values and their meaning.
- * @return This component's layout data
- */
- LayoutData getLayoutData();
-
- /**
- * Returns the container which is holding this container, or {@code null} if it's not assigned to anything.
- * @return Parent container or null
- */
- Container getParent();
-
- /**
- * Returns {@code true} if the supplied Container is either the direct or indirect Parent of this component.
- * @param parent Container to test if it's the parent or grand-parent of this component
- * @return {@code true} if the container is either the direct or indirect parent of this component, otherwise {@code false}
- */
- boolean hasParent(Container parent);
-
- /**
- * Returns the TextGUI that this component is currently part of. If the component hasn't been added to any container
- * or in any other way placed into a GUI system, this method will return null.
- * @return The TextGUI that this component belongs to, or null if none
- */
- TextGUI getTextGUI();
-
- /**
- * Returns true if this component is inside of the specified Container. It might be a direct child or not, this
- * method makes no difference. If {@code getParent()} is not the same instance as {@code container}, but if this
- * method returns true, you can be sure that this component is not a direct child.
- * @param container Container to test if this component is inside
- * @return True if this component is contained in some way within the {@code container}
- */
- boolean isInside(Container container);
-
- /**
- * Returns the renderer used to draw this component and measure its preferred size. You probably won't need to call
- * this method unless you know exactly which ComponentRenderer implementation is used and you need to customize it.
- * @return Renderer this component is using
- */
- ComponentRenderer<? extends Component> getRenderer();
-
- /**
- * Marks the component as invalid and requiring to be re-drawn at next opportunity. Container components should take
- * this as a hint to layout the child components again.
- */
- void invalidate();
-
- /**
- * Takes a border object and moves this component inside it and then returns it again. This makes it easy to quickly
- * wrap a component on creation, like this:
- * <pre>
- * container.addComponent(new Button("Test").withBorder(Borders.singleLine()));
- * </pre>
- * @param border
- * @return
- */
- Border withBorder(Border border);
-
- /**
- * Translates a position local to the container to the base pane's coordinate space. For a window-based GUI, this
- * be a coordinate in the window's coordinate space. If the component belongs to no base pane, it will return
- * {@code null}.
- * @param position Position to translate (relative to the container's top-left corner)
- * @return Position in base pane space, or {@code null} if the component is an orphan
- */
- TerminalPosition toBasePane(TerminalPosition position);
-
- /**
- * Translates a position local to the container to global coordinate space. This should be the absolute coordinate
- * in the terminal screen, taking no windows or containers into account. If the component belongs to no base pane,
- * it will return {@code null}.
- * @param position Position to translate (relative to the container's top-left corner)
- * @return Position in global (or absolute) coordinates, or {@code null} if the component is an orphan
- */
- TerminalPosition toGlobal(TerminalPosition position);
-
- /**
- * Returns the BasePane that this container belongs to. In a window-based GUI system, this will be a Window.
- * @return The base pane this component is placed on, or {@code null} if none
- */
- BasePane getBasePane();
-
- /**
- * Same as calling {@code panel.addComponent(thisComponent)}
- * @param panel Panel to add this component to
- * @return Itself
- */
- Component addTo(Panel panel);
-
- /**
- * Called by the GUI system when you add a component to a container; DO NOT CALL THIS YOURSELF!
- * @param container Container that this component was just added to
- */
- void onAdded(Container container);
-
- /**
- * Called by the GUI system when you remove a component from a container; DO NOT CALL THIS YOURSELF!
- * @param container Container that this component was just removed from
- */
- void onRemoved(Container container);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * This interface defines a renderer for a component, an external class that does the sizing and rendering. All
- * components will have a default renderer defined, which can usually be overridden manually and swapped out for a
- * different renderer, but also themes can contain renderer definitions which are automatically assigned to their
- * associated components.
- * @param <T> Type of the component which this renderer is designed for
- * @author Martin
- */
-public interface ComponentRenderer<T extends Component> {
- /**
- * Given the supplied component, how large does this renderer want the component to be? Notice that this is the
- * responsibility of the renderer and not the component itself, since the component has no idea what its visual
- * representation looks like.
- * @param component Component to calculate the preferred size of
- * @return The size this renderer would like the component to take up
- */
- TerminalSize getPreferredSize(T component);
-
- /**
- * Using the supplied graphics object, draws the component passed in.
- * @param graphics Graphics object to use for drawing
- * @param component Component to draw
- */
- void drawComponent(TextGUIGraphics graphics, T component);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-/**
- * A Composite is a Container that contains only one (or zero) component. Normally it is a kind of decorator, like a
- * border, that wraps a single component for visualization purposes.
- * @author Martin
- */
-public interface Composite {
- /**
- * Returns the component that this Composite is wrapping
- * @return Component the composite is wrapping
- */
- Component getComponent();
-
- /**
- * Sets the component which is inside this Composite. If you call this method with null, it removes the component
- * wrapped by this Composite.
- * @param component Component to wrap
- */
- void setComponent(Component component);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.input.KeyStroke;
-import java.util.Collection;
-
-/**
- * Container is a component that contains a collection of child components. The basic example of an implementation of
- * this is the {@code Panel} class which uses a layout manager to size and position the children over its area. Note
- * that there is no method for adding components to the container, since this depends on the implementation. In general,
- * composites that contains one one (or zero) children, the method for specifying the child is in {@code Composite}.
- * Multi-child containers are generally using the {@code Panel} implementation which has an {@code addComponent(..)}
- * method.
- * @author Martin
- */
-public interface Container extends Component {
-
- /**
- * Returns the number of children this container currently has
- * @return Number of children currently in this container
- */
- int getChildCount();
-
- /**
- * Returns collection that is to be considered a copy of the list of children contained inside of this object.
- * Modifying this list will not affect any internal state.
- * @return Child-components inside of this Container
- */
- Collection<Component> getChildren();
-
- /**
- * Returns {@code true} if this container contains the supplied component either directly or indirectly through
- * intermediate containers.
- * @param component Component to check if it's part of this container
- * @return {@code true} if the component is inside this Container, otherwise {@code false}
- */
- boolean containsComponent(Component component);
-
- /**
- * Removes the component from the container. This should remove the component from the Container's internal data
- * structure as well as call the onRemoved(..) method on the component itself if it was found inside the container.
- * @param component Component to remove from the Container
- * @return {@code true} if the component existed inside the container and was removed, {@code false} otherwise
- */
- boolean removeComponent(Component component);
-
- /**
- * Given an interactable, find the next one in line to receive focus. If the interactable isn't inside this
- * container, this method should return {@code null}.
- *
- * @param fromThis Component from which to get the next interactable, or if
- * null, pick the first available interactable
- * @return The next interactable component, or null if there are no more
- * interactables in the list
- */
- Interactable nextFocus(Interactable fromThis);
-
- /**
- * Given an interactable, find the previous one in line to receive focus. If the interactable isn't inside this
- * container, this method should return {@code null}.
- *
- * @param fromThis Component from which to get the previous interactable,
- * or if null, pick the last interactable in the list
- * @return The previous interactable component, or null if there are no more
- * interactables in the list
- */
- Interactable previousFocus(Interactable fromThis);
-
- /**
- * If an interactable component inside this container received a keyboard event that wasn't handled, the GUI system
- * will recursively send the event to each parent container to give each of them a chance to consume the event.
- * Return {@code false} if the implementer doesn't care about this particular keystroke and it will be automatically
- * sent up the hierarchy the to next container. If you return {@code true}, the event will stop here and won't be
- * reported as unhandled.
- * @param key Keystroke that was ignored by the interactable inside this container
- * @return {@code true} if this event was handled by this container and shouldn't be processed anymore,
- * {@code false} if the container didn't take any action on the event and want to pass it on
- */
- boolean handleInput(KeyStroke key);
-
- /**
- * Takes a lookup map and updates it with information about where all the interactables inside of this container
- * are located.
- * @param interactableLookupMap Interactable map to update
- */
- void updateLookupMap(InteractableLookupMap interactableLookupMap);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.graphics.ThemeDefinition;
-
-/**
- * Default window decoration renderer that is used unless overridden with another decoration renderer. The windows are
- * drawn using a bevel colored line and the window title in the top-left corner, very similar to ordinary titled
- * borders.
- *
- * @author Martin
- */
-public class DefaultWindowDecorationRenderer implements WindowDecorationRenderer {
- @Override
- public TextGUIGraphics draw(TextGUI textGUI, TextGUIGraphics graphics, Window window) {
- String title = window.getTitle();
- if(title == null) {
- title = "";
- }
-
- ThemeDefinition themeDefinition = graphics.getThemeDefinition(DefaultWindowDecorationRenderer.class);
- char horizontalLine = themeDefinition.getCharacter("HORIZONTAL_LINE", Symbols.SINGLE_LINE_HORIZONTAL);
- char verticalLine = themeDefinition.getCharacter("VERTICAL_LINE", Symbols.SINGLE_LINE_VERTICAL);
- char bottomLeftCorner = themeDefinition.getCharacter("BOTTOM_LEFT_CORNER", Symbols.SINGLE_LINE_BOTTOM_LEFT_CORNER);
- char topLeftCorner = themeDefinition.getCharacter("TOP_LEFT_CORNER", Symbols.SINGLE_LINE_TOP_LEFT_CORNER);
- char bottomRightCorner = themeDefinition.getCharacter("BOTTOM_RIGHT_CORNER", Symbols.SINGLE_LINE_BOTTOM_RIGHT_CORNER);
- char topRightCorner = themeDefinition.getCharacter("TOP_RIGHT_CORNER", Symbols.SINGLE_LINE_TOP_RIGHT_CORNER);
-
- TerminalSize drawableArea = graphics.getSize();
- graphics.applyThemeStyle(themeDefinition.getPreLight());
- graphics.drawLine(new TerminalPosition(0, drawableArea.getRows() - 2), new TerminalPosition(0, 1), verticalLine);
- graphics.drawLine(new TerminalPosition(1, 0), new TerminalPosition(drawableArea.getColumns() - 2, 0), horizontalLine);
- graphics.setCharacter(0, 0, topLeftCorner);
- graphics.setCharacter(0, drawableArea.getRows() - 1, bottomLeftCorner);
-
- graphics.applyThemeStyle(themeDefinition.getNormal());
-
- graphics.drawLine(
- new TerminalPosition(drawableArea.getColumns() - 1, 1),
- new TerminalPosition(drawableArea.getColumns() - 1, drawableArea.getRows() - 2),
- verticalLine);
- graphics.drawLine(
- new TerminalPosition(1, drawableArea.getRows() - 1),
- new TerminalPosition(drawableArea.getColumns() - 2, drawableArea.getRows() - 1),
- horizontalLine);
-
- graphics.setCharacter(drawableArea.getColumns() - 1, 0, topRightCorner);
- graphics.setCharacter(drawableArea.getColumns() - 1, drawableArea.getRows() - 1, bottomRightCorner);
-
- if(!title.isEmpty()) {
- graphics.putString(2, 0, TerminalTextUtils.fitString(title, drawableArea.getColumns() - 3));
- }
-
- return graphics.newTextGraphics(new TerminalPosition(1, 1), graphics.getSize().withRelativeColumns(-2).withRelativeRows(-2));
- }
-
- @Override
- public TerminalSize getDecoratedSize(Window window, TerminalSize contentAreaSize) {
- return contentAreaSize
- .withRelativeColumns(2)
- .withRelativeRows(2)
- .max(new TerminalSize(TerminalTextUtils.getColumnWidth(window.getTitle()) + 4, 1)); //Make sure the title fits!
- }
-
- private static final TerminalPosition OFFSET = new TerminalPosition(1, 1);
-
- @Override
- public TerminalPosition getOffset(Window window) {
- return OFFSET;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.List;
-
-/**
- * The default window manager implementation used by Lanterna. New windows will be generally added in a tiled manner,
- * starting in the top-left corner and moving down-right as new windows are added. By using the various window hints
- * that are available you have some control over how the window manager will place and size the windows.
- *
- * @author Martin
- */
-public class DefaultWindowManager implements WindowManager {
-
- private final WindowDecorationRenderer windowDecorationRenderer;
- private TerminalSize lastKnownScreenSize;
-
- /**
- * Default constructor, will create a window manager that uses {@code DefaultWindowDecorationRenderer} for drawing
- * window decorations. Any size calculations done before the text GUI has actually been started and displayed on
- * the terminal will assume the terminal size is 80x24.
- */
- public DefaultWindowManager() {
- this(new DefaultWindowDecorationRenderer());
- }
-
- /**
- * Creates a new {@code DefaultWindowManager} with a specific window decoration renderer. Any size calculations done
- * before the text GUI has actually been started and displayed on the terminal will assume the terminal size is
- * 80x24.
- *
- * @param windowDecorationRenderer Window decoration renderer to use when drawing windows
- */
- public DefaultWindowManager(WindowDecorationRenderer windowDecorationRenderer) {
- this(windowDecorationRenderer, null);
- }
-
- /**
- * Creates a new {@code DefaultWindowManager} using a {@code DefaultWindowDecorationRenderer} for drawing window
- * decorations. Any size calculations done before the text GUI has actually been started and displayed on the
- * terminal will use the size passed in with the {@code initialScreenSize} parameter
- *
- * @param initialScreenSize Size to assume the terminal has until the text GUI is started and can be notified of the
- * correct size
- */
- public DefaultWindowManager(TerminalSize initialScreenSize) {
- this(new DefaultWindowDecorationRenderer(), initialScreenSize);
- }
-
- /**
- * Creates a new {@code DefaultWindowManager} using a specified {@code windowDecorationRenderer} for drawing window
- * decorations. Any size calculations done before the text GUI has actually been started and displayed on the
- * terminal will use the size passed in with the {@code initialScreenSize} parameter
- *
- * @param windowDecorationRenderer Window decoration renderer to use when drawing windows
- * @param initialScreenSize Size to assume the terminal has until the text GUI is started and can be notified of the
- * correct size
- */
- public DefaultWindowManager(WindowDecorationRenderer windowDecorationRenderer, TerminalSize initialScreenSize) {
- this.windowDecorationRenderer = windowDecorationRenderer;
- if(initialScreenSize != null) {
- this.lastKnownScreenSize = initialScreenSize;
- }
- else {
- this.lastKnownScreenSize = new TerminalSize(80, 24);
- }
- }
-
- @Override
- public boolean isInvalid() {
- return false;
- }
-
- @Override
- public WindowDecorationRenderer getWindowDecorationRenderer(Window window) {
- if(window.getHints().contains(Window.Hint.NO_DECORATIONS)) {
- return new EmptyWindowDecorationRenderer();
- }
- return windowDecorationRenderer;
- }
-
- @Override
- public void onAdded(WindowBasedTextGUI textGUI, Window window, List<Window> allWindows) {
- WindowDecorationRenderer decorationRenderer = getWindowDecorationRenderer(window);
- TerminalSize expectedDecoratedSize = decorationRenderer.getDecoratedSize(window, window.getPreferredSize());
- window.setDecoratedSize(expectedDecoratedSize);
-
- if(window.getHints().contains(Window.Hint.FIXED_POSITION)) {
- //Don't place the window, assume the position is already set
- }
- else if(allWindows.isEmpty()) {
- window.setPosition(TerminalPosition.OFFSET_1x1);
- }
- else if(window.getHints().contains(Window.Hint.CENTERED)) {
- int left = (lastKnownScreenSize.getColumns() - expectedDecoratedSize.getColumns()) / 2;
- int top = (lastKnownScreenSize.getRows() - expectedDecoratedSize.getRows()) / 2;
- window.setPosition(new TerminalPosition(left, top));
- }
- else {
- TerminalPosition nextPosition = allWindows.get(allWindows.size() - 1).getPosition().withRelative(2, 1);
- if(nextPosition.getColumn() + expectedDecoratedSize.getColumns() > lastKnownScreenSize.getColumns() ||
- nextPosition.getRow() + expectedDecoratedSize.getRows() > lastKnownScreenSize.getRows()) {
- nextPosition = TerminalPosition.OFFSET_1x1;
- }
- window.setPosition(nextPosition);
- }
-
- // Finally, run through the usual calculations so the window manager's usual prepare method can have it's say
- prepareWindow(lastKnownScreenSize, window);
- }
-
- @Override
- public void onRemoved(WindowBasedTextGUI textGUI, Window window, List<Window> allWindows) {
- //NOP
- }
-
- @Override
- public void prepareWindows(WindowBasedTextGUI textGUI, List<Window> allWindows, TerminalSize screenSize) {
- this.lastKnownScreenSize = screenSize;
- for(Window window: allWindows) {
- prepareWindow(screenSize, window);
- }
- }
-
- /**
- * Called by {@link DefaultWindowManager} when iterating through all windows to decide their size and position. If
- * you override {@link DefaultWindowManager} to add your own logic to how windows are placed on the screen, you can
- * override this method and selectively choose which window to interfere with. Note that the two key properties that
- * are read by the GUI system after preparing all windows are the position and decorated size. Your custom
- * implementation should set these two fields directly on the window. You can infer the decorated size from the
- * content size by using the window decoration renderer that is attached to the window manager.
- *
- * @param screenSize Size of the terminal that is available to draw on
- * @param window Window to prepare decorated size and position for
- */
- protected void prepareWindow(TerminalSize screenSize, Window window) {
- WindowDecorationRenderer decorationRenderer = getWindowDecorationRenderer(window);
- TerminalSize contentAreaSize;
- if(window.getHints().contains(Window.Hint.FIXED_SIZE)) {
- contentAreaSize = window.getSize();
- }
- else {
- contentAreaSize = window.getPreferredSize();
- }
- TerminalSize size = decorationRenderer.getDecoratedSize(window, contentAreaSize);
- TerminalPosition position = window.getPosition();
-
- if(window.getHints().contains(Window.Hint.FULL_SCREEN)) {
- position = TerminalPosition.TOP_LEFT_CORNER;
- size = screenSize;
- }
- else if(window.getHints().contains(Window.Hint.EXPANDED)) {
- position = TerminalPosition.OFFSET_1x1;
- size = screenSize.withRelative(
- -Math.min(4, screenSize.getColumns()),
- -Math.min(3, screenSize.getRows()));
- if(!size.equals(window.getDecoratedSize())) {
- window.invalidate();
- }
- }
- else if(window.getHints().contains(Window.Hint.FIT_TERMINAL_WINDOW) ||
- window.getHints().contains(Window.Hint.CENTERED)) {
- //If the window is too big for the terminal, move it up towards 0x0 and if that's not enough then shrink
- //it instead
- while(position.getRow() > 0 && position.getRow() + size.getRows() > screenSize.getRows()) {
- position = position.withRelativeRow(-1);
- }
- while(position.getColumn() > 0 && position.getColumn() + size.getColumns() > screenSize.getColumns()) {
- position = position.withRelativeColumn(-1);
- }
- if(position.getRow() + size.getRows() > screenSize.getRows()) {
- size = size.withRows(screenSize.getRows() - position.getRow());
- }
- if(position.getColumn() + size.getColumns() > screenSize.getColumns()) {
- size = size.withColumns(screenSize.getColumns() - position.getColumn());
- }
- if(window.getHints().contains(Window.Hint.CENTERED)) {
- int left = (lastKnownScreenSize.getColumns() - size.getColumns()) / 2;
- int top = (lastKnownScreenSize.getRows() - size.getRows()) / 2;
- position = new TerminalPosition(left, top);
- }
- }
-
- window.setPosition(position);
- window.setDecoratedSize(size);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-/**
- * Enum for distinguishing between horizontal and vertical directions. Used in {@code LinearLayout} and
- * {@code Separator}.
- * @author Martin
-*/
-public enum Direction {
- /**
- * Horizontal direction, meaning something is moving along the x-axis (or column-axis)
- */
- HORIZONTAL, //See? I can spell it!
- /**
- * Vertical directory, meaning something is moving along the y-axis (or row-axis)
- */
- VERTICAL,
- ;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-
-/**
- * Simple component which draws a solid color over its area. The size this component will request is specified through
- * it's constructor.
- *
- * @author Martin
- */
-public class EmptySpace extends AbstractComponent<EmptySpace> {
- private final TerminalSize size;
- private TextColor color;
-
- /**
- * Creates an EmptySpace with size 1x1 and a default color chosen from the theme
- */
- public EmptySpace() {
- this(null, TerminalSize.ONE);
- }
-
- /**
- * Creates an EmptySpace with a specified color and preferred size of 1x1
- * @param color Color to use (null will make it use the theme)
- */
- public EmptySpace(TextColor color) {
- this(color, TerminalSize.ONE);
- }
-
- /**
- * Creates an EmptySpace with a specified preferred size (color will be chosen from the theme)
- * @param size Preferred size
- */
- public EmptySpace(TerminalSize size) {
- this(null, size);
- }
-
- /**
- * Creates an EmptySpace with a specified color (null will make it use a color from the theme) and preferred size
- * @param color Color to use (null will make it use the theme)
- * @param size Preferred size
- */
- public EmptySpace(TextColor color, TerminalSize size) {
- this.color = color;
- this.size = size;
- }
-
- /**
- * Changes the color this component will use when drawn
- * @param color New color to draw the component with, if {@code null} then the component will use the theme's
- * default color
- */
- public void setColor(TextColor color) {
- this.color = color;
- }
-
- /**
- * Returns the color this component is drawn with, or {@code null} if this component uses whatever the default color
- * the theme is set to use
- * @return Color used when drawing or {@code null} if it's using the theme
- */
- public TextColor getColor() {
- return color;
- }
-
- @Override
- protected ComponentRenderer<EmptySpace> createDefaultRenderer() {
- return new ComponentRenderer<EmptySpace>() {
-
- @Override
- public TerminalSize getPreferredSize(EmptySpace component) {
- return size;
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, EmptySpace component) {
- graphics.applyThemeStyle(graphics.getThemeDefinition(EmptySpace.class).getNormal());
- if(color != null) {
- graphics.setBackgroundColor(color);
- }
- graphics.fill(' ');
- }
- };
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * Implementation of WindowDecorationRenderer that is doesn't render any window decorations
- * @author Martin
- */
-public class EmptyWindowDecorationRenderer implements WindowDecorationRenderer {
- @Override
- public TextGUIGraphics draw(TextGUI textGUI, TextGUIGraphics graphics, Window window) {
- return graphics;
- }
-
- @Override
- public TerminalSize getDecoratedSize(Window window, TerminalSize contentAreaSize) {
- return contentAreaSize;
- }
-
- @Override
- public TerminalPosition getOffset(Window window) {
- return TerminalPosition.TOP_LEFT_CORNER;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.*;
-
-/**
- * This emulates the behaviour of the GridLayout in SWT (as opposed to the one in AWT/Swing). I originally ported the
- * SWT class itself but due to licensing concerns (the eclipse license is not compatible with LGPL) I was advised not to
- * do that. This is a partial implementation and some of the semantics have changed, but in general it works the same
- * way so the SWT documentation will generally match.
- * <p>
- * You use the {@code GridLayout} by specifying a number of columns you want your grid to have and then when you add
- * components, you assign {@code LayoutData} to these components using the different static methods in this class
- * ({@code createLayoutData(..)}). You can set components to span both rows and columns, as well as defining how to
- * distribute the available space.
- */
-public class GridLayout implements LayoutManager {
- /**
- * The enum is used to specify where in a grid cell a component should be placed, in the case that the preferred
- * size of the component is smaller than the space in the cell. This class will generally use two alignments, one
- * for horizontal and one for vertical.
- */
- public enum Alignment {
- /**
- * Place the component at the start of the cell (horizontally or vertically) and leave whatever space is left
- * after the preferred size empty.
- */
- BEGINNING,
- /**
- * Place the component at the middle of the cell (horizontally or vertically) and leave the space before and
- * after empty.
- */
- CENTER,
- /**
- * Place the component at the end of the cell (horizontally or vertically) and leave whatever space is left
- * before the preferred size empty.
- */
- END,
- /**
- * Force the component to be the same size as the table cell
- */
- FILL,
- ;
- }
-
- static class GridLayoutData implements LayoutData {
- final Alignment horizontalAlignment;
- final Alignment verticalAlignment;
- final boolean grabExtraHorizontalSpace;
- final boolean grabExtraVerticalSpace;
- final int horizontalSpan;
- final int verticalSpan;
-
- private GridLayoutData(
- Alignment horizontalAlignment,
- Alignment verticalAlignment,
- boolean grabExtraHorizontalSpace,
- boolean grabExtraVerticalSpace,
- int horizontalSpan,
- int verticalSpan) {
-
- if(horizontalSpan < 1 || verticalSpan < 1) {
- throw new IllegalArgumentException("Horizontal/Vertical span must be 1 or greater");
- }
-
- this.horizontalAlignment = horizontalAlignment;
- this.verticalAlignment = verticalAlignment;
- this.grabExtraHorizontalSpace = grabExtraHorizontalSpace;
- this.grabExtraVerticalSpace = grabExtraVerticalSpace;
- this.horizontalSpan = horizontalSpan;
- this.verticalSpan = verticalSpan;
- }
- }
-
- private static GridLayoutData DEFAULT = new GridLayoutData(
- Alignment.BEGINNING,
- Alignment.BEGINNING,
- false,
- false,
- 1,
- 1);
-
- /**
- * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
- * component in case the cell space is larger than the preferred size of the component
- * @param horizontalAlignment Horizontal alignment strategy
- * @param verticalAlignment Vertical alignment strategy
- * @return The layout data object containing the specified alignments
- */
- public static LayoutData createLayoutData(Alignment horizontalAlignment, Alignment verticalAlignment) {
- return createLayoutData(horizontalAlignment, verticalAlignment, false, false);
- }
-
- /**
- * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
- * component in case the cell space is larger than the preferred size of the component. This method also has fields
- * for indicating that the component would like to take more space if available to the container. For example, if
- * the container is assigned is assigned an area of 50x15, but all the child components in the grid together only
- * asks for 40x10, the remaining 10 columns and 5 rows will be empty. If just a single component asks for extra
- * space horizontally and/or vertically, the grid will expand out to fill the entire area and the text space will be
- * assigned to the component that asked for it.
- *
- * @param horizontalAlignment Horizontal alignment strategy
- * @param verticalAlignment Vertical alignment strategy
- * @param grabExtraHorizontalSpace If set to {@code true}, this component will ask to be assigned extra horizontal
- * space if there is any to assign
- * @param grabExtraVerticalSpace If set to {@code true}, this component will ask to be assigned extra vertical
- * space if there is any to assign
- * @return The layout data object containing the specified alignments and size requirements
- */
- public static LayoutData createLayoutData(
- Alignment horizontalAlignment,
- Alignment verticalAlignment,
- boolean grabExtraHorizontalSpace,
- boolean grabExtraVerticalSpace) {
-
- return createLayoutData(horizontalAlignment, verticalAlignment, grabExtraHorizontalSpace, grabExtraVerticalSpace, 1, 1);
- }
-
- /**
- * Creates a layout data object for {@code GridLayout}:s that specify the horizontal and vertical alignment for the
- * component in case the cell space is larger than the preferred size of the component. This method also has fields
- * for indicating that the component would like to take more space if available to the container. For example, if
- * the container is assigned is assigned an area of 50x15, but all the child components in the grid together only
- * asks for 40x10, the remaining 10 columns and 5 rows will be empty. If just a single component asks for extra
- * space horizontally and/or vertically, the grid will expand out to fill the entire area and the text space will be
- * assigned to the component that asked for it. It also puts in data on how many rows and/or columns the component
- * should span.
- *
- * @param horizontalAlignment Horizontal alignment strategy
- * @param verticalAlignment Vertical alignment strategy
- * @param grabExtraHorizontalSpace If set to {@code true}, this component will ask to be assigned extra horizontal
- * space if there is any to assign
- * @param grabExtraVerticalSpace If set to {@code true}, this component will ask to be assigned extra vertical
- * space if there is any to assign
- * @param horizontalSpan How many "cells" this component wants to span horizontally
- * @param verticalSpan How many "cells" this component wants to span vertically
- * @return The layout data object containing the specified alignments, size requirements and cell spanning
- */
- public static LayoutData createLayoutData(
- Alignment horizontalAlignment,
- Alignment verticalAlignment,
- boolean grabExtraHorizontalSpace,
- boolean grabExtraVerticalSpace,
- int horizontalSpan,
- int verticalSpan) {
-
- return new GridLayoutData(
- horizontalAlignment,
- verticalAlignment,
- grabExtraHorizontalSpace,
- grabExtraVerticalSpace,
- horizontalSpan,
- verticalSpan);
- }
-
- /**
- * This is a shortcut method that will create a grid layout data object that will expand its cell as much as is can
- * horizontally and make the component occupy the whole area horizontally and center it vertically
- * @param horizontalSpan How many cells to span horizontally
- * @return Layout data object with the specified span and horizontally expanding as much as it can
- */
- public static LayoutData createHorizontallyFilledLayoutData(int horizontalSpan) {
- return createLayoutData(
- Alignment.FILL,
- Alignment.CENTER,
- true,
- false,
- horizontalSpan,
- 1);
- }
-
- /**
- * This is a shortcut method that will create a grid layout data object that will expand its cell as much as is can
- * vertically and make the component occupy the whole area vertically and center it horizontally
- * @param horizontalSpan How many cells to span vertically
- * @return Layout data object with the specified span and vertically expanding as much as it can
- */
- public static LayoutData createHorizontallyEndAlignedLayoutData(int horizontalSpan) {
- return createLayoutData(
- Alignment.END,
- Alignment.CENTER,
- true,
- false,
- horizontalSpan,
- 1);
- }
-
- private final int numberOfColumns;
- private int horizontalSpacing;
- private int verticalSpacing;
- private int topMarginSize;
- private int bottomMarginSize;
- private int leftMarginSize;
- private int rightMarginSize;
-
- private boolean changed;
-
- /**
- * Creates a new {@code GridLayout} with the specified number of columns. Initially, this layout will have a
- * horizontal spacing of 1 and vertical spacing of 0, with a left and right margin of 1.
- * @param numberOfColumns Number of columns in this grid
- */
- public GridLayout(int numberOfColumns) {
- this.numberOfColumns = numberOfColumns;
- this.horizontalSpacing = 1;
- this.verticalSpacing = 0;
- this.topMarginSize = 0;
- this.bottomMarginSize = 0;
- this.leftMarginSize = 1;
- this.rightMarginSize = 1;
- this.changed = true;
- }
-
- /**
- * Returns the horizontal spacing, i.e. the number of empty columns between each cell
- * @return Horizontal spacing
- */
- public int getHorizontalSpacing() {
- return horizontalSpacing;
- }
-
- /**
- * Sets the horizontal spacing, i.e. the number of empty columns between each cell
- * @param horizontalSpacing New horizontal spacing
- * @return Itself
- */
- public GridLayout setHorizontalSpacing(int horizontalSpacing) {
- if(horizontalSpacing < 0) {
- throw new IllegalArgumentException("Horizontal spacing cannot be less than 0");
- }
- this.horizontalSpacing = horizontalSpacing;
- this.changed = true;
- return this;
- }
-
- /**
- * Returns the vertical spacing, i.e. the number of empty columns between each row
- * @return Vertical spacing
- */
- public int getVerticalSpacing() {
- return verticalSpacing;
- }
-
- /**
- * Sets the vertical spacing, i.e. the number of empty columns between each row
- * @param verticalSpacing New vertical spacing
- * @return Itself
- */
- public GridLayout setVerticalSpacing(int verticalSpacing) {
- if(verticalSpacing < 0) {
- throw new IllegalArgumentException("Vertical spacing cannot be less than 0");
- }
- this.verticalSpacing = verticalSpacing;
- this.changed = true;
- return this;
- }
-
- /**
- * Returns the top margin, i.e. number of empty rows above the first row in the grid
- * @return Top margin, in number of rows
- */
- public int getTopMarginSize() {
- return topMarginSize;
- }
-
- /**
- * Sets the top margin, i.e. number of empty rows above the first row in the grid
- * @param topMarginSize Top margin, in number of rows
- * @return Itself
- */
- public GridLayout setTopMarginSize(int topMarginSize) {
- if(topMarginSize < 0) {
- throw new IllegalArgumentException("Top margin size cannot be less than 0");
- }
- this.topMarginSize = topMarginSize;
- this.changed = true;
- return this;
- }
-
- /**
- * Returns the bottom margin, i.e. number of empty rows below the last row in the grid
- * @return Bottom margin, in number of rows
- */
- public int getBottomMarginSize() {
- return bottomMarginSize;
- }
-
- /**
- * Sets the bottom margin, i.e. number of empty rows below the last row in the grid
- * @param bottomMarginSize Bottom margin, in number of rows
- * @return Itself
- */
- public GridLayout setBottomMarginSize(int bottomMarginSize) {
- if(bottomMarginSize < 0) {
- throw new IllegalArgumentException("Bottom margin size cannot be less than 0");
- }
- this.bottomMarginSize = bottomMarginSize;
- this.changed = true;
- return this;
- }
-
- /**
- * Returns the left margin, i.e. number of empty columns left of the first column in the grid
- * @return Left margin, in number of columns
- */
- public int getLeftMarginSize() {
- return leftMarginSize;
- }
-
- /**
- * Sets the left margin, i.e. number of empty columns left of the first column in the grid
- * @param leftMarginSize Left margin, in number of columns
- * @return Itself
- */
- public GridLayout setLeftMarginSize(int leftMarginSize) {
- if(leftMarginSize < 0) {
- throw new IllegalArgumentException("Left margin size cannot be less than 0");
- }
- this.leftMarginSize = leftMarginSize;
- this.changed = true;
- return this;
- }
-
- /**
- * Returns the right margin, i.e. number of empty columns right of the last column in the grid
- * @return Right margin, in number of columns
- */
- public int getRightMarginSize() {
- return rightMarginSize;
- }
-
- /**
- * Sets the right margin, i.e. number of empty columns right of the last column in the grid
- * @param rightMarginSize Right margin, in number of columns
- * @return Itself
- */
- public GridLayout setRightMarginSize(int rightMarginSize) {
- if(rightMarginSize < 0) {
- throw new IllegalArgumentException("Right margin size cannot be less than 0");
- }
- this.rightMarginSize = rightMarginSize;
- this.changed = true;
- return this;
- }
-
- @Override
- public boolean hasChanged() {
- return this.changed;
- }
-
- @Override
- public TerminalSize getPreferredSize(List<Component> components) {
- TerminalSize preferredSize = TerminalSize.ZERO;
- if(components.isEmpty()) {
- return preferredSize.withRelative(
- leftMarginSize + rightMarginSize,
- topMarginSize + bottomMarginSize);
- }
-
- Component[][] table = buildTable(components);
- table = eliminateUnusedRowsAndColumns(table);
-
- //Figure out each column first, this can be done independently of the row heights
- int preferredWidth = 0;
- int preferredHeight = 0;
- for(int width: getPreferredColumnWidths(table)) {
- preferredWidth += width;
- }
- for(int height: getPreferredRowHeights(table)) {
- preferredHeight += height;
- }
- preferredSize = preferredSize.withRelative(preferredWidth, preferredHeight);
- preferredSize = preferredSize.withRelativeColumns(leftMarginSize + rightMarginSize + (table[0].length - 1) * horizontalSpacing);
- preferredSize = preferredSize.withRelativeRows(topMarginSize + bottomMarginSize + (table.length - 1) * verticalSpacing);
- return preferredSize;
- }
-
- @Override
- public void doLayout(TerminalSize area, List<Component> components) {
- //Sanity check, if the area is way too small, just return
- Component[][] table = buildTable(components);
- table = eliminateUnusedRowsAndColumns(table);
-
- if(area.equals(TerminalSize.ZERO) ||
- table.length == 0 ||
- area.getColumns() <= leftMarginSize + rightMarginSize + ((table[0].length - 1) * horizontalSpacing) ||
- area.getRows() <= bottomMarginSize + topMarginSize + ((table.length - 1) * verticalSpacing)) {
- return;
- }
-
- //Adjust area to the margins
- area = area.withRelative(-leftMarginSize - rightMarginSize, -topMarginSize - bottomMarginSize);
-
- Map<Component, TerminalSize> sizeMap = new IdentityHashMap<Component, TerminalSize>();
- Map<Component, TerminalPosition> positionMap = new IdentityHashMap<Component, TerminalPosition>();
-
- //Figure out each column first, this can be done independently of the row heights
- int[] columnWidths = getPreferredColumnWidths(table);
-
- //Take notes of which columns we can expand if the usable area is larger than what the components want
- Set<Integer> expandableColumns = getExpandableColumns(table);
-
- //Next, start shrinking to make sure it fits the size of the area we are trying to lay out on.
- //Notice we subtract the horizontalSpacing to take the space between components into account
- TerminalSize areaWithoutHorizontalSpacing = area.withRelativeColumns(-horizontalSpacing * (table[0].length - 1));
- int totalWidth = shrinkWidthToFitArea(areaWithoutHorizontalSpacing, columnWidths);
-
- //Finally, if there is extra space, make the expandable columns larger
- while(areaWithoutHorizontalSpacing.getColumns() > totalWidth && !expandableColumns.isEmpty()) {
- totalWidth = grabExtraHorizontalSpace(areaWithoutHorizontalSpacing, columnWidths, expandableColumns, totalWidth);
- }
-
- //Now repeat for rows
- int[] rowHeights = getPreferredRowHeights(table);
- Set<Integer> expandableRows = getExpandableRows(table);
- TerminalSize areaWithoutVerticalSpacing = area.withRelativeRows(-verticalSpacing * (table.length - 1));
- int totalHeight = shrinkHeightToFitArea(areaWithoutVerticalSpacing, rowHeights);
- while(areaWithoutVerticalSpacing.getRows() > totalHeight && !expandableRows.isEmpty()) {
- totalHeight = grabExtraVerticalSpace(areaWithoutVerticalSpacing, rowHeights, expandableRows, totalHeight);
- }
-
- //Ok, all constraints are in place, we can start placing out components. To simplify, do it horizontally first
- //and vertically after
- TerminalPosition tableCellTopLeft = TerminalPosition.TOP_LEFT_CORNER;
- for(int y = 0; y < table.length; y++) {
- tableCellTopLeft = tableCellTopLeft.withColumn(0);
- for(int x = 0; x < table[y].length; x++) {
- Component component = table[y][x];
- if(component != null && !positionMap.containsKey(component)) {
- GridLayoutData layoutData = getLayoutData(component);
- TerminalSize size = component.getPreferredSize();
- TerminalPosition position = tableCellTopLeft;
-
- int availableHorizontalSpace = 0;
- int availableVerticalSpace = 0;
- for (int i = 0; i < layoutData.horizontalSpan; i++) {
- availableHorizontalSpace += columnWidths[x + i] + (i > 0 ? horizontalSpacing : 0);
- }
- for (int i = 0; i < layoutData.verticalSpan; i++) {
- availableVerticalSpace += rowHeights[y + i] + (i > 0 ? verticalSpacing : 0);
- }
-
- //Make sure to obey the size restrictions
- size = size.withColumns(Math.min(size.getColumns(), availableHorizontalSpace));
- size = size.withRows(Math.min(size.getRows(), availableVerticalSpace));
-
- switch (layoutData.horizontalAlignment) {
- case CENTER:
- position = position.withRelativeColumn((availableHorizontalSpace - size.getColumns()) / 2);
- break;
- case END:
- position = position.withRelativeColumn(availableHorizontalSpace - size.getColumns());
- break;
- case FILL:
- size = size.withColumns(availableHorizontalSpace);
- break;
- default:
- break;
- }
- switch (layoutData.verticalAlignment) {
- case CENTER:
- position = position.withRelativeRow((availableVerticalSpace - size.getRows()) / 2);
- break;
- case END:
- position = position.withRelativeRow(availableVerticalSpace - size.getRows());
- break;
- case FILL:
- size = size.withRows(availableVerticalSpace);
- break;
- default:
- break;
- }
-
- sizeMap.put(component, size);
- positionMap.put(component, position);
- }
- tableCellTopLeft = tableCellTopLeft.withRelativeColumn(columnWidths[x] + horizontalSpacing);
- }
- tableCellTopLeft = tableCellTopLeft.withRelativeRow(rowHeights[y] + verticalSpacing);
- }
-
- //Apply the margins here
- for(Component component: components) {
- component.setPosition(positionMap.get(component).withRelative(leftMarginSize, topMarginSize));
- component.setSize(sizeMap.get(component));
- }
- this.changed = false;
- }
-
- private int[] getPreferredColumnWidths(Component[][] table) {
- //actualNumberOfColumns may be different from this.numberOfColumns since some columns may have been eliminated
- int actualNumberOfColumns = table[0].length;
- int columnWidths[] = new int[actualNumberOfColumns];
-
- //Start by letting all span = 1 columns take what they need
- for(Component[] row: table) {
- for(int i = 0; i < actualNumberOfColumns; i++) {
- Component component = row[i];
- if(component == null) {
- continue;
- }
- GridLayoutData layoutData = getLayoutData(component);
- if (layoutData.horizontalSpan == 1) {
- columnWidths[i] = Math.max(columnWidths[i], component.getPreferredSize().getColumns());
- }
- }
- }
-
- //Next, do span > 1 and enlarge if necessary
- for(Component[] row: table) {
- for(int i = 0; i < actualNumberOfColumns; ) {
- Component component = row[i];
- if(component == null) {
- i++;
- continue;
- }
- GridLayoutData layoutData = getLayoutData(component);
- if(layoutData.horizontalSpan > 1) {
- int accumWidth = 0;
- for(int j = i; j < i + layoutData.horizontalSpan; j++) {
- accumWidth += columnWidths[j];
- }
-
- int preferredWidth = component.getPreferredSize().getColumns();
- if(preferredWidth > accumWidth) {
- int columnOffset = 0;
- do {
- columnWidths[i + columnOffset++]++;
- accumWidth++;
- if(columnOffset == layoutData.horizontalSpan) {
- columnOffset = 0;
- }
- }
- while(preferredWidth > accumWidth);
- }
- }
- i += layoutData.horizontalSpan;
- }
- }
- return columnWidths;
- }
-
- private int[] getPreferredRowHeights(Component[][] table) {
- int numberOfRows = table.length;
- int rowHeights[] = new int[numberOfRows];
-
- //Start by letting all span = 1 rows take what they need
- int rowIndex = 0;
- for(Component[] row: table) {
- for(int i = 0; i < row.length; i++) {
- Component component = row[i];
- if(component == null) {
- continue;
- }
- GridLayoutData layoutData = getLayoutData(component);
- if(layoutData.verticalSpan == 1) {
- rowHeights[rowIndex] = Math.max(rowHeights[rowIndex], component.getPreferredSize().getRows());
- }
- }
- rowIndex++;
- }
-
- //Next, do span > 1 and enlarge if necessary
- for(int x = 0; x < numberOfColumns; x++) {
- for(int y = 0; y < numberOfRows && y < table.length; ) {
- if(x >= table[y].length) {
- y++;
- continue;
- }
- Component component = table[y][x];
- if(component == null) {
- y++;
- continue;
- }
- GridLayoutData layoutData = getLayoutData(component);
- if(layoutData.verticalSpan > 1) {
- int accumulatedHeight = 0;
- for(int i = y; i < y + layoutData.verticalSpan; i++) {
- accumulatedHeight += rowHeights[i];
- }
-
- int preferredHeight = component.getPreferredSize().getRows();
- if(preferredHeight > accumulatedHeight) {
- int rowOffset = 0;
- do {
- rowHeights[y + rowOffset++]++;
- accumulatedHeight++;
- if(rowOffset == layoutData.verticalSpan) {
- rowOffset = 0;
- }
- }
- while(preferredHeight > accumulatedHeight);
- }
- }
- y += layoutData.verticalSpan;
- }
- }
- return rowHeights;
- }
-
- private Set<Integer> getExpandableColumns(Component[][] table) {
- Set<Integer> expandableColumns = new TreeSet<Integer>();
- for(Component[] row: table) {
- for (int i = 0; i < row.length; i++) {
- if(row[i] == null) {
- continue;
- }
- GridLayoutData layoutData = getLayoutData(row[i]);
- if(layoutData.grabExtraHorizontalSpace) {
- expandableColumns.add(i);
- }
- }
- }
- return expandableColumns;
- }
-
- private Set<Integer> getExpandableRows(Component[][] table) {
- Set<Integer> expandableRows = new TreeSet<Integer>();
- for(int rowIndex = 0; rowIndex < table.length; rowIndex++) {
- Component[] row = table[rowIndex];
- for (int columnIndex = 0; columnIndex < row.length; columnIndex++) {
- if(row[columnIndex] == null) {
- continue;
- }
- GridLayoutData layoutData = getLayoutData(row[columnIndex]);
- if(layoutData.grabExtraVerticalSpace) {
- expandableRows.add(rowIndex);
- }
- }
- }
- return expandableRows;
- }
-
- private int shrinkWidthToFitArea(TerminalSize area, int[] columnWidths) {
- int totalWidth = 0;
- for(int width: columnWidths) {
- totalWidth += width;
- }
- if(totalWidth > area.getColumns()) {
- int columnOffset = 0;
- do {
- if(columnWidths[columnOffset] > 0) {
- columnWidths[columnOffset]--;
- totalWidth--;
- }
- if(++columnOffset == numberOfColumns) {
- columnOffset = 0;
- }
- }
- while(totalWidth > area.getColumns());
- }
- return totalWidth;
- }
-
- private int shrinkHeightToFitArea(TerminalSize area, int[] rowHeights) {
- int totalHeight = 0;
- for(int height: rowHeights) {
- totalHeight += height;
- }
- if(totalHeight > area.getRows()) {
- int rowOffset = 0;
- do {
- if(rowHeights[rowOffset] > 0) {
- rowHeights[rowOffset]--;
- totalHeight--;
- }
- if(++rowOffset == rowHeights.length) {
- rowOffset = 0;
- }
- }
- while(totalHeight > area.getRows());
- }
- return totalHeight;
- }
-
- private int grabExtraHorizontalSpace(TerminalSize area, int[] columnWidths, Set<Integer> expandableColumns, int totalWidth) {
- for(int columnIndex: expandableColumns) {
- columnWidths[columnIndex]++;
- totalWidth++;
- if(area.getColumns() == totalWidth) {
- break;
- }
- }
- return totalWidth;
- }
-
- private int grabExtraVerticalSpace(TerminalSize area, int[] rowHeights, Set<Integer> expandableRows, int totalHeight) {
- for(int rowIndex: expandableRows) {
- rowHeights[rowIndex]++;
- totalHeight++;
- if(area.getColumns() == totalHeight) {
- break;
- }
- }
- return totalHeight;
- }
-
- private Component[][] buildTable(List<Component> components) {
- List<Component[]> rows = new ArrayList<Component[]>();
- List<int[]> hspans = new ArrayList<int[]>();
- List<int[]> vspans = new ArrayList<int[]>();
-
- int rowCount = 0;
- int rowsExtent = 1;
- Queue<Component> toBePlaced = new LinkedList<Component>(components);
- while(!toBePlaced.isEmpty() || rowCount < rowsExtent) {
- //Start new row
- Component[] row = new Component[numberOfColumns];
- int[] hspan = new int[numberOfColumns];
- int[] vspan = new int[numberOfColumns];
-
- for(int i = 0; i < numberOfColumns; i++) {
- if(i > 0 && hspan[i - 1] > 1) {
- row[i] = row[i-1];
- hspan[i] = hspan[i - 1] - 1;
- vspan[i] = vspan[i - 1];
- }
- else if(rowCount > 0 && vspans.get(rowCount - 1)[i] > 1) {
- row[i] = rows.get(rowCount - 1)[i];
- hspan[i] = hspans.get(rowCount - 1)[i];
- vspan[i] = vspans.get(rowCount - 1)[i] - 1;
- }
- else if(!toBePlaced.isEmpty()) {
- Component component = toBePlaced.poll();
- GridLayoutData gridLayoutData = getLayoutData(component);
-
- row[i] = component;
- hspan[i] = gridLayoutData.horizontalSpan;
- vspan[i] = gridLayoutData.verticalSpan;
- rowsExtent = Math.max(rowsExtent, rowCount + gridLayoutData.verticalSpan);
- }
- else {
- row[i] = null;
- hspan[i] = 1;
- vspan[i] = 1;
- }
- }
-
- rows.add(row);
- hspans.add(hspan);
- vspans.add(vspan);
- rowCount++;
- }
- return rows.toArray(new Component[rows.size()][]);
- }
-
- private Component[][] eliminateUnusedRowsAndColumns(Component[][] table) {
- if(table.length == 0) {
- return table;
- }
- //Could make this into a Set, but I doubt there will be any real gain in performance as these are probably going
- //to be very small.
- List<Integer> rowsToRemove = new ArrayList<Integer>();
- List<Integer> columnsToRemove = new ArrayList<Integer>();
-
- final int tableRows = table.length;
- final int tableColumns = table[0].length;
-
- //Scan for unnecessary columns
- columnLoop:
- for(int column = tableColumns - 1; column > 0; column--) {
- for(int row = 0; row < tableRows; row++) {
- if(table[row][column] != table[row][column - 1]) {
- continue columnLoop;
- }
- }
- columnsToRemove.add(column);
- }
-
- //Scan for unnecessary rows
- rowLoop:
- for(int row = tableRows - 1; row > 0; row--) {
- for(int column = 0; column < tableColumns; column++) {
- if(table[row][column] != table[row - 1][column]) {
- continue rowLoop;
- }
- }
- rowsToRemove.add(row);
- }
-
- //If there's nothing to remove, just return the same
- if(rowsToRemove.isEmpty() && columnsToRemove.isEmpty()) {
- return table;
- }
-
- //Build a new table with rows & columns eliminated
- Component[][] newTable = new Component[tableRows - rowsToRemove.size()][];
- int insertedRowCounter = 0;
- for(int row = 0; row < tableRows; row++) {
- Component[] newColumn = new Component[tableColumns - columnsToRemove.size()];
- int insertedColumnCounter = 0;
- for(int column = 0; column < tableColumns; column++) {
- if(columnsToRemove.contains(column)) {
- continue;
- }
- newColumn[insertedColumnCounter++] = table[row][column];
- }
- newTable[insertedRowCounter++] = newColumn;
- }
- return newTable;
- }
-
- private GridLayoutData getLayoutData(Component component) {
- LayoutData layoutData = component.getLayoutData();
- if(layoutData == null || !(layoutData instanceof GridLayoutData)) {
- return DEFAULT;
- }
- else {
- return (GridLayoutData)layoutData;
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.input.KeyStroke;
-
-/**
- * This interface can be used to programmatically intercept input from the user and decide if the input should be passed
- * on to the interactable. It's also possible to fire custom actions for certain keystrokes.
- */
-public interface InputFilter {
- /**
- * Called when the component is about to receive input from the user and decides if the input should be passed on to
- * the component or not
- * @param interactable Interactable that the input is directed to
- * @param keyStroke User input
- * @return {@code true} if the input should be passed on to the interactable, {@code false} otherwise
- */
- boolean onInput(Interactable interactable, KeyStroke keyStroke);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.input.KeyStroke;
-
-/**
- * This interface marks a component as able to receive keyboard input from the user. Components that do not implement
- * this interface in some way will not be able to receive input focus. Normally if you create a new component, you'll
- * probably want to extend from {@code AbstractInteractableComponent} instead of implementing this one directly.
- *
- * @see AbstractInteractableComponent
- * @author Martin
- */
-public interface Interactable extends Component {
- /**
- * Returns, in local coordinates, where to put the cursor on the screen when this component has focus. If null, the
- * cursor should be hidden. If you component is 5x1 and you want to have the cursor in the middle (when in focus),
- * return [2,0]. The GUI system will convert the position to global coordinates.
- * @return Coordinates of where to place the cursor when this component has focus
- */
- TerminalPosition getCursorLocation();
-
- /**
- * Accepts a KeyStroke as input and processes this as a user input. Depending on what the component does with this
- * key-stroke, there are several results passed back to the GUI system that will decide what to do next. If the
- * event was not handled or ignored, {@code Result.UNHANDLED} should be returned. This will tell the GUI system that
- * the key stroke was not understood by this component and may be dealt with in another way. If event was processed
- * properly, it should return {@code Result.HANDLED}, which will make the GUI system stop processing this particular
- * key-stroke. Furthermore, if the component understood the key-stroke and would like to move focus to a different
- * component, there are the {@code Result.MOVE_FOCUS_*} values. This method should be invoking the input filter, if
- * it is set, to see if the input should be processed or not.
- * @param keyStroke What input was entered by the user
- * @return Result of processing the key-stroke
- */
- Result handleInput(KeyStroke keyStroke);
-
- /**
- * Moves focus in the {@code BasePane} to this component. If the component has not been added to a {@code BasePane}
- * (i.e. a {@code Window} most of the time), does nothing.
- * @return Itself
- */
- Interactable takeFocus();
-
- /**
- * Method called when this component gained keyboard focus.
- * @param direction What direction did the focus come from
- * @param previouslyInFocus Which component had focus previously ({@code null} if none)
- */
- void onEnterFocus(FocusChangeDirection direction, Interactable previouslyInFocus);
-
- /**
- * Method called when keyboard focus moves away from this component
- * @param direction What direction is focus going in
- * @param nextInFocus Which component is receiving focus next (or {@code null} if none)
- */
- void onLeaveFocus(FocusChangeDirection direction, Interactable nextInFocus);
-
- /**
- * Returns {@code true} if this component currently has input focus in its root container.
- * @return {@code true} if the interactable has input focus, {@code false} otherwise
- */
- boolean isFocused();
-
- /**
- * Assigns an input filter to the interactable component. This will intercept any user input and decide if the input
- * should be passed on to the component or not. {@code null} means there is no filter.
- * @param inputFilter Input filter to assign to the interactable
- * @return Itself
- */
- Interactable setInputFilter(InputFilter inputFilter);
-
- /**
- * Returns the input filter currently assigned to the interactable component. This will intercept any user input and
- * decide if the input should be passed on to the component or not. {@code null} means there is no filter.
- * @return Input filter currently assigned to the interactable component
- */
- InputFilter getInputFilter();
-
- /**
- * Enum to represent the various results coming out of the handleKeyStroke method
- */
- enum Result {
- /**
- * This component didn't handle the key-stroke, either because it was not recognized or because it chose to
- * ignore it.
- */
- UNHANDLED,
- /**
- * This component has handled the key-stroke and it should be considered consumed.
- */
- HANDLED,
- /**
- * This component has handled the key-stroke and requests the GUI system to switch focus to next component in
- * an ordered list of components. This should generally be returned if moving focus by using the tab key.
- */
- MOVE_FOCUS_NEXT,
- /**
- * This component has handled the key-stroke and requests the GUI system to switch focus to previous component
- * in an ordered list of components. This should generally be returned if moving focus by using the reverse tab
- * key.
- */
- MOVE_FOCUS_PREVIOUS,
- /**
- * This component has handled the key-stroke and requests the GUI system to switch focus to next component in
- * the general left direction. By convention in Lanterna, if there is no component to the left, it will move up
- * instead. This should generally be returned if moving focus by using the left array key.
- */
- MOVE_FOCUS_LEFT,
- /**
- * This component has handled the key-stroke and requests the GUI system to switch focus to next component in
- * the general right direction. By convention in Lanterna, if there is no component to the right, it will move
- * down instead. This should generally be returned if moving focus by using the right array key.
- */
- MOVE_FOCUS_RIGHT,
- /**
- * This component has handled the key-stroke and requests the GUI system to switch focus to next component in
- * the general up direction. By convention in Lanterna, if there is no component above, it will move left
- * instead. This should generally be returned if moving focus by using the up array key.
- */
- MOVE_FOCUS_UP,
- /**
- * This component has handled the key-stroke and requests the GUI system to switch focus to next component in
- * the general down direction. By convention in Lanterna, if there is no component below, it will move up
- * instead. This should generally be returned if moving focus by using the down array key.
- */
- MOVE_FOCUS_DOWN,
- ;
- }
-
- /**
- * When focus has changed, which direction.
- */
- enum FocusChangeDirection {
- /**
- * The next interactable component, going down. This direction usually comes from the user pressing down array.
- */
- DOWN,
- /**
- * The next interactable component, going right. This direction usually comes from the user pressing right array.
- */
- RIGHT,
- /**
- * The next interactable component, going up. This direction usually comes from the user pressing up array.
- */
- UP,
- /**
- * The next interactable component, going left. This direction usually comes from the user pressing left array.
- */
- LEFT,
- /**
- * The next interactable component, in layout manager order (usually left->right, up->down). This direction
- * usually comes from the user pressing tab key.
- */
- NEXT,
- /**
- * The previous interactable component, reversed layout manager order (usually right->left, down->up). This
- * direction usually comes from the user pressing shift and tab key (reverse tab).
- */
- PREVIOUS,
- /**
- * Focus was changed by calling the {@code RootContainer.setFocusedInteractable(..)} method directly.
- */
- TELEPORT,
- /**
- * Focus has gone away and no component is now in focus
- */
- RESET,
- ;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.*;
-
-/**
- * This class is used to keep a 'map' of the usable area and note where all the interact:ables are. It can then be used
- * to find the next interactable in any direction. It is used inside the GUI system to drive arrow key navigation.
- * @author Martin
- */
-public class InteractableLookupMap {
- private final int[][] lookupMap;
- private final List<Interactable> interactables;
-
- InteractableLookupMap(TerminalSize size) {
- lookupMap = new int[size.getRows()][size.getColumns()];
- interactables = new ArrayList<Interactable>();
- for (int[] aLookupMap : lookupMap) {
- Arrays.fill(aLookupMap, -1);
- }
- }
-
- void reset() {
- interactables.clear();
- for (int[] aLookupMap : lookupMap) {
- Arrays.fill(aLookupMap, -1);
- }
- }
-
- TerminalSize getSize() {
- if (lookupMap.length==0) { return TerminalSize.ZERO; }
- return new TerminalSize(lookupMap[0].length, lookupMap.length);
- }
-
- /**
- * Adds an interactable component to the lookup map
- * @param interactable Interactable to add to the lookup map
- */
- @SuppressWarnings("ConstantConditions")
- public synchronized void add(Interactable interactable) {
- TerminalPosition topLeft = interactable.toBasePane(TerminalPosition.TOP_LEFT_CORNER);
- TerminalSize size = interactable.getSize();
- interactables.add(interactable);
- int index = interactables.size() - 1;
- for(int y = topLeft.getRow(); y < topLeft.getRow() + size.getRows(); y++) {
- for(int x = topLeft.getColumn(); x < topLeft.getColumn() + size.getColumns(); x++) {
- //Make sure it's not outside the map
- if(y >= 0 && y < lookupMap.length &&
- x >= 0 && x < lookupMap[y].length) {
- lookupMap[y][x] = index;
- }
- }
- }
- }
-
- /**
- * Looks up what interactable component is as a particular location in the map
- * @param position Position to look up
- * @return The {@code Interactable} component at the specified location or {@code null} if there's nothing there
- */
- public synchronized Interactable getInteractableAt(TerminalPosition position) {
- if(position.getRow() >= lookupMap.length) {
- return null;
- }
- else if(position.getColumn() >= lookupMap[0].length) {
- return null;
- }
- else if(lookupMap[position.getRow()][position.getColumn()] == -1) {
- return null;
- }
- return interactables.get(lookupMap[position.getRow()][position.getColumn()]);
- }
-
- /**
- * Starting from a particular {@code Interactable} and going up, which is the next interactable?
- * @param interactable What {@code Interactable} to start searching from
- * @return The next {@code Interactable} above the one specified or {@code null} if there are no more
- * {@code Interactable}:s above it
- */
- public synchronized Interactable findNextUp(Interactable interactable) {
- return findNextUpOrDown(interactable, false);
- }
-
- /**
- * Starting from a particular {@code Interactable} and going down, which is the next interactable?
- * @param interactable What {@code Interactable} to start searching from
- * @return The next {@code Interactable} below the one specified or {@code null} if there are no more
- * {@code Interactable}:s below it
- */
- public synchronized Interactable findNextDown(Interactable interactable) {
- return findNextUpOrDown(interactable, true);
- }
-
- //Avoid code duplication in above two methods
- private Interactable findNextUpOrDown(Interactable interactable, boolean isDown) {
- int directionTerm = isDown ? 1 : -1;
- TerminalPosition startPosition = interactable.getCursorLocation();
- if (startPosition == null) {
- // If the currently active interactable component is not showing the cursor, use the top-left position
- // instead if we're going up, or the bottom-left position if we're going down
- if(isDown) {
- startPosition = new TerminalPosition(0, interactable.getSize().getRows() - 1);
- }
- else {
- startPosition = TerminalPosition.TOP_LEFT_CORNER;
- }
- }
- else {
- //Adjust position so that it's at the bottom of the component if we're going down or at the top of the
- //component if we're going right. Otherwise the lookup might product odd results in certain cases.
- if(isDown) {
- startPosition = startPosition.withRow(interactable.getSize().getRows() - 1);
- }
- else {
- startPosition = startPosition.withRow(0);
- }
- }
- startPosition = interactable.toBasePane(startPosition);
- if(startPosition == null) {
- // The structure has changed, our interactable is no longer inside the base pane!
- return null;
- }
- Set<Interactable> disqualified = getDisqualifiedInteractables(startPosition, true);
- TerminalSize size = getSize();
- int maxShiftLeft = interactable.toBasePane(TerminalPosition.TOP_LEFT_CORNER).getColumn();
- maxShiftLeft = Math.max(maxShiftLeft, 0);
- int maxShiftRight = interactable.toBasePane(new TerminalPosition(interactable.getSize().getColumns() - 1, 0)).getColumn();
- maxShiftRight = Math.min(maxShiftRight, size.getColumns() - 1);
- int maxShift = Math.max(startPosition.getColumn() - maxShiftLeft, maxShiftRight - startPosition.getRow());
- for (int searchRow = startPosition.getRow() + directionTerm;
- searchRow >= 0 && searchRow < size.getRows();
- searchRow += directionTerm) {
-
- for (int xShift = 0; xShift <= maxShift; xShift++) {
- for (int modifier : new int[]{1, -1}) {
- if (xShift == 0 && modifier == -1) {
- break;
- }
- int searchColumn = startPosition.getColumn() + (xShift * modifier);
- if (searchColumn < maxShiftLeft || searchColumn > maxShiftRight) {
- continue;
- }
-
- int index = lookupMap[searchRow][searchColumn];
- if (index != -1 && !disqualified.contains(interactables.get(index))) {
- return interactables.get(index);
- }
- }
- }
- }
- return null;
- }
-
- /**
- * Starting from a particular {@code Interactable} and going left, which is the next interactable?
- * @param interactable What {@code Interactable} to start searching from
- * @return The next {@code Interactable} left of the one specified or {@code null} if there are no more
- * {@code Interactable}:s left of it
- */
- public synchronized Interactable findNextLeft(Interactable interactable) {
- return findNextLeftOrRight(interactable, false);
- }
-
- /**
- * Starting from a particular {@code Interactable} and going right, which is the next interactable?
- * @param interactable What {@code Interactable} to start searching from
- * @return The next {@code Interactable} right of the one specified or {@code null} if there are no more
- * {@code Interactable}:s right of it
- */
- public synchronized Interactable findNextRight(Interactable interactable) {
- return findNextLeftOrRight(interactable, true);
- }
-
- //Avoid code duplication in above two methods
- private Interactable findNextLeftOrRight(Interactable interactable, boolean isRight) {
- int directionTerm = isRight ? 1 : -1;
- TerminalPosition startPosition = interactable.getCursorLocation();
- if(startPosition == null) {
- // If the currently active interactable component is not showing the cursor, use the top-left position
- // instead if we're going left, or the top-right position if we're going right
- if(isRight) {
- startPosition = new TerminalPosition(interactable.getSize().getColumns() - 1, 0);
- }
- else {
- startPosition = TerminalPosition.TOP_LEFT_CORNER;
- }
- }
- else {
- //Adjust position so that it's on the left-most side if we're going left or right-most side if we're going
- //right. Otherwise the lookup might product odd results in certain cases
- if(isRight) {
- startPosition = startPosition.withColumn(interactable.getSize().getColumns() - 1);
- }
- else {
- startPosition = startPosition.withColumn(0);
- }
- }
- startPosition = interactable.toBasePane(startPosition);
- if(startPosition == null) {
- // The structure has changed, our interactable is no longer inside the base pane!
- return null;
- }
- Set<Interactable> disqualified = getDisqualifiedInteractables(startPosition, false);
- TerminalSize size = getSize();
- int maxShiftUp = interactable.toBasePane(TerminalPosition.TOP_LEFT_CORNER).getRow();
- maxShiftUp = Math.max(maxShiftUp, 0);
- int maxShiftDown = interactable.toBasePane(new TerminalPosition(0, interactable.getSize().getRows() - 1)).getRow();
- maxShiftDown = Math.min(maxShiftDown, size.getRows() - 1);
- int maxShift = Math.max(startPosition.getRow() - maxShiftUp, maxShiftDown - startPosition.getRow());
- for(int searchColumn = startPosition.getColumn() + directionTerm;
- searchColumn >= 0 && searchColumn < size.getColumns();
- searchColumn += directionTerm) {
-
- for(int yShift = 0; yShift <= maxShift; yShift++) {
- for(int modifier: new int[] { 1, -1 }) {
- if(yShift == 0 && modifier == -1) {
- break;
- }
- int searchRow = startPosition.getRow() + (yShift * modifier);
- if(searchRow < maxShiftUp || searchRow > maxShiftDown) {
- continue;
- }
- int index = lookupMap[searchRow][searchColumn];
- if (index != -1 && !disqualified.contains(interactables.get(index))) {
- return interactables.get(index);
- }
- }
- }
- }
- return null;
- }
-
- private Set<Interactable> getDisqualifiedInteractables(TerminalPosition startPosition, boolean scanHorizontally) {
- Set<Interactable> disqualified = new HashSet<Interactable>();
- if (lookupMap.length == 0) { return disqualified; } // safeguard
-
- TerminalSize size = getSize();
-
- //Adjust start position if necessary
- if(startPosition.getRow() < 0) {
- startPosition = startPosition.withRow(0);
- }
- else if(startPosition.getRow() >= lookupMap.length) {
- startPosition = startPosition.withRow(lookupMap.length - 1);
- }
- if(startPosition.getColumn() < 0) {
- startPosition = startPosition.withColumn(0);
- }
- else if(startPosition.getColumn() >= lookupMap[startPosition.getRow()].length) {
- startPosition = startPosition.withColumn(lookupMap[startPosition.getRow()].length - 1);
- }
-
- if(scanHorizontally) {
- for(int column = 0; column < size.getColumns(); column++) {
- int index = lookupMap[startPosition.getRow()][column];
- if(index != -1) {
- disqualified.add(interactables.get(index));
- }
- }
- }
- else {
- for(int row = 0; row < size.getRows(); row++) {
- int index = lookupMap[row][startPosition.getColumn()];
- if(index != -1) {
- disqualified.add(interactables.get(index));
- }
- }
- }
- return disqualified;
- }
-
- void debug() {
- for(int[] row: lookupMap) {
- for(int value: row) {
- if(value >= 0) {
- System.out.print(" ");
- }
- System.out.print(value);
- }
- System.out.println();
- }
- System.out.println();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-
-/**
- * Extended interface for component renderers used with interactable components. Because only the renderer knows what
- * the component looks like, the component itself cannot know where to place the text cursor, so this method is instead
- * delegated to this interface that extends the regular component renderer.
- *
- * @author Martin
- * @param <T> Type of the component this {@code InteractableRenderer} is designed for
- */
-public interface InteractableRenderer<T extends Component & Interactable> extends ComponentRenderer<T> {
- TerminalPosition getCursorLocation(T component);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.graphics.ThemeDefinition;
-
-import java.util.EnumSet;
-import java.util.List;
-
-/**
- * Label is a simple read-only text display component. It supports customized colors and multi-line text.
- * @author Martin
- */
-public class Label extends AbstractComponent<Label> {
- private String[] lines;
- private Integer labelWidth;
- private TerminalSize labelSize;
- private TextColor foregroundColor;
- private TextColor backgroundColor;
- private final EnumSet<SGR> additionalStyles;
-
- /**
- * Main constructor, creates a new Label displaying a specific text.
- * @param text Text the label will display
- */
- public Label(String text) {
- this.lines = null;
- this.labelSize = TerminalSize.ZERO;
- this.labelWidth = 0;
- this.foregroundColor = null;
- this.backgroundColor = null;
- this.additionalStyles = EnumSet.noneOf(SGR.class);
- setText(text);
- }
-
- /**
- * Protected access to set the internal representation of the text in this label, to be used by sub-classes of label
- * in certain cases where {@code setText(..)} doesn't work. In general, you probably want to stick to
- * {@code setText(..)} instead of this method unless you have a good reason not to.
- * @param lines New lines this label will display
- */
- protected void setLines(String[] lines) {
- this.lines = lines;
- }
-
- /**
- * Updates the text this label is displaying
- * @param text New text to display
- */
- public synchronized void setText(String text) {
- setLines(splitIntoMultipleLines(text));
- this.labelSize = getBounds(lines, labelSize);
- invalidate();
- }
-
- /**
- * Returns the text this label is displaying. Multi-line labels will have their text concatenated with \n, even if
- * they were originally set using multi-line text having \r\n as line terminators.
- * @return String of the text this label is displaying
- */
- public synchronized String getText() {
- if(lines.length == 0) {
- return "";
- }
- StringBuilder bob = new StringBuilder(lines[0]);
- for(int i = 1; i < lines.length; i++) {
- bob.append("\n").append(lines[i]);
- }
- return bob.toString();
- }
-
- /**
- * Utility method for taking a string and turning it into an array of lines. This method is used in order to deal
- * with line endings consistently.
- * @param text Text to split
- * @return Array of strings that forms the lines of the original string
- */
- protected String[] splitIntoMultipleLines(String text) {
- return text.replace("\r", "").split("\n");
- }
-
- /**
- * Returns the area, in terminal columns and rows, required to fully draw the lines passed in.
- * @param lines Lines to measure the size of
- * @param currentBounds Optional (can pass {@code null}) terminal size to use for storing the output values. If the
- * method is called many times and always returning the same value, passing in an external
- * reference of this size will avoid creating new {@code TerminalSize} objects every time
- * @return Size that is required to draw the lines
- */
- protected TerminalSize getBounds(String[] lines, TerminalSize currentBounds) {
- if(currentBounds == null) {
- currentBounds = TerminalSize.ZERO;
- }
- currentBounds = currentBounds.withRows(lines.length);
- if(labelWidth == null || labelWidth == 0) {
- int preferredWidth = 0;
- for(String line : lines) {
- int lineWidth = TerminalTextUtils.getColumnWidth(line);
- if(preferredWidth < lineWidth) {
- preferredWidth = lineWidth;
- }
- }
- currentBounds = currentBounds.withColumns(preferredWidth);
- }
- else {
- List<String> wordWrapped = TerminalTextUtils.getWordWrappedText(labelWidth, lines);
- currentBounds = currentBounds.withColumns(labelWidth).withRows(wordWrapped.size());
- }
- return currentBounds;
- }
-
- /**
- * Overrides the current theme's foreground color and use the one specified. If called with {@code null}, the
- * override is cleared and the theme is used again.
- * @param foregroundColor Foreground color to use when drawing the label, if {@code null} then use the theme's
- * default
- * @return Itself
- */
- public synchronized Label setForegroundColor(TextColor foregroundColor) {
- this.foregroundColor = foregroundColor;
- return this;
- }
-
- /**
- * Returns the foreground color used when drawing the label, or {@code null} if the color is read from the current
- * theme.
- * @return Foreground color used when drawing the label, or {@code null} if the color is read from the current
- * theme.
- */
- public TextColor getForegroundColor() {
- return foregroundColor;
- }
-
- /**
- * Overrides the current theme's background color and use the one specified. If called with {@code null}, the
- * override is cleared and the theme is used again.
- * @param backgroundColor Background color to use when drawing the label, if {@code null} then use the theme's
- * default
- * @return Itself
- */
- public synchronized Label setBackgroundColor(TextColor backgroundColor) {
- this.backgroundColor = backgroundColor;
- return this;
- }
-
- /**
- * Returns the background color used when drawing the label, or {@code null} if the color is read from the current
- * theme.
- * @return Background color used when drawing the label, or {@code null} if the color is read from the current
- * theme.
- */
- public TextColor getBackgroundColor() {
- return backgroundColor;
- }
-
- /**
- * Adds an additional SGR style to use when drawing the label, in case it wasn't enabled by the theme
- * @param sgr SGR style to enable for this label
- * @return Itself
- */
- public synchronized Label addStyle(SGR sgr) {
- additionalStyles.add(sgr);
- return this;
- }
-
- /**
- * Removes an additional SGR style used when drawing the label, previously added by {@code addStyle(..)}. If the
- * style you are trying to remove is specified by the theme, calling this method will have no effect.
- * @param sgr SGR style to remove
- * @return Itself
- */
- public synchronized Label removeStyle(SGR sgr) {
- additionalStyles.remove(sgr);
- return this;
- }
-
- /**
- * Use this method to limit how wide the label can grow. If set to {@code null} there is no limit but if set to a
- * positive integer then the preferred size will be calculated using word wrapping for lines that are longer than
- * this label width. This may make the label increase in height as new rows may be requested. Please note that some
- * layout managers might assign more space to the label and because of this the wrapping might not be as you expect
- * it. If set to 0, the label will request the same space as if set to {@code null}, but when drawing it will apply
- * word wrapping instead of truncation in order to fit the label inside the designated area if it's smaller than
- * what was requested. By default this is set to 0.
- *
- * @param labelWidth Either {@code null} or 0 for no limit on how wide the label can be, where 0 indicates word
- * wrapping should be used if the assigned area is smaller than the requested size, or a positive
- * integer setting the requested maximum width at what point word wrapping will begin
- * @return Itself
- */
- public synchronized Label setLabelWidth(Integer labelWidth) {
- this.labelWidth = labelWidth;
- return this;
- }
-
- /**
- * Returns the limit how wide the label can grow. If set to {@code null} or 0 there is no limit but if set to a
- * positive integer then the preferred size will be calculated using word wrapping for lines that are longer than
- * the label width. This may make the label increase in height as new rows may be requested. Please note that some
- * layout managers might assign more space to the label and because of this the wrapping might not be as you expect
- * it. If set to 0, the label will request the same space as if set to {@code null}, but when drawing it will apply
- * word wrapping instead of truncation in order to fit the label inside the designated area if it's smaller than
- * what was requested.
- * @return Either {@code null} or 0 for no limit on how wide the label can be, where 0 indicates word
- * wrapping should be used if the assigned area is smaller than the requested size, or a positive
- * integer setting the requested maximum width at what point word wrapping will begin
- */
- public Integer getLabelWidth() {
- return labelWidth;
- }
-
- @Override
- protected ComponentRenderer<Label> createDefaultRenderer() {
- return new ComponentRenderer<Label>() {
- @Override
- public TerminalSize getPreferredSize(Label Label) {
- return labelSize;
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, Label component) {
- ThemeDefinition themeDefinition = graphics.getThemeDefinition(Label.class);
- graphics.applyThemeStyle(themeDefinition.getNormal());
- if(foregroundColor != null) {
- graphics.setForegroundColor(foregroundColor);
- }
- if(backgroundColor != null) {
- graphics.setBackgroundColor(backgroundColor);
- }
- for(SGR sgr: additionalStyles) {
- graphics.enableModifiers(sgr);
- }
-
- String[] linesToDraw;
- if(component.getLabelWidth() == null) {
- linesToDraw = component.lines;
- }
- else {
- linesToDraw = TerminalTextUtils.getWordWrappedText(graphics.getSize().getColumns(), component.lines).toArray(new String[0]);
- }
-
- for(int row = 0; row < Math.min(graphics.getSize().getRows(), linesToDraw.length); row++) {
- String line = linesToDraw[row];
- if(graphics.getSize().getColumns() >= labelSize.getColumns()) {
- graphics.putString(0, row, line);
- }
- else {
- int availableColumns = graphics.getSize().getColumns();
- String fitString = TerminalTextUtils.fitString(line, availableColumns);
- graphics.putString(0, row, fitString);
- }
- }
- }
- };
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-/**
- * Empty interface to use for values that can be used as a layout meta-data on components.
- * @author martin
- */
-public interface LayoutData {
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-import java.util.List;
-
-/**
- * A layout manager is a class that takes an area of usable space and a list of components to fit on that space. This
- * is very similar to how AWT/Swing/SWT works. Lanterna contains a number of layout managers built-in that will arrange
- * components in various ways, but you can also write your own. The typical way of providing customization and tuning,
- * so the layout manager can distinguish between components and treat them in different ways, is to create a class
- * and/or objects based on the {@code LayoutData} object, which can be assigned to each {@code Component}.
- * @see AbsoluteLayout
- * @see BorderLayout
- * @see GridLayout
- * @see LinearLayout
- * @author Martin
- */
-public interface LayoutManager {
-
- /**
- * This method returns the dimensions it would prefer to have to be able to layout all components while giving all
- * of them as much space as they are asking for.
- * @param components List of components
- * @return Size the layout manager would like to have
- */
- TerminalSize getPreferredSize(List<Component> components);
-
- /**
- * Given a size constraint, update the location and size of each component in the component list by laying them out
- * in the available area. This method will call {@code setPosition(..)} and {@code setSize(..)} on the Components.
- * @param area Size available to this layout manager to lay out the components on
- * @param components List of components to lay out
- */
- void doLayout(TerminalSize area, List<Component> components);
-
- /**
- * Returns true if the internal state of this LayoutManager has changed since the last call to doLayout. This will
- * tell the container that it needs to call doLayout again.
- * @return {@code true} if this layout manager's internal state has changed since the last call to {@code doLayout}
- */
- boolean hasChanged();
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.List;
-
-/**
- * Simple layout manager the puts all components on a single line, either horizontally or vertically.
- */
-public class LinearLayout implements LayoutManager {
- /**
- * This enum type will decide the alignment of a component on the counter-axis, meaning the horizontal alignment on
- * vertical {@code LinearLayout}s and vertical alignment on horizontal {@code LinearLayout}s.
- */
- public enum Alignment {
- /**
- * The component will be placed to the left (for vertical layouts) or top (for horizontal layouts)
- */
- Beginning,
- /**
- * The component will be placed horizontally centered (for vertical layouts) or vertically centered (for
- * horizontal layouts)
- */
- Center,
- /**
- * The component will be placed to the right (for vertical layouts) or bottom (for horizontal layouts)
- */
- End,
- /**
- * The component will be forced to take up all the horizontal space (for vertical layouts) or vertical space
- * (for horizontal layouts)
- */
- Fill,
- }
-
- private static class LinearLayoutData implements LayoutData {
- private final Alignment alignment;
-
- public LinearLayoutData(Alignment alignment) {
- this.alignment = alignment;
- }
- }
-
- /**
- * Creates a {@code LayoutData} for {@code LinearLayout} that assigns a component to a particular alignment on its
- * counter-axis, meaning the horizontal alignment on vertical {@code LinearLayout}s and vertical alignment on
- * horizontal {@code LinearLayout}s.
- * @param alignment Alignment to store in the {@code LayoutData} object
- * @return {@code LayoutData} object created for {@code LinearLayout}s with the specified alignment
- * @see Alignment
- */
- public static LayoutData createLayoutData(Alignment alignment) {
- return new LinearLayoutData(alignment);
- }
-
- private final Direction direction;
- private int spacing;
- private boolean changed;
-
- /**
- * Default constructor, creates a vertical {@code LinearLayout}
- */
- public LinearLayout() {
- this(Direction.VERTICAL);
- }
-
- /**
- * Standard constructor that creates a {@code LinearLayout} with a specified direction to position the components on
- * @param direction Direction for this {@code Direction}
- */
- public LinearLayout(Direction direction) {
- this.direction = direction;
- this.spacing = direction == Direction.HORIZONTAL ? 1 : 0;
- this.changed = true;
- }
-
- /**
- * Sets the amount of empty space to put in between components. For horizontal layouts, this is number of columns
- * (by default 1) and for vertical layouts this is number of rows (by default 0).
- * @param spacing Spacing between components, either in number of columns or rows depending on the direction
- * @return Itself
- */
- public LinearLayout setSpacing(int spacing) {
- this.spacing = spacing;
- this.changed = true;
- return this;
- }
-
- /**
- * Returns the amount of empty space to put in between components. For horizontal layouts, this is number of columns
- * (by default 1) and for vertical layouts this is number of rows (by default 0).
- * @return Spacing between components, either in number of columns or rows depending on the direction
- */
- public int getSpacing() {
- return spacing;
- }
-
- @Override
- public TerminalSize getPreferredSize(List<Component> components) {
- if(direction == Direction.VERTICAL) {
- return getPreferredSizeVertically(components);
- }
- else {
- return getPreferredSizeHorizontally(components);
- }
- }
-
- private TerminalSize getPreferredSizeVertically(List<Component> components) {
- int maxWidth = 0;
- int height = 0;
- for(Component component: components) {
- TerminalSize preferredSize = component.getPreferredSize();
- if(maxWidth < preferredSize.getColumns()) {
- maxWidth = preferredSize.getColumns();
- }
- height += preferredSize.getRows();
- }
- height += spacing * (components.size() - 1);
- return new TerminalSize(maxWidth, height);
- }
-
- private TerminalSize getPreferredSizeHorizontally(List<Component> components) {
- int maxHeight = 0;
- int width = 0;
- for(Component component: components) {
- TerminalSize preferredSize = component.getPreferredSize();
- if(maxHeight < preferredSize.getRows()) {
- maxHeight = preferredSize.getRows();
- }
- width += preferredSize.getColumns();
- }
- width += spacing * (components.size() - 1);
- return new TerminalSize(width, maxHeight);
- }
-
- @Override
- public boolean hasChanged() {
- return changed;
- }
-
- @Override
- public void doLayout(TerminalSize area, List<Component> components) {
- if(direction == Direction.VERTICAL) {
- doVerticalLayout(area, components);
- }
- else {
- doHorizontalLayout(area, components);
- }
- this.changed = false;
- }
-
- private void doVerticalLayout(TerminalSize area, List<Component> components) {
- int remainingVerticalSpace = area.getRows();
- int availableHorizontalSpace = area.getColumns();
- for(Component component: components) {
- if(remainingVerticalSpace <= 0) {
- component.setPosition(TerminalPosition.TOP_LEFT_CORNER);
- component.setSize(TerminalSize.ZERO);
- }
- else {
- LinearLayoutData layoutData = (LinearLayoutData)component.getLayoutData();
- Alignment alignment = Alignment.Beginning;
- if(layoutData != null) {
- alignment = layoutData.alignment;
- }
-
- TerminalSize preferredSize = component.getPreferredSize();
- TerminalSize decidedSize = new TerminalSize(
- Math.min(availableHorizontalSpace, preferredSize.getColumns()),
- Math.min(remainingVerticalSpace, preferredSize.getRows()));
- if(alignment == Alignment.Fill) {
- decidedSize = decidedSize.withColumns(availableHorizontalSpace);
- alignment = Alignment.Beginning;
- }
-
- TerminalPosition position = component.getPosition();
- position = position.withRow(area.getRows() - remainingVerticalSpace);
- switch(alignment) {
- case End:
- position = position.withColumn(availableHorizontalSpace - decidedSize.getColumns());
- break;
- case Center:
- position = position.withColumn((availableHorizontalSpace - decidedSize.getColumns()) / 2);
- break;
- case Beginning:
- default:
- position = position.withColumn(0);
- break;
- }
- component.setPosition(position);
- component.setSize(component.getSize().with(decidedSize));
- remainingVerticalSpace -= decidedSize.getRows() + spacing;
- }
- }
- }
-
- private void doHorizontalLayout(TerminalSize area, List<Component> components) {
- int remainingHorizontalSpace = area.getColumns();
- int availableVerticalSpace = area.getRows();
- for(Component component: components) {
- if(remainingHorizontalSpace <= 0) {
- component.setPosition(TerminalPosition.TOP_LEFT_CORNER);
- component.setSize(TerminalSize.ZERO);
- }
- else {
- LinearLayoutData layoutData = (LinearLayoutData)component.getLayoutData();
- Alignment alignment = Alignment.Beginning;
- if(layoutData != null) {
- alignment = layoutData.alignment;
- }
- TerminalSize preferredSize = component.getPreferredSize();
- TerminalSize decidedSize = new TerminalSize(
- Math.min(remainingHorizontalSpace, preferredSize.getColumns()),
- Math.min(availableVerticalSpace, preferredSize.getRows()));
- if(alignment == Alignment.Fill) {
- decidedSize = decidedSize.withRows(availableVerticalSpace);
- alignment = Alignment.Beginning;
- }
-
- TerminalPosition position = component.getPosition();
- position = position.withColumn(area.getColumns() - remainingHorizontalSpace);
- switch(alignment) {
- case End:
- position = position.withRow(availableVerticalSpace - decidedSize.getRows());
- break;
- case Center:
- position = position.withRow((availableVerticalSpace - decidedSize.getRows()) / 2);
- break;
- case Beginning:
- default:
- position = position.withRow(0);
- break;
- }
- component.setPosition(position);
- component.setSize(component.getSize().with(decidedSize));
- remainingHorizontalSpace -= decidedSize.getColumns() + spacing;
- }
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.bundle.LocalizedUIBundle;
-
-import java.util.Locale;
-
-/**
- * Set of predefined localized string.<br>
- * All this strings are localized by using {@link LocalizedUIBundle}.<br>
- * Changing the locale by calling {@link Locale#setDefault(Locale)}.
- * @author silveryocha.
- */
-public final class LocalizedString {
-
- /**
- * "OK"
- */
- public final static LocalizedString OK = new LocalizedString("short.label.ok");
- /**
- * "Cancel"
- */
- public final static LocalizedString Cancel = new LocalizedString("short.label.cancel");
- /**
- * "Yes"
- */
- public final static LocalizedString Yes = new LocalizedString("short.label.yes");
- /**
- * "No"
- */
- public final static LocalizedString No = new LocalizedString("short.label.no");
- /**
- * "Close"
- */
- public final static LocalizedString Close = new LocalizedString("short.label.close");
- /**
- * "Abort"
- */
- public final static LocalizedString Abort = new LocalizedString("short.label.abort");
- /**
- * "Ignore"
- */
- public final static LocalizedString Ignore = new LocalizedString("short.label.ignore");
- /**
- * "Retry"
- */
- public final static LocalizedString Retry = new LocalizedString("short.label.retry");
- /**
- * "Continue"
- */
- public final static LocalizedString Continue = new LocalizedString("short.label.continue");
- /**
- * "Open"
- */
- public final static LocalizedString Open = new LocalizedString("short.label.open");
- /**
- * "Save"
- */
- public final static LocalizedString Save = new LocalizedString("short.label.save");
-
- private final String bundleKey;
-
- private LocalizedString(final String bundleKey) {
- this.bundleKey = bundleKey;
- }
-
- @Override
- public String toString() {
- return LocalizedUIBundle.get(Locale.getDefault(), bundleKey);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.graphics.BasicTextImage;
-import com.googlecode.lanterna.graphics.TextImage;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-import com.googlecode.lanterna.screen.Screen;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.screen.VirtualScreen;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.util.*;
-
-/**
- * This is the main Text GUI implementation built into Lanterna, supporting multiple tiled windows and a dynamic
- * background area that can be fully customized. If you want to create a text-based GUI with windows and controls,
- * it's very likely this is what you want to use.
- *
- * @author Martin
- */
-public class MultiWindowTextGUI extends AbstractTextGUI implements WindowBasedTextGUI {
- private final VirtualScreen virtualScreen;
- private final WindowManager windowManager;
- private final BasePane backgroundPane;
- private final List<Window> windows;
- private final IdentityHashMap<Window, TextImage> windowRenderBufferCache;
- private final WindowPostRenderer postRenderer;
-
- private Window activeWindow;
- private boolean eofWhenNoWindows;
-
- /**
- * Creates a new {@code MultiWindowTextGUI} that uses the specified {@code Screen} as the backend for all drawing
- * operations. The screen will be automatically wrapped in a {@code VirtualScreen} in order to deal with GUIs
- * becoming too big to fit the terminal. The background area of the GUI will be solid blue.
- * @param screen Screen to use as the backend for drawing operations
- */
- public MultiWindowTextGUI(Screen screen) {
- this(screen, TextColor.ANSI.BLUE);
- }
-
- /**
- * Creates a new {@code MultiWindowTextGUI} that uses the specified {@code Screen} as the backend for all drawing
- * operations. The screen will be automatically wrapped in a {@code VirtualScreen} in order to deal with GUIs
- * becoming too big to fit the terminal. The background area of the GUI will be solid blue
- * @param guiThreadFactory Factory implementation to use when creating the {@code TextGUIThread}
- * @param screen Screen to use as the backend for drawing operations
- */
- public MultiWindowTextGUI(TextGUIThreadFactory guiThreadFactory, Screen screen) {
- this(guiThreadFactory,
- screen,
- new DefaultWindowManager(),
- new WindowShadowRenderer(),
- new EmptySpace(TextColor.ANSI.BLUE));
- }
-
- /**
- * Creates a new {@code MultiWindowTextGUI} that uses the specified {@code Screen} as the backend for all drawing
- * operations. The screen will be automatically wrapped in a {@code VirtualScreen} in order to deal with GUIs
- * becoming too big to fit the terminal. The background area of the GUI is a solid color as decided by the
- * {@code backgroundColor} parameter.
- * @param screen Screen to use as the backend for drawing operations
- * @param backgroundColor Color to use for the GUI background
- */
- public MultiWindowTextGUI(
- Screen screen,
- TextColor backgroundColor) {
-
- this(screen, new DefaultWindowManager(), new EmptySpace(backgroundColor));
- }
-
- /**
- * Creates a new {@code MultiWindowTextGUI} that uses the specified {@code Screen} as the backend for all drawing
- * operations. The screen will be automatically wrapped in a {@code VirtualScreen} in order to deal with GUIs
- * becoming too big to fit the terminal. The background area of the GUI is the component passed in as the
- * {@code background} parameter, forced to full size.
- * @param screen Screen to use as the backend for drawing operations
- * @param windowManager Window manager implementation to use
- * @param background Component to use as the background of the GUI, behind all the windows
- */
- public MultiWindowTextGUI(
- Screen screen,
- WindowManager windowManager,
- Component background) {
-
- this(screen, windowManager, new WindowShadowRenderer(), background);
- }
-
- /**
- * Creates a new {@code MultiWindowTextGUI} that uses the specified {@code Screen} as the backend for all drawing
- * operations. The screen will be automatically wrapped in a {@code VirtualScreen} in order to deal with GUIs
- * becoming too big to fit the terminal. The background area of the GUI is the component passed in as the
- * {@code background} parameter, forced to full size.
- * @param screen Screen to use as the backend for drawing operations
- * @param windowManager Window manager implementation to use
- * @param postRenderer {@code WindowPostRenderer} object to invoke after each window has been drawn
- * @param background Component to use as the background of the GUI, behind all the windows
- */
- public MultiWindowTextGUI(
- Screen screen,
- WindowManager windowManager,
- WindowPostRenderer postRenderer,
- Component background) {
-
- this(new SameTextGUIThread.Factory(), screen, windowManager, postRenderer, background);
- }
-
- /**
- * Creates a new {@code MultiWindowTextGUI} that uses the specified {@code Screen} as the backend for all drawing
- * operations. The screen will be automatically wrapped in a {@code VirtualScreen} in order to deal with GUIs
- * becoming too big to fit the terminal. The background area of the GUI is the component passed in as the
- * {@code background} parameter, forced to full size.
- * @param guiThreadFactory Factory implementation to use when creating the {@code TextGUIThread}
- * @param screen Screen to use as the backend for drawing operations
- * @param windowManager Window manager implementation to use
- * @param postRenderer {@code WindowPostRenderer} object to invoke after each window has been drawn
- * @param background Component to use as the background of the GUI, behind all the windows
- */
- public MultiWindowTextGUI(
- TextGUIThreadFactory guiThreadFactory,
- Screen screen,
- WindowManager windowManager,
- WindowPostRenderer postRenderer,
- Component background) {
-
- this(guiThreadFactory, new VirtualScreen(screen), windowManager, postRenderer, background);
- }
-
- private MultiWindowTextGUI(
- TextGUIThreadFactory guiThreadFactory,
- VirtualScreen screen,
- WindowManager windowManager,
- WindowPostRenderer postRenderer,
- Component background) {
-
- super(guiThreadFactory, screen);
- if(windowManager == null) {
- throw new IllegalArgumentException("Creating a window-based TextGUI requires a WindowManager");
- }
- if(background == null) {
- //Use a sensible default instead of throwing
- background = new EmptySpace(TextColor.ANSI.BLUE);
- }
- this.virtualScreen = screen;
- this.windowManager = windowManager;
- this.backgroundPane = new AbstractBasePane() {
- @Override
- public TextGUI getTextGUI() {
- return MultiWindowTextGUI.this;
- }
-
- @Override
- public TerminalPosition toGlobal(TerminalPosition localPosition) {
- return localPosition;
- }
-
- public TerminalPosition fromGlobal(TerminalPosition globalPosition) {
- return globalPosition;
- }
- };
- this.backgroundPane.setComponent(background);
- this.windows = new LinkedList<Window>();
- this.windowRenderBufferCache = new IdentityHashMap<Window, TextImage>();
- this.postRenderer = postRenderer;
- this.eofWhenNoWindows = false;
- }
-
- @Override
- public synchronized boolean isPendingUpdate() {
- for(Window window: windows) {
- if(window.isInvalid()) {
- return true;
- }
- }
- return super.isPendingUpdate() || backgroundPane.isInvalid() || windowManager.isInvalid();
- }
-
- @Override
- public synchronized void updateScreen() throws IOException {
- TerminalSize minimumTerminalSize = TerminalSize.ZERO;
- for(Window window: windows) {
- if(window.isVisible()) {
- if (window.getHints().contains(Window.Hint.FULL_SCREEN) ||
- window.getHints().contains(Window.Hint.FIT_TERMINAL_WINDOW) ||
- window.getHints().contains(Window.Hint.EXPANDED)) {
- //Don't take full screen windows or auto-sized windows into account
- continue;
- }
- TerminalPosition lastPosition = window.getPosition();
- minimumTerminalSize = minimumTerminalSize.max(
- //Add position to size to get the bottom-right corner of the window
- window.getDecoratedSize().withRelative(
- Math.max(lastPosition.getColumn(), 0),
- Math.max(lastPosition.getRow(), 0)));
- }
- }
- virtualScreen.setMinimumSize(minimumTerminalSize);
- super.updateScreen();
- }
-
- @Override
- protected synchronized KeyStroke readKeyStroke() throws IOException {
- KeyStroke keyStroke = super.pollInput();
- if(eofWhenNoWindows && keyStroke == null && windows.isEmpty()) {
- return new KeyStroke(KeyType.EOF);
- }
- else if(keyStroke != null) {
- return keyStroke;
- }
- else {
- return super.readKeyStroke();
- }
- }
-
- @Override
- protected synchronized void drawGUI(TextGUIGraphics graphics) {
- backgroundPane.draw(graphics);
- getWindowManager().prepareWindows(this, Collections.unmodifiableList(windows), graphics.getSize());
- for(Window window: windows) {
- if (window.isVisible()) {
- // First draw windows to a buffer, then copy it to the real destination. This is to make physical off-screen
- // drawing work better. Store the buffers in a cache so we don't have to re-create them every time.
- TextImage textImage = windowRenderBufferCache.get(window);
- if (textImage == null || !textImage.getSize().equals(window.getDecoratedSize())) {
- textImage = new BasicTextImage(window.getDecoratedSize());
- windowRenderBufferCache.put(window, textImage);
- }
- TextGUIGraphics windowGraphics = new TextGUIGraphics(this, textImage.newTextGraphics(), graphics.getTheme());
-
- TerminalPosition contentOffset = TerminalPosition.TOP_LEFT_CORNER;
- if (!window.getHints().contains(Window.Hint.NO_DECORATIONS)) {
- WindowDecorationRenderer decorationRenderer = getWindowManager().getWindowDecorationRenderer(window);
- windowGraphics = decorationRenderer.draw(this, windowGraphics, window);
- contentOffset = decorationRenderer.getOffset(window);
- }
-
- window.draw(windowGraphics);
- window.setContentOffset(contentOffset);
- Borders.joinLinesWithFrame(windowGraphics);
-
- graphics.drawImage(window.getPosition(), textImage);
-
- if (postRenderer != null && !window.getHints().contains(Window.Hint.NO_POST_RENDERING)) {
- postRenderer.postRender(graphics, this, window);
- }
- }
- }
-
- // Purge the render buffer cache from windows that have been removed
- windowRenderBufferCache.keySet().retainAll(windows);
- }
-
- @Override
- public synchronized TerminalPosition getCursorPosition() {
- Window activeWindow = getActiveWindow();
- if(activeWindow != null) {
- return activeWindow.toGlobal(activeWindow.getCursorPosition());
- }
- else {
- return backgroundPane.getCursorPosition();
- }
- }
-
- /**
- * Sets whether the TextGUI should return EOF when you try to read input while there are no windows in the window
- * manager. Setting this to true (on by default) will make the GUI automatically exit when the last window has been
- * closed.
- * @param eofWhenNoWindows Should the GUI return EOF when there are no windows left
- */
- public void setEOFWhenNoWindows(boolean eofWhenNoWindows) {
- this.eofWhenNoWindows = eofWhenNoWindows;
- }
-
- /**
- * Returns whether the TextGUI should return EOF when you try to read input while there are no windows in the window
- * manager. When this is true (true by default) will make the GUI automatically exit when the last window has been
- * closed.
- * @return Should the GUI return EOF when there are no windows left
- */
- public boolean isEOFWhenNoWindows() {
- return eofWhenNoWindows;
- }
-
- @Override
- public synchronized Interactable getFocusedInteractable() {
- Window activeWindow = getActiveWindow();
- if(activeWindow != null) {
- return activeWindow.getFocusedInteractable();
- }
- else {
- return backgroundPane.getFocusedInteractable();
- }
- }
-
- @Override
- public synchronized boolean handleInput(KeyStroke keyStroke) {
- Window activeWindow = getActiveWindow();
- if(activeWindow != null) {
- return activeWindow.handleInput(keyStroke);
- }
- else {
- return backgroundPane.handleInput(keyStroke);
- }
- }
-
- @Override
- public WindowManager getWindowManager() {
- return windowManager;
- }
-
- @Override
- public synchronized WindowBasedTextGUI addWindow(Window window) {
- //To protect against NPE if the user forgot to set a content component
- if(window.getComponent() == null) {
- window.setComponent(new EmptySpace(TerminalSize.ONE));
- }
-
- if(window.getTextGUI() != null) {
- window.getTextGUI().removeWindow(window);
- }
- window.setTextGUI(this);
- windowManager.onAdded(this, window, windows);
- if(!windows.contains(window)) {
- windows.add(window);
- }
- if(!window.getHints().contains(Window.Hint.NO_FOCUS)) {
- setActiveWindow(window);
- }
- invalidate();
- return this;
- }
-
- @Override
- public WindowBasedTextGUI addWindowAndWait(Window window) {
- addWindow(window);
- window.waitUntilClosed();
- return this;
- }
-
- @Override
- public synchronized WindowBasedTextGUI removeWindow(Window window) {
- if(!windows.remove(window)) {
- //Didn't contain this window
- return this;
- }
- window.setTextGUI(null);
- windowManager.onRemoved(this, window, windows);
- if(activeWindow == window) {
- //Go backward in reverse and find the first suitable window
- for(int index = windows.size() - 1; index >= 0; index--) {
- Window candidate = windows.get(index);
- if(!candidate.getHints().contains(Window.Hint.NO_FOCUS)) {
- setActiveWindow(candidate);
- break;
- }
- }
- }
- invalidate();
- return this;
- }
-
- @Override
- public void waitForWindowToClose(Window window) {
- while(window.getTextGUI() != null) {
- boolean sleep = true;
- TextGUIThread guiThread = getGUIThread();
- if(Thread.currentThread() == guiThread.getThread()) {
- try {
- sleep = !guiThread.processEventsAndUpdate();
- }
- catch(EOFException ignore) {
- //The GUI has closed so allow exit
- break;
- }
- catch(IOException e) {
- throw new RuntimeException("Unexpected IOException while waiting for window to close", e);
- }
- }
- if(sleep) {
- try {
- Thread.sleep(1);
- }
- catch(InterruptedException ignore) {}
- }
- }
- }
-
- @Override
- public synchronized Collection<Window> getWindows() {
- return Collections.unmodifiableList(new ArrayList<Window>(windows));
- }
-
- @Override
- public synchronized MultiWindowTextGUI setActiveWindow(Window activeWindow) {
- this.activeWindow = activeWindow;
- return this;
- }
-
- @Override
- public synchronized Window getActiveWindow() {
- return activeWindow;
- }
-
- @Override
- public BasePane getBackgroundPane() {
- return backgroundPane;
- }
-
- @Override
- public Screen getScreen() {
- return virtualScreen;
- }
-
- @Override
- public WindowPostRenderer getWindowPostRenderer() {
- return postRenderer;
- }
-
- @Override
- public synchronized WindowBasedTextGUI moveToTop(Window window) {
- if(!windows.contains(window)) {
- throw new IllegalArgumentException("Window " + window + " isn't in MultiWindowTextGUI " + this);
- }
- windows.remove(window);
- windows.add(window);
- invalidate();
- return this;
- }
-
- /**
- * Switches the active window by cyclically shuffling the window list. If {@code reverse} parameter is {@code false}
- * then the current top window is placed at the bottom of the stack and the window immediately behind it is the new
- * top. If {@code reverse} is set to {@code true} then the window at the bottom of the stack is moved up to the
- * front and the previous top window will be immediately below it
- * @param reverse Direction to cycle through the windows
- * @return Itself
- */
- public synchronized WindowBasedTextGUI cycleActiveWindow(boolean reverse) {
- if(windows.isEmpty() || windows.size() == 1 || activeWindow.getHints().contains(Window.Hint.MODAL)) {
- return this;
- }
- Window originalActiveWindow = activeWindow;
- Window nextWindow = getNextWindow(reverse, originalActiveWindow);
- while(nextWindow.getHints().contains(Window.Hint.NO_FOCUS)) {
- nextWindow = getNextWindow(reverse, nextWindow);
- if(nextWindow == originalActiveWindow) {
- return this;
- }
- }
-
- if(reverse) {
- moveToTop(nextWindow);
- }
- else {
- windows.remove(originalActiveWindow);
- windows.add(0, originalActiveWindow);
- }
- setActiveWindow(nextWindow);
- return this;
- }
-
- private Window getNextWindow(boolean reverse, Window window) {
- int index = windows.indexOf(window);
- if(reverse) {
- if(++index >= windows.size()) {
- index = 0;
- }
- }
- else {
- if(--index < 0) {
- index = windows.size() - 1;
- }
- }
- return windows.get(index);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyStroke;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This class is the basic building block for creating user interfaces, being the standard implementation of
- * {@code Container} that supports multiple children. A {@code Panel} is a component that can contain one or more
- * other components, including nested panels. The panel itself doesn't have any particular appearance and isn't
- * interactable by itself, although you can set a border for the panel and interactable components inside the panel will
- * receive input focus as expected.
- *
- * @author Martin
- */
-public class Panel extends AbstractComponent<Panel> implements Container {
- private final List<Component> components;
- private LayoutManager layoutManager;
- private TerminalSize cachedPreferredSize;
-
- /**
- * Default constructor, creates a new panel with no child components and by default set to a vertical
- * {@code LinearLayout} layout manager.
- */
- public Panel() {
- components = new ArrayList<Component>();
- layoutManager = new LinearLayout();
- cachedPreferredSize = null;
- }
-
- /**
- * Adds a new child component to the panel. Where within the panel the child will be displayed is up to the layout
- * manager assigned to this panel.
- * @param component Child component to add to this panel
- * @return Itself
- */
- public synchronized Panel addComponent(Component component) {
- if(component == null) {
- throw new IllegalArgumentException("Cannot add null component");
- }
- if(components.contains(component)) {
- return this;
- }
- components.add(component);
- component.onAdded(this);
- invalidate();
- return this;
- }
-
- /**
- * This method is a shortcut for calling:
- * <pre>
- * {@code
- * component.setLayoutData(layoutData);
- * panel.addComponent(component);
- * }
- * </pre>
- * @param component Component to add to the panel
- * @param layoutData Layout data to assign to the component
- * @return Itself
- */
- public Panel addComponent(Component component, LayoutData layoutData) {
- if(component != null) {
- component.setLayoutData(layoutData);
- addComponent(component);
- }
- return this;
- }
-
- @Override
- public boolean containsComponent(Component component) {
- return component != null && component.hasParent(this);
- }
-
- @Override
- public synchronized boolean removeComponent(Component component) {
- if(component == null) {
- throw new IllegalArgumentException("Cannot remove null component");
- }
- int index = components.indexOf(component);
- if(index == -1) {
- return false;
- }
- if(getBasePane() != null && getBasePane().getFocusedInteractable() == component) {
- getBasePane().setFocusedInteractable(null);
- }
- components.remove(index);
- component.onRemoved(this);
- invalidate();
- return true;
- }
-
- /**
- * Removes all child components from this panel
- * @return Itself
- */
- public synchronized Panel removeAllComponents() {
- for(Component component: new ArrayList<Component>(components)) {
- removeComponent(component);
- }
- return this;
- }
-
- /**
- * Assigns a new layout manager to this panel, replacing the previous layout manager assigned. Please note that if
- * the panel is not empty at the time you assign a new layout manager, the existing components might not show up
- * where you expect them and their layout data property might need to be re-assigned.
- * @param layoutManager New layout manager this panel should be using
- * @return Itself
- */
- public synchronized Panel setLayoutManager(LayoutManager layoutManager) {
- if(layoutManager == null) {
- layoutManager = new AbsoluteLayout();
- }
- this.layoutManager = layoutManager;
- invalidate();
- return this;
- }
-
- /**
- * Returns the layout manager assigned to this panel
- * @return
- */
- public LayoutManager getLayoutManager() {
- return layoutManager;
- }
-
- @Override
- public int getChildCount() {
- synchronized(components) {
- return components.size();
- }
- }
-
- @Override
- public Collection<Component> getChildren() {
- synchronized(components) {
- return new ArrayList<Component>(components);
- }
- }
-
- @Override
- protected ComponentRenderer<Panel> createDefaultRenderer() {
- return new ComponentRenderer<Panel>() {
-
- @Override
- public TerminalSize getPreferredSize(Panel component) {
- cachedPreferredSize = layoutManager.getPreferredSize(components);
- return cachedPreferredSize;
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, Panel component) {
- if(isInvalid()) {
- layout(graphics.getSize());
- }
- for(Component child: components) {
- TextGUIGraphics componentGraphics = graphics.newTextGraphics(child.getPosition(), child.getSize());
- child.draw(componentGraphics);
- }
- }
- };
- }
-
- @Override
- public TerminalSize calculatePreferredSize() {
- if(cachedPreferredSize != null && !isInvalid()) {
- return cachedPreferredSize;
- }
- return super.calculatePreferredSize();
- }
-
- @Override
- public boolean isInvalid() {
- for(Component component: components) {
- if(component.isInvalid()) {
- return true;
- }
- }
- return super.isInvalid() || layoutManager.hasChanged();
- }
-
- @Override
- public Interactable nextFocus(Interactable fromThis) {
- boolean chooseNextAvailable = (fromThis == null);
-
- for (Component component : components) {
- if (chooseNextAvailable) {
- if (component instanceof Interactable) {
- return (Interactable) component;
- }
- else if (component instanceof Container) {
- Interactable firstInteractable = ((Container)(component)).nextFocus(null);
- if (firstInteractable != null) {
- return firstInteractable;
- }
- }
- continue;
- }
-
- if (component == fromThis) {
- chooseNextAvailable = true;
- continue;
- }
-
- if (component instanceof Container) {
- Container container = (Container) component;
- if (fromThis.isInside(container)) {
- Interactable next = container.nextFocus(fromThis);
- if (next == null) {
- chooseNextAvailable = true;
- } else {
- return next;
- }
- }
- }
- }
- return null;
- }
-
- @Override
- public Interactable previousFocus(Interactable fromThis) {
- boolean chooseNextAvailable = (fromThis == null);
-
- List<Component> revComponents = new ArrayList<Component>(components);
- Collections.reverse(revComponents);
-
- for (Component component : revComponents) {
- if (chooseNextAvailable) {
- if (component instanceof Interactable) {
- return (Interactable) component;
- }
- if (component instanceof Container) {
- Interactable lastInteractable = ((Container)(component)).previousFocus(null);
- if (lastInteractable != null) {
- return lastInteractable;
- }
- }
- continue;
- }
-
- if (component == fromThis) {
- chooseNextAvailable = true;
- continue;
- }
-
- if (component instanceof Container) {
- Container container = (Container) component;
- if (fromThis.isInside(container)) {
- Interactable next = container.previousFocus(fromThis);
- if (next == null) {
- chooseNextAvailable = true;
- } else {
- return next;
- }
- }
- }
- }
- return null;
- }
-
- @Override
- public boolean handleInput(KeyStroke key) {
- return false;
- }
-
- @Override
- public void updateLookupMap(InteractableLookupMap interactableLookupMap) {
- for(Component component: components) {
- if(component instanceof Container) {
- ((Container)component).updateLookupMap(interactableLookupMap);
- }
- else if(component instanceof Interactable) {
- interactableLookupMap.add((Interactable)component);
- }
- }
- }
-
- @Override
- public void invalidate() {
- super.invalidate();
-
- //Propagate
- for(Component component: components) {
- component.invalidate();
- }
- }
-
- private void layout(TerminalSize size) {
- layoutManager.doLayout(size, components);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-/**
- * Utility class for quickly bunching up components in a panel, arranged in a particular pattern
- * @author Martin
- */
-public class Panels {
-
- /**
- * Creates a new {@code Panel} with a {@code LinearLayout} layout manager in horizontal mode and adds all the
- * components passed in
- * @param components Components to be added to the new {@code Panel}, in order
- * @return The new {@code Panel}
- */
- public static Panel horizontal(Component... components) {
- Panel panel = new Panel();
- panel.setLayoutManager(new LinearLayout(Direction.HORIZONTAL));
- for(Component component: components) {
- panel.addComponent(component);
- }
- return panel;
- }
-
- /**
- * Creates a new {@code Panel} with a {@code LinearLayout} layout manager in vertical mode and adds all the
- * components passed in
- * @param components Components to be added to the new {@code Panel}, in order
- * @return The new {@code Panel}
- */
- public static Panel vertical(Component... components) {
- Panel panel = new Panel();
- panel.setLayoutManager(new LinearLayout(Direction.VERTICAL));
- for(Component component: components) {
- panel.addComponent(component);
- }
- return panel;
- }
-
- /**
- * Creates a new {@code Panel} with a {@code GridLayout} layout manager and adds all the components passed in
- * @param columns Number of columns in the grid
- * @param components Components to be added to the new {@code Panel}, in order
- * @return The new {@code Panel}
- */
- public static Panel grid(int columns, Component... components) {
- Panel panel = new Panel();
- panel.setLayoutManager(new GridLayout(columns));
- for(Component component: components) {
- panel.addComponent(component);
- }
- return panel;
- }
-
- //Cannot instantiate
- private Panels() {}
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * The list box will display a number of items, of which one and only one can be marked as selected.
- * The user can select an item in the list box by pressing the return key or space bar key. If you
- * select one item when another item is already selected, the previously selected item will be
- * deselected and the highlighted item will be the selected one instead.
- * @author Martin
- */
-public class RadioBoxList<V> extends AbstractListBox<V, RadioBoxList<V>> {
- /**
- * Listener interface that can be attached to the {@code RadioBoxList} in order to be notified on user actions
- */
- public interface Listener {
- /**
- * Called by the {@code RadioBoxList} when the user changes which item is selected
- * @param selectedIndex Index of the newly selected item, or -1 if the selection has been cleared (can only be
- * done programmatically)
- * @param previousSelection The index of the previously selected item which is now no longer selected, or -1 if
- * nothing was previously selected
- */
- void onSelectionChanged(int selectedIndex, int previousSelection);
- }
-
- private final List<Listener> listeners;
- private int checkedIndex;
-
- /**
- * Creates a new RadioCheckBoxList with no items. The size of the {@code RadioBoxList} will be as big as is required
- * to display all items.
- */
- public RadioBoxList() {
- this(null);
- }
-
- /**
- * Creates a new RadioCheckBoxList with a specified size. If the items in the {@code RadioBoxList} cannot fit in the
- * size specified, scrollbars will be used
- * @param preferredSize Size of the {@code RadioBoxList} or {@code null} to have it try to be as big as necessary to
- * be able to draw all items
- */
- public RadioBoxList(TerminalSize preferredSize) {
- super(preferredSize);
- this.listeners = new CopyOnWriteArrayList<Listener>();
- this.checkedIndex = -1;
- }
-
- @Override
- protected ListItemRenderer<V,RadioBoxList<V>> createDefaultListItemRenderer() {
- return new RadioBoxListItemRenderer<V>();
- }
-
- @Override
- public synchronized Result handleKeyStroke(KeyStroke keyStroke) {
- if(keyStroke.getKeyType() == KeyType.Enter ||
- (keyStroke.getKeyType() == KeyType.Character && keyStroke.getCharacter() == ' ')) {
- checkedIndex = getSelectedIndex();
- invalidate();
- return Result.HANDLED;
- }
- return super.handleKeyStroke(keyStroke);
- }
-
- @Override
- public synchronized RadioBoxList<V> clearItems() {
- setCheckedIndex(-1);
- return super.clearItems();
- }
-
- /**
- * This method will see if an object is the currently selected item in this RadioCheckBoxList
- * @param object Object to test if it's the selected one
- * @return {@code true} if the supplied object is what's currently selected in the list box,
- * {@code false} otherwise. Returns null if the supplied object is not an item in the list box.
- */
- public synchronized Boolean isChecked(V object) {
- if(object == null)
- return null;
-
- if(indexOf(object) == -1)
- return null;
-
- return checkedIndex == indexOf(object);
- }
-
- /**
- * This method will see if an item, addressed by index, is the currently selected item in this
- * RadioCheckBoxList
- * @param index Index of the item to check if it's currently selected
- * @return {@code true} if the currently selected object is at the supplied index,
- * {@code false} otherwise. Returns false if the index is out of range.
- */
- @SuppressWarnings("SimplifiableIfStatement")
- public synchronized boolean isChecked(int index) {
- if(index < 0 || index >= getItemCount()) {
- return false;
- }
-
- return checkedIndex == index;
- }
-
- /**
- * Sets the currently checked item by the value itself. If null, the selection is cleared. When changing selection,
- * any previously selected item is deselected.
- * @param item Item to be checked
- */
- public synchronized void setCheckedItem(V item) {
- if(item == null) {
- setCheckedIndex(-1);
- }
- else {
- setCheckedItemIndex(indexOf(item));
- }
- }
-
- /**
- * Sets the currently selected item by index. If the index is out of range, it does nothing.
- * @param index Index of the item to be selected
- */
- public synchronized void setCheckedItemIndex(int index) {
- if(index < -1 || index >= getItemCount())
- return;
-
- setCheckedIndex(index);
- }
-
- /**
- * @return The index of the item which is currently selected, or -1 if there is no selection
- */
- public int getCheckedItemIndex() {
- return checkedIndex;
- }
-
- /**
- * @return The object currently selected, or null if there is no selection
- */
- public synchronized V getCheckedItem() {
- if(checkedIndex == -1 || checkedIndex >= getItemCount())
- return null;
-
- return getItemAt(checkedIndex);
- }
-
- /**
- * Un-checks the currently checked item (if any) and leaves the radio check box in a state where no item is checked.
- */
- public synchronized void clearSelection() {
- setCheckedIndex(-1);
- }
-
- /**
- * Adds a new listener to the {@code RadioBoxList} that will be called on certain user actions
- * @param listener Listener to attach to this {@code RadioBoxList}
- * @return Itself
- */
- public RadioBoxList<V> addListener(Listener listener) {
- if(listener != null && !listeners.contains(listener)) {
- listeners.add(listener);
- }
- return this;
- }
-
- /**
- * Removes a listener from this {@code RadioBoxList} so that if it had been added earlier, it will no longer be
- * called on user actions
- * @param listener Listener to remove from this {@code RadioBoxList}
- * @return Itself
- */
- public RadioBoxList<V> removeListener(Listener listener) {
- listeners.remove(listener);
- return this;
- }
-
- private void setCheckedIndex(int index) {
- final int previouslyChecked = checkedIndex;
- this.checkedIndex = index;
- invalidate();
- runOnGUIThreadIfExistsOtherwiseRunDirect(new Runnable() {
- @Override
- public void run() {
- for(Listener listener: listeners) {
- listener.onSelectionChanged(-1, previouslyChecked);
- }
- }
- });
- }
-
- /**
- * Default renderer for this component which is used unless overridden. The selected state is drawn on the left side
- * of the item label using a "< >" block filled with an "o" if the item is the selected one
- * @param <V>
- */
- public static class RadioBoxListItemRenderer<V> extends ListItemRenderer<V,RadioBoxList<V>> {
- @Override
- public int getHotSpotPositionOnLine(int selectedIndex) {
- return 1;
- }
-
- @Override
- public String getLabel(RadioBoxList<V> listBox, int index, V item) {
- String check = " ";
- if(listBox.checkedIndex == index)
- check = "o";
-
- String text = (item != null ? item : "<null>").toString();
- return "<" + check + "> " + text;
- }
- }
-
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-/**
- * This {@link TextGUIThread} implementation is assuming the GUI event thread will be the same as the thread that
- * creates the {@link TextGUI} objects. This means on the thread you create the GUI on, when you are done you pass over
- * control to lanterna and let it manage the GUI for you. When the GUI is done, you'll get back control again over the
- * thread. This is different from {@code SeparateTextGUIThread} which spawns a new thread that manages the GUI and
- * leaves the current thread for you to handle.<p>
- * Here are two examples of how to use {@code SameTextGUIThread}:
- * <pre>
- * {@code
- * MultiWindowTextGUI textGUI = new MultiWindowTextGUI(new SameTextGUIThread.Factory(), screen);
- * // ... add components ...
- * while(weWantToContinueRunningTheGUI) {
- * if(!textGUI.getGUIThread().processEventsAndUpdate()) {
- * Thread.sleep(1);
- * }
- * }
- * // ... tear down ...
- * }
- * </pre>
- * In the example above, we use very precise control over events processing and when to update the GUI. In the example
- * below we pass some of that control over to Lanterna, since the thread won't resume until the window is closed.
- * <pre>
- * {@code
- * MultiWindowTextGUI textGUI = new MultiWindowTextGUI(new SameTextGUIThread.Factory(), screen);
- * Window window = new MyWindow();
- * textGUI.addWindowAndWait(window); // This call will run the event/update loop and won't return until "window" is closed
- * // ... tear down ...
- * }
- * </pre>
- * @see SeparateTextGUIThread
- * @see TextGUIThread
- */
-public class SameTextGUIThread extends AbstractTextGUIThread {
-
- private final Thread guiThread;
-
- private SameTextGUIThread(TextGUI textGUI) {
- super(textGUI);
- guiThread = Thread.currentThread();
- }
-
- @Override
- public Thread getThread() {
- return guiThread;
- }
-
- @Override
- public void invokeAndWait(Runnable runnable) throws IllegalStateException, InterruptedException {
- if(guiThread == null || guiThread == Thread.currentThread()) {
- runnable.run();
- }
- super.invokeAndWait(runnable);
- }
-
- /**
- * Default factory class for {@code SameTextGUIThread}, you need to pass this to the {@code TextGUI} constructor if
- * you want it to use this class
- */
- public static class Factory implements TextGUIThreadFactory {
- @Override
- public TextGUIThread createTextGUIThread(TextGUI textGUI) {
- return new SameTextGUIThread(textGUI);
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.Symbols;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.ThemeDefinition;
-
-/**
- * Classic scrollbar that can be used to display where inside a larger component a view is showing. This implementation
- * is not interactable and needs to be driven externally, meaning you can't focus on the scrollbar itself, you have to
- * update its state as part of another component being modified. {@code ScrollBar}s are either horizontal or vertical,
- * which affects the way they appear and how they are drawn.
- * <p>
- * This class works on two concepts, the min-position-max values and the view size. The minimum value is always 0 and
- * cannot be changed. The maximum value is 100 and can be adjusted programmatically. Position value is whever along the
- * axis of 0 to max the scrollbar's tracker currently is placed. The view size is an important concept, it determines
- * how big the tracker should be and limits the position so that it can only reach {@code maximum value - view size}.
- * <p>
- * The regular way to use the {@code ScrollBar} class is to tie it to the model-view of another component and set the
- * scrollbar's maximum to the total height (or width, if the scrollbar is horizontal) of the model-view. View size
- * should then be assigned based on the current size of the view, meaning as the terminal and/or the GUI changes and the
- * components visible space changes, the scrollbar's view size is updated along with it. Finally the position of the
- * scrollbar should be equal to the scroll offset in the component.
- *
- * @author Martin
- */
-public class ScrollBar extends AbstractComponent<ScrollBar> {
-
- private final Direction direction;
- private int maximum;
- private int position;
- private int viewSize;
-
- /**
- * Creates a new {@code ScrollBar} with a specified direction
- * @param direction Direction of the scrollbar
- */
- public ScrollBar(Direction direction) {
- this.direction = direction;
- this.maximum = 100;
- this.position = 0;
- this.viewSize = 0;
- }
-
- /**
- * Returns the direction of this {@code ScrollBar}
- * @return Direction of this {@code ScrollBar}
- */
- public Direction getDirection() {
- return direction;
- }
-
- /**
- * Sets the maximum value the scrollbar's position (minus the view size) can have
- * @param maximum Maximum value
- * @return Itself
- */
- public ScrollBar setScrollMaximum(int maximum) {
- if(maximum < 0) {
- throw new IllegalArgumentException("Cannot set ScrollBar maximum to " + maximum);
- }
- this.maximum = maximum;
- invalidate();
- return this;
- }
-
- /**
- * Returns the maximum scroll value
- * @return Maximum scroll value
- */
- public int getScrollMaximum() {
- return maximum;
- }
-
-
- /**
- * Sets the scrollbar's position, should be a value between 0 and {@code maximum - view size}
- * @param position Scrollbar's tracker's position
- * @return Itself
- */
- public ScrollBar setScrollPosition(int position) {
- this.position = Math.min(position, this.maximum);
- invalidate();
- return this;
- }
-
- /**
- * Returns the position of the {@code ScrollBar}'s tracker
- * @return Position of the {@code ScrollBar}'s tracker
- */
- public int getScrollPosition() {
- return position;
- }
-
- /**
- * Sets the view size of the scrollbar, determining how big the scrollbar's tracker should be and also affecting the
- * maximum value of tracker's position
- * @param viewSize View size of the scrollbar
- * @return Itself
- */
- public ScrollBar setViewSize(int viewSize) {
- this.viewSize = viewSize;
- return this;
- }
-
- /**
- * Returns the view size of the scrollbar
- * @return View size of the scrollbar
- */
- public int getViewSize() {
- if(viewSize > 0) {
- return viewSize;
- }
- if(direction == Direction.HORIZONTAL) {
- return getSize().getColumns();
- }
- else {
- return getSize().getRows();
- }
- }
-
- @Override
- protected ComponentRenderer<ScrollBar> createDefaultRenderer() {
- return new DefaultScrollBarRenderer();
- }
-
- /**
- * Helper class for making new {@code ScrollBar} renderers a little bit cleaner
- */
- public static abstract class ScrollBarRenderer implements ComponentRenderer<ScrollBar> {
- @Override
- public TerminalSize getPreferredSize(ScrollBar component) {
- return TerminalSize.ONE;
- }
- }
-
- /**
- * Default renderer for {@code ScrollBar} which will be used unless overridden. This will draw a scrollbar using
- * arrows at each extreme end, a background color for spaces between those arrows and the tracker and then the
- * tracker itself in three different styles depending on the size of the tracker. All characters and colors are
- * customizable through whatever theme is currently in use.
- */
- public static class DefaultScrollBarRenderer extends ScrollBarRenderer {
-
- private boolean growScrollTracker;
-
- /**
- * Default constructor
- */
- public DefaultScrollBarRenderer() {
- this.growScrollTracker = true;
- }
-
- /**
- * Should tracker automatically grow in size along with the {@code ScrollBar} (default: {@code true})
- * @param growScrollTracker Automatically grow tracker
- */
- public void setGrowScrollTracker(boolean growScrollTracker) {
- this.growScrollTracker = growScrollTracker;
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, ScrollBar component) {
- TerminalSize size = graphics.getSize();
- Direction direction = component.getDirection();
- int position = component.getScrollPosition();
- int maximum = component.getScrollMaximum();
- int viewSize = component.getViewSize();
-
- if(size.getRows() == 0 || size.getColumns() == 0) {
- return;
- }
-
- //Adjust position if necessary
- if(position + viewSize >= maximum) {
- position = Math.max(0, maximum - viewSize);
- component.setScrollPosition(position);
- }
-
- ThemeDefinition themeDefinition = graphics.getThemeDefinition(ScrollBar.class);
- graphics.applyThemeStyle(themeDefinition.getNormal());
-
- if(direction == Direction.VERTICAL) {
- if(size.getRows() == 1) {
- graphics.setCharacter(0, 0, themeDefinition.getCharacter("VERTICAL_BACKGROUND", Symbols.BLOCK_MIDDLE));
- }
- else if(size.getRows() == 2) {
- graphics.setCharacter(0, 0, themeDefinition.getCharacter("UP_ARROW", Symbols.ARROW_UP));
- graphics.setCharacter(0, 1, themeDefinition.getCharacter("DOWN_ARROW", Symbols.ARROW_DOWN));
- }
- else {
- int scrollableArea = size.getRows() - 2;
- int scrollTrackerSize = 1;
- if(growScrollTracker) {
- float ratio = clampRatio((float) viewSize / (float) maximum);
- scrollTrackerSize = Math.max(1, (int) (ratio * (float) scrollableArea));
- }
-
- float ratio = clampRatio((float)position / (float)(maximum - viewSize));
- int scrollTrackerPosition = (int)(ratio * (float)(scrollableArea - scrollTrackerSize)) + 1;
-
- graphics.setCharacter(0, 0, themeDefinition.getCharacter("UP_ARROW", Symbols.ARROW_UP));
- graphics.drawLine(0, 1, 0, size.getRows() - 2, themeDefinition.getCharacter("VERTICAL_BACKGROUND", Symbols.BLOCK_MIDDLE));
- graphics.setCharacter(0, size.getRows() - 1, themeDefinition.getCharacter("DOWN_ARROW", Symbols.ARROW_DOWN));
- if(scrollTrackerSize == 1) {
- graphics.setCharacter(0, scrollTrackerPosition, themeDefinition.getCharacter("VERTICAL_SMALL_TRACKER", Symbols.SOLID_SQUARE_SMALL));
- }
- else if(scrollTrackerSize == 2) {
- graphics.setCharacter(0, scrollTrackerPosition, themeDefinition.getCharacter("VERTICAL_TRACKER_TOP", (char)0x28c));
- graphics.setCharacter(0, scrollTrackerPosition + 1, themeDefinition.getCharacter("VERTICAL_TRACKER_BOTTOM", 'v'));
- }
- else {
- graphics.setCharacter(0, scrollTrackerPosition, themeDefinition.getCharacter("VERTICAL_TRACKER_TOP", (char)0x28c));
- graphics.drawLine(0, scrollTrackerPosition + 1, 0, scrollTrackerPosition + scrollTrackerSize - 2, themeDefinition.getCharacter("VERTICAL_TRACKER_BACKGROUND", ' '));
- graphics.setCharacter(0, scrollTrackerPosition + (scrollTrackerSize / 2), themeDefinition.getCharacter("VERTICAL_SMALL_TRACKER", Symbols.SOLID_SQUARE_SMALL));
- graphics.setCharacter(0, scrollTrackerPosition + scrollTrackerSize - 1, themeDefinition.getCharacter("VERTICAL_TRACKER_BOTTOM", 'v'));
- }
- }
- }
- else {
- if(size.getColumns() == 1) {
- graphics.setCharacter(0, 0, themeDefinition.getCharacter("HORIZONTAL_BACKGROUND", Symbols.BLOCK_MIDDLE));
- }
- else if(size.getColumns() == 2) {
- graphics.setCharacter(0, 0, Symbols.ARROW_LEFT);
- graphics.setCharacter(1, 0, Symbols.ARROW_RIGHT);
- }
- else {
- int scrollableArea = size.getColumns() - 2;
- int scrollTrackerSize = 1;
- if(growScrollTracker) {
- float ratio = clampRatio((float) viewSize / (float) maximum);
- scrollTrackerSize = Math.max(1, (int) (ratio * (float) scrollableArea));
- }
-
- float ratio = clampRatio((float)position / (float)(maximum - viewSize));
- int scrollTrackerPosition = (int)(ratio * (float)(scrollableArea - scrollTrackerSize)) + 1;
-
- graphics.setCharacter(0, 0, themeDefinition.getCharacter("LEFT_ARROW", Symbols.ARROW_LEFT));
- graphics.drawLine(1, 0, size.getColumns() - 2, 0, themeDefinition.getCharacter("HORIZONTAL_BACKGROUND", Symbols.BLOCK_MIDDLE));
- graphics.setCharacter(size.getColumns() - 1, 0, themeDefinition.getCharacter("RIGHT_ARROW", Symbols.ARROW_RIGHT));
- if(scrollTrackerSize == 1) {
- graphics.setCharacter(scrollTrackerPosition, 0, themeDefinition.getCharacter("HORIZONTAL_SMALL_TRACKER", Symbols.SOLID_SQUARE_SMALL));
- }
- else if(scrollTrackerSize == 2) {
- graphics.setCharacter(scrollTrackerPosition, 0, themeDefinition.getCharacter("HORIZONTAL_TRACKER_LEFT", '<'));
- graphics.setCharacter(scrollTrackerPosition + 1, 0, themeDefinition.getCharacter("HORIZONTAL_TRACKER_RIGHT", '>'));
- }
- else {
- graphics.setCharacter(scrollTrackerPosition, 0, themeDefinition.getCharacter("HORIZONTAL_TRACKER_LEFT", '<'));
- graphics.drawLine(scrollTrackerPosition + 1, 0, scrollTrackerPosition + scrollTrackerSize - 2, 0, themeDefinition.getCharacter("HORIZONTAL_TRACKER_BACKGROUND", ' '));
- graphics.setCharacter(scrollTrackerPosition + (scrollTrackerSize / 2), 0, themeDefinition.getCharacter("HORIZONTAL_SMALL_TRACKER", Symbols.SOLID_SQUARE_SMALL));
- graphics.setCharacter(scrollTrackerPosition + scrollTrackerSize - 1, 0, themeDefinition.getCharacter("HORIZONTAL_TRACKER_RIGHT", '>'));
- }
- }
- }
- }
-
- private float clampRatio(float value) {
- if(value < 0.0f) {
- return 0.0f;
- }
- else if(value > 1.0f) {
- return 1.0f;
- }
- else {
- return value;
- }
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import java.io.EOFException;
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Default implementation of TextGUIThread, this class runs the GUI event processing on a dedicated thread. The GUI
- * needs to be explicitly started in order for the event processing loop to begin, so you must call {@code start()}
- * for this. The GUI thread will stop if {@code stop()} is called, the input stream returns EOF or an exception is
- * thrown from inside the event handling loop.
- * <p>
- * Here is an example of how to use this {@code TextGUIThread}:
- * <pre>
- * {@code
- * MultiWindowTextGUI textGUI = new MultiWindowTextGUI(new SeparateTextGUIThread.Factory(), screen);
- * // ... add components ...
- * ((AsynchronousTextGUIThread)textGUI.getGUIThread()).start();
- * // ... this thread will continue while the GUI runs on a separate thread ...
- * }
- * </pre>
- * @see TextGUIThread
- * @see SameTextGUIThread
- * @author Martin
- */
-public class SeparateTextGUIThread extends AbstractTextGUIThread implements AsynchronousTextGUIThread {
- private volatile State state;
- private final Thread textGUIThread;
- private final CountDownLatch waitLatch;
-
- private SeparateTextGUIThread(TextGUI textGUI) {
- super(textGUI);
- this.waitLatch = new CountDownLatch(1);
- this.textGUIThread = new Thread("LanternaGUI") {
- @Override
- public void run() {
- mainGUILoop();
- }
- };
- state = State.CREATED;
- }
-
- @Override
- public void start() {
- textGUIThread.start();
- state = State.STARTED;
- }
-
- @Override
- public void stop() {
- if(state != State.STARTED) {
- return;
- }
-
- state = State.STOPPING;
- }
-
- @Override
- public void waitForStop() throws InterruptedException {
- waitLatch.await();
- }
-
- @Override
- public State getState() {
- return state;
- }
-
- @Override
- public Thread getThread() {
- return textGUIThread;
- }
-
- @Override
- public void invokeLater(Runnable runnable) throws IllegalStateException {
- if(state != State.STARTED) {
- throw new IllegalStateException("Cannot schedule " + runnable + " for execution on the TextGUIThread " +
- "because the thread is in " + state + " state");
- }
- super.invokeLater(runnable);
- }
-
- private void mainGUILoop() {
- try {
- //Draw initial screen, after this only draw when the GUI is marked as invalid
- try {
- textGUI.updateScreen();
- }
- catch(IOException e) {
- exceptionHandler.onIOException(e);
- }
- catch(RuntimeException e) {
- exceptionHandler.onRuntimeException(e);
- }
- while(state == State.STARTED) {
- try {
- if (!processEventsAndUpdate()) {
- try {
- Thread.sleep(1);
- }
- catch(InterruptedException ignored) {}
- }
- }
- catch(EOFException e) {
- stop();
- break; //Break out quickly from the main loop
- }
- catch(IOException e) {
- if(exceptionHandler.onIOException(e)) {
- stop();
- break;
- }
- }
- catch(RuntimeException e) {
- if(exceptionHandler.onRuntimeException(e)) {
- stop();
- break;
- }
- }
- }
- }
- finally {
- state = State.STOPPED;
- waitLatch.countDown();
- }
- }
-
-
- /**
- * Factory class for creating SeparateTextGUIThread objects
- */
- public static class Factory implements TextGUIThreadFactory {
- @Override
- public TextGUIThread createTextGUIThread(TextGUI textGUI) {
- return new SeparateTextGUIThread(textGUI);
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.Symbols;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.ThemeDefinition;
-
-/**
- * Static non-interactive component that is typically rendered as a single line. Normally this component is used to
- * separate component from each other in situations where a bordered panel isn't ideal. By default the separator will
- * ask for a size of 1x1 so you'll need to make it bigger, either through the layout manager or by overriding the
- * preferred size.
- * @author Martin
- */
-public class Separator extends AbstractComponent<Separator> {
-
- private final Direction direction;
-
- /**
- * Creates a new {@code Separator} for a specific direction, which will decide whether to draw a horizontal line or
- * a vertical line
- *
- * @param direction Direction of the line to draw within the separator
- */
- public Separator(Direction direction) {
- if(direction == null) {
- throw new IllegalArgumentException("Cannot create a separator with a null direction");
- }
- this.direction = direction;
- }
-
- /**
- * Returns the direction of the line drawn for this separator
- * @return Direction of the line drawn for this separator
- */
- public Direction getDirection() {
- return direction;
- }
-
- @Override
- protected DefaultSeparatorRenderer createDefaultRenderer() {
- return new DefaultSeparatorRenderer();
- }
-
- /**
- * Helper interface that doesn't add any new methods but makes coding new button renderers a little bit more clear
- */
- public static abstract class SeparatorRenderer implements ComponentRenderer<Separator> {
- }
-
- /**
- * This is the default separator renderer that is used if you don't override anything. With this renderer, the
- * separator has a preferred size of one but will take up the whole area it is given and fill that space with either
- * horizontal or vertical lines, depending on the direction of the {@code Separator}
- */
- public static class DefaultSeparatorRenderer extends SeparatorRenderer {
- @Override
- public TerminalSize getPreferredSize(Separator component) {
- return TerminalSize.ONE;
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, Separator component) {
- ThemeDefinition themeDefinition = graphics.getThemeDefinition(Separator.class);
- graphics.applyThemeStyle(themeDefinition.getNormal());
- char character = themeDefinition.getCharacter(component.getDirection().name().toUpperCase(),
- component.getDirection() == Direction.HORIZONTAL ? Symbols.SINGLE_LINE_HORIZONTAL : Symbols.SINGLE_LINE_VERTICAL);
- graphics.fill(character);
- }
- }
-}
+++ /dev/null
- * [DONE] Label background color
- * [DONE] Editable TextArea
- * Overlapping windows - X-Y offset
- * [DONE] ListBox page up/page down
- * Menus
- * [DONE] TextBox fill character (Issue 66)
- * [DONE] Telnet support
- * [DONE] Proper Table class (+scroll)
- * [DONE] Manual setFocus() for components
- * [DONE] Resize terminal (http://invisible-island.net/xterm/ctlseqs/ctlseqs.html)
- * Tabbed panel
- * Render window title other place than left-aligned
-
-From Brad C. by email to lanterna-discuss on 2014-06-09:
- * [DONE] Border Colors - Would be nice if you could optionally override the Theme to include a Background and Foreground color on the various borders.
- * [DONE] Double Border - Much like the Standard border, but using ACS.DOUBLE_LINE.*
- * [DONE] Text Box (On Key Press) - Allow for an Override on Key Press of the Text Box Component. This would allow someone to apply a default action specific to that component. In this case, I wanted the ENTER key to perform an action.
- * [DONE] Wrapped Label - I developed a Label component that wraps according to the width of the parent container. This is similar to the logic you have for your label, other than I am doing some Regular Expression work to wrap at logical spaces. I believe yours simply cuts off the line and adds the periods (...)
- * Custom Theme Categories - As I am developing my own components, I found the need to utilize custom categories.. This quickly became problematic, and I was only able to solve it by creating an extended Theme class of my own. It works, but it would be nice if those of us who write components had a way of using something other than the Category Enumeration.
- * CommonProfile Accessor - We use a Terminal that is slightly customized (Anzio), so it would have been great if I could have extended CommonProfile (like you do for Putty) and implemented my own. Right now it's protected, so I am unable to do so..
- * Colors - I think there would be some value in having more control of the colors (foreground and background) for most of the components. Basically overriding the theme itself.
- * [DONE] ListView Component - I am working on this now, but I am looking at a component that resembles a fully featured ListView component. Much like a table, only the entire "Row" is a single interactable item.
\ No newline at end of file
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.input.KeyStroke;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Pattern;
-
-/**
- * This component keeps a text content that is editable by the user. A TextBox can be single line or multiline and lets
- * the user navigate the cursor in the text area by using the arrow keys, page up, page down, home and end. For
- * multi-line {@code TextBox}:es, scrollbars will be automatically displayed if needed.
- * <p>
- * Size-wise, a {@code TextBox} should be hard-coded to a particular size, it's not good at guessing how large it should
- * be. You can do this through the constructor.
- */
-public class TextBox extends AbstractInteractableComponent<TextBox> {
-
- /**
- * Enum value to force a {@code TextBox} to be either single line or multi line. This is usually auto-detected if
- * the text box has some initial content by scanning that content for \n characters.
- */
- public enum Style {
- /**
- * The {@code TextBox} contains a single line of text and is typically drawn on one row
- */
- SINGLE_LINE,
- /**
- * The {@code TextBox} contains a none, one or many lines of text and is normally drawn over multiple lines
- */
- MULTI_LINE,
- ;
- }
-
- private final List<String> lines;
- private final Style style;
-
- private TerminalPosition caretPosition;
- private boolean caretWarp;
- private boolean readOnly;
- private boolean horizontalFocusSwitching;
- private boolean verticalFocusSwitching;
- private int maxLineLength;
- private int longestRow;
- private char unusedSpaceCharacter;
- private Character mask;
- private Pattern validationPattern;
-
- /**
- * Default constructor, this creates a single-line {@code TextBox} of size 10 which is initially empty
- */
- public TextBox() {
- this(new TerminalSize(10, 1), "", Style.SINGLE_LINE);
- }
-
- /**
- * Constructor that creates a {@code TextBox} with an initial content and attempting to be big enough to display
- * the whole text at once without scrollbars
- * @param initialContent Initial content of the {@code TextBox}
- */
- public TextBox(String initialContent) {
- this(null, initialContent, initialContent.contains("\n") ? Style.MULTI_LINE : Style.SINGLE_LINE);
- }
-
- /**
- * Creates a {@code TextBox} that has an initial content and attempting to be big enough to display the whole text
- * at once without scrollbars.
- *
- * @param initialContent Initial content of the {@code TextBox}
- * @param style Forced style instead of auto-detecting
- */
- public TextBox(String initialContent, Style style) {
- this(null, initialContent, style);
- }
-
- /**
- * Creates a new empty {@code TextBox} with a specific size
- * @param preferredSize Size of the {@code TextBox}
- */
- public TextBox(TerminalSize preferredSize) {
- this(preferredSize, (preferredSize != null && preferredSize.getRows() > 1) ? Style.MULTI_LINE : Style.SINGLE_LINE);
- }
-
- /**
- * Creates a new empty {@code TextBox} with a specific size and style
- * @param preferredSize Size of the {@code TextBox}
- * @param style Style to use
- */
- public TextBox(TerminalSize preferredSize, Style style) {
- this(preferredSize, "", style);
- }
-
- /**
- * Creates a new empty {@code TextBox} with a specific size and initial content
- * @param preferredSize Size of the {@code TextBox}
- * @param initialContent Initial content of the {@code TextBox}
- */
- public TextBox(TerminalSize preferredSize, String initialContent) {
- this(preferredSize, initialContent, (preferredSize != null && preferredSize.getRows() > 1) || initialContent.contains("\n") ? Style.MULTI_LINE : Style.SINGLE_LINE);
- }
-
- /**
- * Main constructor of the {@code TextBox} which decides size, initial content and style
- * @param preferredSize Size of the {@code TextBox}
- * @param initialContent Initial content of the {@code TextBox}
- * @param style Style to use for this {@code TextBox}, instead of auto-detecting
- */
- public TextBox(TerminalSize preferredSize, String initialContent, Style style) {
- this.lines = new ArrayList<String>();
- this.style = style;
- this.readOnly = false;
- this.caretWarp = false;
- this.verticalFocusSwitching = true;
- this.horizontalFocusSwitching = (style == Style.SINGLE_LINE);
- this.caretPosition = TerminalPosition.TOP_LEFT_CORNER;
- this.maxLineLength = -1;
- this.longestRow = 1; //To fit the cursor
- this.unusedSpaceCharacter = ' ';
- this.mask = null;
- this.validationPattern = null;
- setText(initialContent);
- if (preferredSize == null) {
- preferredSize = new TerminalSize(Math.max(10, longestRow), lines.size());
- }
- setPreferredSize(preferredSize);
- }
-
- /**
- * Sets a pattern on which the content of the text box is to be validated. For multi-line TextBox:s, the pattern is
- * checked against each line individually, not the content as a whole. Partial matchings will not be allowed, the
- * whole pattern must match, however, empty lines will always be allowed. When the user tried to modify the content
- * of the TextBox in a way that does not match the pattern, the operation will be silently ignored. If you set this
- * pattern to {@code null}, all validation is turned off.
- * @param validationPattern Pattern to validate the lines in this TextBox against, or {@code null} to disable
- * @return itself
- */
- public synchronized TextBox setValidationPattern(Pattern validationPattern) {
- if(validationPattern != null) {
- for(String line: lines) {
- if(!validated(line)) {
- throw new IllegalStateException("TextBox validation pattern " + validationPattern + " does not match existing content");
- }
- }
- }
- this.validationPattern = validationPattern;
- return this;
- }
-
- /**
- * Updates the text content of the {@code TextBox} to the supplied string.
- * @param text New text to assign to the {@code TextBox}
- * @return Itself
- */
- public synchronized TextBox setText(String text) {
- String[] split = text.split("\n");
- lines.clear();
- longestRow = 1;
- for(String line : split) {
- addLine(line);
- }
- if(caretPosition.getRow() > lines.size() - 1) {
- caretPosition = caretPosition.withRow(lines.size() - 1);
- }
- if(caretPosition.getColumn() > lines.get(caretPosition.getRow()).length()) {
- caretPosition = caretPosition.withColumn(lines.get(caretPosition.getRow()).length());
- }
- invalidate();
- return this;
- }
-
- @Override
- public TextBoxRenderer getRenderer() {
- return (TextBoxRenderer)super.getRenderer();
- }
-
- /**
- * Adds a single line to the {@code TextBox} at the end, this only works when in multi-line mode
- * @param line Line to add at the end of the content in this {@code TextBox}
- * @return Itself
- */
- public synchronized TextBox addLine(String line) {
- StringBuilder bob = new StringBuilder();
- for(int i = 0; i < line.length(); i++) {
- char c = line.charAt(i);
- if(c == '\n' && style == Style.MULTI_LINE) {
- String string = bob.toString();
- int lineWidth = TerminalTextUtils.getColumnWidth(string);
- lines.add(string);
- if(longestRow < lineWidth + 1) {
- longestRow = lineWidth + 1;
- }
- addLine(line.substring(i + 1));
- return this;
- }
- else if(Character.isISOControl(c)) {
- continue;
- }
-
- bob.append(c);
- }
- String string = bob.toString();
- if(!validated(string)) {
- throw new IllegalStateException("TextBox validation pattern " + validationPattern + " does not match the supplied text");
- }
- int lineWidth = TerminalTextUtils.getColumnWidth(string);
- lines.add(string);
- if(longestRow < lineWidth + 1) {
- longestRow = lineWidth + 1;
- }
- invalidate();
- return this;
- }
-
- /**
- * Sets if the caret should jump to the beginning of the next line if right arrow is pressed while at the end of a
- * line. Similarly, pressing left arrow at the beginning of a line will make the caret jump to the end of the
- * previous line. This only makes sense for multi-line TextBox:es; for single-line ones it has no effect. By default
- * this is {@code false}.
- * @param caretWarp Whether the caret will warp at the beginning/end of lines
- * @return Itself
- */
- public TextBox setCaretWarp(boolean caretWarp) {
- this.caretWarp = caretWarp;
- return this;
- }
-
- /**
- * Checks whether caret warp mode is enabled or not. See {@code setCaretWarp} for more details.
- * @return {@code true} if caret warp mode is enabled
- */
- public boolean isCaretWarp() {
- return caretWarp;
- }
-
- /**
- * Returns the position of the caret, as a {@code TerminalPosition} where the row and columns equals the coordinates
- * in a multi-line {@code TextBox} and for single-line {@code TextBox} you can ignore the {@code row} component.
- * @return Position of the text input caret
- */
- public TerminalPosition getCaretPosition() {
- return caretPosition;
- }
-
- /**
- * Returns the text in this {@code TextBox}, for multi-line mode all lines will be concatenated together with \n as
- * separator.
- * @return The text inside this {@code TextBox}
- */
- public synchronized String getText() {
- StringBuilder bob = new StringBuilder(lines.get(0));
- for(int i = 1; i < lines.size(); i++) {
- bob.append("\n").append(lines.get(i));
- }
- return bob.toString();
- }
-
- /**
- * Helper method, it will return the content of the {@code TextBox} unless it's empty in which case it will return
- * the supplied default value
- * @param defaultValueIfEmpty Value to return if the {@code TextBox} is empty
- * @return Text in the {@code TextBox} or {@code defaultValueIfEmpty} is the {@code TextBox} is empty
- */
- public String getTextOrDefault(String defaultValueIfEmpty) {
- String text = getText();
- if(text.isEmpty()) {
- return defaultValueIfEmpty;
- }
- return text;
- }
-
- /**
- * Returns the current text mask, meaning the substitute to draw instead of the text inside the {@code TextBox}.
- * This is normally used for password input fields so the password isn't shown
- * @return Current text mask or {@code null} if there is no mask
- */
- public Character getMask() {
- return mask;
- }
-
- /**
- * Sets the current text mask, meaning the substitute to draw instead of the text inside the {@code TextBox}.
- * This is normally used for password input fields so the password isn't shown
- * @param mask New text mask or {@code null} if there is no mask
- * @return Itself
- */
- public TextBox setMask(Character mask) {
- if(mask != null && TerminalTextUtils.isCharCJK(mask)) {
- throw new IllegalArgumentException("Cannot use a CJK character as a mask");
- }
- this.mask = mask;
- invalidate();
- return this;
- }
-
- /**
- * Returns {@code true} if this {@code TextBox} is in read-only mode, meaning text input from the user through the
- * keyboard is prevented
- * @return {@code true} if this {@code TextBox} is in read-only mode
- */
- public boolean isReadOnly() {
- return readOnly;
- }
-
- /**
- * Sets the read-only mode of the {@code TextBox}, meaning text input from the user through the keyboard is
- * prevented. The user can still focus and scroll through the text in this mode.
- * @param readOnly If {@code true} then the {@code TextBox} will switch to read-only mode
- * @return Itself
- */
- public TextBox setReadOnly(boolean readOnly) {
- this.readOnly = readOnly;
- invalidate();
- return this;
- }
-
- /**
- * If {@code true}, the component will switch to the next available component above if the cursor is at the top of
- * the TextBox and the user presses the 'up' array key, or switch to the next available component below if the
- * cursor is at the bottom of the TextBox and the user presses the 'down' array key. The means that for single-line
- * TextBox:es, pressing up and down will always switch focus.
- * @return {@code true} if vertical focus switching is enabled
- */
- public boolean isVerticalFocusSwitching() {
- return verticalFocusSwitching;
- }
-
- /**
- * If set to {@code true}, the component will switch to the next available component above if the cursor is at the
- * top of the TextBox and the user presses the 'up' array key, or switch to the next available component below if
- * the cursor is at the bottom of the TextBox and the user presses the 'down' array key. The means that for
- * single-line TextBox:es, pressing up and down will always switch focus with this mode enabled.
- * @param verticalFocusSwitching If called with true, vertical focus switching will be enabled
- * @return Itself
- */
- public TextBox setVerticalFocusSwitching(boolean verticalFocusSwitching) {
- this.verticalFocusSwitching = verticalFocusSwitching;
- return this;
- }
-
- /**
- * If {@code true}, the TextBox will switch focus to the next available component to the left if the cursor in the
- * TextBox is at the left-most position (index 0) on the row and the user pressed the 'left' arrow key, or vice
- * versa for pressing the 'right' arrow key when the cursor in at the right-most position of the current row.
- * @return {@code true} if horizontal focus switching is enabled
- */
- public boolean isHorizontalFocusSwitching() {
- return horizontalFocusSwitching;
- }
-
- /**
- * If set to {@code true}, the TextBox will switch focus to the next available component to the left if the cursor
- * in the TextBox is at the left-most position (index 0) on the row and the user pressed the 'left' arrow key, or
- * vice versa for pressing the 'right' arrow key when the cursor in at the right-most position of the current row.
- * @param horizontalFocusSwitching If called with true, horizontal focus switching will be enabled
- * @return Itself
- */
- public TextBox setHorizontalFocusSwitching(boolean horizontalFocusSwitching) {
- this.horizontalFocusSwitching = horizontalFocusSwitching;
- return this;
- }
-
- /**
- * Returns the line on the specific row. For non-multiline TextBox:es, calling this with index set to 0 will return
- * the same as calling {@code getText()}. If the row index is invalid (less than zero or equals or larger than the
- * number of rows), this method will throw IndexOutOfBoundsException.
- * @param index
- * @return The line at the specified index, as a String
- * @throws IndexOutOfBoundsException if the row index is less than zero or too large
- */
- public synchronized String getLine(int index) {
- return lines.get(index);
- }
-
- /**
- * Returns the number of lines currently in this TextBox. For single-line TextBox:es, this will always return 1.
- * @return Number of lines of text currently in this TextBox
- */
- public synchronized int getLineCount() {
- return lines.size();
- }
-
- @Override
- protected TextBoxRenderer createDefaultRenderer() {
- return new DefaultTextBoxRenderer();
- }
-
- @Override
- public synchronized Result handleKeyStroke(KeyStroke keyStroke) {
- if(readOnly) {
- return handleKeyStrokeReadOnly(keyStroke);
- }
- String line = lines.get(caretPosition.getRow());
- switch(keyStroke.getKeyType()) {
- case Character:
- if(maxLineLength == -1 || maxLineLength > line.length() + 1) {
- line = line.substring(0, caretPosition.getColumn()) + keyStroke.getCharacter() + line.substring(caretPosition.getColumn());
- if(validated(line)) {
- lines.set(caretPosition.getRow(), line);
- caretPosition = caretPosition.withRelativeColumn(1);
- }
- }
- return Result.HANDLED;
- case Backspace:
- if(caretPosition.getColumn() > 0) {
- line = line.substring(0, caretPosition.getColumn() - 1) + line.substring(caretPosition.getColumn());
- if(validated(line)) {
- lines.set(caretPosition.getRow(), line);
- caretPosition = caretPosition.withRelativeColumn(-1);
- }
- }
- else if(style == Style.MULTI_LINE && caretPosition.getRow() > 0) {
- String concatenatedLines = lines.get(caretPosition.getRow() - 1) + line;
- if(validated(concatenatedLines)) {
- lines.remove(caretPosition.getRow());
- caretPosition = caretPosition.withRelativeRow(-1);
- caretPosition = caretPosition.withColumn(lines.get(caretPosition.getRow()).length());
- lines.set(caretPosition.getRow(), concatenatedLines);
- }
- }
- return Result.HANDLED;
- case Delete:
- if(caretPosition.getColumn() < line.length()) {
- line = line.substring(0, caretPosition.getColumn()) + line.substring(caretPosition.getColumn() + 1);
- if(validated(line)) {
- lines.set(caretPosition.getRow(), line);
- }
- }
- else if(style == Style.MULTI_LINE && caretPosition.getRow() < lines.size() - 1) {
- String concatenatedLines = line + lines.get(caretPosition.getRow() + 1);
- if(validated(concatenatedLines)) {
- lines.set(caretPosition.getRow(), concatenatedLines);
- lines.remove(caretPosition.getRow() + 1);
- }
- }
- return Result.HANDLED;
- case ArrowLeft:
- if(caretPosition.getColumn() > 0) {
- caretPosition = caretPosition.withRelativeColumn(-1);
- }
- else if(style == Style.MULTI_LINE && caretWarp && caretPosition.getRow() > 0) {
- caretPosition = caretPosition.withRelativeRow(-1);
- caretPosition = caretPosition.withColumn(lines.get(caretPosition.getRow()).length());
- }
- else if(horizontalFocusSwitching) {
- return Result.MOVE_FOCUS_LEFT;
- }
- return Result.HANDLED;
- case ArrowRight:
- if(caretPosition.getColumn() < lines.get(caretPosition.getRow()).length()) {
- caretPosition = caretPosition.withRelativeColumn(1);
- }
- else if(style == Style.MULTI_LINE && caretWarp && caretPosition.getRow() < lines.size() - 1) {
- caretPosition = caretPosition.withRelativeRow(1);
- caretPosition = caretPosition.withColumn(0);
- }
- else if(horizontalFocusSwitching) {
- return Result.MOVE_FOCUS_RIGHT;
- }
- return Result.HANDLED;
- case ArrowUp:
- if(caretPosition.getRow() > 0) {
- int trueColumnPosition = TerminalTextUtils.getColumnIndex(lines.get(caretPosition.getRow()), caretPosition.getColumn());
- caretPosition = caretPosition.withRelativeRow(-1);
- line = lines.get(caretPosition.getRow());
- if(trueColumnPosition > TerminalTextUtils.getColumnWidth(line)) {
- caretPosition = caretPosition.withColumn(line.length());
- }
- else {
- caretPosition = caretPosition.withColumn(TerminalTextUtils.getStringCharacterIndex(line, trueColumnPosition));
- }
- }
- else if(verticalFocusSwitching) {
- return Result.MOVE_FOCUS_UP;
- }
- return Result.HANDLED;
- case ArrowDown:
- if(caretPosition.getRow() < lines.size() - 1) {
- int trueColumnPosition = TerminalTextUtils.getColumnIndex(lines.get(caretPosition.getRow()), caretPosition.getColumn());
- caretPosition = caretPosition.withRelativeRow(1);
- line = lines.get(caretPosition.getRow());
- if(trueColumnPosition > TerminalTextUtils.getColumnWidth(line)) {
- caretPosition = caretPosition.withColumn(line.length());
- }
- else {
- caretPosition = caretPosition.withColumn(TerminalTextUtils.getStringCharacterIndex(line, trueColumnPosition));
- }
- }
- else if(verticalFocusSwitching) {
- return Result.MOVE_FOCUS_DOWN;
- }
- return Result.HANDLED;
- case End:
- caretPosition = caretPosition.withColumn(line.length());
- return Result.HANDLED;
- case Enter:
- if(style == Style.SINGLE_LINE) {
- return Result.MOVE_FOCUS_NEXT;
- }
- String newLine = line.substring(caretPosition.getColumn());
- String oldLine = line.substring(0, caretPosition.getColumn());
- if(validated(newLine) && validated(oldLine)) {
- lines.set(caretPosition.getRow(), oldLine);
- lines.add(caretPosition.getRow() + 1, newLine);
- caretPosition = caretPosition.withColumn(0).withRelativeRow(1);
- }
- return Result.HANDLED;
- case Home:
- caretPosition = caretPosition.withColumn(0);
- return Result.HANDLED;
- case PageDown:
- caretPosition = caretPosition.withRelativeRow(getSize().getRows());
- if(caretPosition.getRow() > lines.size() - 1) {
- caretPosition = caretPosition.withRow(lines.size() - 1);
- }
- if(lines.get(caretPosition.getRow()).length() < caretPosition.getColumn()) {
- caretPosition = caretPosition.withColumn(lines.get(caretPosition.getRow()).length());
- }
- return Result.HANDLED;
- case PageUp:
- caretPosition = caretPosition.withRelativeRow(-getSize().getRows());
- if(caretPosition.getRow() < 0) {
- caretPosition = caretPosition.withRow(0);
- }
- if(lines.get(caretPosition.getRow()).length() < caretPosition.getColumn()) {
- caretPosition = caretPosition.withColumn(lines.get(caretPosition.getRow()).length());
- }
- return Result.HANDLED;
- default:
- }
- return super.handleKeyStroke(keyStroke);
- }
-
- private boolean validated(String line) {
- return validationPattern == null || line.isEmpty() || validationPattern.matcher(line).matches();
- }
-
- private Result handleKeyStrokeReadOnly(KeyStroke keyStroke) {
- switch (keyStroke.getKeyType()) {
- case ArrowLeft:
- if(getRenderer().getViewTopLeft().getColumn() == 0 && horizontalFocusSwitching) {
- return Result.MOVE_FOCUS_LEFT;
- }
- getRenderer().setViewTopLeft(getRenderer().getViewTopLeft().withRelativeColumn(-1));
- return Result.HANDLED;
- case ArrowRight:
- if(getRenderer().getViewTopLeft().getColumn() + getSize().getColumns() == longestRow && horizontalFocusSwitching) {
- return Result.MOVE_FOCUS_RIGHT;
- }
- getRenderer().setViewTopLeft(getRenderer().getViewTopLeft().withRelativeColumn(1));
- return Result.HANDLED;
- case ArrowUp:
- if(getRenderer().getViewTopLeft().getRow() == 0 && verticalFocusSwitching) {
- return Result.MOVE_FOCUS_UP;
- }
- getRenderer().setViewTopLeft(getRenderer().getViewTopLeft().withRelativeRow(-1));
- return Result.HANDLED;
- case ArrowDown:
- if(getRenderer().getViewTopLeft().getRow() + getSize().getRows() == lines.size() && verticalFocusSwitching) {
- return Result.MOVE_FOCUS_DOWN;
- }
- getRenderer().setViewTopLeft(getRenderer().getViewTopLeft().withRelativeRow(1));
- return Result.HANDLED;
- case Home:
- getRenderer().setViewTopLeft(TerminalPosition.TOP_LEFT_CORNER);
- return Result.HANDLED;
- case End:
- getRenderer().setViewTopLeft(TerminalPosition.TOP_LEFT_CORNER.withRow(getLineCount() - getSize().getRows()));
- return Result.HANDLED;
- case PageDown:
- getRenderer().setViewTopLeft(getRenderer().getViewTopLeft().withRelativeRow(getSize().getRows()));
- return Result.HANDLED;
- case PageUp:
- getRenderer().setViewTopLeft(getRenderer().getViewTopLeft().withRelativeRow(-getSize().getRows()));
- return Result.HANDLED;
- default:
- }
- return super.handleKeyStroke(keyStroke);
- }
-
- /**
- * Helper interface that doesn't add any new methods but makes coding new text box renderers a little bit more clear
- */
- public interface TextBoxRenderer extends InteractableRenderer<TextBox> {
- TerminalPosition getViewTopLeft();
- void setViewTopLeft(TerminalPosition position);
- }
-
- /**
- * This is the default text box renderer that is used if you don't override anything. With this renderer, the text
- * box is filled with a solid background color and the text is drawn on top of it. Scrollbars are added for
- * multi-line text whenever the text inside the {@code TextBox} does not fit in the available area.
- */
- public static class DefaultTextBoxRenderer implements TextBoxRenderer {
- private TerminalPosition viewTopLeft;
- private ScrollBar verticalScrollBar;
- private ScrollBar horizontalScrollBar;
- private boolean hideScrollBars;
-
- /**
- * Default constructor
- */
- public DefaultTextBoxRenderer() {
- viewTopLeft = TerminalPosition.TOP_LEFT_CORNER;
- verticalScrollBar = new ScrollBar(Direction.VERTICAL);
- horizontalScrollBar = new ScrollBar(Direction.HORIZONTAL);
- hideScrollBars = false;
- }
-
- @Override
- public TerminalPosition getViewTopLeft() {
- return viewTopLeft;
- }
-
- @Override
- public void setViewTopLeft(TerminalPosition position) {
- if(position.getColumn() < 0) {
- position = position.withColumn(0);
- }
- if(position.getRow() < 0) {
- position = position.withRow(0);
- }
- viewTopLeft = position;
- }
-
- @Override
- public TerminalPosition getCursorLocation(TextBox component) {
- if(component.isReadOnly()) {
- return null;
- }
-
- //Adjust caret position if necessary
- TerminalPosition caretPosition = component.getCaretPosition();
- String line = component.getLine(caretPosition.getRow());
- caretPosition = caretPosition.withColumn(Math.min(caretPosition.getColumn(), line.length()));
-
- return caretPosition
- .withColumn(TerminalTextUtils.getColumnIndex(line, caretPosition.getColumn()))
- .withRelativeColumn(-viewTopLeft.getColumn())
- .withRelativeRow(-viewTopLeft.getRow());
- }
-
- @Override
- public TerminalSize getPreferredSize(TextBox component) {
- return new TerminalSize(component.longestRow, component.lines.size());
- }
-
- /**
- * Controls whether scrollbars should be visible or not when a multi-line {@code TextBox} has more content than
- * it can draw in the area it was assigned (default: false)
- * @param hideScrollBars If {@code true}, don't show scrollbars if the multi-line content is bigger than the
- * area
- */
- public void setHideScrollBars(boolean hideScrollBars) {
- this.hideScrollBars = hideScrollBars;
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, TextBox component) {
- TerminalSize realTextArea = graphics.getSize();
- if(realTextArea.getRows() == 0 || realTextArea.getColumns() == 0) {
- return;
- }
- boolean drawVerticalScrollBar = false;
- boolean drawHorizontalScrollBar = false;
- int textBoxLineCount = component.getLineCount();
- if(!hideScrollBars && textBoxLineCount > realTextArea.getRows() && realTextArea.getColumns() > 1) {
- realTextArea = realTextArea.withRelativeColumns(-1);
- drawVerticalScrollBar = true;
- }
- if(!hideScrollBars && component.longestRow > realTextArea.getColumns() && realTextArea.getRows() > 1) {
- realTextArea = realTextArea.withRelativeRows(-1);
- drawHorizontalScrollBar = true;
- if(textBoxLineCount > realTextArea.getRows() && realTextArea.getRows() == graphics.getSize().getRows()) {
- realTextArea = realTextArea.withRelativeColumns(-1);
- drawVerticalScrollBar = true;
- }
- }
-
- drawTextArea(graphics.newTextGraphics(TerminalPosition.TOP_LEFT_CORNER, realTextArea), component);
-
- //Draw scrollbars, if any
- if(drawVerticalScrollBar) {
- verticalScrollBar.setViewSize(realTextArea.getRows());
- verticalScrollBar.setScrollMaximum(textBoxLineCount);
- verticalScrollBar.setScrollPosition(viewTopLeft.getRow());
- verticalScrollBar.draw(graphics.newTextGraphics(
- new TerminalPosition(graphics.getSize().getColumns() - 1, 0),
- new TerminalSize(1, graphics.getSize().getRows() - 1)));
- }
- if(drawHorizontalScrollBar) {
- horizontalScrollBar.setViewSize(realTextArea.getColumns());
- horizontalScrollBar.setScrollMaximum(component.longestRow - 1);
- horizontalScrollBar.setScrollPosition(viewTopLeft.getColumn());
- horizontalScrollBar.draw(graphics.newTextGraphics(
- new TerminalPosition(0, graphics.getSize().getRows() - 1),
- new TerminalSize(graphics.getSize().getColumns() - 1, 1)));
- }
- }
-
- private void drawTextArea(TextGUIGraphics graphics, TextBox component) {
- TerminalSize textAreaSize = graphics.getSize();
- if(viewTopLeft.getColumn() + textAreaSize.getColumns() > component.longestRow) {
- viewTopLeft = viewTopLeft.withColumn(component.longestRow - textAreaSize.getColumns());
- if(viewTopLeft.getColumn() < 0) {
- viewTopLeft = viewTopLeft.withColumn(0);
- }
- }
- if(viewTopLeft.getRow() + textAreaSize.getRows() > component.getLineCount()) {
- viewTopLeft = viewTopLeft.withRow(component.getLineCount() - textAreaSize.getRows());
- if(viewTopLeft.getRow() < 0) {
- viewTopLeft = viewTopLeft.withRow(0);
- }
- }
- if (component.isFocused()) {
- graphics.applyThemeStyle(graphics.getThemeDefinition(TextBox.class).getActive());
- }
- else {
- graphics.applyThemeStyle(graphics.getThemeDefinition(TextBox.class).getNormal());
- }
- graphics.fill(component.unusedSpaceCharacter);
-
- if(!component.isReadOnly()) {
- //Adjust caret position if necessary
- TerminalPosition caretPosition = component.getCaretPosition();
- String caretLine = component.getLine(caretPosition.getRow());
- caretPosition = caretPosition.withColumn(Math.min(caretPosition.getColumn(), caretLine.length()));
-
- //Adjust the view if necessary
- int trueColumnPosition = TerminalTextUtils.getColumnIndex(caretLine, caretPosition.getColumn());
- if (trueColumnPosition < viewTopLeft.getColumn()) {
- viewTopLeft = viewTopLeft.withColumn(trueColumnPosition);
- }
- else if (trueColumnPosition >= textAreaSize.getColumns() + viewTopLeft.getColumn()) {
- viewTopLeft = viewTopLeft.withColumn(trueColumnPosition - textAreaSize.getColumns() + 1);
- }
- if (caretPosition.getRow() < viewTopLeft.getRow()) {
- viewTopLeft = viewTopLeft.withRow(caretPosition.getRow());
- }
- else if (caretPosition.getRow() >= textAreaSize.getRows() + viewTopLeft.getRow()) {
- viewTopLeft = viewTopLeft.withRow(caretPosition.getRow() - textAreaSize.getRows() + 1);
- }
-
- //Additional corner-case for CJK characters
- if(trueColumnPosition - viewTopLeft.getColumn() == graphics.getSize().getColumns() - 1) {
- if(caretLine.length() > caretPosition.getColumn() &&
- TerminalTextUtils.isCharCJK(caretLine.charAt(caretPosition.getColumn()))) {
- viewTopLeft = viewTopLeft.withRelativeColumn(1);
- }
- }
- }
-
- for (int row = 0; row < textAreaSize.getRows(); row++) {
- int rowIndex = row + viewTopLeft.getRow();
- if(rowIndex >= component.lines.size()) {
- continue;
- }
- String line = component.lines.get(rowIndex);
- graphics.putString(0, row, TerminalTextUtils.fitString(line, viewTopLeft.getColumn(), textAreaSize.getColumns()));
- }
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.graphics.Theme;
-import com.googlecode.lanterna.input.KeyStroke;
-
-import java.io.IOException;
-
-/**
- * This is the base interface for advanced text GUIs supported in Lanterna. You may want to use this in combination with
- * a TextGUIThread, that can be created/retrieved by using {@code getGUIThread()}.
- * @author Martin
- */
-public interface TextGUI {
- /**
- * Sets the global theme to be used by this TextGUI. This value will be set on every TextGUIGraphics object created
- * for drawing the GUI, but individual components can override this if they want. If you don't call this method
- * you should assume that a default theme is assigned by the library.
- * @param theme Theme to use as the default theme for this TextGUI
- */
- void setTheme(Theme theme);
-
- /**
- * Drains the input queue and passes the key strokes to the GUI system for processing. For window-based system, it
- * will send each key stroke to the active window for processing. If the input read gives an EOF, it will throw
- * EOFException and this is normally the signal to shut down the GUI (any command coming in before the EOF will be
- * processed as usual before this).
- * @return {@code true} if at least one key stroke was read and processed, {@code false} if there was nothing on the
- * input queue (only for non-blocking IO)
- * @throws java.io.IOException In case there was an underlying I/O error
- * @throws java.io.EOFException In the input stream received an EOF marker
- */
- boolean processInput() throws IOException;
-
- /**
- * Updates the screen, to make any changes visible to the user.
- * @throws java.io.IOException In case there was an underlying I/O error
- */
- void updateScreen() throws IOException;
-
- /**
- * This method can be used to determine if any component has requested a redraw. If this method returns
- * {@code true}, you may want to call {@code updateScreen()}.
- * @return {@code true} if this TextGUI has a change and is waiting for someone to call {@code updateScreen()}
- */
- boolean isPendingUpdate();
-
- /**
- * The first time this method is called, it will create a new TextGUIThread object that you can use to automatically
- * manage this TextGUI instead of manually calling {@code processInput()} and {@code updateScreen()}. After the
- * initial call, it will return the same object as it was originally returning.
- * @return A {@code TextGUIThread} implementation that can be used to asynchronously manage the GUI
- */
- TextGUIThread getGUIThread();
-
- /**
- * Returns the interactable component currently in focus
- * @return Component that is currently in input focus
- */
- Interactable getFocusedInteractable();
-
- /**
- * Adds a listener to this TextGUI to fire events on.
- * @param listener Listener to add
- */
- void addListener(Listener listener);
-
- /**
- * Removes a listener from this TextGUI so that it will no longer receive events
- * @param listener Listener to remove
- */
- void removeListener(Listener listener);
-
- /**
- * Listener interface for TextGUI, firing on events related to the overall GUI
- */
- interface Listener {
- /**
- * Fired either when no component was in focus during a keystroke or if the focused component and all its parent
- * containers chose not to handle the event. This event listener should also return {@code true} if the event
- * was processed in any way that requires the TextGUI to update itself, otherwise {@code false}.
- * @param textGUI TextGUI that had the event
- * @param keyStroke Keystroke that was unhandled
- * @return If the outcome of this KeyStroke processed by the implementer requires the TextGUI to re-draw, return
- * {@code true} here, otherwise {@code false}
- */
- boolean onUnhandledKeyStroke(TextGUI textGUI, KeyStroke keyStroke);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-/**
- * This interface is the base part in the Lanterna Text GUI component hierarchy
- * @author Martin
- */
-public interface TextGUIElement {
- /**
- * Draws the GUI element using the supplied TextGUIGraphics object. This is the main method to implement when you
- * want to create your own GUI components.
- * @param graphics Graphics object to use when drawing the component
- */
- void draw(TextGUIGraphics graphics);
-
- /**
- * Checks if this element (or any of its child components, if any) has signaled that what it's currently displaying
- * is out of date and needs re-drawing.
- * @return {@code true} if the component is invalid and needs redrawing, {@code false} otherwise
- */
- boolean isInvalid();
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.graphics.*;
-import com.googlecode.lanterna.screen.TabBehaviour;
-
-import java.util.Collection;
-import java.util.EnumSet;
-
-/**
- * TextGraphics implementation used by TextGUI when doing any drawing operation.
- * @author Martin
- */
-public final class TextGUIGraphics implements ThemedTextGraphics, TextGraphics {
- private final TextGUI textGUI;
- private final ImmutableThemedTextGraphics backend;
-
- TextGUIGraphics(TextGUI textGUI, TextGraphics backend, Theme theme) {
- this.backend = new ImmutableThemedTextGraphics(backend, theme);
- this.textGUI = textGUI;
- }
-
- @Override
- public Theme getTheme() {
- return backend.getTheme();
- }
-
- /**
- * Returns a new {@code TextGUIGraphics} object that has another theme attached to it
- * @param theme Theme to be used with the new {@code TextGUIGraphics}
- * @return New {@code TextGUIGraphics} that has the specified theme
- */
- public TextGUIGraphics withTheme(Theme theme) {
- return new TextGUIGraphics(textGUI, backend.getUnderlyingTextGraphics(), theme);
- }
-
- /**
- * Returns the {@code TextGUI} this {@code TextGUIGraphics} belongs to
- * @return {@code TextGUI} this {@code TextGUIGraphics} belongs to
- */
- public TextGUI getTextGUI() {
- return textGUI;
- }
-
- @Override
- public TextGUIGraphics newTextGraphics(TerminalPosition topLeftCorner, TerminalSize size) throws IllegalArgumentException {
- return new TextGUIGraphics(textGUI, backend.getUnderlyingTextGraphics().newTextGraphics(topLeftCorner, size), backend.getTheme());
- }
-
- @Override
- public TextGUIGraphics applyThemeStyle(ThemeStyle themeStyle) {
- backend.applyThemeStyle(themeStyle);
- return this;
- }
-
- @Override
- public ThemeDefinition getThemeDefinition(Class<?> clazz) {
- return backend.getThemeDefinition(clazz);
- }
-
- @Override
- public TerminalSize getSize() {
- return backend.getSize();
- }
-
- @Override
- public TextColor getBackgroundColor() {
- return backend.getBackgroundColor();
- }
-
- @Override
- public TextGUIGraphics setBackgroundColor(TextColor backgroundColor) {
- backend.setBackgroundColor(backgroundColor);
- return this;
- }
-
- @Override
- public TextColor getForegroundColor() {
- return backend.getForegroundColor();
- }
-
- @Override
- public TextGUIGraphics setForegroundColor(TextColor foregroundColor) {
- backend.setForegroundColor(foregroundColor);
- return this;
- }
-
- @Override
- public TextGUIGraphics enableModifiers(SGR... modifiers) {
- backend.enableModifiers(modifiers);
- return this;
- }
-
- @Override
- public TextGUIGraphics disableModifiers(SGR... modifiers) {
- backend.disableModifiers(modifiers);
- return this;
- }
-
- @Override
- public TextGUIGraphics setModifiers(EnumSet<SGR> modifiers) {
- backend.setModifiers(modifiers);
- return this;
- }
-
- @Override
- public TextGUIGraphics clearModifiers() {
- backend.clearModifiers();
- return this;
- }
-
- @Override
- public EnumSet<SGR> getActiveModifiers() {
- return backend.getActiveModifiers();
- }
-
- @Override
- public TabBehaviour getTabBehaviour() {
- return backend.getTabBehaviour();
- }
-
- @Override
- public TextGUIGraphics setTabBehaviour(TabBehaviour tabBehaviour) {
- backend.setTabBehaviour(tabBehaviour);
- return this;
- }
-
- @Override
- public TextGUIGraphics fill(char c) {
- backend.fill(c);
- return this;
- }
-
- @Override
- public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- backend.fillRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- backend.fillRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- backend.drawRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, TextCharacter character) {
- backend.drawRectangle(topLeft, size, character);
- return this;
- }
-
- @Override
- public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- backend.fillTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- backend.fillTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- backend.drawTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, TextCharacter character) {
- backend.drawTriangle(p1, p2, p3, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, char character) {
- backend.drawLine(fromPoint, toPoint, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, TextCharacter character) {
- backend.drawLine(fromPoint, toPoint, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, char character) {
- backend.drawLine(fromX, fromY, toX, toY, character);
- return this;
- }
-
- @Override
- public TextGraphics drawLine(int fromX, int fromY, int toX, int toY, TextCharacter character) {
- backend.drawLine(fromX, fromY, toX, toY, character);
- return this;
- }
-
- @Override
- public TextGraphics drawImage(TerminalPosition topLeft, TextImage image) {
- backend.drawImage(topLeft, image);
- return this;
- }
-
- @Override
- public TextGraphics drawImage(TerminalPosition topLeft, TextImage image, TerminalPosition sourceImageTopLeft, TerminalSize sourceImageSize) {
- backend.drawImage(topLeft, image, sourceImageTopLeft, sourceImageSize);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(TerminalPosition position, char character) {
- backend.setCharacter(position, character);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(TerminalPosition position, TextCharacter character) {
- backend.setCharacter(position, character);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(int column, int row, char character) {
- backend.setCharacter(column, row, character);
- return this;
- }
-
- @Override
- public TextGraphics setCharacter(int column, int row, TextCharacter character) {
- backend.setCharacter(column, row, character);
- return this;
- }
-
- @Override
- public TextGUIGraphics putString(int column, int row, String string) {
- backend.putString(column, row, string);
- return this;
- }
-
- @Override
- public TextGUIGraphics putString(TerminalPosition position, String string) {
- backend.putString(position, string);
- return this;
- }
-
- @Override
- public TextGUIGraphics putString(int column, int row, String string, SGR extraModifier, SGR... optionalExtraModifiers) {
- backend.putString(column, row, string, extraModifier, optionalExtraModifiers);
- return this;
- }
-
- @Override
- public TextGUIGraphics putString(TerminalPosition position, String string, SGR extraModifier, SGR... optionalExtraModifiers) {
- backend.putString(position, string, extraModifier, optionalExtraModifiers);
- return this;
- }
-
- @Override
- public TextGraphics putString(int column, int row, String string, Collection<SGR> extraModifiers) {
- backend.putString(column, row, string, extraModifiers);
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(int column, int row) {
- return backend.getCharacter(column, row);
- }
-
- @Override
- public TextCharacter getCharacter(TerminalPosition position) {
- return backend.getCharacter(position);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import java.io.IOException;
-
-/**
- * Class that represents the thread this is expected to run the event/input/update loop for the {@code TextGUI}. There
- * are mainly two implementations of this interface, one for having lanterna automatically spawn a new thread for doing
- * all the processing and leaving the creator thread free to do other things, and one that assumes the creator thread
- * will hand over control to lanterna for as long as the GUI is running.
- * @see SameTextGUIThread
- * @see SeparateTextGUIThread
- * @author Martin
- */
-public interface TextGUIThread {
- /**
- * Invokes custom code on the GUI thread. Even if the current thread <b>is</b> the GUI thread, the code will be
- * executed at a later time when the event processing is done.
- *
- * @param runnable Code to run asynchronously
- * @throws java.lang.IllegalStateException If the GUI thread is not running
- */
- void invokeLater(Runnable runnable) throws IllegalStateException;
-
- /**
- * Main method to call when you are managing the event/input/update loop yourself. This method will run one round
- * through the GUI's event/input queue and update the visuals if required. If the operation did nothing (returning
- * {@code false}) you could sleep for a millisecond and then try again. If you use {@code SameTextGUIThread} you
- * must either call this method directly to make the GUI update or use one of the methods on
- * {@code WindowBasedTextGUI} that blocks until a particular window has closed.
- * @return {@code true} if there was anything to process or the GUI was updated, otherwise {@code false}
- * @throws IOException
- */
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- boolean processEventsAndUpdate() throws IOException;
-
- /**
- * Schedules custom code to be executed on the GUI thread and waits until the code has been executed before
- * returning. If this is run on the GUI thread, it will immediately run the {@code Runnable} and then return.
- *
- * @param runnable Code to be run and waited for completion before this method returns
- * @throws IllegalStateException If the GUI thread is not running
- * @throws InterruptedException If the caller thread was interrupted while waiting for the task to be executed
- */
- void invokeAndWait(Runnable runnable) throws IllegalStateException, InterruptedException;
-
- /**
- * Updates the exception handler used by this TextGUIThread. The exception handler will be invoked when an exception
- * occurs in the main event loop. You can then decide how to log this exception and if you want to terminate the
- * thread or not.
- * @param exceptionHandler Handler to inspect exceptions
- */
- void setExceptionHandler(ExceptionHandler exceptionHandler);
-
- /**
- * Returns the Java thread which is processing GUI events and updating the screen
- * @return Thread which is processing events and updating the screen
- */
- Thread getThread();
-
- /**
- * This interface defines an exception handler, that is used for looking at exceptions that occurs during the main
- * event loop of the TextGUIThread. You can for example use this for logging, but also decide if you want the
- * exception to kill the thread.
- */
- interface ExceptionHandler {
- /**
- * Will be called when an IOException has occurred in the main event thread
- * @param e IOException that occurred
- * @return If you return {@code true}, the event thread will be terminated
- */
- boolean onIOException(IOException e);
-
- /**
- * Will be called when a RuntimeException has occurred in the main event thread
- * @param e RuntimeException that occurred
- * @return If you return {@code true}, the event thread will be terminated
- */
- boolean onRuntimeException(RuntimeException e);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2;
-
-/**
- * Factory class for creating {@code TextGUIThread} objects. This is used by {@code TextGUI} implementations to assign
- * their local {@code TextGUIThread} reference
- */
-public interface TextGUIThreadFactory {
- /**
- * Creates a new {@code TextGUIThread} objects
- * @param textGUI {@code TextGUI} this {@code TextGUIThread} should be associated with
- * @return The new {@code TextGUIThread}
- */
- TextGUIThread createTextGUIThread(TextGUI textGUI);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * Window is a base unit in the TextGUI system, it represents a collection of components grouped together, usually
- * surrounded by a border and a title. Modern computer system GUIs are normally based around the metaphor of windows,
- * so I don't think you should have any problems understanding what this means.
- * @author Martin
- */
-public interface Window extends BasePane {
- /**
- * Window hints are meta-data stored along with the window that can be used to give the GUI system some ideas of how
- * this window wants to be treated. There are no guarantees that the hints will be honoured though. You can declare
- * your own window hints by sub-classing this class.
- */
- class Hint {
- /**
- * With this hint, the TextGUI system should not draw any decorations around the window. Decorated size will be
- * the same as the window size.
- */
- public static final Hint NO_DECORATIONS = new Hint();
-
- /**
- * With this hint, the TextGUI system should skip running any post renderers for the window. By default this
- * means the window won't have any shadow.
- */
- public static final Hint NO_POST_RENDERING = new Hint();
-
- /**
- * With this hint, the window should never receive focus by the window manager
- */
- public static final Hint NO_FOCUS = new Hint();
-
- /**
- * With this hint, the window wants to be at the center of the terminal instead of using the cascading layout
- * which is the standard.
- */
- public static final Hint CENTERED = new Hint();
-
- /**
- * Windows with this hint should not be positioned by the window manager, rather they should use whatever
- * position is pre-set.
- */
- public static final Hint FIXED_POSITION = new Hint();
-
- /**
- * Windows with this hint should not be automatically sized by the window manager (using
- * {@code getPreferredSize()}), rather should rely on the code manually setting the size of the window using
- * {@code setSize(..)}.
- */
- public static final Hint FIXED_SIZE = new Hint();
-
- /**
- * With this hint, don't let the window grow larger than the terminal screen, rather set components to a smaller
- * size than they prefer.
- */
- public static final Hint FIT_TERMINAL_WINDOW = new Hint();
-
- /**
- * This hint tells the window manager that this window should have exclusive access to the keyboard input until
- * it is closed. For window managers that allows the user to switch between open windows, putting a window on
- * the screen with this hint should make the window manager temporarily disable that function until the window
- * is closed.
- */
- public static final Hint MODAL = new Hint();
-
- /**
- * A window with this hint would like to be placed covering the entire screen. Use this in combination with
- * NO_DECORATIONS if you want the content area to take up the entire terminal.
- */
- public static final Hint FULL_SCREEN = new Hint();
-
- /**
- * This window hint tells the window manager that the window should be taking up almost the entire screen,
- * leaving only a small space around it. This is different from {@code FULL_SCREEN} which takes all available
- * space and completely hide the background and any other window behind it.
- */
- public static final Hint EXPANDED = new Hint();
-
- protected Hint() {
- }
- }
-
- @Override
- WindowBasedTextGUI getTextGUI();
-
- /**
- * DON'T CALL THIS METHOD YOURSELF, it is called automatically by the TextGUI system when you add a window. If you
- * call it with the intention of adding the window to the specified TextGUI, you need to read the documentation
- * on how to use windows.
- * @param textGUI TextGUI this window belongs to from now on
- */
- void setTextGUI(WindowBasedTextGUI textGUI);
-
- /**
- * This method returns the title of the window, which is normally drawn at the top-left corder of the window
- * decoration, but depending on the {@code WindowDecorationRenderer} used by the {@code TextGUI}
- * @return title of the window
- */
- String getTitle();
-
- /**
- * This values is optionally used by the window manager to decide if the windows should be drawn or not. In an
- * invisible state, the window is still considered active in the TextGUI but just not drawn and not receiving any
- * input events. Please note that window managers may choose not to implement this.
- *
- * @return Whether the window wants to be visible or not
- */
- boolean isVisible();
-
- /**
- * This values is optionally used by the window manager to decide if the windows should be drawn or not. In an
- * invisible state, the window is still considered active in the TextGUI but just not drawn and not receiving any
- * input events. Please note that window managers may choose not to implement this.
- *
- * @param visible whether the window should be visible or not
- */
- void setVisible(boolean visible);
-
- /**
- * This method is used to determine if the window requires re-drawing. The most common cause for this is the some
- * of its components has changed and we need a re-draw to make these changes visible.
- * @return {@code true} if the window would like to be re-drawn, {@code false} if the window doesn't need
- */
- @Override
- boolean isInvalid();
-
- /**
- * Invalidates the whole window (including all of its child components) which will cause it to be recalculated
- * and redrawn.
- */
- @Override
- void invalidate();
-
- /**
- * Returns the size this window would like to be
- * @return Desired size of this window
- */
- TerminalSize getPreferredSize();
-
- /**
- * Closes the window, which will remove it from the GUI
- */
- void close();
-
- /**
- * Updates the set of active hints for this window. Please note that it's up to the window manager if these hints
- * will be honored or not.
- * @param hints Set of hints to be active for this window
- */
- void setHints(Collection<Hint> hints);
-
- /**
- * Returns a set of window hints that can be used by the text gui system, the window manager or any other part that
- * is interacting with windows.
- * @return Set of hints defined for this window
- */
- Set<Hint> getHints();
-
- /**
- * Returns the position of the window, as last specified by the window manager. This position does not include
- * window decorations but is the top-left position of the first usable space of the window.
- * @return Position, relative to the top-left corner of the terminal, of the top-left corner of the window
- */
- TerminalPosition getPosition();
-
- /**
- * This method is called by the GUI system to update the window on where the window manager placed it. Calling this
- * yourself will have no effect other than making the {@code getPosition()} call incorrect until the next redraw.
- * @param topLeft Global coordinates of the top-left corner of the window
- */
- void setPosition(TerminalPosition topLeft);
-
- /**
- * Returns the last known size of the window. This is in general derived from the last drawing operation, how large
- * area the window was allowed to draw on. This size does not include window decorations.
- * @return Size of the window
- */
- TerminalSize getSize();
-
- /**
- * This method is called by the GUI system to update the window on how large it is, excluding window decorations.
- * Calling this yourself will generally make no difference in the size of the window, since it will be reset on the
- * next redraw based on how large area the TextGraphics given is covering. However, if you add the FIXED_SIZE
- * window hint, the auto-size calculation will be turned off and you can use this method to set how large you want
- * the window to be.
- * @param size New size of the window
- */
- void setSize(TerminalSize size);
-
- /**
- * Returns the last known size of the window including window decorations put on by the window manager. The value
- * returned here is passed in during drawing by the TextGUI through {@code setDecoratedSize(..)}.
- * @return Size of the window, including window decorations
- */
- TerminalSize getDecoratedSize();
-
- /**
- * This method is called by the GUI system to update the window on how large it is, counting window decorations too.
- * Calling this yourself will have no effect other than making the {@code getDecoratedSize()} call incorrect until
- * the next redraw.
- * @param decoratedSize Size of the window, including window decorations
- */
- void setDecoratedSize(TerminalSize decoratedSize);
-
- /**
- * This method is called by the GUI system to update the window on, as of the last drawing operation, the distance
- * from the top-left position of the window including decorations to the top-left position of the actual content
- * area. If this window has no decorations, it will be always 0x0. Do not call this method yourself.
- * @param offset Offset from the top-left corner of the window (including decorations) to the top-left corner of
- * the content area.
- */
- void setContentOffset(TerminalPosition offset);
-
- /**
- * Waits for the window to close. Please note that this can cause deadlocks if care is not taken. Also, this method
- * will swallow any interrupts, if you need a wait method that throws InterruptedException, you'll have to implement
- * this yourself.
- */
- void waitUntilClosed();
-
- ///////////////////////////////////////////////////////////////
- //// Below here are methods from BasePane ////
- //// We duplicate them here to make the JavaDoc more clear ////
- ///////////////////////////////////////////////////////////////
- /**
- * Called by the GUI system (or something imitating the GUI system) to draw the window. The TextGUIGraphics object
- * should be used to perform the drawing operations.
- * @param graphics TextGraphics object to draw with
- */
- @Override
- void draw(TextGUIGraphics graphics);
-
- /**
- * Called by the GUI system's window manager when it has decided that this window should receive the keyboard input.
- * The window will decide what to do with this input, usually sending it to one of its sub-components, but if it
- * isn't able to find any handler for this input it should return {@code false} so that the window manager can take
- * further decisions on what to do with it.
- * @param key Keyboard input
- * @return {@code true} If the window could handle the input, false otherwise
- */
- @Override
- boolean handleInput(KeyStroke key);
-
- /**
- * Sets the top-level component in the window, this will be the only component unless it's a container of some kind
- * that you add child-components to.
- * @param component Component to use as the top-level object in the Window
- */
- @Override
- void setComponent(Component component);
-
- /**
- * Returns the component which is the top-level in the component hierarchy inside this window.
- * @return Top-level component in the window
- */
- @Override
- Component getComponent();
-
- /**
- * Returns the component in the window that currently has input focus. There can only be one component at a time
- * being in focus.
- * @return Interactable component that is currently in receiving input focus
- */
- @Override
- Interactable getFocusedInteractable();
-
- /**
- * Sets the component currently in focus within this window, or sets no component in focus if {@code null}
- * is passed in.
- * @param interactable Interactable to focus, or {@code null} to clear focus
- */
- @Override
- void setFocusedInteractable(Interactable interactable);
-
- /**
- * Returns the position of where to put the terminal cursor according to this window. This is typically
- * derived from which component has focus, or {@code null} if no component has focus or if the window doesn't
- * want the cursor to be visible. Note that the coordinates are in local coordinate space, relative to the top-left
- * corner of the window. You can use your TextGUI implementation to translate these to global coordinates.
- * @return Local position of where to place the cursor, or {@code null} if the cursor shouldn't be visible
- */
- @Override
- TerminalPosition getCursorPosition();
-
- /**
- * Returns a position in the window's local coordinate space to global coordinates
- * @param localPosition The local position to translate
- * @return The local position translated to global coordinates
- */
- @Override
- TerminalPosition toGlobal(TerminalPosition localPosition);
-
- /**
- * Returns a position expressed in global coordinates, i.e. row and column offset from the top-left corner of the
- * terminal into a position relative to the top-left corner of the window. Calling
- * {@code fromGlobal(toGlobal(..))} should return the exact same position.
- * @param position Position expressed in global coordinates to translate to local coordinates of this Window
- * @return The global coordinates expressed as local coordinates
- */
- TerminalPosition fromGlobal(TerminalPosition position);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.screen.Screen;
-import com.googlecode.lanterna.screen.VirtualScreen;
-
-import java.util.Collection;
-
-/**
- * Extension of the TextGUI interface, this is intended as the base interface for any TextGUI that intends to make use
- * of the Window class.
- * @author Martin
- */
-public interface WindowBasedTextGUI extends TextGUI {
- /**
- * Returns the window manager that is currently controlling this TextGUI. The window manager is in charge of placing
- * the windows on the surface and also deciding how they behave and move around.
- * @return Window manager that is currently controlling the windows in the terminal
- */
- WindowManager getWindowManager();
-
- /**
- * Adds a window to the TextGUI system, depending on the window manager this window may or may not be immediately
- * visible. By adding a window to the GUI, it will be associated with this GUI and can receive focus and events from
- * it. This method call will return immediately, if you want the call to block until the window is closed, please
- * use {@code addWindowAndWait(..)}.
- *
- * Windows are internally stored as a stack and newer windows are added at the top of the stack. The GUI system will
- * render windows in a predictable order from bottom to top. You can modify the stack by using
- * {@code moveToTop(..)} to move a Window from its current position in the stack to the top.
- *
- * @param window Window to add to the GUI
- * @return The WindowBasedTextGUI Itself
- */
- WindowBasedTextGUI addWindow(Window window);
-
- /**
- * Adds a window to the TextGUI system, depending on the window manager this window may or may not be immediately
- * visible. By adding a window to the GUI, it will be associated with this GUI and can receive focus and events from
- * it. This method block until the added window is removed or closed, if you want the call to return immediately,
- * please use {@code addWindow(..)}. This method call is useful for modal dialogs that requires a certain user input
- * before the application can continue.
- *
- * Windows are internally stored as a stack and newer windows are added at the top of the stack. The GUI system will
- * render windows in a predictable order from bottom to top. You can modify the stack by using
- * {@code moveToTop(..)} to move a Window from its current position in the stack to the top.
- *
- * @param window Window to add to the GUI
- * @return The WindowBasedTextGUI Itself
- */
- WindowBasedTextGUI addWindowAndWait(Window window);
-
- /**
- * Removes a window from the TextGUI. This is effectively the same as closing the window. The window will be
- * unassociated from this TextGUI and will no longer receive any events for it. Any threads waiting on the window
- * to close will be resumed.
- *
- * @param window Window to close
- * @return The WindowBasedTextGUI itself
- */
- WindowBasedTextGUI removeWindow(Window window);
-
- /**
- * Returns a list of all windows currently in the TextGUI. The list is unmodifiable and just a snapshot of what the
- * state was when the method was invoked. If windows are added/removed after the method call, the list will not
- * reflect this.
- * @return Unmodifiable list of all windows in the TextGUI at the time of the call
- */
- Collection<Window> getWindows();
-
- /**
- * Selects a particular window to be considered 'active' and receive all input events
- * @param activeWindow Window to become active and receive input events
- * @return The WindowBasedTextGUI itself
- */
- WindowBasedTextGUI setActiveWindow(Window activeWindow);
-
- /**
- * Returns the window which the TextGUI considers the active one at the time of the method call. The active window
- * is generally the one which relieves all keyboard input.
- * @return Active window in the TextGUI or {@code null}
- */
- Window getActiveWindow();
-
-
- /**
- * Returns the container for the background, which works as a single large component that takes up the whole
- * terminal area and is always behind all windows.
- * @return The {@code BasePane} used by this {@code WindowBasedTextGUI}
- */
- BasePane getBackgroundPane();
-
- /**
- * Returns the {@link Screen} for this {@link WindowBasedTextGUI}
- * @return the {@link Screen} used by this {@link WindowBasedTextGUI}
- */
- Screen getScreen();
-
- /**
- * Returns the {@link WindowPostRenderer} for this {@link WindowBasedTextGUI}
- * @return the {@link WindowPostRenderer} for this {@link WindowBasedTextGUI}
- */
- WindowPostRenderer getWindowPostRenderer();
-
- /**
- * Windows are internally stored as a stack and newer windows are added at the top of the stack. The GUI system will
- * render windows in a predictable order from bottom to top. This method allows you to move a Window from its
- * current position in the stack to the top, meaning it will be rendered last. This mean it will overlap all other
- * windows and because of this visually appear on top.
- * @param window Window in the stack to move to the top position
- * @return The WindowBasedTextGUI Itself
- */
- WindowBasedTextGUI moveToTop(Window window);
-
- /**
- * Takes the previously active window and makes it active, or if in reverse mode, takes the window at the bottom of
- * the stack, moves it to the front and makes it active.
- * @param reverse Direction to cycle through the windows
- * @return The WindowBasedTextGUI Itself
- */
- WindowBasedTextGUI cycleActiveWindow(boolean reverse);
-
- /**
- * Waits for the specified window to be closed
- * @param abstractWindow Window to wait for
- */
- void waitForWindowToClose(Window abstractWindow);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * Interface that defines a class that draws window decorations, i.e. a surrounding layer around the window that usually
- * looks like a border to make it easier for a user to visually separate the windows.
- * @see DefaultWindowDecorationRenderer
- * @author Martin
- */
-public interface WindowDecorationRenderer {
- /**
- * Draws the window decorations for a particular window and returns a new TextGraphics that is locked to the area
- * inside of the window decorations where the content of the window should be drawn
- * @param textGUI Which TextGUI is calling
- * @param graphics Graphics to use for drawing
- * @param window Window to draw
- * @return A new TextGraphics that is limited to the area inside the decorations just drawn
- */
- TextGUIGraphics draw(TextGUI textGUI, TextGUIGraphics graphics, Window window);
-
- /**
- * Retrieves the full size of the window, including all window decorations, given all components inside the window.
- * @param window Window to calculate size for
- * @param contentAreaSize Size of the content area in the window
- * @return Full size of the window, including decorations
- */
- TerminalSize getDecoratedSize(Window window, TerminalSize contentAreaSize);
-
- /**
- * Returns how much to step right and down from the top left position of the window decorations to the top left
- * position of the actual window
- * @param window Window to get the offset for
- * @return Position of the top left corner of the window, relative to the top left corner of the window decoration
- */
- TerminalPosition getOffset(Window window);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.List;
-
-/**
- * Window manager is a class that is plugged in to a {@code WindowBasedTextGUI} to manage the position and placement
- * of windows. The window manager doesn't contain the list of windows so it normally does not need to maintain much
- * state but it is passed all required objects as the window model changes.
- * @see DefaultWindowManager
- * @author Martin
- */
-public interface WindowManager {
-
- /**
- * Will be polled by the the {@link WindowBasedTextGUI} to see if the window manager believes an update is required.
- * For example, it could be that there is no input, no events and none of the components are invalid, but the window
- * manager decides for some other reason that the GUI needs to be updated, in that case you should return
- * {@code true} here. Please note that returning {@code false} will not prevent updates from happening, it's just
- * stating that the window manager isn't aware of some internal state change that would require an update.
- * @return {@code true} if the window manager believes the GUI needs to be update, {@code false} otherwise
- */
- boolean isInvalid();
-
- /**
- * Returns the {@code WindowDecorationRenderer} for a particular window
- * @param window Window to get the decoration renderer for
- * @return {@code WindowDecorationRenderer} for the window
- */
- WindowDecorationRenderer getWindowDecorationRenderer(Window window);
-
- /**
- * Called whenever a window is added to the {@code WindowBasedTextGUI}. This gives the window manager an opportunity
- * to setup internal state, if required, or decide on an initial position of the window
- * @param textGUI GUI that the window was added too
- * @param window Window that was added
- * @param allWindows All windows, including the new window, in the GUI
- */
- void onAdded(WindowBasedTextGUI textGUI, Window window, List<Window> allWindows);
-
- /**
- * Called whenever a window is removed from a {@code WindowBasedTextGUI}. This gives the window manager an
- * opportunity to clear internal state if needed.
- * @param textGUI GUI that the window was removed from
- * @param window Window that was removed
- * @param allWindows All windows, excluding the removed window, in the GUI
- */
- @SuppressWarnings("EmptyMethod")
- void onRemoved(WindowBasedTextGUI textGUI, Window window, List<Window> allWindows);
-
- /**
- * Called by the GUI system before iterating through all windows during the drawing process. The window manager
- * should ensure the position and decorated size of all windows at this point by using
- * {@code Window.setPosition(..)} and {@code Window.setDecoratedSize(..)}. Be sure to inspect the window hints
- * assigned to the window, in case you want to try to honour them. Use the
- * {@link #getWindowDecorationRenderer(Window)} method to get the currently assigned window decoration rendering
- * class which can tell you the decorated size of a window given it's content size.
- *
- * @param textGUI Text GUI that is about to draw the windows
- * @param allWindows All windows that are going to be drawn, in the order they will be drawn
- * @param screenSize Size of the terminal that is available to draw on
- */
- void prepareWindows(WindowBasedTextGUI textGUI, List<Window> allWindows, TerminalSize screenSize);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.graphics.TextGraphics;
-
-/**
- * Classes implementing this interface can be used along with DefaultWindowManagerTextGUI to put some extra processing
- * after a window has been rendered. This is used for making window shadows but can be used for anything.
- * @see WindowShadowRenderer
- * @author Martin
- */
-public interface WindowPostRenderer {
- /**
- * Called by DefaultWindowTextGUI immediately after a Window has been rendered, to let you do post-processing.
- * You will have a TextGraphics object that can draw to the whole screen, so you need to inspect the window's
- * position and decorated size to figure out where the bounds are
- * @param textGraphics Graphics object you can use to draw with
- * @param textGUI TextGUI that we are in
- * @param window Window that was just rendered
- */
- void postRender(
- TextGraphics textGraphics,
- TextGUI textGUI,
- Window window);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.graphics.TextGraphics;
-
-/**
- * This WindowPostRenderer implementation draws a shadow under the window
- *
- * @author Martin
- */
-public class WindowShadowRenderer implements WindowPostRenderer {
- @Override
- public void postRender(
- TextGraphics textGraphics,
- TextGUI textGUI,
- Window window) {
-
- TerminalPosition windowPosition = window.getPosition();
- TerminalSize decoratedWindowSize = window.getDecoratedSize();
- textGraphics.setForegroundColor(TextColor.ANSI.BLACK);
- textGraphics.setBackgroundColor(TextColor.ANSI.BLACK);
- textGraphics.enableModifiers(SGR.BOLD);
- TerminalPosition lowerLeft = windowPosition.withRelativeColumn(2).withRelativeRow(decoratedWindowSize.getRows());
- TerminalPosition lowerRight = lowerLeft.withRelativeColumn(decoratedWindowSize.getColumns() - 1);
- textGraphics.drawLine(lowerLeft, lowerRight, ' ');
- TerminalPosition upperRight = lowerRight.withRelativeRow(-decoratedWindowSize.getRows() + 1);
- textGraphics.drawLine(lowerRight, upperRight, ' ');
-
- //Fill the remaining hole
- upperRight = upperRight.withRelativeColumn(-1);
- lowerRight = lowerRight.withRelativeColumn(-1);
- textGraphics.drawLine(upperRight, lowerRight, ' ');
-
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.gui2.Window;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Abstract class for dialog building, containing much shared code between different kinds of dialogs
- * @param <B> The real type of the builder class
- * @param <T> Type of dialog this builder is building
- * @author Martin
- */
-public abstract class AbstractDialogBuilder<B, T extends DialogWindow> {
- protected String title;
- protected String description;
- protected Set<Window.Hint> extraWindowHints;
-
- /**
- * Default constructor for a dialog builder
- * @param title Title to assign to the dialog
- */
- public AbstractDialogBuilder(String title) {
- this.title = title;
- this.description = null;
- this.extraWindowHints = Collections.singleton(Window.Hint.CENTERED);
- }
-
- /**
- * Changes the title of the dialog
- * @param title New title
- * @return Itself
- */
- public B setTitle(String title) {
- if(title == null) {
- title = "";
- }
- this.title = title;
- return self();
- }
-
- /**
- * Returns the title that the built dialog will have
- * @return Title that the built dialog will have
- */
- public String getTitle() {
- return title;
- }
-
- /**
- * Changes the description of the dialog
- * @param description New description
- * @return Itself
- */
- public B setDescription(String description) {
- this.description = description;
- return self();
- }
-
- /**
- * Returns the description that the built dialog will have
- * @return Description that the built dialog will have
- */
- public String getDescription() {
- return description;
- }
-
- /**
- * Assigns a set of extra window hints that you want the built dialog to have
- * @param extraWindowHints Window hints to assign to the window in addition to the ones the builder will put
- * @return Itself
- */
- public B setExtraWindowHints(Set<Window.Hint> extraWindowHints) {
- this.extraWindowHints = extraWindowHints;
- return self();
- }
-
- /**
- * Returns the list of extra window hints that will be assigned to the window when built
- * @return List of extra window hints that will be assigned to the window when built
- */
- public Set<Window.Hint> getExtraWindowHints() {
- return extraWindowHints;
- }
-
- /**
- * Helper method for casting this to {@code type} parameter {@code B}
- * @return {@code this} as {@code B}
- */
- protected abstract B self();
-
- /**
- * Builds the dialog according to the builder implementation
- * @return New dialog object
- */
- protected abstract T buildDialog();
-
- /**
- * Builds a new dialog following the specifications of this builder
- * @return New dialog built following the specifications of this builder
- */
- public final T build() {
- T dialog = buildDialog();
- if(!extraWindowHints.isEmpty()) {
- Set<Window.Hint> combinedHints = new HashSet<Window.Hint>(dialog.getHints());
- combinedHints.addAll(extraWindowHints);
- dialog.setHints(combinedHints);
- }
- return dialog;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.*;
-
-import java.util.List;
-
-/**
- * Dialog containing a multiple item action list box
- * @author Martin
- */
-public class ActionListDialog extends DialogWindow {
-
- ActionListDialog(
- String title,
- String description,
- TerminalSize actionListPreferredSize,
- boolean canCancel,
- List<Runnable> actions) {
-
- super(title);
- if(actions.isEmpty()) {
- throw new IllegalStateException("ActionListDialog needs at least one item");
- }
-
- ActionListBox listBox = new ActionListBox(actionListPreferredSize);
- for(final Runnable action: actions) {
- listBox.addItem(action.toString(), new Runnable() {
- @Override
- public void run() {
- action.run();
- close();
- }
- });
- }
-
- Panel mainPanel = new Panel();
- mainPanel.setLayoutManager(
- new GridLayout(1)
- .setLeftMarginSize(1)
- .setRightMarginSize(1));
- if(description != null) {
- mainPanel.addComponent(new Label(description));
- mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
- }
- listBox.setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.FILL,
- GridLayout.Alignment.CENTER,
- true,
- false))
- .addTo(mainPanel);
- mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
-
- if(canCancel) {
- Panel buttonPanel = new Panel();
- buttonPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(1));
- buttonPanel.addComponent(new Button(LocalizedString.Cancel.toString(), new Runnable() {
- @Override
- public void run() {
- onCancel();
- }
- }).setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.CENTER, GridLayout.Alignment.CENTER, true, false)));
- buttonPanel.setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.END,
- GridLayout.Alignment.CENTER,
- false,
- false))
- .addTo(mainPanel);
- }
- setComponent(mainPanel);
- }
-
- private void onCancel() {
- close();
- }
-
- /**
- * Helper method for immediately displaying a {@code ActionListDialog}, the method will return when the dialog is
- * closed
- * @param textGUI Text GUI the dialog should be added to
- * @param title Title of the dialog
- * @param description Description of the dialog
- * @param items Items in the {@code ActionListBox}, the label will be taken from each {@code Runnable} by calling
- * {@code toString()} on each one
- */
- public static void showDialog(WindowBasedTextGUI textGUI, String title, String description, Runnable... items) {
- ActionListDialog actionListDialog = new ActionListDialogBuilder()
- .setTitle(title)
- .setDescription(description)
- .addActions(items)
- .build();
- actionListDialog.showDialog(textGUI);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Dialog builder for the {@code ActionListDialog} class, use this to create instances of that class and to customize
- * them
- * @author Martin
- */
-public class ActionListDialogBuilder extends AbstractDialogBuilder<ActionListDialogBuilder, ActionListDialog> {
- private TerminalSize listBoxSize;
- private boolean canCancel;
- private List<Runnable> actions;
-
- /**
- * Default constructor
- */
- public ActionListDialogBuilder() {
- super("ActionListDialogBuilder");
- this.listBoxSize = null;
- this.canCancel = true;
- this.actions = new ArrayList<Runnable>();
- }
-
- @Override
- protected ActionListDialogBuilder self() {
- return this;
- }
-
- @Override
- protected ActionListDialog buildDialog() {
- return new ActionListDialog(
- title,
- description,
- listBoxSize,
- canCancel,
- actions);
- }
-
- /**
- * Sets the size of the internal {@code ActionListBox} in columns and rows, forcing scrollbars to appear if the
- * space isn't big enough to contain all the items
- * @param listBoxSize Size of the {@code ActionListBox}
- * @return Itself
- */
- public ActionListDialogBuilder setListBoxSize(TerminalSize listBoxSize) {
- this.listBoxSize = listBoxSize;
- return this;
- }
-
- /**
- * Returns the specified size of the internal {@code ActionListBox} or {@code null} if there is no size and the list
- * box will attempt to take up enough size to draw all items
- * @return Specified size of the internal {@code ActionListBox} or {@code null} if there is no size
- */
- public TerminalSize getListBoxSize() {
- return listBoxSize;
- }
-
- /**
- * Sets if the dialog can be cancelled or not (default: {@code true})
- * @param canCancel If {@code true}, the user has the option to cancel the dialog, if {@code false} there is no such
- * button in the dialog
- * @return Itself
- */
- public ActionListDialogBuilder setCanCancel(boolean canCancel) {
- this.canCancel = canCancel;
- return this;
- }
-
- /**
- * Returns {@code true} if the dialog can be cancelled once it's opened
- * @return {@code true} if the dialog can be cancelled once it's opened
- */
- public boolean isCanCancel() {
- return canCancel;
- }
-
- /**
- * Adds an additional action to the {@code ActionListBox} that is to be displayed when the dialog is opened
- * @param label Label of the new action
- * @param action Action to perform if the user selects this item
- * @return Itself
- */
- public ActionListDialogBuilder addAction(final String label, final Runnable action) {
- return addAction(new Runnable() {
- @Override
- public String toString() {
- return label;
- }
-
- @Override
- public void run() {
- action.run();
- }
- });
- }
-
- /**
- * Adds an additional action to the {@code ActionListBox} that is to be displayed when the dialog is opened. The
- * label of this item will be derived by calling {@code toString()} on the runnable
- * @param action Action to perform if the user selects this item
- * @return Itself
- */
- public ActionListDialogBuilder addAction(Runnable action) {
- this.actions.add(action);
- return this;
- }
-
- /**
- * Adds additional actions to the {@code ActionListBox} that is to be displayed when the dialog is opened. The
- * label of the items will be derived by calling {@code toString()} on each runnable
- * @param actions Items to add to the {@code ActionListBox}
- * @return Itself
- */
- public ActionListDialogBuilder addActions(Runnable... actions) {
- this.actions.addAll(Arrays.asList(actions));
- return this;
- }
-
- /**
- * Returns a copy of the internal list of actions currently inside this builder that will be assigned to the
- * {@code ActionListBox} in the dialog when built
- * @return Copy of the internal list of actions currently inside this builder
- */
- public List<Runnable> getActions() {
- return new ArrayList<Runnable>(actions);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.gui2.*;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Thin layer on top of the {@code AbstractWindow} class that automatically sets properties and hints to the window to
- * make it act more like a modal dialog window
- */
-public abstract class DialogWindow extends AbstractWindow {
-
- private static final Set<Hint> GLOBAL_DIALOG_HINTS =
- Collections.unmodifiableSet(new HashSet<Hint>(Collections.singletonList(Hint.MODAL)));
-
- /**
- * Default constructor, takes a title for the dialog and runs code shared for dialogs
- * @param title
- */
- protected DialogWindow(String title) {
- super(title);
- setHints(GLOBAL_DIALOG_HINTS);
- }
-
- /**
- * Opens the dialog by showing it on the GUI and doesn't return until the dialog has been closed
- * @param textGUI Text GUI to add the dialog to
- * @return Depending on the {@code DialogWindow} implementation, by default {@code null}
- */
- public Object showDialog(WindowBasedTextGUI textGUI) {
- textGUI.addWindow(this);
-
- //Wait for the window to close, in case the window manager doesn't honor the MODAL hint
- waitUntilClosed();
- return null;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.*;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Comparator;
-
-/**
- * Dialog that allows the user to iterate the file system and pick file to open/save
- *
- * @author Martin
- */
-public class FileDialog extends DialogWindow {
-
- private final ActionListBox fileListBox;
- private final ActionListBox directoryListBox;
- private final TextBox fileBox;
- private final Button okButton;
- private final boolean showHiddenFilesAndDirs;
-
- private File directory;
- private File selectedFile;
-
- /**
- * Default constructor for {@code FileDialog}
- * @param title Title of the dialog
- * @param description Description of the dialog, is displayed at the top of the content area
- * @param actionLabel Label to use on the "confirm" button, for example "open" or "save"
- * @param dialogSize Rough estimation of how big you want the dialog to be
- * @param showHiddenFilesAndDirs If {@code true}, hidden files and directories will be visible
- * @param selectedObject Initially selected file node
- */
- public FileDialog(
- String title,
- String description,
- String actionLabel,
- TerminalSize dialogSize,
- boolean showHiddenFilesAndDirs,
- File selectedObject) {
- super(title);
- this.selectedFile = null;
- this.showHiddenFilesAndDirs = showHiddenFilesAndDirs;
-
- if(selectedObject == null || !selectedObject.exists()) {
- selectedObject = new File("").getAbsoluteFile();
- }
- selectedObject = selectedObject.getAbsoluteFile();
-
- Panel contentPane = new Panel();
- contentPane.setLayoutManager(new GridLayout(2));
-
- if(description != null) {
- new Label(description)
- .setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.BEGINNING,
- GridLayout.Alignment.CENTER,
- false,
- false,
- 2,
- 1))
- .addTo(contentPane);
- }
-
- int unitWidth = dialogSize.getColumns() / 3;
- int unitHeight = dialogSize.getRows();
-
- new FileSystemLocationLabel()
- .setLayoutData(GridLayout.createLayoutData(
- GridLayout.Alignment.FILL,
- GridLayout.Alignment.CENTER,
- true,
- false,
- 2,
- 1))
- .addTo(contentPane);
-
- fileListBox = new ActionListBox(new TerminalSize(unitWidth * 2, unitHeight));
- fileListBox.withBorder(Borders.singleLine())
- .setLayoutData(GridLayout.createLayoutData(
- GridLayout.Alignment.BEGINNING,
- GridLayout.Alignment.CENTER,
- false,
- false))
- .addTo(contentPane);
- directoryListBox = new ActionListBox(new TerminalSize(unitWidth, unitHeight));
- directoryListBox.withBorder(Borders.singleLine())
- .addTo(contentPane);
-
- fileBox = new TextBox()
- .setLayoutData(GridLayout.createLayoutData(
- GridLayout.Alignment.FILL,
- GridLayout.Alignment.CENTER,
- true,
- false,
- 2,
- 1))
- .addTo(contentPane);
-
- new Separator(Direction.HORIZONTAL)
- .setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.FILL,
- GridLayout.Alignment.CENTER,
- true,
- false,
- 2,
- 1))
- .addTo(contentPane);
-
- okButton = new Button(actionLabel, new OkHandler());
- Panels.grid(2,
- okButton,
- new Button(LocalizedString.Cancel.toString(), new CancelHandler()))
- .setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.END, GridLayout.Alignment.CENTER, false, false, 2, 1))
- .addTo(contentPane);
-
- if(selectedObject.isFile()) {
- directory = selectedObject.getParentFile();
- fileBox.setText(selectedObject.getName());
- }
- else if(selectedObject.isDirectory()) {
- directory = selectedObject;
- }
-
- reloadViews(directory);
- setComponent(contentPane);
- }
-
- /**
- * {@inheritDoc}
- * @param textGUI Text GUI to add the dialog to
- * @return The file which was selected in the dialog or {@code null} if the dialog was cancelled
- */
- @Override
- public File showDialog(WindowBasedTextGUI textGUI) {
- selectedFile = null;
- super.showDialog(textGUI);
- return selectedFile;
- }
-
- private class OkHandler implements Runnable {
- @Override
- public void run() {
- if(!fileBox.getText().isEmpty()) {
- selectedFile = new File(directory, fileBox.getText());
- close();
- }
- else {
- MessageDialog.showMessageDialog(getTextGUI(), "Error", "Please select a valid file name", MessageDialogButton.OK);
- }
- }
- }
-
- private class CancelHandler implements Runnable {
- @Override
- public void run() {
- selectedFile = null;
- close();
- }
- }
-
- private class DoNothing implements Runnable {
- @Override
- public void run() {
- }
- }
-
- private void reloadViews(final File directory) {
- directoryListBox.clearItems();
- fileListBox.clearItems();
- File []entries = directory.listFiles();
- if(entries == null) {
- return;
- }
- Arrays.sort(entries, new Comparator<File>() {
- @Override
- public int compare(File o1, File o2) {
- return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase());
- }
- });
- directoryListBox.addItem("..", new Runnable() {
- @Override
- public void run() {
- FileDialog.this.directory = directory.getAbsoluteFile().getParentFile();
- reloadViews(directory.getAbsoluteFile().getParentFile());
- }
- });
- for(final File entry: entries) {
- if(entry.isHidden() && !showHiddenFilesAndDirs) {
- continue;
- }
- if(entry.isDirectory()) {
- directoryListBox.addItem(entry.getName(), new Runnable() {
- @Override
- public void run() {
- FileDialog.this.directory = entry;
- reloadViews(entry);
- }
- });
- }
- else {
- fileListBox.addItem(entry.getName(), new Runnable() {
- @Override
- public void run() {
- fileBox.setText(entry.getName());
- setFocusedInteractable(okButton);
- }
- });
- }
- }
- if(fileListBox.isEmpty()) {
- fileListBox.addItem("<empty>", new DoNothing());
- }
- }
-
- private class FileSystemLocationLabel extends Label {
- public FileSystemLocationLabel() {
- super("");
- setPreferredSize(TerminalSize.ONE);
- }
-
- @Override
- public void onBeforeDrawing() {
- TerminalSize area = getSize();
- String absolutePath = directory.getAbsolutePath();
- int absolutePathLengthInColumns = TerminalTextUtils.getColumnWidth(absolutePath);
- if(area.getColumns() < absolutePathLengthInColumns) {
- absolutePath = absolutePath.substring(absolutePathLengthInColumns - area.getColumns());
- absolutePath = "..." + absolutePath.substring(Math.min(absolutePathLengthInColumns, 3));
- }
- setText(absolutePath);
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.LocalizedString;
-
-import java.io.File;
-
-/**
- * Dialog builder for the {@code FileDialog} class, use this to create instances of that class and to customize
- * them
- * @author Martin
- */
-public class FileDialogBuilder extends AbstractDialogBuilder<FileDialogBuilder, FileDialog> {
-
- private String actionLabel;
- private TerminalSize suggestedSize;
- private File selectedFile;
- private boolean showHiddenDirectories;
-
- /**
- * Default constructor
- */
- public FileDialogBuilder() {
- super("FileDialog");
- actionLabel = LocalizedString.OK.toString();
- suggestedSize = new TerminalSize(45, 10);
- showHiddenDirectories = false;
- selectedFile = null;
- }
-
- @Override
- protected FileDialog buildDialog() {
- return new FileDialog(title, description, actionLabel, suggestedSize, showHiddenDirectories, selectedFile);
- }
-
- /**
- * Defines the label to be but on the confirmation button (default: "ok"). You probably want to set this to
- * {@code LocalizedString.Save.toString()} or {@code LocalizedString.Open.toString()}
- * @param actionLabel Label to put on the confirmation button
- * @return Itself
- */
- public FileDialogBuilder setActionLabel(String actionLabel) {
- this.actionLabel = actionLabel;
- return this;
- }
-
- /**
- * Returns the label on the confirmation button
- * @return Label on the confirmation button
- */
- public String getActionLabel() {
- return actionLabel;
- }
-
- /**
- * Sets the suggested size for the file dialog, it won't have exactly this size but roughly. Default suggested size
- * is 45x10.
- * @param suggestedSize Suggested size for the file dialog
- * @return Itself
- */
- public FileDialogBuilder setSuggestedSize(TerminalSize suggestedSize) {
- this.suggestedSize = suggestedSize;
- return this;
- }
-
- /**
- * Returns the suggested size for the file dialog
- * @return Suggested size for the file dialog
- */
- public TerminalSize getSuggestedSize() {
- return suggestedSize;
- }
-
- /**
- * Sets the file that is initially selected in the dialog
- * @param selectedFile File that is initially selected in the dialog
- * @return Itself
- */
- public FileDialogBuilder setSelectedFile(File selectedFile) {
- this.selectedFile = selectedFile;
- return this;
- }
-
- /**
- * Returns the file that is initially selected in the dialog
- * @return File that is initially selected in the dialog
- */
- public File getSelectedFile() {
- return selectedFile;
- }
-
- /**
- * Sets if hidden files and directories should be visible in the dialog (default: {@code false}
- * @param showHiddenDirectories If {@code true} then hidden files and directories will be visible
- */
- public void setShowHiddenDirectories(boolean showHiddenDirectories) {
- this.showHiddenDirectories = showHiddenDirectories;
- }
-
- /**
- * Checks if hidden files and directories will be visible in the dialog
- * @return If {@code true} then hidden files and directories will be visible
- */
- public boolean isShowHiddenDirectories() {
- return showHiddenDirectories;
- }
-
- @Override
- protected FileDialogBuilder self() {
- return this;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.*;
-
-import java.util.List;
-
-/**
- * Dialog that allows the user to select an item from a list
- *
- * @param <T> Type of elements in the list
- * @author Martin
- */
-public class ListSelectDialog<T> extends DialogWindow {
- private T result;
-
- ListSelectDialog(
- String title,
- String description,
- TerminalSize listBoxPreferredSize,
- boolean canCancel,
- List<T> content) {
-
- super(title);
- this.result = null;
- if(content.isEmpty()) {
- throw new IllegalStateException("ListSelectDialog needs at least one item");
- }
-
- ActionListBox listBox = new ActionListBox(listBoxPreferredSize);
- for(final T item: content) {
- listBox.addItem(item.toString(), new Runnable() {
- @Override
- public void run() {
- onSelect(item);
- }
- });
- }
-
- Panel mainPanel = new Panel();
- mainPanel.setLayoutManager(
- new GridLayout(1)
- .setLeftMarginSize(1)
- .setRightMarginSize(1));
- if(description != null) {
- mainPanel.addComponent(new Label(description));
- mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
- }
- listBox.setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.FILL,
- GridLayout.Alignment.CENTER,
- true,
- false))
- .addTo(mainPanel);
- mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
-
- if(canCancel) {
- Panel buttonPanel = new Panel();
- buttonPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(1));
- buttonPanel.addComponent(new Button(LocalizedString.Cancel.toString(), new Runnable() {
- @Override
- public void run() {
- onCancel();
- }
- }).setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.CENTER, GridLayout.Alignment.CENTER, true, false)));
- buttonPanel.setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.END,
- GridLayout.Alignment.CENTER,
- false,
- false))
- .addTo(mainPanel);
- }
- setComponent(mainPanel);
- }
-
- private void onSelect(T item) {
- result = item;
- close();
- }
-
- private void onCancel() {
- close();
- }
-
- /**
- * {@inheritDoc}
- *
- * @param textGUI Text GUI to add the dialog to
- * @return The item in the list that was selected or {@code null} if the dialog was cancelled
- */
- @Override
- public T showDialog(WindowBasedTextGUI textGUI) {
- result = null;
- super.showDialog(textGUI);
- return result;
- }
-
- /**
- * Shortcut for quickly creating a new dialog
- * @param textGUI Text GUI to add the dialog to
- * @param title Title of the dialog
- * @param description Description of the dialog
- * @param items Items in the dialog
- * @param <T> Type of items in the dialog
- * @return The selected item or {@code null} if cancelled
- */
- public static <T> T showDialog(WindowBasedTextGUI textGUI, String title, String description, T... items) {
- return showDialog(textGUI, title, description, null, items);
- }
-
- /**
- * Shortcut for quickly creating a new dialog
- * @param textGUI Text GUI to add the dialog to
- * @param title Title of the dialog
- * @param description Description of the dialog
- * @param listBoxHeight Maximum height of the list box, scrollbars will be used if there are more items
- * @param items Items in the dialog
- * @param <T> Type of items in the dialog
- * @return The selected item or {@code null} if cancelled
- */
- public static <T> T showDialog(WindowBasedTextGUI textGUI, String title, String description, int listBoxHeight, T... items) {
- int width = 0;
- for(T item: items) {
- width = Math.max(width, TerminalTextUtils.getColumnWidth(item.toString()));
- }
- width += 2;
- return showDialog(textGUI, title, description, new TerminalSize(width, listBoxHeight), items);
- }
-
- /**
- * Shortcut for quickly creating a new dialog
- * @param textGUI Text GUI to add the dialog to
- * @param title Title of the dialog
- * @param description Description of the dialog
- * @param listBoxSize Maximum size of the list box, scrollbars will be used if the items cannot fit
- * @param items Items in the dialog
- * @param <T> Type of items in the dialog
- * @return The selected item or {@code null} if cancelled
- */
- public static <T> T showDialog(WindowBasedTextGUI textGUI, String title, String description, TerminalSize listBoxSize, T... items) {
- ListSelectDialog<T> listSelectDialog = new ListSelectDialogBuilder<T>()
- .setTitle(title)
- .setDescription(description)
- .setListBoxSize(listBoxSize)
- .addListItems(items)
- .build();
- return listSelectDialog.showDialog(textGUI);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Dialog builder for the {@code ListSelectDialog} class, use this to create instances of that class and to customize
- * them
- * @author Martin
- */
-public class ListSelectDialogBuilder<T> extends AbstractDialogBuilder<ListSelectDialogBuilder<T>, ListSelectDialog<T>> {
- private TerminalSize listBoxSize;
- private boolean canCancel;
- private List<T> content;
-
- /**
- * Default constructor
- */
- public ListSelectDialogBuilder() {
- super("ListSelectDialog");
- this.listBoxSize = null;
- this.canCancel = true;
- this.content = new ArrayList<T>();
- }
-
- @Override
- protected ListSelectDialogBuilder<T> self() {
- return this;
- }
-
- @Override
- protected ListSelectDialog<T> buildDialog() {
- return new ListSelectDialog<T>(
- title,
- description,
- listBoxSize,
- canCancel,
- content);
- }
-
- /**
- * Sets the size of the list box in the dialog, scrollbars will be used if there is not enough space to draw all
- * items. If set to {@code null}, the dialog will ask for enough space to be able to draw all items.
- * @param listBoxSize Size of the list box in the dialog
- * @return Itself
- */
- public ListSelectDialogBuilder<T> setListBoxSize(TerminalSize listBoxSize) {
- this.listBoxSize = listBoxSize;
- return this;
- }
-
- /**
- * Size of the list box in the dialog or {@code null} if the dialog will ask for enough space to draw all items
- * @return Size of the list box in the dialog or {@code null} if the dialog will ask for enough space to draw all items
- */
- public TerminalSize getListBoxSize() {
- return listBoxSize;
- }
-
- /**
- * Sets if the dialog can be cancelled or not (default: {@code true})
- * @param canCancel If {@code true}, the user has the option to cancel the dialog, if {@code false} there is no such
- * button in the dialog
- * @return Itself
- */
- public ListSelectDialogBuilder<T> setCanCancel(boolean canCancel) {
- this.canCancel = canCancel;
- return this;
- }
-
- /**
- * Returns {@code true} if the dialog can be cancelled once it's opened
- * @return {@code true} if the dialog can be cancelled once it's opened
- */
- public boolean isCanCancel() {
- return canCancel;
- }
-
- /**
- * Adds an item to the list box at the end
- * @param item Item to add to the list box
- * @return Itself
- */
- public ListSelectDialogBuilder<T> addListItem(T item) {
- this.content.add(item);
- return this;
- }
-
- /**
- * Adds a list of items to the list box at the end, in the order they are passed in
- * @param items Items to add to the list box
- * @return Itself
- */
- public ListSelectDialogBuilder<T> addListItems(T... items) {
- this.content.addAll(Arrays.asList(items));
- return this;
- }
-
- /**
- * Returns a copy of the list of items in the list box
- * @return Copy of the list of items in the list box
- */
- public List<T> getListItems() {
- return new ArrayList<T>(content);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.*;
-
-/**
- * Simple message dialog that displays a message and has optional selection/confirmation buttons
- *
- * @author Martin
- */
-public class MessageDialog extends DialogWindow {
-
- private MessageDialogButton result;
-
- MessageDialog(
- String title,
- String text,
- MessageDialogButton... buttons) {
-
- super(title);
- this.result = null;
- if(buttons == null || buttons.length == 0) {
- buttons = new MessageDialogButton[] { MessageDialogButton.OK };
- }
-
- Panel buttonPanel = new Panel();
- buttonPanel.setLayoutManager(new GridLayout(buttons.length).setHorizontalSpacing(1));
- for(final MessageDialogButton button: buttons) {
- buttonPanel.addComponent(new Button(button.toString(), new Runnable() {
- @Override
- public void run() {
- result = button;
- close();
- }
- }));
- }
-
- Panel mainPanel = new Panel();
- mainPanel.setLayoutManager(
- new GridLayout(1)
- .setLeftMarginSize(1)
- .setRightMarginSize(1));
- mainPanel.addComponent(new Label(text));
- mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
- buttonPanel.setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.END,
- GridLayout.Alignment.CENTER,
- false,
- false))
- .addTo(mainPanel);
- setComponent(mainPanel);
- }
-
- /**
- * {@inheritDoc}
- * @param textGUI Text GUI to add the dialog to
- * @return The selected button's enum value
- */
- @Override
- public MessageDialogButton showDialog(WindowBasedTextGUI textGUI) {
- result = null;
- super.showDialog(textGUI);
- return result;
- }
-
- /**
- * Shortcut for quickly displaying a message box
- * @param textGUI The GUI to display the message box on
- * @param title Title of the message box
- * @param text Main message of the message box
- * @param buttons Buttons that the user can confirm the message box with
- * @return Which button the user selected
- */
- public static MessageDialogButton showMessageDialog(
- WindowBasedTextGUI textGUI,
- String title,
- String text,
- MessageDialogButton... buttons) {
- MessageDialogBuilder builder = new MessageDialogBuilder()
- .setTitle(title)
- .setText(text);
- if(buttons.length == 0) {
- builder.addButton(MessageDialogButton.OK);
- }
- for(MessageDialogButton button: buttons) {
- builder.addButton(button);
- }
- return builder.build().showDialog(textGUI);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Dialog builder for the {@code MessageDialog} class, use this to create instances of that class and to customize
- * them
- * @author Martin
- */
-public class MessageDialogBuilder {
- private String title;
- private String text;
- private List<MessageDialogButton> buttons;
-
- /**
- * Default constructor
- */
- public MessageDialogBuilder() {
- this.title = "MessageDialog";
- this.text = "Text";
- this.buttons = new ArrayList<MessageDialogButton>();
- }
-
- /**
- * Builds a new {@code MessageDialog} from the properties in the builder
- * @return Newly build {@code MessageDialog}
- */
- public MessageDialog build() {
- return new MessageDialog(
- title,
- text,
- buttons.toArray(new MessageDialogButton[buttons.size()]));
- }
-
- /**
- * Sets the title of the {@code MessageDialog}
- * @param title New title of the message dialog
- * @return Itself
- */
- public MessageDialogBuilder setTitle(String title) {
- if(title == null) {
- title = "";
- }
- this.title = title;
- return this;
- }
-
- /**
- * Sets the main text of the {@code MessageDialog}
- * @param text Main text of the {@code MessageDialog}
- * @return Itself
- */
- public MessageDialogBuilder setText(String text) {
- if(text == null) {
- text = "";
- }
- this.text = text;
- return this;
- }
-
- /**
- * Adds a button to the dialog
- * @param button Button to add to the dialog
- * @return Itself
- */
- public MessageDialogBuilder addButton(MessageDialogButton button) {
- if(button != null) {
- buttons.add(button);
- }
- return this;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.gui2.LocalizedString;
-
-/**
- * This enum has the available selection of buttons that you can add to a {@code MessageDialog}. They are used both for
- * specifying which buttons the dialog will have but is also returned when the user makes a selection
- *
- * @author Martin
- */
-public enum MessageDialogButton {
- /**
- * "OK"
- */
- OK(LocalizedString.OK),
- /**
- * "Cancel"
- */
- Cancel(LocalizedString.Cancel),
- /**
- * "Yes"
- */
- Yes(LocalizedString.Yes),
- /**
- * "No"
- */
- No(LocalizedString.No),
- /**
- * "Close"
- */
- Close(LocalizedString.Close),
- /**
- * "Abort"
- */
- Abort(LocalizedString.Abort),
- /**
- * "Ignore"
- */
- Ignore(LocalizedString.Ignore),
- /**
- * "Retry"
- */
- Retry(LocalizedString.Retry),
-
- /**
- * "Continue"
- */
- Continue(LocalizedString.Continue);
-
- private final LocalizedString label;
-
- MessageDialogButton(final LocalizedString label) {
- this.label = label;
- }
-
- @Override
- public String toString() {
- return label.toString();
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.*;
-
-import java.math.BigInteger;
-import java.util.regex.Pattern;
-
-/**
- * {@code TextInputDialog} is a modal text input dialog that prompts the user to enter a text string. The class supports
- * validation and password masking. The builder class to help setup {@code TextInputDialog}s is
- * {@code TextInputDialogBuilder}.
- */
-public class TextInputDialog extends DialogWindow {
-
- private final TextBox textBox;
- private final TextInputDialogResultValidator validator;
- private String result;
-
- TextInputDialog(
- String title,
- String description,
- TerminalSize textBoxPreferredSize,
- String initialContent,
- TextInputDialogResultValidator validator,
- boolean password) {
-
- super(title);
- this.result = null;
- this.textBox = new TextBox(textBoxPreferredSize, initialContent);
- this.validator = validator;
-
- if(password) {
- textBox.setMask('*');
- }
-
- Panel buttonPanel = new Panel();
- buttonPanel.setLayoutManager(new GridLayout(2).setHorizontalSpacing(1));
- buttonPanel.addComponent(new Button(LocalizedString.OK.toString(), new Runnable() {
- @Override
- public void run() {
- onOK();
- }
- }).setLayoutData(GridLayout.createLayoutData(GridLayout.Alignment.CENTER, GridLayout.Alignment.CENTER, true, false)));
- buttonPanel.addComponent(new Button(LocalizedString.Cancel.toString(), new Runnable() {
- @Override
- public void run() {
- onCancel();
- }
- }));
-
- Panel mainPanel = new Panel();
- mainPanel.setLayoutManager(
- new GridLayout(1)
- .setLeftMarginSize(1)
- .setRightMarginSize(1));
- if(description != null) {
- mainPanel.addComponent(new Label(description));
- }
- mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
- textBox.setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.FILL,
- GridLayout.Alignment.CENTER,
- true,
- false))
- .addTo(mainPanel);
- mainPanel.addComponent(new EmptySpace(TerminalSize.ONE));
- buttonPanel.setLayoutData(
- GridLayout.createLayoutData(
- GridLayout.Alignment.END,
- GridLayout.Alignment.CENTER,
- false,
- false))
- .addTo(mainPanel);
- setComponent(mainPanel);
- }
-
- private void onOK() {
- String text = textBox.getText();
- if(validator != null) {
- String errorMessage = validator.validate(text);
- if(errorMessage != null) {
- MessageDialog.showMessageDialog(getTextGUI(), getTitle(), errorMessage, MessageDialogButton.OK);
- return;
- }
- }
- result = text;
- close();
- }
-
- private void onCancel() {
- close();
- }
-
- @Override
- public String showDialog(WindowBasedTextGUI textGUI) {
- result = null;
- super.showDialog(textGUI);
- return result;
- }
-
- /**
- * Shortcut for quickly showing a {@code TextInputDialog}
- * @param textGUI GUI to show the dialog on
- * @param title Title of the dialog
- * @param description Description of the dialog
- * @param initialContent What content to place in the text box initially
- * @return The string the user typed into the text box, or {@code null} if the dialog was cancelled
- */
- public static String showDialog(WindowBasedTextGUI textGUI, String title, String description, String initialContent) {
- TextInputDialog textInputDialog = new TextInputDialogBuilder()
- .setTitle(title)
- .setDescription(description)
- .setInitialContent(initialContent)
- .build();
- return textInputDialog.showDialog(textGUI);
- }
-
- /**
- * Shortcut for quickly showing a {@code TextInputDialog} that only accepts numbers
- * @param textGUI GUI to show the dialog on
- * @param title Title of the dialog
- * @param description Description of the dialog
- * @param initialContent What content to place in the text box initially
- * @return The number the user typed into the text box, or {@code null} if the dialog was cancelled
- */
- public static BigInteger showNumberDialog(WindowBasedTextGUI textGUI, String title, String description, String initialContent) {
- TextInputDialog textInputDialog = new TextInputDialogBuilder()
- .setTitle(title)
- .setDescription(description)
- .setInitialContent(initialContent)
- .setValidationPattern(Pattern.compile("[0-9]+"), "Not a number")
- .build();
- String numberString = textInputDialog.showDialog(textGUI);
- return numberString != null ? new BigInteger(numberString) : null;
- }
-
- /**
- * Shortcut for quickly showing a {@code TextInputDialog} with password masking
- * @param textGUI GUI to show the dialog on
- * @param title Title of the dialog
- * @param description Description of the dialog
- * @param initialContent What content to place in the text box initially
- * @return The string the user typed into the text box, or {@code null} if the dialog was cancelled
- */
- public static String showPasswordDialog(WindowBasedTextGUI textGUI, String title, String description, String initialContent) {
- TextInputDialog textInputDialog = new TextInputDialogBuilder()
- .setTitle(title)
- .setDescription(description)
- .setInitialContent(initialContent)
- .setPasswordInput(true)
- .build();
- return textInputDialog.showDialog(textGUI);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.TerminalSize;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Dialog builder for the {@code TextInputDialog} class, use this to create instances of that class and to customize
- * them
- * @author Martin
- */
-public class TextInputDialogBuilder extends AbstractDialogBuilder<TextInputDialogBuilder, TextInputDialog> {
- private String initialContent;
- private TerminalSize textBoxSize;
- private TextInputDialogResultValidator validator;
- private boolean passwordInput;
-
- /**
- * Default constructor
- */
- public TextInputDialogBuilder() {
- super("TextInputDialog");
- this.initialContent = "";
- this.textBoxSize = null;
- this.validator = null;
- this.passwordInput = false;
- }
-
- @Override
- protected TextInputDialogBuilder self() {
- return this;
- }
-
- protected TextInputDialog buildDialog() {
- TerminalSize size = textBoxSize;
- if ((initialContent == null || initialContent.trim().equals("")) && size == null) {
- size = new TerminalSize(40, 1);
- }
- return new TextInputDialog(
- title,
- description,
- size,
- initialContent,
- validator,
- passwordInput);
- }
-
- /**
- * Sets the initial content the dialog will have
- * @param initialContent Initial content the dialog will have
- * @return Itself
- */
- public TextInputDialogBuilder setInitialContent(String initialContent) {
- this.initialContent = initialContent;
- return this;
- }
-
- /**
- * Returns the initial content the dialog will have
- * @return Initial content the dialog will have
- */
- public String getInitialContent() {
- return initialContent;
- }
-
- /**
- * Sets the size of the text box the dialog will have
- * @param textBoxSize Size of the text box the dialog will have
- * @return Itself
- */
- public TextInputDialogBuilder setTextBoxSize(TerminalSize textBoxSize) {
- this.textBoxSize = textBoxSize;
- return this;
- }
-
- /**
- * Returns the size of the text box the dialog will have
- * @return Size of the text box the dialog will have
- */
- public TerminalSize getTextBoxSize() {
- return textBoxSize;
- }
-
- /**
- * Sets the validator that will be attached to the text box in the dialog
- * @param validator Validator that will be attached to the text box in the dialog
- * @return Itself
- */
- public TextInputDialogBuilder setValidator(TextInputDialogResultValidator validator) {
- this.validator = validator;
- return this;
- }
-
- /**
- * Returns the validator that will be attached to the text box in the dialog
- * @return validator that will be attached to the text box in the dialog
- */
- public TextInputDialogResultValidator getValidator() {
- return validator;
- }
-
- /**
- * Helper method that assigned a validator to the text box the dialog will have which matches the pattern supplied
- * @param pattern Pattern to validate the text box
- * @param errorMessage Error message to show when the pattern doesn't match
- * @return Itself
- */
- public TextInputDialogBuilder setValidationPattern(final Pattern pattern, final String errorMessage) {
- return setValidator(new TextInputDialogResultValidator() {
- @Override
- public String validate(String content) {
- Matcher matcher = pattern.matcher(content);
- if(!matcher.matches()) {
- if(errorMessage == null) {
- return "Invalid input";
- }
- return errorMessage;
- }
- return null;
- }
- });
- }
-
- /**
- * Sets if the text box the dialog will have contains a password and should be masked (default: {@code false})
- * @param passwordInput {@code true} if the text box should be password masked, {@code false} otherwise
- * @return Itself
- */
- public TextInputDialogBuilder setPasswordInput(boolean passwordInput) {
- this.passwordInput = passwordInput;
- return this;
- }
-
- /**
- * Returns {@code true} if the text box the dialog will have contains a password and should be masked
- * @return {@code true} if the text box the dialog will have contains a password and should be masked
- */
- public boolean isPasswordInput() {
- return passwordInput;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.dialogs;
-
-/**
- * Interface to implement for custom validation of text input in a {@code TextInputDialog}
- * @author Martin
- */
-public interface TextInputDialogResultValidator {
- /**
- * Tests the content in the text box if it is valid or not
- * @param content Current content of the text box
- * @return {@code null} if the content is valid, or an error message explaining what's wrong with the content
- * otherwise
- */
- String validate(String content);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.gui2.dialogs;
-
-import com.googlecode.lanterna.gui2.*;
-
-/**
- * Dialog that displays a text message, an optional spinning indicator and an optional progress bar. There is no buttons
- * in this dialog so it has to be explicitly closed through code.
- * @author martin
- */
-public class WaitingDialog extends DialogWindow {
- private WaitingDialog(String title, String text) {
- super(title);
-
- Panel mainPanel = Panels.horizontal(
- new Label(text),
- AnimatedLabel.createClassicSpinningLine());
- setComponent(mainPanel);
- }
-
- @Override
- public Object showDialog(WindowBasedTextGUI textGUI) {
- showDialog(textGUI, true);
- return null;
- }
-
- /**
- * Displays the waiting dialog and optionally blocks until another thread closes it
- * @param textGUI GUI to add the dialog to
- * @param blockUntilClosed If {@code true}, the method call will block until another thread calls {@code close()} on
- * the dialog, otherwise the method call returns immediately
- */
- public void showDialog(WindowBasedTextGUI textGUI, boolean blockUntilClosed) {
- textGUI.addWindow(this);
-
- if(blockUntilClosed) {
- //Wait for the window to close, in case the window manager doesn't honor the MODAL hint
- waitUntilClosed();
- }
- }
-
- /**
- * Creates a new waiting dialog
- * @param title Title of the waiting dialog
- * @param text Text to display on the waiting dialog
- * @return Created waiting dialog
- */
- public static WaitingDialog createDialog(String title, String text) {
- return new WaitingDialog(title, text);
- }
-
- /**
- * Creates and displays a waiting dialog without blocking for it to finish
- * @param textGUI GUI to add the dialog to
- * @param title Title of the waiting dialog
- * @param text Text to display on the waiting dialog
- * @return Created waiting dialog
- */
- public static WaitingDialog showDialog(WindowBasedTextGUI textGUI, String title, String text) {
- WaitingDialog waitingDialog = createDialog(title, text);
- waitingDialog.showDialog(textGUI, false);
- return waitingDialog;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.ThemeDefinition;
-import com.googlecode.lanterna.gui2.TextGUIGraphics;
-
-/**
- * Default implementation of {@code TableCellRenderer}
- * @param <V> Type of data stored in each table cell
- * @author Martin
- */
-public class DefaultTableCellRenderer<V> implements TableCellRenderer<V> {
- @Override
- public TerminalSize getPreferredSize(Table<V> table, V cell, int columnIndex, int rowIndex) {
- String[] lines = getContent(cell);
- int maxWidth = 0;
- for(String line: lines) {
- int length = TerminalTextUtils.getColumnWidth(line);
- if(maxWidth < length) {
- maxWidth = length;
- }
- }
- return new TerminalSize(maxWidth, lines.length);
- }
-
- @Override
- public void drawCell(Table<V> table, V cell, int columnIndex, int rowIndex, TextGUIGraphics textGUIGraphics) {
- ThemeDefinition themeDefinition = textGUIGraphics.getThemeDefinition(Table.class);
- if((table.getSelectedColumn() == columnIndex && table.getSelectedRow() == rowIndex) ||
- (table.getSelectedRow() == rowIndex && !table.isCellSelection())) {
- if(table.isFocused()) {
- textGUIGraphics.applyThemeStyle(themeDefinition.getActive());
- }
- else {
- textGUIGraphics.applyThemeStyle(themeDefinition.getSelected());
- }
- textGUIGraphics.fill(' '); //Make sure to fill the whole cell first
- }
- else {
- textGUIGraphics.applyThemeStyle(themeDefinition.getNormal());
- }
- String[] lines = getContent(cell);
- int rowCount = 0;
- for(String line: lines) {
- textGUIGraphics.putString(0, rowCount++, line);
- }
- }
-
- private String[] getContent(V cell) {
- String[] lines;
- if(cell == null) {
- lines = new String[] { "" };
- }
- else {
- lines = cell.toString().split("\r?\n");
- }
- return lines;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.TextGUIGraphics;
-
-/**
- * Default implementation of {@code TableHeaderRenderer}
- * @author Martin
- */
-public class DefaultTableHeaderRenderer<V> implements TableHeaderRenderer<V> {
- @Override
- public TerminalSize getPreferredSize(Table<V> table, String label, int columnIndex) {
- if(label == null) {
- return TerminalSize.ZERO;
- }
- return new TerminalSize(TerminalTextUtils.getColumnWidth(label), 1);
- }
-
- @Override
- public void drawHeader(Table<V> table, String label, int index, TextGUIGraphics textGUIGraphics) {
- textGUIGraphics.applyThemeStyle(textGUIGraphics.getThemeDefinition(Table.class).getCustom("HEADER"));
- textGUIGraphics.putString(0, 0, label);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.gui2.Direction;
-import com.googlecode.lanterna.gui2.ScrollBar;
-import com.googlecode.lanterna.gui2.TextGUIGraphics;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Default implementation of {@code TableRenderer}
- * @param <V> Type of data stored in each table cell
- * @author Martin
- */
-public class DefaultTableRenderer<V> implements TableRenderer<V> {
-
- private final ScrollBar verticalScrollBar;
- private final ScrollBar horizontalScrollBar;
-
- private TableCellBorderStyle headerVerticalBorderStyle;
- private TableCellBorderStyle headerHorizontalBorderStyle;
- private TableCellBorderStyle cellVerticalBorderStyle;
- private TableCellBorderStyle cellHorizontalBorderStyle;
-
- //So that we don't have to recalculate the size every time. This still isn't optimal but shouganai.
- private TerminalSize cachedSize;
- private List<Integer> columnSizes;
- private List<Integer> rowSizes;
- private int headerSizeInRows;
-
- /**
- * Default constructor
- */
- public DefaultTableRenderer() {
- verticalScrollBar = new ScrollBar(Direction.VERTICAL);
- horizontalScrollBar = new ScrollBar(Direction.HORIZONTAL);
-
- headerVerticalBorderStyle = TableCellBorderStyle.None;
- headerHorizontalBorderStyle = TableCellBorderStyle.EmptySpace;
- cellVerticalBorderStyle = TableCellBorderStyle.None;
- cellHorizontalBorderStyle = TableCellBorderStyle.EmptySpace;
-
- cachedSize = null;
-
- columnSizes = new ArrayList<Integer>();
- rowSizes = new ArrayList<Integer>();
- headerSizeInRows = 0;
- }
-
- /**
- * Sets the style to be used when separating the table header row from the actual "data" cells below. This will
- * cause a new line to be added under the header labels, unless set to {@code TableCellBorderStyle.None}.
- *
- * @param headerVerticalBorderStyle Style to use to separate Table header from body
- */
- public void setHeaderVerticalBorderStyle(TableCellBorderStyle headerVerticalBorderStyle) {
- this.headerVerticalBorderStyle = headerVerticalBorderStyle;
- }
-
- /**
- * Sets the style to be used when separating the table header labels from each other. This will cause a new
- * column to be added in between each label, unless set to {@code TableCellBorderStyle.None}.
- *
- * @param headerHorizontalBorderStyle Style to use when separating header columns horizontally
- */
- public void setHeaderHorizontalBorderStyle(TableCellBorderStyle headerHorizontalBorderStyle) {
- this.headerHorizontalBorderStyle = headerHorizontalBorderStyle;
- }
-
- /**
- * Sets the style to be used when vertically separating table cells from each other. This will cause a new line
- * to be added between every row, unless set to {@code TableCellBorderStyle.None}.
- *
- * @param cellVerticalBorderStyle Style to use to separate table cells vertically
- */
- public void setCellVerticalBorderStyle(TableCellBorderStyle cellVerticalBorderStyle) {
- this.cellVerticalBorderStyle = cellVerticalBorderStyle;
- }
-
- /**
- * Sets the style to be used when horizontally separating table cells from each other. This will cause a new
- * column to be added between every row, unless set to {@code TableCellBorderStyle.None}.
- *
- * @param cellHorizontalBorderStyle Style to use to separate table cells horizontally
- */
- public void setCellHorizontalBorderStyle(TableCellBorderStyle cellHorizontalBorderStyle) {
- this.cellHorizontalBorderStyle = cellHorizontalBorderStyle;
- }
-
- private boolean isHorizontallySpaced() {
- return headerHorizontalBorderStyle != TableCellBorderStyle.None ||
- cellHorizontalBorderStyle != TableCellBorderStyle.None;
- }
-
- @Override
- public TerminalSize getPreferredSize(Table<V> table) {
- //Quick bypass if the table hasn't changed
- if(!table.isInvalid() && cachedSize != null) {
- return cachedSize;
- }
-
- TableModel<V> tableModel = table.getTableModel();
- int viewLeftColumn = table.getViewLeftColumn();
- int viewTopRow = table.getViewTopRow();
- int visibleColumns = table.getVisibleColumns();
- int visibleRows = table.getVisibleRows();
- List<List<V>> rows = tableModel.getRows();
- List<String> columnHeaders = tableModel.getColumnLabels();
- TableHeaderRenderer<V> tableHeaderRenderer = table.getTableHeaderRenderer();
- TableCellRenderer<V> tableCellRenderer = table.getTableCellRenderer();
-
- if(visibleColumns == 0) {
- visibleColumns = tableModel.getColumnCount();
- }
- if(visibleRows == 0) {
- visibleRows = tableModel.getRowCount();
- }
-
- columnSizes.clear();
- rowSizes.clear();
-
- if(tableModel.getColumnCount() == 0) {
- return TerminalSize.ZERO;
- }
-
- for(int rowIndex = 0; rowIndex < rows.size(); rowIndex++) {
- List<V> row = rows.get(rowIndex);
- for(int columnIndex = viewLeftColumn; columnIndex < Math.min(row.size(), viewLeftColumn + visibleColumns); columnIndex++) {
- V cell = row.get(columnIndex);
- int columnSize = tableCellRenderer.getPreferredSize(table, cell, columnIndex, rowIndex).getColumns();
- int listOffset = columnIndex - viewLeftColumn;
- if(columnSizes.size() == listOffset) {
- columnSizes.add(columnSize);
- }
- else {
- if(columnSizes.get(listOffset) < columnSize) {
- columnSizes.set(listOffset, columnSize);
- }
- }
- }
-
- //Do the headers too, on the first iteration
- if(rowIndex == 0) {
- for(int columnIndex = viewLeftColumn; columnIndex < Math.min(row.size(), viewLeftColumn + visibleColumns); columnIndex++) {
- int columnSize = tableHeaderRenderer.getPreferredSize(table, columnHeaders.get(columnIndex), columnIndex).getColumns();
- int listOffset = columnIndex - viewLeftColumn;
- if(columnSizes.size() == listOffset) {
- columnSizes.add(columnSize);
- }
- else {
- if(columnSizes.get(listOffset) < columnSize) {
- columnSizes.set(listOffset, columnSize);
- }
- }
- }
- }
- }
-
- for(int columnIndex = 0; columnIndex < columnHeaders.size(); columnIndex++) {
- for(int rowIndex = viewTopRow; rowIndex < Math.min(rows.size(), viewTopRow + visibleRows); rowIndex++) {
- V cell = rows.get(rowIndex).get(columnIndex);
- int rowSize = tableCellRenderer.getPreferredSize(table, cell, columnIndex, rowIndex).getRows();
- int listOffset = rowIndex - viewTopRow;
- if(rowSizes.size() == listOffset) {
- rowSizes.add(rowSize);
- }
- else {
- if(rowSizes.get(listOffset) < rowSize) {
- rowSizes.set(listOffset, rowSize);
- }
- }
- }
- }
-
- int preferredRowSize = 0;
- int preferredColumnSize = 0;
- for(int size: columnSizes) {
- preferredColumnSize += size;
- }
- for(int size: rowSizes) {
- preferredRowSize += size;
- }
-
- headerSizeInRows = 0;
- for(int columnIndex = 0; columnIndex < columnHeaders.size(); columnIndex++) {
- int headerRows = tableHeaderRenderer.getPreferredSize(table, columnHeaders.get(columnIndex), columnIndex).getRows();
- if(headerSizeInRows < headerRows) {
- headerSizeInRows = headerRows;
- }
- }
- preferredRowSize += headerSizeInRows;
-
- if(headerVerticalBorderStyle != TableCellBorderStyle.None) {
- preferredRowSize++; //Spacing between header and body
- }
- if(cellVerticalBorderStyle != TableCellBorderStyle.None) {
- if(!rows.isEmpty()) {
- preferredRowSize += Math.min(rows.size(), visibleRows) - 1; //Vertical space between cells
- }
- }
- if(isHorizontallySpaced()) {
- if(!columnHeaders.isEmpty()) {
- preferredColumnSize += Math.min(tableModel.getColumnCount(), visibleColumns) - 1; //Spacing between the columns
- }
- }
-
- //Add on space taken by scrollbars (if needed)
- if(visibleRows < rows.size()) {
- preferredColumnSize++;
- }
- if(visibleColumns < tableModel.getColumnCount()) {
- preferredRowSize++;
- }
-
- cachedSize = new TerminalSize(preferredColumnSize, preferredRowSize);
- return cachedSize;
- }
-
- @Override
- public TerminalPosition getCursorLocation(Table<V> component) {
- return null;
- }
-
- @Override
- public void drawComponent(TextGUIGraphics graphics, Table<V> table) {
- //Get the size
- TerminalSize area = graphics.getSize();
-
- //Don't even bother
- if(area.getRows() == 0 || area.getColumns() == 0) {
- return;
- }
-
- int topPosition = drawHeader(graphics, table);
- drawRows(graphics, table, topPosition);
- }
-
- private int drawHeader(TextGUIGraphics graphics, Table<V> table) {
- TableHeaderRenderer<V> tableHeaderRenderer = table.getTableHeaderRenderer();
- List<String> headers = table.getTableModel().getColumnLabels();
- int viewLeftColumn = table.getViewLeftColumn();
- int visibleColumns = table.getVisibleColumns();
- if(visibleColumns == 0) {
- visibleColumns = table.getTableModel().getColumnCount();
- }
- int topPosition = 0;
- int leftPosition = 0;
- int endColumnIndex = Math.min(headers.size(), viewLeftColumn + visibleColumns);
- for(int index = viewLeftColumn; index < endColumnIndex; index++) {
- String label = headers.get(index);
- TerminalSize size = new TerminalSize(columnSizes.get(index - viewLeftColumn), headerSizeInRows);
- tableHeaderRenderer.drawHeader(table, label, index, graphics.newTextGraphics(new TerminalPosition(leftPosition, 0), size));
- leftPosition += size.getColumns();
- if(headerHorizontalBorderStyle != TableCellBorderStyle.None && index < (endColumnIndex - 1)) {
- graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getNormal());
- graphics.setCharacter(leftPosition, 0, getVerticalCharacter(headerHorizontalBorderStyle));
- leftPosition++;
- }
- }
- topPosition += headerSizeInRows;
-
- if(headerVerticalBorderStyle != TableCellBorderStyle.None) {
- leftPosition = 0;
- graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getNormal());
- for(int i = 0; i < columnSizes.size(); i++) {
- if(i > 0) {
- graphics.setCharacter(
- leftPosition,
- topPosition,
- getJunctionCharacter(
- headerVerticalBorderStyle,
- headerHorizontalBorderStyle,
- cellHorizontalBorderStyle));
- leftPosition++;
- }
- int columnWidth = columnSizes.get(i);
- graphics.drawLine(leftPosition, topPosition, leftPosition + columnWidth - 1, topPosition, getHorizontalCharacter(headerVerticalBorderStyle));
- leftPosition += columnWidth;
- }
- //Expand out the line in case the area is bigger
- if(leftPosition < graphics.getSize().getColumns()) {
- graphics.drawLine(leftPosition, topPosition, graphics.getSize().getColumns() - 1, topPosition, getHorizontalCharacter(headerVerticalBorderStyle));
- }
- topPosition++;
- }
- return topPosition;
- }
-
- private void drawRows(TextGUIGraphics graphics, Table<V> table, int topPosition) {
- TerminalSize area = graphics.getSize();
- TableCellRenderer<V> tableCellRenderer = table.getTableCellRenderer();
- TableModel<V> tableModel = table.getTableModel();
- List<List<V>> rows = tableModel.getRows();
- int viewTopRow = table.getViewTopRow();
- int viewLeftColumn = table.getViewLeftColumn();
- int visibleRows = table.getVisibleRows();
- int visibleColumns = table.getVisibleColumns();
- if(visibleColumns == 0) {
- visibleColumns = tableModel.getColumnCount();
- }
- if(visibleRows == 0) {
- visibleRows = tableModel.getRowCount();
- }
-
- //Exit if there are no rows
- if(rows.isEmpty()) {
- return;
- }
-
- //Draw scrollbars (if needed)
- if(visibleRows < rows.size()) {
- TerminalSize verticalScrollBarPreferredSize = verticalScrollBar.getPreferredSize();
- int scrollBarHeight = graphics.getSize().getRows() - topPosition;
- if(visibleColumns < tableModel.getColumnCount()) {
- scrollBarHeight--;
- }
- verticalScrollBar.setPosition(new TerminalPosition(graphics.getSize().getColumns() - verticalScrollBarPreferredSize.getColumns(), topPosition));
- verticalScrollBar.setSize(verticalScrollBarPreferredSize.withRows(scrollBarHeight));
- verticalScrollBar.setScrollMaximum(rows.size());
- verticalScrollBar.setViewSize(visibleRows);
- verticalScrollBar.setScrollPosition(viewTopRow);
- verticalScrollBar.draw(graphics.newTextGraphics(verticalScrollBar.getPosition(), verticalScrollBar.getSize()));
- graphics = graphics.newTextGraphics(TerminalPosition.TOP_LEFT_CORNER, graphics.getSize().withRelativeColumns(-verticalScrollBarPreferredSize.getColumns()));
- }
- if(visibleColumns < tableModel.getColumnCount()) {
- TerminalSize horizontalScrollBarPreferredSize = horizontalScrollBar.getPreferredSize();
- int scrollBarWidth = graphics.getSize().getColumns();
- horizontalScrollBar.setPosition(new TerminalPosition(0, graphics.getSize().getRows() - horizontalScrollBarPreferredSize.getRows()));
- horizontalScrollBar.setSize(horizontalScrollBarPreferredSize.withColumns(scrollBarWidth));
- horizontalScrollBar.setScrollMaximum(tableModel.getColumnCount());
- horizontalScrollBar.setViewSize(visibleColumns);
- horizontalScrollBar.setScrollPosition(viewLeftColumn);
- horizontalScrollBar.draw(graphics.newTextGraphics(horizontalScrollBar.getPosition(), horizontalScrollBar.getSize()));
- graphics = graphics.newTextGraphics(TerminalPosition.TOP_LEFT_CORNER, graphics.getSize().withRelativeRows(-horizontalScrollBarPreferredSize.getRows()));
- }
-
- int leftPosition;
- for(int rowIndex = viewTopRow; rowIndex < Math.min(viewTopRow + visibleRows, rows.size()); rowIndex++) {
- leftPosition = 0;
- List<V> row = rows.get(rowIndex);
- for(int columnIndex = viewLeftColumn; columnIndex < Math.min(viewLeftColumn + visibleColumns, row.size()); columnIndex++) {
- if(columnIndex > viewLeftColumn) {
- if(table.getSelectedRow() == rowIndex && !table.isCellSelection()) {
- if(table.isFocused()) {
- graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getActive());
- }
- else {
- graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getSelected());
- }
- }
- else {
- graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getNormal());
- }
- graphics.setCharacter(leftPosition, topPosition, getVerticalCharacter(cellHorizontalBorderStyle));
- leftPosition++;
- }
- V cell = row.get(columnIndex);
- TerminalPosition cellPosition = new TerminalPosition(leftPosition, topPosition);
- TerminalSize cellArea = new TerminalSize(columnSizes.get(columnIndex - viewLeftColumn), rowSizes.get(rowIndex - viewTopRow));
- tableCellRenderer.drawCell(table, cell, columnIndex, rowIndex, graphics.newTextGraphics(cellPosition, cellArea));
- leftPosition += cellArea.getColumns();
- if(leftPosition > area.getColumns()) {
- break;
- }
- }
- topPosition += rowSizes.get(rowIndex - viewTopRow);
- if(cellVerticalBorderStyle != TableCellBorderStyle.None) {
- leftPosition = 0;
- graphics.applyThemeStyle(graphics.getThemeDefinition(Table.class).getNormal());
- for(int i = 0; i < columnSizes.size(); i++) {
- if(i > 0) {
- graphics.setCharacter(
- leftPosition,
- topPosition,
- getJunctionCharacter(
- cellVerticalBorderStyle,
- cellHorizontalBorderStyle,
- cellHorizontalBorderStyle));
- leftPosition++;
- }
- int columnWidth = columnSizes.get(i);
- graphics.drawLine(leftPosition, topPosition, leftPosition + columnWidth - 1, topPosition, getHorizontalCharacter(cellVerticalBorderStyle));
- leftPosition += columnWidth;
- }
- topPosition += 1;
- }
- if(topPosition > area.getRows()) {
- break;
- }
- }
- }
-
- private char getHorizontalCharacter(TableCellBorderStyle style) {
- switch(style) {
- case SingleLine:
- return Symbols.SINGLE_LINE_HORIZONTAL;
- case DoubleLine:
- return Symbols.DOUBLE_LINE_HORIZONTAL;
- default:
- return ' ';
- }
- }
-
- private char getVerticalCharacter(TableCellBorderStyle style) {
- switch(style) {
- case SingleLine:
- return Symbols.SINGLE_LINE_VERTICAL;
- case DoubleLine:
- return Symbols.DOUBLE_LINE_VERTICAL;
- default:
- return ' ';
- }
- }
-
- private char getJunctionCharacter(TableCellBorderStyle mainStyle, TableCellBorderStyle styleAbove, TableCellBorderStyle styleBelow) {
- if(mainStyle == TableCellBorderStyle.SingleLine) {
- if(styleAbove == TableCellBorderStyle.SingleLine) {
- if(styleBelow == TableCellBorderStyle.SingleLine) {
- return Symbols.SINGLE_LINE_CROSS;
- }
- else if(styleBelow == TableCellBorderStyle.DoubleLine) {
- //There isn't any character for this, give upper side priority
- return Symbols.SINGLE_LINE_T_UP;
- }
- else {
- return Symbols.SINGLE_LINE_T_UP;
- }
- }
- else if(styleAbove == TableCellBorderStyle.DoubleLine) {
- if(styleBelow == TableCellBorderStyle.SingleLine) {
- //There isn't any character for this, give upper side priority
- return Symbols.SINGLE_LINE_T_DOUBLE_UP;
- }
- else if(styleBelow == TableCellBorderStyle.DoubleLine) {
- return Symbols.DOUBLE_LINE_VERTICAL_SINGLE_LINE_CROSS;
- }
- else {
- return Symbols.SINGLE_LINE_T_DOUBLE_UP;
- }
- }
- else {
- if(styleBelow == TableCellBorderStyle.SingleLine) {
- return Symbols.SINGLE_LINE_T_DOWN;
- }
- else if(styleBelow == TableCellBorderStyle.DoubleLine) {
- return Symbols.SINGLE_LINE_T_DOUBLE_DOWN;
- }
- else {
- return Symbols.SINGLE_LINE_HORIZONTAL;
- }
- }
- }
- else if(mainStyle == TableCellBorderStyle.DoubleLine) {
- if(styleAbove == TableCellBorderStyle.SingleLine) {
- if(styleBelow == TableCellBorderStyle.SingleLine) {
- return Symbols.DOUBLE_LINE_HORIZONTAL_SINGLE_LINE_CROSS;
- }
- else if(styleBelow == TableCellBorderStyle.DoubleLine) {
- //There isn't any character for this, give upper side priority
- return Symbols.DOUBLE_LINE_T_SINGLE_UP;
- }
- else {
- return Symbols.DOUBLE_LINE_T_SINGLE_UP;
- }
- }
- else if(styleAbove == TableCellBorderStyle.DoubleLine) {
- if(styleBelow == TableCellBorderStyle.SingleLine) {
- //There isn't any character for this, give upper side priority
- return Symbols.DOUBLE_LINE_T_UP;
- }
- else if(styleBelow == TableCellBorderStyle.DoubleLine) {
- return Symbols.DOUBLE_LINE_CROSS;
- }
- else {
- return Symbols.DOUBLE_LINE_T_UP;
- }
- }
- else {
- if(styleBelow == TableCellBorderStyle.SingleLine) {
- return Symbols.DOUBLE_LINE_T_SINGLE_DOWN;
- }
- else if(styleBelow == TableCellBorderStyle.DoubleLine) {
- return Symbols.DOUBLE_LINE_T_DOWN;
- }
- else {
- return Symbols.DOUBLE_LINE_HORIZONTAL;
- }
- }
- }
- else {
- return ' ';
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-import com.googlecode.lanterna.gui2.*;
-import com.googlecode.lanterna.input.KeyStroke;
-
-/**
- * The table class is an interactable component that displays a grid of cells containing data along with a header of
- * labels. It supports scrolling when the number of rows and/or columns gets too large to fit and also supports
- * user selection which is either row-based or cell-based. User will move the current selection by using the arrow keys
- * on the keyboard.
- * @param <V> Type of data to store in the table cells, presented through {@code toString()}
- * @author Martin
- */
-public class Table<V> extends AbstractInteractableComponent<Table<V>> {
- private TableModel<V> tableModel;
- private TableHeaderRenderer<V> tableHeaderRenderer;
- private TableCellRenderer<V> tableCellRenderer;
- private Runnable selectAction;
- private boolean cellSelection;
- private int visibleRows;
- private int visibleColumns;
- private int viewTopRow;
- private int viewLeftColumn;
- private int selectedRow;
- private int selectedColumn;
- private boolean escapeByArrowKey;
-
- /**
- * Creates a new {@code Table} with the number of columns as specified by the array of labels
- * @param columnLabels Creates one column per label in the array, must be more than one
- */
- public Table(String... columnLabels) {
- if(columnLabels.length == 0) {
- throw new IllegalArgumentException("Table needs at least one column");
- }
- this.tableHeaderRenderer = new DefaultTableHeaderRenderer<V>();
- this.tableCellRenderer = new DefaultTableCellRenderer<V>();
- this.tableModel = new TableModel<V>(columnLabels);
- this.selectAction = null;
- this.visibleColumns = 0;
- this.visibleRows = 0;
- this.viewTopRow = 0;
- this.viewLeftColumn = 0;
- this.cellSelection = false;
- this.selectedRow = 0;
- this.selectedColumn = -1;
- this.escapeByArrowKey = true;
- }
-
- /**
- * Returns the underlying table model
- * @return Underlying table model
- */
- public TableModel<V> getTableModel() {
- return tableModel;
- }
-
- /**
- * Updates the table with a new table model, effectively replacing the content of the table completely
- * @param tableModel New table model
- * @return Itself
- */
- public synchronized Table<V> setTableModel(TableModel<V> tableModel) {
- if(tableModel == null) {
- throw new IllegalArgumentException("Cannot assign a null TableModel");
- }
- this.tableModel = tableModel;
- invalidate();
- return this;
- }
-
- /**
- * Returns the {@code TableCellRenderer} used by this table when drawing cells
- * @return {@code TableCellRenderer} used by this table when drawing cells
- */
- public TableCellRenderer<V> getTableCellRenderer() {
- return tableCellRenderer;
- }
-
- /**
- * Replaces the {@code TableCellRenderer} used by this table when drawing cells
- * @param tableCellRenderer New {@code TableCellRenderer} to use
- * @return Itself
- */
- public synchronized Table<V> setTableCellRenderer(TableCellRenderer<V> tableCellRenderer) {
- this.tableCellRenderer = tableCellRenderer;
- invalidate();
- return this;
- }
-
- /**
- * Returns the {@code TableHeaderRenderer} used by this table when drawing the table's header
- * @return {@code TableHeaderRenderer} used by this table when drawing the table's header
- */
- public TableHeaderRenderer<V> getTableHeaderRenderer() {
- return tableHeaderRenderer;
- }
-
- /**
- * Replaces the {@code TableHeaderRenderer} used by this table when drawing the table's header
- * @param tableHeaderRenderer New {@code TableHeaderRenderer} to use
- * @return Itself
- */
- public synchronized Table<V> setTableHeaderRenderer(TableHeaderRenderer<V> tableHeaderRenderer) {
- this.tableHeaderRenderer = tableHeaderRenderer;
- invalidate();
- return this;
- }
-
- /**
- * Sets the number of columns this table should show. If there are more columns in the table model, a scrollbar will
- * be used to allow the user to scroll left and right and view all columns.
- * @param visibleColumns Number of columns to display at once
- */
- public synchronized void setVisibleColumns(int visibleColumns) {
- this.visibleColumns = visibleColumns;
- invalidate();
- }
-
- /**
- * Returns the number of columns this table will show. If there are more columns in the table model, a scrollbar
- * will be used to allow the user to scroll left and right and view all columns.
- * @return Number of visible columns for this table
- */
- public int getVisibleColumns() {
- return visibleColumns;
- }
-
- /**
- * Sets the number of rows this table will show. If there are more rows in the table model, a scrollbar will be used
- * to allow the user to scroll up and down and view all rows.
- * @param visibleRows Number of rows to display at once
- */
- public synchronized void setVisibleRows(int visibleRows) {
- this.visibleRows = visibleRows;
- invalidate();
- }
-
- /**
- * Returns the number of rows this table will show. If there are more rows in the table model, a scrollbar will be
- * used to allow the user to scroll up and down and view all rows.
- * @return Number of rows to display at once
- */
- public int getVisibleRows() {
- return visibleRows;
- }
-
- /**
- * Returns the index of the row that is currently the first row visible. This is always 0 unless scrolling has been
- * enabled and either the user or the software (through {@code setViewTopRow(..)}) has scrolled down.
- * @return Index of the row that is currently the first row visible
- */
- public int getViewTopRow() {
- return viewTopRow;
- }
-
- /**
- * Sets the view row offset for the first row to display in the table. Calling this with 0 will make the first row
- * in the model be the first visible row in the table.
- *
- * @param viewTopRow Index of the row that is currently the first row visible
- * @return Itself
- */
- public synchronized Table<V> setViewTopRow(int viewTopRow) {
- this.viewTopRow = viewTopRow;
- return this;
- }
-
- /**
- * Returns the index of the column that is currently the first column visible. This is always 0 unless scrolling has
- * been enabled and either the user or the software (through {@code setViewLeftColumn(..)}) has scrolled to the
- * right.
- * @return Index of the column that is currently the first column visible
- */
- public int getViewLeftColumn() {
- return viewLeftColumn;
- }
-
- /**
- * Sets the view column offset for the first column to display in the table. Calling this with 0 will make the first
- * column in the model be the first visible column in the table.
- *
- * @param viewLeftColumn Index of the column that is currently the first column visible
- * @return Itself
- */
- public synchronized Table<V> setViewLeftColumn(int viewLeftColumn) {
- this.viewLeftColumn = viewLeftColumn;
- return this;
- }
-
- /**
- * Returns the currently selection column index, if in cell-selection mode. Otherwise it returns -1.
- * @return In cell-selection mode returns the index of the selected column, otherwise -1
- */
- public int getSelectedColumn() {
- return selectedColumn;
- }
-
- /**
- * If in cell selection mode, updates which column is selected and ensures the selected column is visible in the
- * view. If not in cell selection mode, does nothing.
- * @param selectedColumn Index of the column that should be selected
- * @return Itself
- */
- public synchronized Table<V> setSelectedColumn(int selectedColumn) {
- if(cellSelection) {
- this.selectedColumn = selectedColumn;
- ensureSelectedItemIsVisible();
- }
- return this;
- }
-
- /**
- * Returns the index of the currently selected row
- * @return Index of the currently selected row
- */
- public int getSelectedRow() {
- return selectedRow;
- }
-
- /**
- * Sets the index of the selected row and ensures the selected row is visible in the view
- * @param selectedRow Index of the row to select
- * @return Itself
- */
- public synchronized Table<V> setSelectedRow(int selectedRow) {
- this.selectedRow = selectedRow;
- ensureSelectedItemIsVisible();
- return this;
- }
-
- /**
- * If {@code true}, the user will be able to select and navigate individual cells, otherwise the user can only
- * select full rows.
- * @param cellSelection {@code true} if cell selection should be enabled, {@code false} for row selection
- * @return Itself
- */
- public synchronized Table<V> setCellSelection(boolean cellSelection) {
- this.cellSelection = cellSelection;
- if(cellSelection && selectedColumn == -1) {
- selectedColumn = 0;
- }
- else if(!cellSelection) {
- selectedColumn = -1;
- }
- return this;
- }
-
- /**
- * Returns {@code true} if this table is in cell-selection mode, otherwise {@code false}
- * @return {@code true} if this table is in cell-selection mode, otherwise {@code false}
- */
- public boolean isCellSelection() {
- return cellSelection;
- }
-
- /**
- * Assigns an action to run whenever the user presses the enter key while focused on the table. If called with
- * {@code null}, no action will be run.
- * @param selectAction Action to perform when user presses the enter key
- * @return Itself
- */
- public synchronized Table<V> setSelectAction(Runnable selectAction) {
- this.selectAction = selectAction;
- return this;
- }
-
- /**
- * Returns {@code true} if this table can be navigated away from when the selected row is at one of the extremes and
- * the user presses the array key to continue in that direction. With {@code escapeByArrowKey} set to {@code true},
- * this will move focus away from the table in the direction the user pressed, if {@code false} then nothing will
- * happen.
- * @return {@code true} if user can switch focus away from the table using arrow keys, {@code false} otherwise
- */
- public boolean isEscapeByArrowKey() {
- return escapeByArrowKey;
- }
-
- /**
- * Sets the flag for if this table can be navigated away from when the selected row is at one of the extremes and
- * the user presses the array key to continue in that direction. With {@code escapeByArrowKey} set to {@code true},
- * this will move focus away from the table in the direction the user pressed, if {@code false} then nothing will
- * happen.
- * @param escapeByArrowKey {@code true} if user can switch focus away from the table using arrow keys, {@code false} otherwise
- * @return Itself
- */
- public synchronized Table<V> setEscapeByArrowKey(boolean escapeByArrowKey) {
- this.escapeByArrowKey = escapeByArrowKey;
- return this;
- }
-
- @Override
- protected TableRenderer<V> createDefaultRenderer() {
- return new DefaultTableRenderer<V>();
- }
-
- @Override
- public TableRenderer<V> getRenderer() {
- return (TableRenderer<V>)super.getRenderer();
- }
-
- @Override
- public Result handleKeyStroke(KeyStroke keyStroke) {
- switch(keyStroke.getKeyType()) {
- case ArrowUp:
- if(selectedRow > 0) {
- selectedRow--;
- }
- else if(escapeByArrowKey) {
- return Result.MOVE_FOCUS_UP;
- }
- break;
- case ArrowDown:
- if(selectedRow < tableModel.getRowCount() - 1) {
- selectedRow++;
- }
- else if(escapeByArrowKey) {
- return Result.MOVE_FOCUS_DOWN;
- }
- break;
- case ArrowLeft:
- if(cellSelection && selectedColumn > 0) {
- selectedColumn--;
- }
- else if(escapeByArrowKey) {
- return Result.MOVE_FOCUS_LEFT;
- }
- break;
- case ArrowRight:
- if(cellSelection && selectedColumn < tableModel.getColumnCount() - 1) {
- selectedColumn++;
- }
- else if(escapeByArrowKey) {
- return Result.MOVE_FOCUS_RIGHT;
- }
- break;
- case Enter:
- Runnable runnable = selectAction; //To avoid synchronizing
- if(runnable != null) {
- runnable.run();
- }
- else {
- return Result.MOVE_FOCUS_NEXT;
- }
- break;
- default:
- return super.handleKeyStroke(keyStroke);
- }
- ensureSelectedItemIsVisible();
- invalidate();
- return Result.HANDLED;
- }
-
- private void ensureSelectedItemIsVisible() {
- if(visibleRows > 0 && selectedRow < viewTopRow) {
- viewTopRow = selectedRow;
- }
- else if(visibleRows > 0 && selectedRow >= viewTopRow + visibleRows) {
- viewTopRow = Math.max(0, selectedRow - visibleRows + 1);
- }
- if(selectedColumn != -1) {
- if(visibleColumns > 0 && selectedColumn < viewLeftColumn) {
- viewLeftColumn = selectedColumn;
- }
- else if(visibleColumns > 0 && selectedColumn >= viewLeftColumn + visibleColumns) {
- viewLeftColumn = Math.max(0, selectedColumn - visibleColumns + 1);
- }
- }
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-/**
- * Describing how table cells are separated when drawn
- */
-public enum TableCellBorderStyle {
- /**
- * There is no separation between table cells, they are drawn immediately next to each other
- */
- None,
- /**
- * There is a single space of separation between the cells, drawn as a single line
- */
- SingleLine,
- /**
- * There is a single space of separation between the cells, drawn as a double line
- */
- DoubleLine,
- /**
- * There is a single space of separation between the cells, kept empty
- */
- EmptySpace,
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.TextGUIGraphics;
-
-/**
- * The main interface to implement when you need to customize the way table cells are drawn
- *
- * @param <V> Type of data in the table cells
- * @author Martin
- */
-public interface TableCellRenderer<V> {
- /**
- * Called by the table when it wants to know how big a particular table cell should be
- * @param table Table containing the cell
- * @param cell Data stored in the cell
- * @param columnIndex Column index of the cell
- * @param rowIndex Row index of the cell
- * @return Size this renderer would like the cell to have
- */
- TerminalSize getPreferredSize(Table<V> table, V cell, int columnIndex, int rowIndex);
-
- /**
- * Called by the table when it's time to draw a cell, you can see how much size is available by checking the size of
- * the {@code textGUIGraphics}. The top-left position of the graphics object is the top-left position of this cell.
- * @param table Table containing the cell
- * @param cell Data stored in the cell
- * @param columnIndex Column index of the cell
- * @param rowIndex Row index of the cell
- * @param textGUIGraphics Graphics object to draw with
- */
- void drawCell(Table<V> table, V cell, int columnIndex, int rowIndex, TextGUIGraphics textGUIGraphics);
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.TextGUIGraphics;
-
-/**
- * This interface can be implemented if you want to customize how table headers are drawn.
- * @param <V> Type of data stored in each table cell
- * @author Martin
- */
-public interface TableHeaderRenderer<V> {
- /**
- * Called by the table when it wants to know how big a particular table header should be
- * @param table Table containing the header
- * @param label Label for this header
- * @param columnIndex Column index of the header
- * @return Size this renderer would like the header to have
- */
- TerminalSize getPreferredSize(Table<V> table, String label, int columnIndex);
-
- /**
- * Called by the table when it's time to draw a header, you can see how much size is available by checking the size
- * of the {@code textGUIGraphics}. The top-left position of the graphics object is the top-left position of this
- * header.
- * @param table Table containing the header
- * @param label Label for this header
- * @param index Column index of the header
- * @param textGUIGraphics Graphics object to header with
- */
- void drawHeader(Table<V> table, String label, int index, TextGUIGraphics textGUIGraphics);
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-import java.util.*;
-
-/**
- * A {@code TableModel} contains the data model behind a table, here is where all the action cell values and header
- * labels are stored.
- *
- * @author Martin
- */
-public class TableModel<V> {
- private final List<String> columns;
- private final List<List<V>> rows;
-
- /**
- * Default constructor, creates a new model with same number of columns as labels supplied
- * @param columnLabels Labels for the column headers
- */
- public TableModel(String... columnLabels) {
- this.columns = new ArrayList<String>(Arrays.asList(columnLabels));
- this.rows = new ArrayList<List<V>>();
- }
-
- /**
- * Returns the number of columns in the model
- * @return Number of columns in the model
- */
- public synchronized int getColumnCount() {
- return columns.size();
- }
-
- /**
- * Returns number of rows in the model
- * @return Number of rows in the model
- */
- public synchronized int getRowCount() {
- return rows.size();
- }
-
- /**
- * Returns all rows in the model as a list of lists containing the data as elements
- * @return All rows in the model as a list of lists containing the data as elements
- */
- public synchronized List<List<V>> getRows() {
- List<List<V>> copy = new ArrayList<List<V>>();
- for(List<V> row: rows) {
- copy.add(new ArrayList<V>(row));
- }
- return copy;
- }
-
- /**
- * Returns all column header label as a list of strings
- * @return All column header label as a list of strings
- */
- public synchronized List<String> getColumnLabels() {
- return new ArrayList<String>(columns);
- }
-
- /**
- * Returns a row from the table as a list of the cell data
- * @param index Index of the row to return
- * @return Row from the table as a list of the cell data
- */
- public synchronized List<V> getRow(int index) {
- return new ArrayList<V>(rows.get(index));
- }
-
- /**
- * Adds a new row to the table model at the end
- * @param values Data to associate with the new row, mapped column by column in order
- * @return Itself
- */
- public synchronized TableModel<V> addRow(V... values) {
- addRow(Arrays.asList(values));
- return this;
- }
-
- /**
- * Adds a new row to the table model at the end
- * @param values Data to associate with the new row, mapped column by column in order
- * @return Itself
- */
- public synchronized TableModel<V> addRow(Collection<V> values) {
- insertRow(getRowCount(), values);
- return this;
- }
-
- /**
- * Inserts a new row to the table model at a particular index
- * @param index Index the new row should have, 0 means the first row and <i>row count</i> will append the row at the
- * end
- * @param values Data to associate with the new row, mapped column by column in order
- * @return Itself
- */
- public synchronized TableModel<V> insertRow(int index, Collection<V> values) {
- ArrayList<V> list = new ArrayList<V>(values);
- rows.add(index, list);
- return this;
- }
-
- /**
- * Removes a row at a particular index from the table model
- * @param index Index of the row to remove
- * @return Itself
- */
- public synchronized TableModel<V> removeRow(int index) {
- rows.remove(index);
- return this;
- }
-
- /**
- * Returns the label of a column header
- * @param index Index of the column to retrieve the header label for
- * @return Label of the column selected
- */
- public synchronized String getColumnLabel(int index) {
- return columns.get(index);
- }
-
- /**
- * Updates the label of a column header
- * @param index Index of the column to update the header label for
- * @param newLabel New label to assign to the column header
- * @return Itself
- */
- public synchronized TableModel<V> setColumnLabel(int index, String newLabel) {
- columns.set(index, newLabel);
- return this;
- }
-
- /**
- * Adds a new column into the table model as the last column. You can optionally supply values for the existing rows
- * through the {@code newColumnValues}.
- * @param label Label for the header of the new column
- * @param newColumnValues Optional values to assign to the existing rows, where the first element in the array will
- * be the value of the first row and so on...
- * @return Itself
- */
- public synchronized TableModel<V> addColumn(String label, V[] newColumnValues) {
- return insertColumn(getColumnCount(), label, newColumnValues);
- }
-
- /**
- * Adds a new column into the table model at a specified index. You can optionally supply values for the existing
- * rows through the {@code newColumnValues}.
- * @param index Index for the new column
- * @param label Label for the header of the new column
- * @param newColumnValues Optional values to assign to the existing rows, where the first element in the array will
- * be the value of the first row and so on...
- * @return Itself
- */
- public synchronized TableModel<V> insertColumn(int index, String label, V[] newColumnValues) {
- columns.add(index, label);
- for(int i = 0; i < rows.size(); i++) {
- List<V> row = rows.get(i);
-
- //Pad row with null if necessary
- for(int j = row.size(); j < index; j++) {
- row.add(null);
- }
-
- if(newColumnValues != null && i < newColumnValues.length && newColumnValues[i] != null) {
- row.add(index, newColumnValues[i]);
- }
- else {
- row.add(index, null);
- }
- }
- return this;
- }
-
- /**
- * Removes a column from the table model
- * @param index Index of the column to remove
- * @return Itself
- */
- public synchronized TableModel<V> removeColumn(int index) {
- columns.remove(index);
- for(List<V> row : rows) {
- row.remove(index);
- }
- return this;
- }
-
- /**
- * Returns the cell value stored at a specific column/row coordinate.
- * @param columnIndex Column index of the cell
- * @param rowIndex Row index of the cell
- * @return The data value stored in this cell
- */
- public synchronized V getCell(int columnIndex, int rowIndex) {
- if(rowIndex < 0 || columnIndex < 0) {
- throw new IndexOutOfBoundsException("Invalid row or column index: " + rowIndex + " " + columnIndex);
- }
- else if (rowIndex >= getRowCount()) {
- throw new IndexOutOfBoundsException("TableModel has " + getRowCount() + " rows, invalid access at rowIndex " + rowIndex);
- }
- if(columnIndex >= getColumnCount()) {
- throw new IndexOutOfBoundsException("TableModel has " + columnIndex + " columns, invalid access at columnIndex " + columnIndex);
- }
- return rows.get(rowIndex).get(columnIndex);
- }
-
- /**
- * Updates the call value stored at a specific column/row coordinate.
- * @param columnIndex Column index of the cell
- * @param rowIndex Row index of the cell
- * @param value New value to assign to the cell
- * @return Itself
- */
- public synchronized TableModel<V> setCell(int columnIndex, int rowIndex, V value) {
- getCell(columnIndex, rowIndex);
- List<V> row = rows.get(rowIndex);
-
- //Pad row with null if necessary
- for(int j = row.size(); j < columnIndex; j++) {
- row.add(null);
- }
-
- V existingValue = row.get(columnIndex);
- if(existingValue == value) {
- return this;
- }
- row.set(columnIndex, value);
- return this;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.gui2.table;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.gui2.InteractableRenderer;
-import com.googlecode.lanterna.gui2.TextGUIGraphics;
-
-/**
- * Formalized interactable renderer for tables
- * @author Martin
- */
-public interface TableRenderer<V> extends InteractableRenderer<Table<V>> {
- @Override
- void drawComponent(TextGUIGraphics graphics, Table<V> component);
-
- @Override
- TerminalSize getPreferredSize(Table<V> component);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.List;
-
-/**
- * Character pattern that matches characters pressed while ALT key is held down
- *
- * @author Martin, Andreas
- */
-public class AltAndCharacterPattern implements CharacterPattern {
-
- @Override
- public Matching match(List<Character> seq) {
- int size = seq.size();
- if (size > 2 || seq.get(0) != KeyDecodingProfile.ESC_CODE) {
- return null; // nope
- }
- if (size == 1) {
- return Matching.NOT_YET; // maybe later
- }
- if ( Character.isISOControl(seq.get(1)) ) {
- return null; // nope
- }
- KeyStroke ks = new KeyStroke(seq.get(1), false, true);
- return new Matching( ks ); // yep
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Very simple pattern that matches the input stream against a pre-defined list of characters. For the pattern to match,
- * the list of characters must match exactly what's coming in on the input stream.
- *
- * @author Martin, Andreas
- */
-public class BasicCharacterPattern implements CharacterPattern {
- private final KeyStroke result;
- private final char[] pattern;
-
- /**
- * Creates a new BasicCharacterPattern that matches a particular sequence of characters into a {@code KeyStroke}
- * @param result {@code KeyStroke} that this pattern will translate to
- * @param pattern Sequence of characters that translates into the {@code KeyStroke}
- */
- public BasicCharacterPattern(KeyStroke result, char... pattern) {
- this.result = result;
- this.pattern = pattern;
- }
-
- /**
- * Returns the characters that makes up this pattern, as an array that is a copy of the array used internally
- * @return Array of characters that defines this pattern
- */
- public char[] getPattern() {
- return Arrays.copyOf(pattern, pattern.length);
- }
-
- /**
- * Returns the keystroke that this pattern results in
- * @return The keystoke this pattern will return if it matches
- */
- public KeyStroke getResult() {
- return result;
- }
-
- @Override
- public Matching match(List<Character> seq) {
- int size = seq.size();
-
- if(size > pattern.length) {
- return null; // nope
- }
- for (int i = 0; i < size; i++) {
- if (pattern[i] != seq.get(i)) {
- return null; // nope
- }
- }
- if (size == pattern.length) {
- return new Matching( getResult() ); // yep
- } else {
- return Matching.NOT_YET; // maybe later
- }
- }
-
- @Override
- public boolean equals(Object obj) {
- if (!(obj instanceof BasicCharacterPattern)) {
- return false;
- }
-
- BasicCharacterPattern other = (BasicCharacterPattern) obj;
- return Arrays.equals(this.pattern, other.pattern);
- }
-
- @Override
- public int hashCode() {
- int hash = 3;
- hash = 53 * hash + Arrays.hashCode(this.pattern);
- return hash;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.List;
-
-/**
- * Used to compare a list of character if they match a particular pattern, and in that case, return the kind of
- * keystroke this pattern represents
- *
- * @author Martin, Andreas
- */
-@SuppressWarnings("WeakerAccess")
-public interface CharacterPattern {
-
- /**
- * Given a list of characters, determine whether it exactly matches
- * any known KeyStroke, and whether a longer sequence can possibly match.
- * @param seq of characters to check
- * @return see {@code Matching}
- */
- Matching match(List<Character> seq);
-
- /**
- * This immutable class describes a matching result. It wraps two items,
- * partialMatch and fullMatch.
- * <dl>
- * <dt>fullMatch</dt><dd>
- * The resulting KeyStroke if the pattern matched, otherwise null.<br>
- * Example: if the tested sequence is {@code Esc [ A}, and if the
- * pattern recognized this as {@code ArrowUp}, then this field has
- * a value like {@code new KeyStroke(KeyType.ArrowUp)}</dd>
- * <dt>partialMatch</dt><dd>
- * {@code true}, if appending appropriate characters at the end of the
- * sequence <i>can</i> produce a match.<br>
- * Example: if the tested sequence is "Esc [", and the Pattern would match
- * "Esc [ A", then this field would be set to {@code true}.</dd>
- * </dl>
- * In principle, a sequence can match one KeyStroke, but also say that if
- * another character is available, then a different KeyStroke might result.
- * This can happen, if (e.g.) a single CharacterPattern-instance matches
- * both the Escape key and a longer Escape-sequence.
- */
- class Matching {
- public final KeyStroke fullMatch;
- public final boolean partialMatch;
-
- /**
- * Re-usable result for "not yet" half-matches
- */
- public static final Matching NOT_YET = new Matching( true, null );
-
- /**
- * Convenience constructor for exact matches
- *
- * @param fullMatch the KeyStroke that matched the sequence
- */
- public Matching(KeyStroke fullMatch) {
- this(false,fullMatch);
- }
- /**
- * General constructor<p>
- * For mismatches rather use {@code null} and for "not yet" matches use NOT_YET.
- * Use this constructor, where a sequence may yield both fullMatch and
- * partialMatch or for merging result Matchings of multiple patterns.
- *
- * @param partialMatch true if further characters could lead to a match
- * @param fullMatch The perfectly matching KeyStroke
- */
- public Matching(boolean partialMatch, KeyStroke fullMatch) {
- this.partialMatch = partialMatch;
- this.fullMatch = fullMatch;
- }
-
- @Override
- public String toString() {
- return "Matching{" + "partialMatch=" + partialMatch + ", fullMatch=" + fullMatch + '}';
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.List;
-
-/**
- * Character pattern that matches characters pressed while ALT and CTRL keys are held down
- *
- * @author Martin, Andreas
- */
-public class CtrlAltAndCharacterPattern implements CharacterPattern {
-
- @Override
- public Matching match(List<Character> seq) {
- int size = seq.size();
- if (size > 2 || seq.get(0) != KeyDecodingProfile.ESC_CODE) {
- return null; // nope
- }
- if (size == 1) {
- return Matching.NOT_YET; // maybe later
- }
- char ch = seq.get(1);
- if (ch < 32) {
- // Control-chars: exclude Esc(^[), but still include ^\, ^], ^^ and ^_
- char ctrlCode;
- switch (ch) {
- case KeyDecodingProfile.ESC_CODE: return null; // nope
- case 0: /* ^@ */ ctrlCode = ' '; break;
- case 28: /* ^\ */ ctrlCode = '\\'; break;
- case 29: /* ^] */ ctrlCode = ']'; break;
- case 30: /* ^^ */ ctrlCode = '^'; break;
- case 31: /* ^_ */ ctrlCode = '_'; break;
- default: ctrlCode = (char)('a' - 1 + ch);
- }
- KeyStroke ks = new KeyStroke( ctrlCode, true, true);
- return new Matching( ks ); // yep
- } else {
- return null; // nope
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.List;
-
-/**
- * Character pattern that matches characters pressed while CTRL key is held down
- *
- * @author Martin, Andreas
- */
-public class CtrlAndCharacterPattern implements CharacterPattern {
- @Override
- public Matching match(List<Character> seq) {
- int size = seq.size(); char ch = seq.get(0);
- if (size != 1) {
- return null; // nope
- }
- if (ch < 32) {
- // Control-chars: exclude lf,cr,Tab,Esc(^[), but still include ^\, ^], ^^ and ^_
- char ctrlCode;
- switch (ch) {
- case '\n': case '\r': case '\t':
- case KeyDecodingProfile.ESC_CODE: return null; // nope
- case 0: /* ^@ */ ctrlCode = ' '; break;
- case 28: /* ^\ */ ctrlCode = '\\'; break;
- case 29: /* ^] */ ctrlCode = ']'; break;
- case 30: /* ^^ */ ctrlCode = '^'; break;
- case 31: /* ^_ */ ctrlCode = '_'; break;
- default: ctrlCode = (char)('a' - 1 + ch);
- }
- KeyStroke ks = new KeyStroke( ctrlCode, true, false);
- return new Matching( ks ); // yep
- } else {
- return null; // nope
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * This profile attempts to collect as many code combinations as possible without causing any collisions between
- * patterns. The patterns in here are tested with Linux terminal, XTerm, Gnome terminal, XFCE terminal, Cygwin and
- * Mac OS X terminal.
- *
- * @author Martin
- */
-public class DefaultKeyDecodingProfile implements KeyDecodingProfile {
-
- private static final List<CharacterPattern> COMMON_PATTERNS
- = new ArrayList<CharacterPattern>(Arrays.asList(
- new CharacterPattern[]{
- new BasicCharacterPattern(new KeyStroke(KeyType.Escape), ESC_CODE),
- new BasicCharacterPattern(new KeyStroke(KeyType.Tab), '\t'),
- new BasicCharacterPattern(new KeyStroke(KeyType.Enter), '\n'),
- new BasicCharacterPattern(new KeyStroke(KeyType.Enter), '\r', '\u0000'), //OS X
- new BasicCharacterPattern(new KeyStroke(KeyType.Backspace), (char) 0x7f),
- new BasicCharacterPattern(new KeyStroke(KeyType.F1), ESC_CODE, '[', '[', 'A'), //Linux
- new BasicCharacterPattern(new KeyStroke(KeyType.F2), ESC_CODE, '[', '[', 'B'), //Linux
- new BasicCharacterPattern(new KeyStroke(KeyType.F3), ESC_CODE, '[', '[', 'C'), //Linux
- new BasicCharacterPattern(new KeyStroke(KeyType.F4), ESC_CODE, '[', '[', 'D'), //Linux
- new BasicCharacterPattern(new KeyStroke(KeyType.F5), ESC_CODE, '[', '[', 'E'), //Linux
-
- new EscapeSequenceCharacterPattern(),
- new NormalCharacterPattern(),
- new AltAndCharacterPattern(),
- new CtrlAndCharacterPattern(),
- new CtrlAltAndCharacterPattern(),
- new ScreenInfoCharacterPattern(),
- new MouseCharacterPattern()
- }));
-
- @Override
- public Collection<CharacterPattern> getPatterns() {
- return new ArrayList<CharacterPattern>(COMMON_PATTERNS);
- }
-
-}
+++ /dev/null
-package com.googlecode.lanterna.input;
-
-import static com.googlecode.lanterna.input.KeyDecodingProfile.ESC_CODE;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * This implementation of CharacterPattern matches two similar patterns
- * of Escape sequences, that many terminals produce for special keys.<p>
- *
- * These sequences all start with Escape, followed by either an open bracket
- * or a capital letter O (these two are treated as equivalent).<p>
- *
- * Then follows a list of zero or up to two decimals separated by a
- * semicolon, and a non-digit last character.<p>
- *
- * If the last character is a tilde (~) then the first number defines
- * the key (through stdMap), otherwise the last character itself defines
- * the key (through finMap).<p>
- *
- * The second number, if provided by the terminal, specifies the modifier
- * state (shift,alt,ctrl). The value is 1 + sum(modifiers), where shift is 1,
- * alt is 2 and ctrl is 4.<p>
- *
- * The two maps stdMap and finMap can be customized in subclasses to add,
- * remove or replace keys - to support non-standard Terminals.<p>
- *
- * Examples: (on a gnome terminal)<br>
- * ArrowUp is "Esc [ A"; Alt-ArrowUp is "Esc [ 1 ; 3 A"<br>
- * both are handled by finMap mapping 'A' to ArrowUp <br><br>
- * F6 is "Esc [ 1 7 ~"; Ctrl-Shift-F6 is "Esc [ 1 7 ; 6 R"<br>
- * both are handled by stdMap mapping 17 to F6 <br><br>
- *
- * @author Andreas
- *
- */
-public class EscapeSequenceCharacterPattern implements CharacterPattern {
- // state machine used to match key sequence:
- private enum State {
- START, INTRO, NUM1, NUM2, DONE
- }
- // bit-values for modifier keys: only used internally
- public static final int SHIFT = 1, ALT = 2, CTRL = 4;
-
- /**
- * Map of recognized "standard pattern" sequences:<br>
- * e.g.: 24 -> F12 : "Esc [ <b>24</b> ~"
- */
- protected final Map<Integer, KeyType> stdMap = new HashMap<Integer, KeyType>();
- /**
- * Map of recognized "finish pattern" sequences:<br>
- * e.g.: 'A' -> ArrowUp : "Esc [ <b>A</b>"
- */
- protected final Map<Character, KeyType> finMap = new HashMap<Character, KeyType>();
- /**
- * A flag to control, whether an Esc-prefix for an Esc-sequence is to be treated
- * as Alt-pressed. Some Terminals (e.g. putty) report the Alt-modifier like that.<p>
- * If the application is e.g. more interested in seeing separate Escape and plain
- * Arrow keys, then it should replace this class by a subclass that sets this flag
- * to false. (It might then also want to remove the CtrlAltAndCharacterPattern.)
- */
- protected boolean useEscEsc = true;
-
- /**
- * Create an instance with a standard set of mappings.
- */
- public EscapeSequenceCharacterPattern() {
- finMap.put('A', KeyType.ArrowUp);
- finMap.put('B', KeyType.ArrowDown);
- finMap.put('C', KeyType.ArrowRight);
- finMap.put('D', KeyType.ArrowLeft);
- finMap.put('E', KeyType.Unknown); // gnome-terminal center key on numpad
- finMap.put('G', KeyType.Unknown); // putty center key on numpad
- finMap.put('H', KeyType.Home);
- finMap.put('F', KeyType.End);
- finMap.put('P', KeyType.F1);
- finMap.put('Q', KeyType.F2);
- finMap.put('R', KeyType.F3);
- finMap.put('S', KeyType.F4);
- finMap.put('Z', KeyType.ReverseTab);
-
- stdMap.put(1, KeyType.Home);
- stdMap.put(2, KeyType.Insert);
- stdMap.put(3, KeyType.Delete);
- stdMap.put(4, KeyType.End);
- stdMap.put(5, KeyType.PageUp);
- stdMap.put(6, KeyType.PageDown);
- stdMap.put(11, KeyType.F1);
- stdMap.put(12, KeyType.F2);
- stdMap.put(13, KeyType.F3);
- stdMap.put(14, KeyType.F4);
- stdMap.put(15, KeyType.F5);
- stdMap.put(16, KeyType.F5);
- stdMap.put(17, KeyType.F6);
- stdMap.put(18, KeyType.F7);
- stdMap.put(19, KeyType.F8);
- stdMap.put(20, KeyType.F9);
- stdMap.put(21, KeyType.F10);
- stdMap.put(23, KeyType.F11);
- stdMap.put(24, KeyType.F12);
- stdMap.put(25, KeyType.F13);
- stdMap.put(26, KeyType.F14);
- stdMap.put(28, KeyType.F15);
- stdMap.put(29, KeyType.F16);
- stdMap.put(31, KeyType.F17);
- stdMap.put(32, KeyType.F18);
- stdMap.put(33, KeyType.F19);
- }
-
- /**
- * combines a KeyType and modifiers into a KeyStroke.
- * Subclasses can override this for customization purposes.
- *
- * @param key the KeyType as determined by parsing the sequence.
- * It will be null, if the pattern looked like a key sequence but wasn't
- * identified.
- * @param mods the bitmask of the modifer keys pressed along with the key.
- * @return either null (to report mis-match), or a valid KeyStroke.
- */
- protected KeyStroke getKeyStroke(KeyType key, int mods) {
- boolean bShift = false, bCtrl = false, bAlt = false;
- if (key == null) { return null; } // alternative: key = KeyType.Unknown;
- if (mods >= 0) { // only use when non-negative!
- bShift = (mods & SHIFT) != 0;
- bAlt = (mods & ALT) != 0;
- bCtrl = (mods & CTRL) != 0;
- }
- return new KeyStroke( key , bCtrl, bAlt, bShift);
- }
-
- /**
- * combines the raw parts of the sequence into a KeyStroke.
- * This method does not check the first char, but overrides may do so.
- *
- * @param first the char following after Esc in the sequence (either [ or O)
- * @param num1 the first decimal, or 0 if not in the sequence
- * @param num2 the second decimal, or 0 if not in the sequence
- * @param last the terminating char.
- * @param bEsc whether an extra Escape-prefix was found.
- * @return either null (to report mis-match), or a valid KeyStroke.
- */
- protected KeyStroke getKeyStrokeRaw(char first,int num1,int num2,char last,boolean bEsc) {
- KeyType kt = null; boolean bPuttyCtrl = false;
- if (last == '~' && stdMap.containsKey(num1)) {
- kt = stdMap.get(num1);
- } else if (finMap.containsKey(last)) {
- kt = finMap.get(last);
- // Putty sends ^[OA for ctrl arrow-up, ^[[A for plain arrow-up:
- // but only for A-D -- other ^[O... sequences are just plain keys
- if (first == 'O' && last >= 'A' && last <= 'D') { bPuttyCtrl = true; }
- // if we ever stumble into "keypad-mode", then it will end up inverted.
- } else {
- kt = null; // unknown key.
- }
- int mods = num2 - 1;
- if (bEsc) {
- if (mods >= 0) { mods |= ALT; }
- else { mods = ALT; }
- }
- if (bPuttyCtrl) {
- if (mods >= 0) { mods |= CTRL; }
- else { mods = CTRL; }
- }
- return getKeyStroke( kt, mods );
- }
-
- @Override
- public Matching match(List<Character> cur) {
- State state = State.START;
- int num1 = 0, num2 = 0;
- char first = '\0', last = '\0';
- boolean bEsc = false;
-
- for (char ch : cur) {
- switch (state) {
- case START:
- if (ch != ESC_CODE) {
- return null; // nope
- }
- state = State.INTRO;
- continue;
- case INTRO:
- // Recognize a second Escape to mean "Alt is pressed".
- // (at least putty sends it that way)
- if (useEscEsc && ch == ESC_CODE && ! bEsc) {
- bEsc = true; continue;
- }
-
- // Key sequences supported by this class must
- // start either with Esc-[ or Esc-O
- if (ch != '[' && ch != 'O') {
- return null; // nope
- }
- first = ch; state = State.NUM1;
- continue;
- case NUM1:
- if (ch == ';') {
- state = State.NUM2;
- } else if (Character.isDigit(ch)) {
- num1 = num1 * 10 + Character.digit(ch, 10);
- } else {
- last = ch; state = State.DONE;
- }
- continue;
- case NUM2:
- if (Character.isDigit(ch)) {
- num2 = num2 * 10 + Character.digit(ch, 10);
- } else {
- last = ch; state = State.DONE;
- }
- continue;
- case DONE: // once done, extra characters spoil it
- return null; // nope
- }
- }
- if (state == State.DONE) {
- KeyStroke ks = getKeyStrokeRaw(first,num1,num2,last,bEsc);
- return ks != null ? new Matching( ks ) : null; // depends
- } else {
- return Matching.NOT_YET; // maybe later
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import com.googlecode.lanterna.input.CharacterPattern.Matching;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.util.*;
-
-/**
- * Used to read the input stream character by character and generate {@code Key} objects to be put in the input queue.
- *
- * @author Martin, Andreas
- */
-public class InputDecoder {
- private final Reader source;
- private final List<CharacterPattern> bytePatterns;
- private final List<Character> currentMatching;
- private boolean seenEOF;
- private int timeoutUnits;
-
- /**
- * Creates a new input decoder using a specified Reader as the source to read characters from
- * @param source Reader to read characters from, will be wrapped by a BufferedReader
- */
- public InputDecoder(final Reader source) {
- this.source = new BufferedReader(source);
- this.bytePatterns = new ArrayList<CharacterPattern>();
- this.currentMatching = new ArrayList<Character>();
- this.seenEOF = false;
- this.timeoutUnits = 0; // default is no wait at all
- }
-
- /**
- * Adds another key decoding profile to this InputDecoder, which means all patterns from the profile will be used
- * when decoding input.
- * @param profile Profile to add
- */
- public void addProfile(KeyDecodingProfile profile) {
- for (CharacterPattern pattern : profile.getPatterns()) {
- synchronized(bytePatterns) {
- //If an equivalent pattern already exists, remove it first
- bytePatterns.remove(pattern);
- bytePatterns.add(pattern);
- }
- }
- }
-
- /**
- * Returns a collection of all patterns registered in this InputDecoder.
- * @return Collection of patterns in the InputDecoder
- */
- public synchronized Collection<CharacterPattern> getPatterns() {
- synchronized(bytePatterns) {
- return new ArrayList<CharacterPattern>(bytePatterns);
- }
- }
-
- /**
- * Removes one pattern from the list of patterns in this InputDecoder
- * @param pattern Pattern to remove
- * @return {@code true} if the supplied pattern was found and was removed, otherwise {@code false}
- */
- public boolean removePattern(CharacterPattern pattern) {
- synchronized(bytePatterns) {
- return bytePatterns.remove(pattern);
- }
- }
-
- /**
- * Sets the number of 1/4-second units for how long to try to get further input
- * to complete an escape-sequence for a special Key.
- *
- * Negative numbers are mapped to 0 (no wait at all), and unreasonably high
- * values are mapped to a maximum of 240 (1 minute).
- */
- public void setTimeoutUnits(int units) {
- timeoutUnits = (units < 0) ? 0 :
- (units > 240) ? 240 :
- units;
- }
- /**
- * queries the current timeoutUnits value. One unit is 1/4 second.
- * @return The timeout this InputDecoder will use when waiting for additional input, in units of 1/4 seconds
- */
- public int getTimeoutUnits() {
- return timeoutUnits;
- }
-
- /**
- * Reads and decodes the next key stroke from the input stream
- * @return Key stroke read from the input stream, or {@code null} if none
- * @throws IOException If there was an I/O error when reading from the input stream
- */
- public synchronized KeyStroke getNextCharacter(boolean blockingIO) throws IOException {
-
- KeyStroke bestMatch = null;
- int bestLen = 0;
- int curLen = 0;
-
- while(true) {
-
- if ( curLen < currentMatching.size() ) {
- // (re-)consume characters previously read:
- curLen++;
- }
- else {
- // If we already have a bestMatch but a chance for a longer match
- // then we poll for the configured number of timeout units:
- // It would be much better, if we could just read with a timeout,
- // but lacking that, we wait 1/4s units and check for readiness.
- if (bestMatch != null) {
- int timeout = getTimeoutUnits();
- while (timeout > 0 && ! source.ready() ) {
- try {
- timeout--; Thread.sleep(250);
- } catch (InterruptedException e) { timeout = 0; }
- }
- }
- // if input is available, we can just read a char without waiting,
- // otherwise, for readInput() with no bestMatch found yet,
- // we have to wait blocking for more input:
- if ( source.ready() || ( blockingIO && bestMatch == null ) ) {
- int readChar = source.read();
- if (readChar == -1) {
- seenEOF = true;
- if(currentMatching.isEmpty()) {
- return new KeyStroke(KeyType.EOF);
- }
- break;
- }
- currentMatching.add( (char)readChar );
- curLen++;
- } else { // no more available input at this time.
- // already found something:
- if (bestMatch != null) {
- break; // it's something...
- }
- // otherwise: no KeyStroke yet
- return null;
- }
- }
-
- List<Character> curSub = currentMatching.subList(0, curLen);
- Matching matching = getBestMatch( curSub );
-
- // fullMatch found...
- if (matching.fullMatch != null) {
- bestMatch = matching.fullMatch;
- bestLen = curLen;
-
- if (! matching.partialMatch) {
- // that match and no more
- break;
- } else {
- // that match, but maybe more
- continue;
- }
- }
- // No match found yet, but there's still potential...
- else if ( matching.partialMatch ) {
- continue;
- }
- // no longer match possible at this point:
- else {
- if (bestMatch != null ) {
- // there was already a previous full-match, use it:
- break;
- } else { // invalid input!
- // remove the whole fail and re-try finding a KeyStroke...
- curSub.clear(); // or just 1 char? currentMatching.remove(0);
- curLen = 0;
- continue;
- }
- }
- }
-
- //Did we find anything? Otherwise return null
- if(bestMatch == null) {
- if(seenEOF) {
- currentMatching.clear();
- return new KeyStroke(KeyType.EOF);
- }
- return null;
- }
-
- List<Character> bestSub = currentMatching.subList(0, bestLen );
- bestSub.clear(); // remove matched characters from input
- return bestMatch;
- }
-
- private Matching getBestMatch(List<Character> characterSequence) {
- boolean partialMatch = false;
- KeyStroke bestMatch = null;
- synchronized(bytePatterns) {
- for(CharacterPattern pattern : bytePatterns) {
- Matching res = pattern.match(characterSequence);
- if (res != null) {
- if (res.partialMatch) { partialMatch = true; }
- if (res.fullMatch != null) { bestMatch = res.fullMatch; }
- }
- }
- }
- return new Matching(partialMatch, bestMatch);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.io.IOException;
-
-/**
- * Objects implementing this interface can read character streams and transform them into {@code Key} objects which can
- * be read in a FIFO manner.
- *
- * @author Martin
- */
-public interface InputProvider {
- /**
- * Returns the next {@code Key} off the input queue or null if there is no more input events available. Note, this
- * method call is <b>not</b> blocking, it returns null immediately if there is nothing on the input stream.
- * @return Key object which represents a keystroke coming in through the input stream
- * @throws java.io.IOException Propagated error if the underlying stream gave errors
- */
- KeyStroke pollInput() throws IOException;
-
- /**
- * Returns the next {@code Key} off the input queue or blocks until one is available. <b>NOTE:</b> In previous
- * versions of Lanterna, this method was <b>not</b> blocking. From lanterna 3, it is blocking and you can call
- * {@code pollInput()} for the non-blocking version.
- * @return Key object which represents a keystroke coming in through the input stream
- * @throws java.io.IOException Propagated error if the underlying stream gave errors
- */
- KeyStroke readInput() throws IOException;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.Collection;
-
-/**
- * In order to convert a stream of characters into objects representing keystrokes, we need to apply logic on this
- * stream to detect special characters. In lanterna, this is done by using a set of character patterns which are matched
- * against the stream until we've found the best match. This interface represents a set of such patterns, a 'profile'
- * with is used when decoding the input. There is a default profile, DefaultKeyDecodingProfile, which will probably
- * do what you need but you can also extend and define your own patterns.
- *
- * @author Martin
- */
-public interface KeyDecodingProfile {
- /**
- * Static constant for the ESC key
- */
- char ESC_CODE = (char) 0x1b;
-
- /**
- * Returns a collection of character patterns that makes up this profile
- * @return Collection of patterns in this profile
- */
- Collection<CharacterPattern> getPatterns();
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-
-/**
- * Represents the user pressing a key on the keyboard. If the user held down ctrl and/or alt before pressing the key,
- * this may be recorded in this class, depending on the terminal implementation and if such information in available.
- * KeyStroke objects are normally constructed by a KeyDecodingProfile, which works off a character stream that likely
- * coming from the system's standard input. Because of this, the class can only represent what can be read and
- * interpreted from the input stream; for example, certain key-combinations like ctrl+i is indistinguishable from a tab
- * key press.
- * <p>
- * Use the <tt>keyType</tt> field to determine what kind of key was pressed. For ordinary letters, numbers and symbols, the
- * <tt>keyType</tt> will be <tt>KeyType.Character</tt> and the actual character value of the key is in the
- * <tt>character</tt> field. Please note that return (\n) and tab (\t) are not sorted under type <tt>KeyType.Character</tt>
- * but <tt>KeyType.Enter</tt> and <tt>KeyType.Tab</tt> instead.
- * @author martin
- */
-public class KeyStroke {
- private final KeyType keyType;
- private final Character character;
- private final boolean ctrlDown;
- private final boolean altDown;
- private final boolean shiftDown;
- private final long eventTime;
-
- /**
- * Constructs a KeyStroke based on a supplied keyType; character will be null and both ctrl and alt will be
- * considered not pressed. If you try to construct a KeyStroke with type KeyType.Character with this constructor, it
- * will always throw an exception; use another overload that allows you to specify the character value instead.
- * @param keyType Type of the key pressed by this keystroke
- */
- public KeyStroke(KeyType keyType) {
- this(keyType, false, false);
- }
-
- /**
- * Constructs a KeyStroke based on a supplied keyType; character will be null.
- * If you try to construct a KeyStroke with type KeyType.Character with this constructor, it
- * will always throw an exception; use another overload that allows you to specify the character value instead.
- * @param keyType Type of the key pressed by this keystroke
- * @param ctrlDown Was ctrl held down when the main key was pressed?
- * @param altDown Was alt held down when the main key was pressed?
- */
- public KeyStroke(KeyType keyType, boolean ctrlDown, boolean altDown) {
- this(keyType, null, ctrlDown, altDown, false);
- }
-
- /**
- * Constructs a KeyStroke based on a supplied keyType; character will be null.
- * If you try to construct a KeyStroke with type KeyType.Character with this constructor, it
- * will always throw an exception; use another overload that allows you to specify the character value instead.
- * @param keyType Type of the key pressed by this keystroke
- * @param ctrlDown Was ctrl held down when the main key was pressed?
- * @param altDown Was alt held down when the main key was pressed?
- * @param shiftDown Was shift held down when the main key was pressed?
- */
- public KeyStroke(KeyType keyType, boolean ctrlDown, boolean altDown, boolean shiftDown) {
- this(keyType, null, ctrlDown, altDown, shiftDown);
- }
-
- /**
- * Constructs a KeyStroke based on a supplied character, keyType is implicitly KeyType.Character.
- * <p>
- * A character-based KeyStroke does not support the shiftDown flag, as the shift state has
- * already been accounted for in the character itself, depending on user's keyboard layout.
- * @param character Character that was typed on the keyboard
- * @param ctrlDown Was ctrl held down when the main key was pressed?
- * @param altDown Was alt held down when the main key was pressed?
- */
- public KeyStroke(Character character, boolean ctrlDown, boolean altDown) {
- this(KeyType.Character, character, ctrlDown, altDown, false);
- }
-
- private KeyStroke(KeyType keyType, Character character, boolean ctrlDown, boolean altDown, boolean shiftDown) {
- if(keyType == KeyType.Character && character == null) {
- throw new IllegalArgumentException("Cannot construct a KeyStroke with type KeyType.Character but no character information");
- }
- //Enforce character for some key types
- switch(keyType) {
- case Backspace:
- character = '\b';
- break;
- case Enter:
- character = '\n';
- break;
- case Tab:
- character = '\t';
- break;
- default:
- }
- this.keyType = keyType;
- this.character = character;
- this.shiftDown = shiftDown;
- this.ctrlDown = ctrlDown;
- this.altDown = altDown;
- this.eventTime = System.currentTimeMillis();
- }
-
- /**
- * Type of key that was pressed on the keyboard, as represented by the KeyType enum. If the value if
- * KeyType.Character, you need to call getCharacter() to find out which letter, number or symbol that was actually
- * pressed.
- * @return Type of key on the keyboard that was pressed
- */
- public KeyType getKeyType() {
- return keyType;
- }
-
- /**
- * For keystrokes of ordinary keys (letters, digits, symbols), this method returns the actual character value of the
- * key. For all other key types, it returns null.
- * @return Character value of the key pressed, or null if it was a special key
- */
- public Character getCharacter() {
- return character;
- }
-
- /**
- * @return Returns true if ctrl was help down while the key was typed (depending on terminal implementation)
- */
- public boolean isCtrlDown() {
- return ctrlDown;
- }
-
- /**
- * @return Returns true if alt was help down while the key was typed (depending on terminal implementation)
- */
- public boolean isAltDown() {
- return altDown;
- }
-
- /**
- * @return Returns true if shift was help down while the key was typed (depending on terminal implementation)
- */
- public boolean isShiftDown() {
- return shiftDown;
- }
-
- /**
- * Gets the time when the keystroke was recorded. This isn't necessarily the time the keystroke happened, but when
- * Lanterna received the event, so it may not be accurate down to the millisecond.
- * @return The unix time of when the keystroke happened, in milliseconds
- */
- public long getEventTime() {
- return eventTime;
- }
-
- @Override
- public String toString() {
- return "KeyStroke{" + "keyType=" + keyType + ", character=" + character +
- ", ctrlDown=" + ctrlDown +
- ", altDown=" + altDown +
- ", shiftDown=" + shiftDown + '}';
- }
-
- @Override
- public int hashCode() {
- int hash = 3;
- hash = 41 * hash + (this.keyType != null ? this.keyType.hashCode() : 0);
- hash = 41 * hash + (this.character != null ? this.character.hashCode() : 0);
- hash = 41 * hash + (this.ctrlDown ? 1 : 0);
- hash = 41 * hash + (this.altDown ? 1 : 0);
- hash = 41 * hash + (this.shiftDown ? 1 : 0);
- return hash;
- }
-
- @SuppressWarnings("SimplifiableIfStatement")
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final KeyStroke other = (KeyStroke) obj;
- if (this.keyType != other.keyType) {
- return false;
- }
- if (this.character != other.character && (this.character == null || !this.character.equals(other.character))) {
- return false;
- }
- return this.ctrlDown == other.ctrlDown &&
- this.altDown == other.altDown &&
- this.shiftDown == other.shiftDown;
- }
-
- /**
- * Creates a Key from a string representation in Vim's key notation.
- *
- * @param keyStr the string representation of this key
- * @return the created {@link KeyType}
- */
- public static KeyStroke fromString(String keyStr) {
- String keyStrLC = keyStr.toLowerCase();
- KeyStroke k;
- if (keyStr.length() == 1) {
- k = new KeyStroke(KeyType.Character, keyStr.charAt(0), false, false, false);
- } else if (keyStr.startsWith("<") && keyStr.endsWith(">")) {
- if (keyStrLC.equals("<s-tab>")) {
- k = new KeyStroke(KeyType.ReverseTab);
- } else if (keyStr.contains("-")) {
- ArrayList<String> segments = new ArrayList<String>(Arrays.asList(keyStr.substring(1, keyStr.length() - 1).split("-")));
- if (segments.size() < 2) {
- throw new IllegalArgumentException("Invalid vim notation: " + keyStr);
- }
- String characterStr = segments.remove(segments.size() - 1);
- boolean altPressed = false;
- boolean ctrlPressed = false;
- for (String modifier : segments) {
- if ("c".equals(modifier.toLowerCase())) {
- ctrlPressed = true;
- } else if ("a".equals(modifier.toLowerCase())) {
- altPressed = true;
- } else if ("s".equals(modifier.toLowerCase())) {
- characterStr = characterStr.toUpperCase();
- }
- }
- k = new KeyStroke(characterStr.charAt(0), ctrlPressed, altPressed);
- } else {
- if (keyStrLC.startsWith("<esc")) {
- k = new KeyStroke(KeyType.Escape);
- } else if (keyStrLC.equals("<cr>") || keyStrLC.equals("<enter>") || keyStrLC.equals("<return>")) {
- k = new KeyStroke(KeyType.Enter);
- } else if (keyStrLC.equals("<bs>")) {
- k = new KeyStroke(KeyType.Backspace);
- } else if (keyStrLC.equals("<tab>")) {
- k = new KeyStroke(KeyType.Tab);
- } else if (keyStrLC.equals("<space>")) {
- k = new KeyStroke(' ', false, false);
- } else if (keyStrLC.equals("<up>")) {
- k = new KeyStroke(KeyType.ArrowUp);
- } else if (keyStrLC.equals("<down>")) {
- k = new KeyStroke(KeyType.ArrowDown);
- } else if (keyStrLC.equals("<left>")) {
- k = new KeyStroke(KeyType.ArrowLeft);
- } else if (keyStrLC.equals("<right>")) {
- k = new KeyStroke(KeyType.ArrowRight);
- } else if (keyStrLC.equals("<insert>")) {
- k = new KeyStroke(KeyType.Insert);
- } else if (keyStrLC.equals("<del>")) {
- k = new KeyStroke(KeyType.Delete);
- } else if (keyStrLC.equals("<home>")) {
- k = new KeyStroke(KeyType.Home);
- } else if (keyStrLC.equals("<end>")) {
- k = new KeyStroke(KeyType.End);
- } else if (keyStrLC.equals("<pageup>")) {
- k = new KeyStroke(KeyType.PageUp);
- } else if (keyStrLC.equals("<pagedown>")) {
- k = new KeyStroke(KeyType.PageDown);
- } else if (keyStrLC.equals("<f1>")) {
- k = new KeyStroke(KeyType.F1);
- } else if (keyStrLC.equals("<f2>")) {
- k = new KeyStroke(KeyType.F2);
- } else if (keyStrLC.equals("<f3>")) {
- k = new KeyStroke(KeyType.F3);
- } else if (keyStrLC.equals("<f4>")) {
- k = new KeyStroke(KeyType.F4);
- } else if (keyStrLC.equals("<f5>")) {
- k = new KeyStroke(KeyType.F5);
- } else if (keyStrLC.equals("<f6>")) {
- k = new KeyStroke(KeyType.F6);
- } else if (keyStrLC.equals("<f7>")) {
- k = new KeyStroke(KeyType.F7);
- } else if (keyStrLC.equals("<f8>")) {
- k = new KeyStroke(KeyType.F8);
- } else if (keyStrLC.equals("<f9>")) {
- k = new KeyStroke(KeyType.F9);
- } else if (keyStrLC.equals("<f10>")) {
- k = new KeyStroke(KeyType.F10);
- } else if (keyStrLC.equals("<f11>")) {
- k = new KeyStroke(KeyType.F11);
- } else if (keyStrLC.equals("<f12>")) {
- k = new KeyStroke(KeyType.F12);
- } else {
- throw new IllegalArgumentException("Invalid vim notation: " + keyStr);
- }
- }
- } else {
- throw new IllegalArgumentException("Invalid vim notation: " + keyStr);
- }
- return k;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-
-/**
- * This enum is a categorization of the various keys available on a normal computer keyboard that are usable
- * (detectable) by a terminal environment. For ordinary numbers, letters and symbols, the enum value is <i>Character</i>
- * but please keep in mind that newline and tab, usually represented by \n and \t, are considered their own separate
- * values by this enum (<i>Enter</i> and <i>Tab</i>).
- * <p>
- * Previously (before Lanterna 3.0), this enum was embedded inside the Key class.
- *
- * @author Martin
- */
-public enum KeyType {
- /**
- * This value corresponds to a regular character 'typed', usually alphanumeric or a symbol. The one special case
- * here is the enter key which could be expected to be returned as a '\n' character but is actually returned as a
- * separate {@code KeyType} (see below). Tab, backspace and some others works this way too.
- */
- Character,
- Escape,
- Backspace,
- ArrowLeft,
- ArrowRight,
- ArrowUp,
- ArrowDown,
- Insert,
- Delete,
- Home,
- End,
- PageUp,
- PageDown,
- Tab,
- ReverseTab,
- Enter,
- F1,
- F2,
- F3,
- F4,
- F5,
- F6,
- F7,
- F8,
- F9,
- F10,
- F11,
- F12,
- F13,
- F14,
- F15,
- F16,
- F17,
- F18,
- F19,
- Unknown,
-
- //"Virtual" KeyStroke types
- /**
- * This value is only internally within Lanterna to understand where the cursor currently is, it's not expected to
- * be returned by the API to an input read call.
- */
- CursorLocation,
- /**
- * This type is not really a key stroke but actually a 'catch-all' for mouse related events. Please note that mouse
- * event capturing must first be enabled and many terminals don't suppose this extension at all.
- */
- MouseEvent,
- /**
- * This value is returned when you try to read input and the input stream has been closed.
- */
- EOF,
- ;
-}
+++ /dev/null
-package com.googlecode.lanterna.input;
-
-import com.googlecode.lanterna.TerminalPosition;
-
-/**
- * MouseAction, a KeyStroke in disguise, this class contains the information of a single mouse action event.
- */
-public class MouseAction extends KeyStroke {
- private final MouseActionType actionType;
- private final int button;
- private final TerminalPosition position;
-
- /**
- * Constructs a MouseAction based on an action type, a button and a location on the screen
- * @param actionType The kind of mouse event
- * @param button Which button is involved (no button = 0, left button = 1, middle (wheel) button = 2,
- * right button = 3, scroll wheel up = 4, scroll wheel down = 5)
- * @param position Where in the terminal is the mouse cursor located
- */
- public MouseAction(MouseActionType actionType, int button, TerminalPosition position) {
- super(KeyType.MouseEvent, false, false);
- this.actionType = actionType;
- this.button = button;
- this.position = position;
- }
-
- /**
- * Returns the mouse action type so the caller can determine which kind of action was performed.
- * @return The action type of the mouse event
- */
- public MouseActionType getActionType() {
- return actionType;
- }
-
- /**
- * Which button was involved in this event. Please note that for CLICK_RELEASE events, there is no button
- * information available (getButton() will return 0). The standard xterm mapping is:
- * <ul>
- * <li>No button = 0</li>
- * <li>Left button = 1</li>
- * <li>Middle (wheel) button = 2</li>
- * <li>Right button = 3</li>
- * <li>Wheel up = 4</li>
- * <li>Wheel down = 5</li>
- * </ul>
- * @return The button which is clicked down when this event was generated
- */
- public int getButton() {
- return button;
- }
-
- /**
- * The location of the mouse cursor when this event was generated.
- * @return Location of the mouse cursor
- */
- public TerminalPosition getPosition() {
- return position;
- }
-
- @Override
- public String toString() {
- return "MouseAction{actionType=" + actionType + ", button=" + button + ", position=" + position + '}';
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.input;
-
-/**
- * Enum type for the different kinds of mouse actions supported
- */
-public enum MouseActionType {
- CLICK_DOWN,
- CLICK_RELEASE,
- SCROLL_UP,
- SCROLL_DOWN,
- /**
- * Moving the mouse cursor on the screen while holding a button down
- */
- DRAG,
- /**
- * Moving the mouse cursor on the screen without holding any buttons down
- */
- MOVE,
- ;
-}
+++ /dev/null
-package com.googlecode.lanterna.input;
-
-import com.googlecode.lanterna.TerminalPosition;
-
-import java.util.List;
-
-/**
- * Pattern used to detect Xterm-protocol mouse events coming in on the standard input channel
- * Created by martin on 19/07/15.
- *
- * @author Martin, Andreas
- */
-public class MouseCharacterPattern implements CharacterPattern {
- private static final char[] PATTERN = { KeyDecodingProfile.ESC_CODE, '[', 'M' };
-
- @Override
- public Matching match(List<Character> seq) {
- int size = seq.size();
- if (size > 6) {
- return null; // nope
- }
- // check first 3 chars:
- for (int i = 0; i < 3; i++) {
- if ( i >= size ) {
- return Matching.NOT_YET; // maybe later
- }
- if ( seq.get(i) != PATTERN[i] ) {
- return null; // nope
- }
- }
- if (size < 6) {
- return Matching.NOT_YET; // maybe later
- }
- MouseActionType actionType = null;
- int button = (seq.get(3) & 0x3) + 1;
- if(button == 4) {
- //If last two bits are both set, it means button click release
- button = 0;
- }
- int actionCode = (seq.get(3) & 0x60) >> 5;
- switch(actionCode) {
- case(1):
- if(button > 0) {
- actionType = MouseActionType.CLICK_DOWN;
- }
- else {
- actionType = MouseActionType.CLICK_RELEASE;
- }
- break;
- case(2):
- if(button == 0) {
- actionType = MouseActionType.MOVE;
- }
- else {
- actionType = MouseActionType.DRAG;
- }
- break;
- case(3):
- if(button == 1) {
- actionType = MouseActionType.SCROLL_UP;
- button = 4;
- }
- else {
- actionType = MouseActionType.SCROLL_DOWN;
- button = 5;
- }
- break;
- }
- TerminalPosition pos = new TerminalPosition( seq.get(4) - 33, seq.get(5) - 33 );
-
- MouseAction ma = new MouseAction(actionType, button, pos );
- return new Matching( ma ); // yep
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import java.util.List;
-
-/**
- * Character pattern that matches one character as one KeyStroke with the character that was read
- *
- * @author Martin, Andreas
- */
-public class NormalCharacterPattern implements CharacterPattern {
- @Override
- public Matching match(List<Character> seq) {
- if (seq.size() != 1) {
- return null; // nope
- }
- char ch = seq.get(0);
- if (isPrintableChar(ch)) {
- KeyStroke ks = new KeyStroke(ch, false, false);
- return new Matching( ks );
- } else {
- return null; // nope
- }
- }
-
- /**
- * From http://stackoverflow.com/questions/220547/printable-char-in-java
- * @param c character to test
- * @return True if this is a 'normal', printable character, false otherwise
- */
- private static boolean isPrintableChar(char c) {
- if (Character.isISOControl(c)) { return false; }
- Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
- return block != null && block != Character.UnicodeBlock.SPECIALS;
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.input;
-
-import com.googlecode.lanterna.TerminalPosition;
-
-/**
- * ScreenInfoAction, a KeyStroke in disguise, this class contains the reported position of the screen cursor.
- */
-public class ScreenInfoAction extends KeyStroke {
- private final TerminalPosition position;
-
- /**
- * Constructs a ScreenInfoAction based on a location on the screen
- * @param position the TerminalPosition reported from terminal
- */
- public ScreenInfoAction(TerminalPosition position) {
- super(KeyType.CursorLocation);
- this.position = position;
- }
-
- /**
- * The location of the mouse cursor when this event was generated.
- * @return Location of the mouse cursor
- */
- public TerminalPosition getPosition() {
- return position;
- }
-
- @Override
- public String toString() {
- return "ScreenInfoAction{position=" + position + '}';
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.input;
-
-import com.googlecode.lanterna.TerminalPosition;
-
-/**
- * This class recognizes character combinations which are actually a cursor position report. See
- * <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">Wikipedia</a>'s article on ANSI escape codes for more
- * information about how cursor position reporting works ("DSR – Device Status Report").
- *
- * @author Martin, Andreas
- */
-public class ScreenInfoCharacterPattern extends EscapeSequenceCharacterPattern {
- public ScreenInfoCharacterPattern() {
- useEscEsc = false; // stdMap and finMap don't matter here.
- }
- protected KeyStroke getKeyStrokeRaw(char first,int num1,int num2,char last,boolean bEsc) {
- if (first != '[' || last != 'R' || num1 == 0 || num2 == 0 || bEsc) {
- return null; // nope
- }
- if (num1 == 1 && num2 <= 8) {
- return null; // nope: much more likely it's an F3 with modifiers
- }
- TerminalPosition pos = new TerminalPosition(num2, num1);
- return new ScreenInfoAction(pos); // yep
- }
-
- public static ScreenInfoAction tryToAdopt(KeyStroke ks) {
- switch (ks.getKeyType()) {
- case CursorLocation: return (ScreenInfoAction)ks;
- case F3: // reconstruct position from F3's modifiers.
- int col = 1 + (ks.isAltDown() ? ALT : 0)
- + (ks.isCtrlDown() ? CTRL : 0)
- + (ks.isShiftDown()? SHIFT: 0);
- TerminalPosition pos = new TerminalPosition(col,1);
- return new ScreenInfoAction(pos);
- default: return null;
- }
- }
-
-
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.screen;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.graphics.TextImage;
-
-import java.io.IOException;
-
-/**
- * This class implements some of the Screen logic that is not directly tied to the actual implementation of how the
- * Screen translate to the terminal. It keeps data structures for the front- and back buffers, the cursor location and
- * some other simpler states.
- * @author martin
- */
-public abstract class AbstractScreen implements Screen {
- private TerminalPosition cursorPosition;
- private ScreenBuffer backBuffer;
- private ScreenBuffer frontBuffer;
- private final TextCharacter defaultCharacter;
-
- //How to deal with \t characters
- private TabBehaviour tabBehaviour;
-
- //Current size of the screen
- private TerminalSize terminalSize;
-
- //Pending resize of the screen
- private TerminalSize latestResizeRequest;
-
- public AbstractScreen(TerminalSize initialSize) {
- this(initialSize, DEFAULT_CHARACTER);
- }
-
- /**
- * Creates a new Screen on top of a supplied terminal, will query the terminal for its size. The screen is initially
- * blank. You can specify which character you wish to be used to fill the screen initially; this will also be the
- * character used if the terminal is enlarged and you don't set anything on the new areas.
- *
- * @param initialSize Size to initially create the Screen with (can be resized later)
- * @param defaultCharacter What character to use for the initial state of the screen and expanded areas
- */
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
- public AbstractScreen(TerminalSize initialSize, TextCharacter defaultCharacter) {
- this.frontBuffer = new ScreenBuffer(initialSize, defaultCharacter);
- this.backBuffer = new ScreenBuffer(initialSize, defaultCharacter);
- this.defaultCharacter = defaultCharacter;
- this.cursorPosition = new TerminalPosition(0, 0);
- this.tabBehaviour = TabBehaviour.ALIGN_TO_COLUMN_4;
- this.terminalSize = initialSize;
- this.latestResizeRequest = null;
- }
-
- /**
- * @return Position where the cursor will be located after the screen has been refreshed or {@code null} if the
- * cursor is not visible
- */
- @Override
- public TerminalPosition getCursorPosition() {
- return cursorPosition;
- }
-
- /**
- * Moves the current cursor position or hides it. If the cursor is hidden and given a new position, it will be
- * visible after this method call.
- *
- * @param position 0-indexed column and row numbers of the new position, or if {@code null}, hides the cursor
- */
- @Override
- public void setCursorPosition(TerminalPosition position) {
- if(position == null) {
- //Skip any validation checks if we just want to hide the cursor
- this.cursorPosition = null;
- return;
- }
- if(position.getColumn() >= 0 && position.getColumn() < terminalSize.getColumns()
- && position.getRow() >= 0 && position.getRow() < terminalSize.getRows()) {
- this.cursorPosition = position;
- }
- else {
- this.cursorPosition = null;
- }
- }
-
- @Override
- public void setTabBehaviour(TabBehaviour tabBehaviour) {
- if(tabBehaviour != null) {
- this.tabBehaviour = tabBehaviour;
- }
- }
-
- @Override
- public TabBehaviour getTabBehaviour() {
- return tabBehaviour;
- }
-
- @Override
- public void setCharacter(TerminalPosition position, TextCharacter screenCharacter) {
- setCharacter(position.getColumn(), position.getRow(), screenCharacter);
- }
-
- @Override
- public TextGraphics newTextGraphics() {
- return new ScreenTextGraphics(this) {
- @Override
- public TextGraphics drawImage(TerminalPosition topLeft, TextImage image, TerminalPosition sourceImageTopLeft, TerminalSize sourceImageSize) {
- backBuffer.copyFrom(image, sourceImageTopLeft.getRow(), sourceImageSize.getRows(), sourceImageTopLeft.getColumn(), sourceImageSize.getColumns(), topLeft.getRow(), topLeft.getColumn());
- return this;
- }
- };
- }
-
- @Override
- public synchronized void setCharacter(int column, int row, TextCharacter screenCharacter) {
- //It would be nice if we didn't have to care about tabs at this level, but we have no such luxury
- if(screenCharacter.getCharacter() == '\t') {
- //Swap out the tab for a space
- screenCharacter = screenCharacter.withCharacter(' ');
-
- //Now see how many times we have to put spaces...
- for(int i = 0; i < tabBehaviour.replaceTabs("\t", column).length(); i++) {
- backBuffer.setCharacterAt(column + i, row, screenCharacter);
- }
- }
- else {
- //This is the normal case, no special character
- backBuffer.setCharacterAt(column, row, screenCharacter);
- }
-
- //Pad CJK character with a trailing space
- if(TerminalTextUtils.isCharCJK(screenCharacter.getCharacter())) {
- backBuffer.setCharacterAt(column + 1, row, screenCharacter.withCharacter(' '));
- }
- //If there's a CJK character immediately to our left, reset it
- if(column > 0) {
- TextCharacter cjkTest = backBuffer.getCharacterAt(column - 1, row);
- if(cjkTest != null && TerminalTextUtils.isCharCJK(cjkTest.getCharacter())) {
- backBuffer.setCharacterAt(column - 1, row, backBuffer.getCharacterAt(column - 1, row).withCharacter(' '));
- }
- }
- }
-
- @Override
- public synchronized TextCharacter getFrontCharacter(TerminalPosition position) {
- return getFrontCharacter(position.getColumn(), position.getRow());
- }
-
- @Override
- public TextCharacter getFrontCharacter(int column, int row) {
- return getCharacterFromBuffer(frontBuffer, column, row);
- }
-
- @Override
- public synchronized TextCharacter getBackCharacter(TerminalPosition position) {
- return getBackCharacter(position.getColumn(), position.getRow());
- }
-
- @Override
- public TextCharacter getBackCharacter(int column, int row) {
- return getCharacterFromBuffer(backBuffer, column, row);
- }
-
- @Override
- public void refresh() throws IOException {
- refresh(RefreshType.AUTOMATIC);
- }
-
- @Override
- public synchronized void clear() {
- backBuffer.setAll(defaultCharacter);
- }
-
- @Override
- public synchronized TerminalSize doResizeIfNecessary() {
- TerminalSize pendingResize = getAndClearPendingResize();
- if(pendingResize == null) {
- return null;
- }
-
- backBuffer = backBuffer.resize(pendingResize, defaultCharacter);
- frontBuffer = frontBuffer.resize(pendingResize, defaultCharacter);
- return pendingResize;
- }
-
- @Override
- public TerminalSize getTerminalSize() {
- return terminalSize;
- }
-
- /**
- * Returns the front buffer connected to this screen, don't use this unless you know what you are doing!
- * @return This Screen's front buffer
- */
- protected ScreenBuffer getFrontBuffer() {
- return frontBuffer;
- }
-
- /**
- * Returns the back buffer connected to this screen, don't use this unless you know what you are doing!
- * @return This Screen's back buffer
- */
- protected ScreenBuffer getBackBuffer() {
- return backBuffer;
- }
-
- private synchronized TerminalSize getAndClearPendingResize() {
- if(latestResizeRequest != null) {
- terminalSize = latestResizeRequest;
- latestResizeRequest = null;
- return terminalSize;
- }
- return null;
- }
-
- /**
- * Tells this screen that the size has changed and it should, at next opportunity, resize itself and its buffers
- * @param newSize New size the 'real' terminal now has
- */
- protected void addResizeRequest(TerminalSize newSize) {
- latestResizeRequest = newSize;
- }
-
- private TextCharacter getCharacterFromBuffer(ScreenBuffer buffer, int column, int row) {
- if(column > 0) {
- //If we are picking the padding of a CJK character, pick the actual CJK character instead of the padding
- TextCharacter leftOfSpecifiedCharacter = buffer.getCharacterAt(column - 1, row);
- if(leftOfSpecifiedCharacter == null) {
- //If the character left of us doesn't exist, we don't exist either
- return null;
- }
- else if(TerminalTextUtils.isCharCJK(leftOfSpecifiedCharacter.getCharacter())) {
- return leftOfSpecifiedCharacter;
- }
- }
- return buffer.getCharacterAt(column, row);
- }
-
- @Override
- public String toString() {
- return getBackBuffer().toString();
- }
-
- /**
- * Performs the scrolling on its back-buffer.
- */
- @Override
- public void scrollLines(int firstLine, int lastLine, int distance) {
- getBackBuffer().scrollLines(firstLine, lastLine, distance);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.screen;
-
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.graphics.Scrollable;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.InputProvider;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import java.io.IOException;
-
-/**
- * Screen is a fundamental layer in Lanterna, presenting the terminal as a bitmap-like surface where you can perform
- * smaller in-memory operations to a back-buffer, effectively painting out the terminal as you'd like it, and then call
- * {@code refresh} to have the screen automatically apply the changes in the back-buffer to the real terminal. The
- * screen tracks what's visible through a front-buffer, but this is completely managed internally and cannot be expected
- * to know what the terminal looks like if it's being modified externally.
- * <p>
- * If you want to do more complicated drawing operations, please see the class {@code DefaultScreenWriter} which has many
- * utility methods that works on Screens.
- *
- * @author Martin
- */
-public interface Screen extends InputProvider, Scrollable {
- /**
- * This is the character Screen implementations should use as a filler is there are areas not set to any particular
- * character.
- */
- TextCharacter DEFAULT_CHARACTER = new TextCharacter(' ');
-
- /**
- * Before you can use a Screen, you need to start it. By starting the screen, Lanterna will make sure the terminal
- * is in private mode (Screen only supports private mode), clears it (so that is can set the front and back buffers
- * to a known value) and places the cursor in the top left corner. After calling startScreen(), you can begin using
- * the other methods on this interface. When you want to exit from the screen and return to what you had before,
- * you can call {@code stopScreen()}.
- *
- * @throws IOException if there was an underlying IO error when exiting from private mode
- */
- void startScreen() throws IOException;
-
- /**
- * Calling this method will make the underlying terminal leave private mode, effectively going back to whatever
- * state the terminal was in before calling {@code startScreen()}. Once a screen has been stopped, you can start it
- * again with {@code startScreen()} which will restore the screens content to the terminal.
- *
- * @throws IOException if there was an underlying IO error when exiting from private mode
- */
- void stopScreen() throws IOException;
-
- /**
- * Erases all the characters on the screen, effectively giving you a blank area. The default background color will
- * be used. This is effectively the same as calling
- * <pre>fill(TerminalPosition.TOP_LEFT_CORNER, getSize(), TextColor.ANSI.Default)</pre>.
- * <p>
- * Please note that calling this method will only affect the back buffer, you need to call refresh to make the
- * change visible.
- */
- void clear();
-
- /**
- * A screen implementation typically keeps a location on the screen where the cursor will be placed after drawing
- * and refreshing the buffers, this method returns that location. If it returns null, it means that the terminal
- * will attempt to hide the cursor (if supported by the terminal).
- *
- * @return Position where the cursor will be located after the screen has been refreshed or {@code null} if the
- * cursor is not visible
- */
- TerminalPosition getCursorPosition();
-
- /**
- * A screen implementation typically keeps a location on the screen where the cursor will be placed after drawing
- * and refreshing the buffers, this method controls that location. If you pass null, it means that the terminal
- * will attempt to hide the cursor (if supported by the terminal).
- *
- * @param position TerminalPosition of the new position where the cursor should be placed after refresh(), or if
- * {@code null}, hides the cursor
- */
- void setCursorPosition(TerminalPosition position);
-
- /**
- * Gets the behaviour for what to do about tab characters. If a tab character is written to the Screen, it would
- * cause issues because we don't know how the terminal emulator would render it and we wouldn't know what state the
- * front-buffer is in. Because of this, we convert tabs to a determined number of spaces depending on different
- * rules that are available.
- *
- * @return Tab behaviour that is used currently
- * @see TabBehaviour
- */
- TabBehaviour getTabBehaviour();
-
- /**
- * Sets the behaviour for what to do about tab characters. If a tab character is written to the Screen, it would
- * cause issues because we don't know how the terminal emulator would render it and we wouldn't know what state the
- * front-buffer is in. Because of this, we convert tabs to a determined number of spaces depending on different
- * rules that are available.
- *
- * @param tabBehaviour Tab behaviour to use when converting a \t character to a spaces
- * @see TabBehaviour
- */
- void setTabBehaviour(TabBehaviour tabBehaviour);
-
- /**
- * Returns the size of the screen. This call is not blocking but should return the size of the screen as it is
- * represented by the buffer at the time this method is called.
- *
- * @return Size of the screen, in columns and rows
- */
- TerminalSize getTerminalSize();
-
- /**
- * Sets a character in the back-buffer to a specified value with specified colors and modifiers.
- * @param column Column of the character to modify (x coordinate)
- * @param row Row of the character to modify (y coordinate)
- * @param screenCharacter New data to put at the specified position
- */
- void setCharacter(int column, int row, TextCharacter screenCharacter);
-
- /**
- * Sets a character in the back-buffer to a specified value with specified colors and modifiers.
- * @param position Which position in the terminal to modify
- * @param screenCharacter New data to put at the specified position
- */
- void setCharacter(TerminalPosition position, TextCharacter screenCharacter);
-
- /**
- * Creates a new TextGraphics objects that is targeting this Screen for writing to. Any operations done on this
- * TextGraphics will be affecting this screen. Remember to call {@code refresh()} on the screen to see your changes.
- *
- * @return New TextGraphic object targeting this Screen
- */
- TextGraphics newTextGraphics();
-
- /**
- * Reads a character and its associated meta-data from the front-buffer and returns it encapsulated as a
- * ScreenCharacter.
- * @param column Which column to get the character from
- * @param row Which row to get the character from
- * @return A {@code ScreenCharacter} representation of the character in the front-buffer at the specified location
- */
- TextCharacter getFrontCharacter(int column, int row);
-
- /**
- * Reads a character and its associated meta-data from the front-buffer and returns it encapsulated as a
- * ScreenCharacter.
- * @param position What position to read the character from
- * @return A {@code ScreenCharacter} representation of the character in the front-buffer at the specified location
- */
- TextCharacter getFrontCharacter(TerminalPosition position);
-
- /**
- * Reads a character and its associated meta-data from the back-buffer and returns it encapsulated as a
- * ScreenCharacter.
- * @param column Which column to get the character from
- * @param row Which row to get the character from
- * @return A {@code ScreenCharacter} representation of the character in the back-buffer at the specified location
- */
- TextCharacter getBackCharacter(int column, int row);
-
- /**
- * Reads a character and its associated meta-data from the back-buffer and returns it encapsulated as a
- * ScreenCharacter.
- * @param position What position to read the character from
- * @return A {@code ScreenCharacter} representation of the character in the back-buffer at the specified location
- */
- TextCharacter getBackCharacter(TerminalPosition position);
-
- /**
- * This method will take the content from the back-buffer and move it into the front-buffer, making the changes
- * visible to the terminal in the process. The graphics workflow with Screen would involve drawing text and text-like
- * graphics on the back buffer and then finally calling refresh(..) to make it visible to the user.
- * @throws java.io.IOException If there was an underlying I/O error
- * @see RefreshType
- */
- void refresh() throws IOException;
-
- /**
- * This method will take the content from the back-buffer and move it into the front-buffer, making the changes
- * visible to the terminal in the process. The graphics workflow with Screen would involve drawing text and text-like
- * graphics on the back buffer and then finally calling refresh(..) to make it visible to the user.
- * <p>
- * Using this method call instead of {@code refresh()} gives you a little bit more control over how the screen will
- * be refreshed.
- * @param refreshType What type of refresh to do
- * @throws java.io.IOException If there was an underlying I/O error
- * @see RefreshType
- */
- void refresh(RefreshType refreshType) throws IOException;
-
- /**
- * One problem working with Screens is that whenever the terminal is resized, the front and back buffers needs to be
- * adjusted accordingly and the program should have a chance to figure out what to do with this extra space (or less
- * space). The solution is to call, at the start of your rendering code, this method, which will check if the
- * terminal has been resized and in that case update the internals of the Screen. After this call finishes, the
- * screen's internal buffers will match the most recent size report from the underlying terminal.
- *
- * @return If the terminal has been resized since this method was last called, it will return the new size of the
- * terminal. If not, it will return null.
- */
- TerminalSize doResizeIfNecessary();
-
- /**
- * Scroll a range of lines of this Screen according to given distance.
- *
- * Screen implementations of this method do <b>not</b> throw IOException.
- */
- @Override
- void scrollLines(int firstLine, int lastLine, int distance);
-
- /**
- * This enum represents the different ways a Screen can refresh the screen, moving the back-buffer data into the
- * front-buffer that is being displayed.
- */
- enum RefreshType {
- /**
- * Using automatic mode, the Screen will make a guess at which refresh type would be the fastest and use this one.
- */
- AUTOMATIC,
- /**
- * In {@code RefreshType.DELTA} mode, the Screen will calculate a diff between the back-buffer and the
- * front-buffer, then figure out the set of terminal commands that is required to make the front-buffer exactly
- * like the back-buffer. This normally works well when you have modified only parts of the screen, but if you
- * have modified almost everything it will cause a lot of overhead and you should use
- * {@code RefreshType.COMPLETE} instead.
- */
- DELTA,
- /**
- * In {@code RefreshType.COMPLETE} mode, the screen will send a clear command to the terminal, then redraw the
- * whole back-buffer line by line. This is more expensive than {@code RefreshType.COMPLETE}, especially when you
- * have only touched smaller parts of the screen, but can be faster if you have modified most of the content,
- * as well as if you suspect the screen's internal front buffer is out-of-sync with what's really showing on the
- * terminal (you didn't go and call methods on the underlying Terminal while in screen mode, did you?)
- */
- COMPLETE,
- ;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.screen;
-
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.BasicTextImage;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.graphics.TextImage;
-
-/**
- * Defines a buffer used by AbstractScreen and its subclasses to keep its state of what's currently displayed and what
- * the edit buffer looks like. A ScreenBuffer is essentially a two-dimensional array of TextCharacter with some utility
- * methods to inspect and manipulate it in a safe way.
- * @author martin
- */
-public class ScreenBuffer implements TextImage {
- private final BasicTextImage backend;
-
- /**
- * Creates a new ScreenBuffer with a given size and a TextCharacter to initially fill it with
- * @param size Size of the buffer
- * @param filler What character to set as the initial content of the buffer
- */
- public ScreenBuffer(TerminalSize size, TextCharacter filler) {
- this(new BasicTextImage(size, filler));
- }
-
- private ScreenBuffer(BasicTextImage backend) {
- this.backend = backend;
- }
-
- @Override
- public ScreenBuffer resize(TerminalSize newSize, TextCharacter filler) {
- BasicTextImage resizedBackend = backend.resize(newSize, filler);
- return new ScreenBuffer(resizedBackend);
- }
-
- boolean isVeryDifferent(ScreenBuffer other, int threshold) {
- if(!getSize().equals(other.getSize())) {
- throw new IllegalArgumentException("Can only call isVeryDifferent comparing two ScreenBuffers of the same size!"
- + " This is probably a bug in Lanterna.");
- }
- int differences = 0;
- for(int y = 0; y < getSize().getRows(); y++) {
- for(int x = 0; x < getSize().getColumns(); x++) {
- if(!getCharacterAt(x, y).equals(other.getCharacterAt(x, y))) {
- if(++differences >= threshold) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
- ///////////////////////////////////////////////////////////////////////////////
- // Delegate all TextImage calls (except resize) to the backend BasicTextImage
- @Override
- public TerminalSize getSize() {
- return backend.getSize();
- }
-
- @Override
- public TextCharacter getCharacterAt(TerminalPosition position) {
- return backend.getCharacterAt(position);
- }
-
- @Override
- public TextCharacter getCharacterAt(int column, int row) {
- return backend.getCharacterAt(column, row);
- }
-
- @Override
- public void setCharacterAt(TerminalPosition position, TextCharacter character) {
- backend.setCharacterAt(position, character);
- }
-
- @Override
- public void setCharacterAt(int column, int row, TextCharacter character) {
- backend.setCharacterAt(column, row, character);
- }
-
- @Override
- public void setAll(TextCharacter character) {
- backend.setAll(character);
- }
-
- @Override
- public TextGraphics newTextGraphics() {
- return backend.newTextGraphics();
- }
-
- @Override
- public void copyTo(TextImage destination) {
- if(destination instanceof ScreenBuffer) {
- //This will allow the BasicTextImage's copy method to use System.arraycopy (micro-optimization?)
- destination = ((ScreenBuffer)destination).backend;
- }
- backend.copyTo(destination);
- }
-
- @Override
- public void copyTo(TextImage destination, int startRowIndex, int rows, int startColumnIndex, int columns, int destinationRowOffset, int destinationColumnOffset) {
- if(destination instanceof ScreenBuffer) {
- //This will allow the BasicTextImage's copy method to use System.arraycopy (micro-optimization?)
- destination = ((ScreenBuffer)destination).backend;
- }
- backend.copyTo(destination, startRowIndex, rows, startColumnIndex, columns, destinationRowOffset, destinationColumnOffset);
- }
-
- /**
- * Copies the content from a TextImage into this buffer.
- * @param source Source to copy content from
- * @param startRowIndex Which row in the source image to start copying from
- * @param rows How many rows to copy
- * @param startColumnIndex Which column in the source image to start copying from
- * @param columns How many columns to copy
- * @param destinationRowOffset The row offset in this buffer of where to place the copied content
- * @param destinationColumnOffset The column offset in this buffer of where to place the copied content
- */
- public void copyFrom(TextImage source, int startRowIndex, int rows, int startColumnIndex, int columns, int destinationRowOffset, int destinationColumnOffset) {
- source.copyTo(backend, startRowIndex, rows, startColumnIndex, columns, destinationRowOffset, destinationColumnOffset);
- }
-
- @Override
- public void scrollLines(int firstLine, int lastLine, int distance) {
- backend.scrollLines(firstLine, lastLine, distance);
- }
-
- @Override
- public String toString() {
- return backend.toString();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.screen;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.AbstractTextGraphics;
-import com.googlecode.lanterna.graphics.TextGraphics;
-
-/**
- * This is an implementation of TextGraphics that targets the output to a Screen. The ScreenTextGraphics object is valid
- * after screen resizing.
- * @author Martin
- */
-class ScreenTextGraphics extends AbstractTextGraphics {
- private final Screen screen;
-
- /**
- * Creates a new {@code ScreenTextGraphics} targeting the specified screen
- * @param screen Screen we are targeting
- */
- ScreenTextGraphics(Screen screen) {
- super();
- this.screen = screen;
- }
-
- @Override
- public TextGraphics setCharacter(int columnIndex, int rowIndex, TextCharacter textCharacter) {
- //Let the screen do culling
- screen.setCharacter(columnIndex, rowIndex, textCharacter);
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(int column, int row) {
- return screen.getBackCharacter(column, row);
- }
-
- @Override
- public TerminalSize getSize() {
- return screen.getTerminalSize();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.screen;
-
-/**
- * What to do about the tab character when putting on a {@code Screen}. Since tabs are a bit special, their meaning
- * depends on which column the cursor is in when it's printed, we'll need to have some way to tell the Screen what to
- * do when encountering a tab character.
- *
- * @author martin
- */
-public enum TabBehaviour {
- /**
- * Tab characters are not replaced, this will probably have undefined and weird behaviour!
- */
- IGNORE(null, null),
- /**
- * Tab characters are replaced with a single blank space, no matter where the tab was placed.
- */
- CONVERT_TO_ONE_SPACE(1, null),
- /**
- * Tab characters are replaced with two blank spaces, no matter where the tab was placed.
- */
- CONVERT_TO_TWO_SPACES(2, null),
- /**
- * Tab characters are replaced with three blank spaces, no matter where the tab was placed.
- */
- CONVERT_TO_THREE_SPACES(3, null),
- /**
- * Tab characters are replaced with four blank spaces, no matter where the tab was placed.
- */
- CONVERT_TO_FOUR_SPACES(4, null),
- /**
- * Tab characters are replaced with eight blank spaces, no matter where the tab was placed.
- */
- CONVERT_TO_EIGHT_SPACES(8, null),
- /**
- * Tab characters are replaced with enough space characters to reach the next column index that is evenly divisible
- * by 4, simulating a normal tab character when placed inside a text document.
- */
- ALIGN_TO_COLUMN_4(null, 4),
- /**
- * Tab characters are replaced with enough space characters to reach the next column index that is evenly divisible
- * by 8, simulating a normal tab character when placed inside a text document.
- */
- ALIGN_TO_COLUMN_8(null, 8),
- ;
-
- private final Integer replaceFactor;
- private final Integer alignFactor;
-
- TabBehaviour(Integer replaceFactor, Integer alignFactor) {
- this.replaceFactor = replaceFactor;
- this.alignFactor = alignFactor;
- }
-
- /**
- * Given a string, being placed on the screen at column X, returns the same string with all tab characters (\t)
- * replaced according to this TabBehaviour.
- * @param string String that is going to be put to the screen, potentially containing tab characters
- * @param columnIndex Column on the screen where the first character of the string is going to end up
- * @return The input string with all tab characters replaced with spaces, according to this TabBehaviour
- */
- public String replaceTabs(String string, int columnIndex) {
- int tabPosition = string.indexOf('\t');
- while(tabPosition != -1) {
- String tabReplacementHere = getTabReplacement(columnIndex + tabPosition);
- string = string.substring(0, tabPosition) + tabReplacementHere + string.substring(tabPosition + 1);
- tabPosition += tabReplacementHere.length();
- tabPosition = string.indexOf('\t', tabPosition);
- }
- return string;
- }
-
- /**
- * Returns the String that can replace a tab at the specified position, according to this TabBehaviour.
- * @param columnIndex Column index of where the tab character is placed
- * @return String consisting of 1 or more space character
- */
- public String getTabReplacement(int columnIndex) {
- int replaceCount;
- StringBuilder replace = new StringBuilder();
- if(replaceFactor != null) {
- replaceCount = replaceFactor;
- }
- else if (alignFactor != null) {
- replaceCount = alignFactor - (columnIndex % alignFactor);
- }
- else {
- return "\t";
- }
- for(int i = 0; i < replaceCount; i++) {
- replace.append(" ");
- }
- return replace.toString();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.screen;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.graphics.Scrollable;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-import com.googlecode.lanterna.terminal.ResizeListener;
-import com.googlecode.lanterna.terminal.Terminal;
-
-import java.io.IOException;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * This is the default concrete implementation of the Screen interface, a buffered layer sitting on top of a Terminal.
- * If you want to get started with the Screen layer, this is probably the class you want to use. Remember to start the
- * screen before you can use it and stop it when you are done with it. This will place the terminal in private mode
- * during the screen operations and leave private mode afterwards.
- * @author martin
- */
-public class TerminalScreen extends AbstractScreen {
- private final Terminal terminal;
- private boolean isStarted;
- private boolean fullRedrawHint;
- private ScrollHint scrollHint;
-
- /**
- * Creates a new Screen on top of a supplied terminal, will query the terminal for its size. The screen is initially
- * blank. The default character used for unused space (the newly initialized state of the screen and new areas after
- * expanding the terminal size) will be a blank space in 'default' ANSI front- and background color.
- * <p>
- * Before you can display the content of this buffered screen to the real underlying terminal, you must call the
- * {@code startScreen()} method. This will ask the terminal to enter private mode (which is required for Screens to
- * work properly). Similarly, when you are done, you should call {@code stopScreen()} which will exit private mode.
- *
- * @param terminal Terminal object to create the DefaultScreen on top of
- * @throws java.io.IOException If there was an underlying I/O error when querying the size of the terminal
- */
- public TerminalScreen(Terminal terminal) throws IOException {
- this(terminal, DEFAULT_CHARACTER);
- }
-
- /**
- * Creates a new Screen on top of a supplied terminal, will query the terminal for its size. The screen is initially
- * blank. The default character used for unused space (the newly initialized state of the screen and new areas after
- * expanding the terminal size) will be a blank space in 'default' ANSI front- and background color.
- * <p>
- * Before you can display the content of this buffered screen to the real underlying terminal, you must call the
- * {@code startScreen()} method. This will ask the terminal to enter private mode (which is required for Screens to
- * work properly). Similarly, when you are done, you should call {@code stopScreen()} which will exit private mode.
- *
- * @param terminal Terminal object to create the DefaultScreen on top of.
- * @param defaultCharacter What character to use for the initial state of the screen and expanded areas
- * @throws java.io.IOException If there was an underlying I/O error when querying the size of the terminal
- */
- public TerminalScreen(Terminal terminal, TextCharacter defaultCharacter) throws IOException {
- super(terminal.getTerminalSize(), defaultCharacter);
- this.terminal = terminal;
- this.terminal.addResizeListener(new TerminalResizeListener());
- this.isStarted = false;
- this.fullRedrawHint = true;
- }
-
- @Override
- public synchronized void startScreen() throws IOException {
- if(isStarted) {
- return;
- }
-
- isStarted = true;
- getTerminal().enterPrivateMode();
- getTerminal().getTerminalSize();
- getTerminal().clearScreen();
- this.fullRedrawHint = true;
- TerminalPosition cursorPosition = getCursorPosition();
- if(cursorPosition != null) {
- getTerminal().setCursorVisible(true);
- getTerminal().setCursorPosition(cursorPosition.getColumn(), cursorPosition.getRow());
- } else {
- getTerminal().setCursorVisible(false);
- }
- }
-
- @Override
- public void stopScreen() throws IOException {
- stopScreen(true);
- }
-
- public synchronized void stopScreen(boolean flushInput) throws IOException {
- if(!isStarted) {
- return;
- }
-
- if (flushInput) {
- //Drain the input queue
- KeyStroke keyStroke;
- do {
- keyStroke = pollInput();
- }
- while(keyStroke != null && keyStroke.getKeyType() != KeyType.EOF);
- }
-
- getTerminal().exitPrivateMode();
- isStarted = false;
- }
-
- @Override
- public synchronized void refresh(RefreshType refreshType) throws IOException {
- if(!isStarted) {
- return;
- }
- if((refreshType == RefreshType.AUTOMATIC && fullRedrawHint) || refreshType == RefreshType.COMPLETE) {
- refreshFull();
- fullRedrawHint = false;
- }
- else if(refreshType == RefreshType.AUTOMATIC &&
- (scrollHint == null || scrollHint == ScrollHint.INVALID)) {
- double threshold = getTerminalSize().getRows() * getTerminalSize().getColumns() * 0.75;
- if(getBackBuffer().isVeryDifferent(getFrontBuffer(), (int) threshold)) {
- refreshFull();
- }
- else {
- refreshByDelta();
- }
- }
- else {
- refreshByDelta();
- }
- getBackBuffer().copyTo(getFrontBuffer());
- TerminalPosition cursorPosition = getCursorPosition();
- if(cursorPosition != null) {
- getTerminal().setCursorVisible(true);
- //If we are trying to move the cursor to the padding of a CJK character, put it on the actual character instead
- if(cursorPosition.getColumn() > 0 && TerminalTextUtils.isCharCJK(getFrontBuffer().getCharacterAt(cursorPosition.withRelativeColumn(-1)).getCharacter())) {
- getTerminal().setCursorPosition(cursorPosition.getColumn() - 1, cursorPosition.getRow());
- }
- else {
- getTerminal().setCursorPosition(cursorPosition.getColumn(), cursorPosition.getRow());
- }
- } else {
- getTerminal().setCursorVisible(false);
- }
- getTerminal().flush();
- }
-
- private void useScrollHint() throws IOException {
- if (scrollHint == null) { return; }
-
- try {
- if (scrollHint == ScrollHint.INVALID) { return; }
- Terminal term = getTerminal();
- if (term instanceof Scrollable) {
- // just try and see if it cares:
- scrollHint.applyTo( (Scrollable)term );
- // if that didn't throw, then update front buffer:
- scrollHint.applyTo( getFrontBuffer() );
- }
- }
- catch (UnsupportedOperationException uoe) { /* ignore */ }
- finally { scrollHint = null; }
- }
-
- private void refreshByDelta() throws IOException {
- Map<TerminalPosition, TextCharacter> updateMap = new TreeMap<TerminalPosition, TextCharacter>(new ScreenPointComparator());
- TerminalSize terminalSize = getTerminalSize();
-
- useScrollHint();
-
- for(int y = 0; y < terminalSize.getRows(); y++) {
- for(int x = 0; x < terminalSize.getColumns(); x++) {
- TextCharacter backBufferCharacter = getBackBuffer().getCharacterAt(x, y);
- if(!backBufferCharacter.equals(getFrontBuffer().getCharacterAt(x, y))) {
- updateMap.put(new TerminalPosition(x, y), backBufferCharacter);
- }
- if(TerminalTextUtils.isCharCJK(backBufferCharacter.getCharacter())) {
- x++; //Skip the trailing padding
- }
- }
- }
-
- if(updateMap.isEmpty()) {
- return;
- }
- TerminalPosition currentPosition = updateMap.keySet().iterator().next();
- getTerminal().setCursorPosition(currentPosition.getColumn(), currentPosition.getRow());
-
- TextCharacter firstScreenCharacterToUpdate = updateMap.values().iterator().next();
- EnumSet<SGR> currentSGR = firstScreenCharacterToUpdate.getModifiers();
- getTerminal().resetColorAndSGR();
- for(SGR sgr: currentSGR) {
- getTerminal().enableSGR(sgr);
- }
- TextColor currentForegroundColor = firstScreenCharacterToUpdate.getForegroundColor();
- TextColor currentBackgroundColor = firstScreenCharacterToUpdate.getBackgroundColor();
- getTerminal().setForegroundColor(currentForegroundColor);
- getTerminal().setBackgroundColor(currentBackgroundColor);
- for(TerminalPosition position: updateMap.keySet()) {
- if(!position.equals(currentPosition)) {
- getTerminal().setCursorPosition(position.getColumn(), position.getRow());
- currentPosition = position;
- }
- TextCharacter newCharacter = updateMap.get(position);
- if(!currentForegroundColor.equals(newCharacter.getForegroundColor())) {
- getTerminal().setForegroundColor(newCharacter.getForegroundColor());
- currentForegroundColor = newCharacter.getForegroundColor();
- }
- if(!currentBackgroundColor.equals(newCharacter.getBackgroundColor())) {
- getTerminal().setBackgroundColor(newCharacter.getBackgroundColor());
- currentBackgroundColor = newCharacter.getBackgroundColor();
- }
- for(SGR sgr: SGR.values()) {
- if(currentSGR.contains(sgr) && !newCharacter.getModifiers().contains(sgr)) {
- getTerminal().disableSGR(sgr);
- currentSGR.remove(sgr);
- }
- else if(!currentSGR.contains(sgr) && newCharacter.getModifiers().contains(sgr)) {
- getTerminal().enableSGR(sgr);
- currentSGR.add(sgr);
- }
- }
- getTerminal().putCharacter(newCharacter.getCharacter());
- if(TerminalTextUtils.isCharCJK(newCharacter.getCharacter())) {
- //CJK characters advances two columns
- currentPosition = currentPosition.withRelativeColumn(2);
- }
- else {
- //Normal characters advances one column
- currentPosition = currentPosition.withRelativeColumn(1);
- }
- }
- }
-
- private void refreshFull() throws IOException {
- getTerminal().setForegroundColor(TextColor.ANSI.DEFAULT);
- getTerminal().setBackgroundColor(TextColor.ANSI.DEFAULT);
- getTerminal().clearScreen();
- getTerminal().resetColorAndSGR();
- scrollHint = null; // discard any scroll hint for full refresh
-
- EnumSet<SGR> currentSGR = EnumSet.noneOf(SGR.class);
- TextColor currentForegroundColor = TextColor.ANSI.DEFAULT;
- TextColor currentBackgroundColor = TextColor.ANSI.DEFAULT;
- for(int y = 0; y < getTerminalSize().getRows(); y++) {
- getTerminal().setCursorPosition(0, y);
- int currentColumn = 0;
- for(int x = 0; x < getTerminalSize().getColumns(); x++) {
- TextCharacter newCharacter = getBackBuffer().getCharacterAt(x, y);
- if(newCharacter.equals(DEFAULT_CHARACTER)) {
- continue;
- }
-
- if(!currentForegroundColor.equals(newCharacter.getForegroundColor())) {
- getTerminal().setForegroundColor(newCharacter.getForegroundColor());
- currentForegroundColor = newCharacter.getForegroundColor();
- }
- if(!currentBackgroundColor.equals(newCharacter.getBackgroundColor())) {
- getTerminal().setBackgroundColor(newCharacter.getBackgroundColor());
- currentBackgroundColor = newCharacter.getBackgroundColor();
- }
- for(SGR sgr: SGR.values()) {
- if(currentSGR.contains(sgr) && !newCharacter.getModifiers().contains(sgr)) {
- getTerminal().disableSGR(sgr);
- currentSGR.remove(sgr);
- }
- else if(!currentSGR.contains(sgr) && newCharacter.getModifiers().contains(sgr)) {
- getTerminal().enableSGR(sgr);
- currentSGR.add(sgr);
- }
- }
- if(currentColumn != x) {
- getTerminal().setCursorPosition(x, y);
- currentColumn = x;
- }
- getTerminal().putCharacter(newCharacter.getCharacter());
- if(TerminalTextUtils.isCharCJK(newCharacter.getCharacter())) {
- //CJK characters take up two columns
- currentColumn += 2;
- x++;
- }
- else {
- //Normal characters take up one column
- currentColumn += 1;
- }
- }
- }
- }
-
- /**
- * Returns the underlying {@code Terminal} interface that this Screen is using.
- * <p>
- * <b>Be aware:</b> directly modifying the underlying terminal will most likely result in unexpected behaviour if
- * you then go on and try to interact with the Screen. The Screen's back-buffer/front-buffer will not know about
- * the operations you are going on the Terminal and won't be able to properly generate a refresh unless you enforce
- * a {@code Screen.RefreshType.COMPLETE}, at which the entire terminal area will be repainted according to the
- * back-buffer of the {@code Screen}.
- * @return Underlying terminal used by the screen
- */
- @SuppressWarnings("WeakerAccess")
- public Terminal getTerminal() {
- return terminal;
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- return terminal.readInput();
- }
-
- @Override
- public KeyStroke pollInput() throws IOException {
- return terminal.pollInput();
- }
-
- @Override
- public synchronized void clear() {
- super.clear();
- fullRedrawHint = true;
- scrollHint = ScrollHint.INVALID;
- }
-
- @Override
- public synchronized TerminalSize doResizeIfNecessary() {
- TerminalSize newSize = super.doResizeIfNecessary();
- if(newSize != null) {
- fullRedrawHint = true;
- }
- return newSize;
- }
-
- /**
- * Perform the scrolling and save scroll-range and distance in order
- * to be able to optimize Terminal-update later.
- */
- @Override
- public void scrollLines(int firstLine, int lastLine, int distance) {
- // just ignore certain kinds of garbage:
- if (distance == 0 || firstLine > lastLine) { return; }
-
- super.scrollLines(firstLine, lastLine, distance);
-
- // Save scroll hint for next refresh:
- ScrollHint newHint = new ScrollHint(firstLine,lastLine,distance);
- if (scrollHint == null) {
- // no scroll hint yet: use the new one:
- scrollHint = newHint;
- } else if (scrollHint == ScrollHint.INVALID) {
- // scroll ranges already inconsistent since latest refresh!
- // leave at INVALID
- } else if (scrollHint.matches(newHint)) {
- // same range: just accumulate distance:
- scrollHint.distance += newHint.distance;
- } else {
- // different scroll range: no scroll-optimization for next refresh
- this.scrollHint = ScrollHint.INVALID;
- }
- }
-
- private class TerminalResizeListener implements ResizeListener {
- @Override
- public void onResized(Terminal terminal, TerminalSize newSize) {
- addResizeRequest(newSize);
- }
- }
-
- private static class ScreenPointComparator implements Comparator<TerminalPosition> {
- @Override
- public int compare(TerminalPosition o1, TerminalPosition o2) {
- if(o1.getRow() == o2.getRow()) {
- if(o1.getColumn() == o2.getColumn()) {
- return 0;
- } else {
- return new Integer(o1.getColumn()).compareTo(o2.getColumn());
- }
- } else {
- return new Integer(o1.getRow()).compareTo(o2.getRow());
- }
- }
- }
-
- private static class ScrollHint {
- public static final ScrollHint INVALID = new ScrollHint(-1,-1,0);
- public int firstLine, lastLine, distance;
-
- public ScrollHint(int firstLine, int lastLine, int distance) {
- this.firstLine = firstLine;
- this.lastLine = lastLine;
- this.distance = distance;
- }
-
- public boolean matches(ScrollHint other) {
- return this.firstLine == other.firstLine
- && this.lastLine == other.lastLine;
- }
-
- public void applyTo( Scrollable scr ) throws IOException {
- scr.scrollLines(firstLine, lastLine, distance);
- }
- }
-
-}
+++ /dev/null
-package com.googlecode.lanterna.screen;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-
-import java.io.IOException;
-
-/**
- * VirtualScreen wraps a normal screen and presents it as a screen that has a configurable minimum size; if the real
- * screen is smaller than this size, the presented screen will add scrolling to get around it. To anyone using this
- * class, it will appear and behave just as a normal screen. Scrolling is done by using CTRL + arrow keys.
- * <p>
- * The use case for this class is to allow you to set a minimum size that you can count on be honored, no matter how
- * small the user makes the terminal. This should make programming GUIs easier.
- * @author Martin
- */
-public class VirtualScreen extends AbstractScreen {
- private final Screen realScreen;
- private final FrameRenderer frameRenderer;
- private TerminalSize minimumSize;
- private TerminalPosition viewportTopLeft;
- private TerminalSize viewportSize;
-
- /**
- * Creates a new VirtualScreen that wraps a supplied Screen. The screen passed in here should be the real screen
- * that is created on top of the real {@code Terminal}, it will have the correct size and content for what's
- * actually displayed to the user, but this class will present everything as one view with a fixed minimum size,
- * no matter what size the real terminal has.
- * <p>
- * The initial minimum size will be the current size of the screen.
- * @param screen Real screen that will be used when drawing the whole or partial virtual screen
- */
- public VirtualScreen(Screen screen) {
- super(screen.getTerminalSize());
- this.frameRenderer = new DefaultFrameRenderer();
- this.realScreen = screen;
- this.minimumSize = screen.getTerminalSize();
- this.viewportTopLeft = TerminalPosition.TOP_LEFT_CORNER;
- this.viewportSize = minimumSize;
- }
-
- /**
- * Sets the minimum size we want the virtual screen to have. If the user resizes the real terminal to something
- * smaller than this, the virtual screen will refuse to make it smaller and add scrollbars to the view.
- * @param minimumSize Minimum size we want the screen to have
- */
- public void setMinimumSize(TerminalSize minimumSize) {
- this.minimumSize = minimumSize;
- TerminalSize virtualSize = minimumSize.max(realScreen.getTerminalSize());
- if(!minimumSize.equals(virtualSize)) {
- addResizeRequest(virtualSize);
- super.doResizeIfNecessary();
- }
- calculateViewport(realScreen.getTerminalSize());
- }
-
- /**
- * Returns the minimum size this virtual screen can have. If the real terminal is made smaller than this, the
- * virtual screen will draw scrollbars and implement scrolling
- * @return Minimum size configured for this virtual screen
- */
- public TerminalSize getMinimumSize() {
- return minimumSize;
- }
-
- @Override
- public void startScreen() throws IOException {
- realScreen.startScreen();
- }
-
- @Override
- public void stopScreen() throws IOException {
- realScreen.stopScreen();
- }
-
- @Override
- public TextCharacter getFrontCharacter(TerminalPosition position) {
- return null;
- }
-
- @Override
- public void setCursorPosition(TerminalPosition position) {
- super.setCursorPosition(position);
- if(position == null) {
- realScreen.setCursorPosition(null);
- return;
- }
- position = position.withRelativeColumn(-viewportTopLeft.getColumn()).withRelativeRow(-viewportTopLeft.getRow());
- if(position.getColumn() >= 0 && position.getColumn() < viewportSize.getColumns() &&
- position.getRow() >= 0 && position.getRow() < viewportSize.getRows()) {
- realScreen.setCursorPosition(position);
- }
- else {
- realScreen.setCursorPosition(null);
- }
- }
-
- @Override
- public synchronized TerminalSize doResizeIfNecessary() {
- TerminalSize underlyingSize = realScreen.doResizeIfNecessary();
- if(underlyingSize == null) {
- return null;
- }
-
- TerminalSize newVirtualSize = calculateViewport(underlyingSize);
- if(!getTerminalSize().equals(newVirtualSize)) {
- addResizeRequest(newVirtualSize);
- return super.doResizeIfNecessary();
- }
- return newVirtualSize;
- }
-
- private TerminalSize calculateViewport(TerminalSize realTerminalSize) {
- TerminalSize newVirtualSize = minimumSize.max(realTerminalSize);
- if(newVirtualSize.equals(realTerminalSize)) {
- viewportSize = realTerminalSize;
- viewportTopLeft = TerminalPosition.TOP_LEFT_CORNER;
- }
- else {
- TerminalSize newViewportSize = frameRenderer.getViewportSize(realTerminalSize, newVirtualSize);
- if(newViewportSize.getRows() > viewportSize.getRows()) {
- viewportTopLeft = viewportTopLeft.withRow(Math.max(0, viewportTopLeft.getRow() - (newViewportSize.getRows() - viewportSize.getRows())));
- }
- if(newViewportSize.getColumns() > viewportSize.getColumns()) {
- viewportTopLeft = viewportTopLeft.withColumn(Math.max(0, viewportTopLeft.getColumn() - (newViewportSize.getColumns() - viewportSize.getColumns())));
- }
- viewportSize = newViewportSize;
- }
- return newVirtualSize;
- }
-
- @Override
- public void refresh(RefreshType refreshType) throws IOException {
- setCursorPosition(getCursorPosition()); //Make sure the cursor is at the correct position
- if(!viewportSize.equals(realScreen.getTerminalSize())) {
- frameRenderer.drawFrame(
- realScreen.newTextGraphics(),
- realScreen.getTerminalSize(),
- getTerminalSize(),
- viewportTopLeft);
- }
-
- //Copy the rows
- TerminalPosition viewportOffset = frameRenderer.getViewportOffset();
- if(realScreen instanceof AbstractScreen) {
- AbstractScreen asAbstractScreen = (AbstractScreen)realScreen;
- getBackBuffer().copyTo(
- asAbstractScreen.getBackBuffer(),
- viewportTopLeft.getRow(),
- viewportSize.getRows(),
- viewportTopLeft.getColumn(),
- viewportSize.getColumns(),
- viewportOffset.getRow(),
- viewportOffset.getColumn());
- }
- else {
- for(int y = 0; y < viewportSize.getRows(); y++) {
- for(int x = 0; x < viewportSize.getColumns(); x++) {
- realScreen.setCharacter(
- x + viewportOffset.getColumn(),
- y + viewportOffset.getRow(),
- getBackBuffer().getCharacterAt(
- x + viewportTopLeft.getColumn(),
- y + viewportTopLeft.getRow()));
- }
- }
- }
- realScreen.refresh(refreshType);
- }
-
- @Override
- public KeyStroke pollInput() throws IOException {
- return filter(realScreen.pollInput());
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- return filter(realScreen.readInput());
- }
-
- private KeyStroke filter(KeyStroke keyStroke) throws IOException {
- if(keyStroke == null) {
- return null;
- }
- else if(keyStroke.isAltDown() && keyStroke.getKeyType() == KeyType.ArrowLeft) {
- if(viewportTopLeft.getColumn() > 0) {
- viewportTopLeft = viewportTopLeft.withRelativeColumn(-1);
- refresh();
- return null;
- }
- }
- else if(keyStroke.isAltDown() && keyStroke.getKeyType() == KeyType.ArrowRight) {
- if(viewportTopLeft.getColumn() + viewportSize.getColumns() < getTerminalSize().getColumns()) {
- viewportTopLeft = viewportTopLeft.withRelativeColumn(1);
- refresh();
- return null;
- }
- }
- else if(keyStroke.isAltDown() && keyStroke.getKeyType() == KeyType.ArrowUp) {
- if(viewportTopLeft.getRow() > 0) {
- viewportTopLeft = viewportTopLeft.withRelativeRow(-1);
- realScreen.scrollLines(0,viewportSize.getRows()-1,-1);
- refresh();
- return null;
- }
- }
- else if(keyStroke.isAltDown() && keyStroke.getKeyType() == KeyType.ArrowDown) {
- if(viewportTopLeft.getRow() + viewportSize.getRows() < getTerminalSize().getRows()) {
- viewportTopLeft = viewportTopLeft.withRelativeRow(1);
- realScreen.scrollLines(0,viewportSize.getRows()-1,1);
- refresh();
- return null;
- }
- }
- return keyStroke;
- }
-
- @Override
- public void scrollLines(int firstLine, int lastLine, int distance) {
- // do base class stuff (scroll own back buffer)
- super.scrollLines(firstLine, lastLine, distance);
- // vertical range visible in realScreen:
- int vpFirst = viewportTopLeft.getRow(),
- vpRows = viewportSize.getRows();
- // adapt to realScreen range:
- firstLine = Math.max(0, firstLine - vpFirst);
- lastLine = Math.min(vpRows - 1, lastLine - vpFirst);
- // if resulting range non-empty: scroll that range in realScreen:
- if (firstLine <= lastLine) {
- realScreen.scrollLines(firstLine, lastLine, distance);
- }
- }
-
- /**
- * Interface for rendering the virtual screen's frame when the real terminal is too small for the virtual screen
- */
- public interface FrameRenderer {
- /**
- * Given the size of the real terminal and the current size of the virtual screen, how large should the viewport
- * where the screen content is drawn be?
- * @param realSize Size of the real terminal
- * @param virtualSize Size of the virtual screen
- * @return Size of the viewport, according to this FrameRenderer
- */
- TerminalSize getViewportSize(TerminalSize realSize, TerminalSize virtualSize);
-
- /**
- * Where in the virtual screen should the top-left position of the viewport be? To draw the viewport from the
- * top-left position of the screen, return 0x0 (or TerminalPosition.TOP_LEFT_CORNER) here.
- * @return Position of the top-left corner of the viewport inside the screen
- */
- TerminalPosition getViewportOffset();
-
- /**
- * Drawn the 'frame', meaning anything that is outside the viewport (title, scrollbar, etc)
- * @param graphics Graphics to use to text drawing operations
- * @param realSize Size of the real terminal
- * @param virtualSize Size of the virtual screen
- * @param virtualScrollPosition If the virtual screen is larger than the real terminal, this is the current
- * scroll offset the VirtualScreen is using
- */
- void drawFrame(
- TextGraphics graphics,
- TerminalSize realSize,
- TerminalSize virtualSize,
- TerminalPosition virtualScrollPosition);
- }
-
- private static class DefaultFrameRenderer implements FrameRenderer {
- @Override
- public TerminalSize getViewportSize(TerminalSize realSize, TerminalSize virtualSize) {
- if(realSize.getColumns() > 1 && realSize.getRows() > 2) {
- return realSize.withRelativeColumns(-1).withRelativeRows(-2);
- }
- else {
- return realSize;
- }
- }
-
- @Override
- public TerminalPosition getViewportOffset() {
- return TerminalPosition.TOP_LEFT_CORNER;
- }
-
- @Override
- public void drawFrame(
- TextGraphics graphics,
- TerminalSize realSize,
- TerminalSize virtualSize,
- TerminalPosition virtualScrollPosition) {
-
- if(realSize.getColumns() == 1 || realSize.getRows() <= 2) {
- return;
- }
- TerminalSize viewportSize = getViewportSize(realSize, virtualSize);
-
- graphics.setForegroundColor(TextColor.ANSI.WHITE);
- graphics.setBackgroundColor(TextColor.ANSI.BLACK);
- graphics.fill(' ');
- graphics.putString(0, graphics.getSize().getRows() - 1, "Terminal too small, use ALT+arrows to scroll");
-
- int horizontalSize = (int)(((double)(viewportSize.getColumns()) / (double)virtualSize.getColumns()) * (viewportSize.getColumns()));
- int scrollable = viewportSize.getColumns() - horizontalSize - 1;
- int horizontalPosition = (int)((double)scrollable * ((double)virtualScrollPosition.getColumn() / (double)(virtualSize.getColumns() - viewportSize.getColumns())));
- graphics.drawLine(
- new TerminalPosition(horizontalPosition, graphics.getSize().getRows() - 2),
- new TerminalPosition(horizontalPosition + horizontalSize, graphics.getSize().getRows() - 2),
- Symbols.BLOCK_MIDDLE);
-
- int verticalSize = (int)(((double)(viewportSize.getRows()) / (double)virtualSize.getRows()) * (viewportSize.getRows()));
- scrollable = viewportSize.getRows() - verticalSize - 1;
- int verticalPosition = (int)((double)scrollable * ((double)virtualScrollPosition.getRow() / (double)(virtualSize.getRows() - viewportSize.getRows())));
- graphics.drawLine(
- new TerminalPosition(graphics.getSize().getColumns() - 1, verticalPosition),
- new TerminalPosition(graphics.getSize().getColumns() - 1, verticalPosition + verticalSize),
- Symbols.BLOCK_MIDDLE);
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.TextGraphics;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Containing a some very fundamental functionality that should be common (and usable) to all terminal implementations.
- * All the Terminal implementers within Lanterna extends from this class.
- *
- * @author Martin
- */
-public abstract class AbstractTerminal implements Terminal {
-
- private final List<ResizeListener> resizeListeners;
- private TerminalSize lastKnownSize;
-
- protected AbstractTerminal() {
- this.resizeListeners = new ArrayList<ResizeListener>();
- this.lastKnownSize = null;
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- if (listener != null) {
- resizeListeners.add(listener);
- }
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- if (listener != null) {
- resizeListeners.remove(listener);
- }
- }
-
- /**
- * Call this method when the terminal has been resized or the initial size of the terminal has been discovered. It
- * will trigger all resize listeners, but only if the size has changed from before.
- *
- * @param columns Number of columns in the new size
- * @param rows Number of rows in the new size
- */
- protected synchronized void onResized(int columns, int rows) {
- TerminalSize newSize = new TerminalSize(columns, rows);
- if (lastKnownSize == null || !lastKnownSize.equals(newSize)) {
- lastKnownSize = newSize;
- for (ResizeListener resizeListener : resizeListeners) {
- resizeListener.onResized(this, lastKnownSize);
- }
- }
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- return new TerminalTextGraphics(this);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.terminal.ansi.CygwinTerminal;
-import com.googlecode.lanterna.terminal.ansi.UnixTerminal;
-import com.googlecode.lanterna.terminal.swing.*;
-
-import java.awt.*;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.Charset;
-
-/**
- * This TerminalFactory implementation uses a simple auto-detection mechanism for figuring out which terminal
- * implementation to create based on characteristics of the system the program is running on.
- * <p>
- * Note that for all systems with a graphical environment present, the SwingTerminalFrame will be chosen. You can
- * suppress this by calling setForceTextTerminal(true) on this factory.
- * @author martin
- */
-public final class DefaultTerminalFactory implements TerminalFactory {
- private static final OutputStream DEFAULT_OUTPUT_STREAM = System.out;
- private static final InputStream DEFAULT_INPUT_STREAM = System.in;
- private static final Charset DEFAULT_CHARSET = Charset.forName(System.getProperty("file.encoding"));
-
- private final OutputStream outputStream;
- private final InputStream inputStream;
- private final Charset charset;
-
- private TerminalSize initialTerminalSize;
- private boolean forceTextTerminal;
- private boolean forceAWTOverSwing;
- private String title;
- private boolean autoOpenTerminalFrame;
- private TerminalEmulatorAutoCloseTrigger autoCloseTrigger;
- private TerminalEmulatorColorConfiguration colorConfiguration;
- private TerminalEmulatorDeviceConfiguration deviceConfiguration;
- private AWTTerminalFontConfiguration fontConfiguration;
- private MouseCaptureMode mouseCaptureMode;
-
- /**
- * Creates a new DefaultTerminalFactory with all properties set to their defaults
- */
- public DefaultTerminalFactory() {
- this(DEFAULT_OUTPUT_STREAM, DEFAULT_INPUT_STREAM, DEFAULT_CHARSET);
- }
-
- /**
- * Creates a new DefaultTerminalFactory with I/O and character set options customisable.
- * @param outputStream Output stream to use for text-based Terminal implementations
- * @param inputStream Input stream to use for text-based Terminal implementations
- * @param charset Character set to assume the client is using
- */
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
- public DefaultTerminalFactory(OutputStream outputStream, InputStream inputStream, Charset charset) {
- this.outputStream = outputStream;
- this.inputStream = inputStream;
- this.charset = charset;
-
- this.forceTextTerminal = false;
- this.autoOpenTerminalFrame = true;
- this.title = null;
- this.autoCloseTrigger = TerminalEmulatorAutoCloseTrigger.CloseOnExitPrivateMode;
- this.mouseCaptureMode = null;
-
- //SwingTerminal will replace these null values for the default implementation if they are unchanged
- this.colorConfiguration = null;
- this.deviceConfiguration = null;
- this.fontConfiguration = null;
- }
-
- @Override
- public Terminal createTerminal() throws IOException {
- if (GraphicsEnvironment.isHeadless() || forceTextTerminal || System.console() != null) {
- if(isOperatingSystemWindows()) {
- return createCygwinTerminal(outputStream, inputStream, charset);
- }
- else {
- return createUnixTerminal(outputStream, inputStream, charset);
- }
- }
- else {
- return createTerminalEmulator();
- }
- }
-
- /**
- * Creates a new terminal emulator window which will be either Swing-based or AWT-based depending on what is
- * available on the system
- * @return New terminal emulator exposed as a {@link Terminal} interface
- */
- public Terminal createTerminalEmulator() {
- Window window;
- if(!forceAWTOverSwing && hasSwing()) {
- window = createSwingTerminal();
- }
- else {
- window = createAWTTerminal();
- }
-
- if(autoOpenTerminalFrame) {
- window.setVisible(true);
- }
- return (Terminal)window;
- }
-
- public AWTTerminalFrame createAWTTerminal() {
- return new AWTTerminalFrame(
- title,
- initialTerminalSize,
- deviceConfiguration,
- fontConfiguration,
- colorConfiguration,
- autoCloseTrigger);
- }
-
- public SwingTerminalFrame createSwingTerminal() {
- return new SwingTerminalFrame(
- title,
- initialTerminalSize,
- deviceConfiguration,
- fontConfiguration instanceof SwingTerminalFontConfiguration ? (SwingTerminalFontConfiguration)fontConfiguration : null,
- colorConfiguration,
- autoCloseTrigger);
- }
-
- private boolean hasSwing() {
- try {
- Class.forName("javax.swing.JComponent");
- return true;
- }
- catch(Exception ignore) {
- return false;
- }
- }
-
- /**
- * Sets a hint to the TerminalFactory of what size to use when creating the terminal. Most terminals are not created
- * on request but for example the SwingTerminal and SwingTerminalFrame are and this value will be passed down on
- * creation.
- * @param initialTerminalSize Size (in rows and columns) of the newly created terminal
- * @return Reference to itself, so multiple .set-calls can be chained
- */
- public DefaultTerminalFactory setInitialTerminalSize(TerminalSize initialTerminalSize) {
- this.initialTerminalSize = initialTerminalSize;
- return this;
- }
-
- /**
- * Controls whether a SwingTerminalFrame shall always be created if the system is one with a graphical environment
- * @param forceTextTerminal If true, will always create a text-based Terminal
- * @return Reference to itself, so multiple .set-calls can be chained
- */
- public DefaultTerminalFactory setForceTextTerminal(boolean forceTextTerminal) {
- this.forceTextTerminal = forceTextTerminal;
- return this;
- }
-
- /**
- * Normally when a graphical terminal emulator is created by the factory, it will create a
- * {@link SwingTerminalFrame} unless Swing is not present in the system. Setting this property to {@code true} will
- * make it create an {@link AWTTerminalFrame} even if Swing is present
- * @param forceAWTOverSwing If {@code true}, will always create an {@link AWTTerminalFrame} over a
- * {@link SwingTerminalFrame} if asked to create a graphical terminal emulator
- * @return Reference to itself, so multiple .set-calls can be chained
- */
- public DefaultTerminalFactory setForceAWTOverSwing(boolean forceAWTOverSwing) {
- this.forceAWTOverSwing = forceAWTOverSwing;
- return this;
- }
-
- /**
- * Controls whether a SwingTerminalFrame shall be automatically shown (.setVisible(true)) immediately after
- * creation. If {@code false}, you will manually need to call {@code .setVisible(true)} on the JFrame to actually
- * see the terminal window. Default for this value is {@code true}.
- * @param autoOpenTerminalFrame Automatically open SwingTerminalFrame after creation
- */
- public void setAutoOpenTerminalEmulatorWindow(boolean autoOpenTerminalFrame) {
- this.autoOpenTerminalFrame = autoOpenTerminalFrame;
- }
-
- /**
- * Sets the title to use on created SwingTerminalFrames created by this factory
- * @param title Title to use on created SwingTerminalFrames created by this factory
- * @return Reference to itself, so multiple .set-calls can be chained
- */
- public DefaultTerminalFactory setTerminalEmulatorTitle(String title) {
- this.title = title;
- return this;
- }
-
- /**
- * Sets the auto-close trigger to use on created SwingTerminalFrames created by this factory
- * @param autoCloseTrigger Auto-close trigger to use on created SwingTerminalFrames created by this factory
- * @return Reference to itself, so multiple .set-calls can be chained
- */
- public DefaultTerminalFactory setTerminalEmulatorFrameAutoCloseTrigger(TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this.autoCloseTrigger = autoCloseTrigger;
- return this;
- }
-
- /**
- * Sets the color configuration to use on created SwingTerminalFrames created by this factory
- * @param colorConfiguration Color configuration to use on created SwingTerminalFrames created by this factory
- * @return Reference to itself, so multiple .set-calls can be chained
- */
- public DefaultTerminalFactory setTerminalEmulatorColorConfiguration(TerminalEmulatorColorConfiguration colorConfiguration) {
- this.colorConfiguration = colorConfiguration;
- return this;
- }
-
- /**
- * Sets the device configuration to use on created SwingTerminalFrames created by this factory
- * @param deviceConfiguration Device configuration to use on created SwingTerminalFrames created by this factory
- * @return Reference to itself, so multiple .set-calls can be chained
- */
- public DefaultTerminalFactory setTerminalEmulatorDeviceConfiguration(TerminalEmulatorDeviceConfiguration deviceConfiguration) {
- this.deviceConfiguration = deviceConfiguration;
- return this;
- }
-
- /**
- * Sets the font configuration to use on created SwingTerminalFrames created by this factory
- * @param fontConfiguration Font configuration to use on created SwingTerminalFrames created by this factory
- * @return Reference to itself, so multiple .set-calls can be chained
- */
- public DefaultTerminalFactory setTerminalEmulatorFontConfiguration(AWTTerminalFontConfiguration fontConfiguration) {
- this.fontConfiguration = fontConfiguration;
- return this;
- }
-
- /**
- * Sets the mouse capture mode the terminal should use. Please note that this is an extension which isn't widely
- * supported!
- * @param mouseCaptureMode Capture mode for mouse interactions
- * @return Itself
- */
- public DefaultTerminalFactory setMouseCaptureMode(MouseCaptureMode mouseCaptureMode) {
- this.mouseCaptureMode = mouseCaptureMode;
- return this;
- }
-
- private Terminal createCygwinTerminal(OutputStream outputStream, InputStream inputStream, Charset charset) throws IOException {
- return new CygwinTerminal(inputStream, outputStream, charset);
- }
-
- private Terminal createUnixTerminal(OutputStream outputStream, InputStream inputStream, Charset charset) throws IOException {
- UnixTerminal unixTerminal = new UnixTerminal(inputStream, outputStream, charset);
- if(mouseCaptureMode != null) {
- unixTerminal.setMouseCaptureMode(mouseCaptureMode);
- }
- return unixTerminal;
- }
-
- /**
- * Detects whether the running platform is Windows* by looking at the
- * operating system name system property
- */
- private static boolean isOperatingSystemWindows() {
- return System.getProperty("os.name", "").toLowerCase().startsWith("windows");
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal;
-
-import java.io.IOException;
-
-import com.googlecode.lanterna.graphics.Scrollable;
-
-/**
- * This class extends the normal Terminal interface and adds a few more methods that are considered rare and shouldn't
- * be encouraged to be used. Some of these may move into Terminal if it turns out that they are indeed well-supported.
- * Most of these extensions are picked up from here: http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
- *
- * This class is <b>not</b> considered stable and may change within releases. Do not depend on methods in this interface
- * unless you are ok with occasionally having to fix broken code after minor library upgrades.
- * @author Martin
- */
-public interface ExtendedTerminal extends Terminal, Scrollable {
-
- /**
- * Attempts to resize the terminal through dtterm extensions "CSI 8 ; rows ; columns ; t". This isn't widely
- * supported, which is why the method is not exposed through the common Terminal interface.
- * @throws java.io.IOException If the was an underlying I/O error
- */
- void setTerminalSize(int columns, int rows) throws IOException;
-
- /**
- * This methods sets the title of the terminal, which is normally only visible if you are running the application
- * in a terminal emulator in a graphical environment.
- * @param title Title to set on the terminal
- * @throws java.io.IOException If the was an underlying I/O error
- */
- void setTitle(String title) throws IOException;
-
- /**
- * Saves the current window title on a stack managed internally by the terminal.
- * @throws java.io.IOException If the was an underlying I/O error
- */
- void pushTitle() throws IOException;
-
- /**
- * Replaces the terminal title with the top element from the title stack managed by the terminal (the element is
- * removed from the stack as expected)
- * @throws java.io.IOException If the was an underlying I/O error
- */
- void popTitle() throws IOException;
-
- /**
- * Iconifies the terminal, this likely means minimizing the window with most window managers
- * @throws IOException If the was an underlying I/O error
- */
- void iconify() throws IOException;
-
- /**
- * De-iconifies the terminal, which likely means restoring it from minimized state with most window managers
- * @throws IOException If the was an underlying I/O error
- */
- void deiconify() throws IOException;
-
- /**
- * Maximizes the terminal, so that it takes up all available space
- * @throws IOException If the was an underlying I/O error
- */
- void maximize() throws IOException;
-
- /**
- * Restores the terminal back to its previous size, after having been maximized
- * @throws IOException If the was an underlying I/O error
- */
- void unmaximize() throws IOException;
-
- /**
- * Enabled or disables capturing of mouse event. This is not recommended to use as most users are not familiar with
- * the fact that terminal emulators allow capturing mouse input. You can decide which events you want to capture but
- * be careful since different terminal emulators will support these modes differently. Mouse capture mode will be
- * automatically disabled when the application exits through a shutdown hook.
- *
- * @param mouseCaptureMode Which mouse events to capture, pass in {@code null} to disable mouse input capturing
- * @throws IOException If the was an underlying I/O error
- */
- void setMouseCaptureMode(MouseCaptureMode mouseCaptureMode) throws IOException;
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal;
-
-/**
- * Interface extending ExtendedTerminal that removes the IOException throw clause.
- *
- * @author Martin
- * @author Andreas
- */
-public interface IOSafeExtendedTerminal extends IOSafeTerminal,ExtendedTerminal {
-
- @Override
- void setTerminalSize(int columns, int rows);
-
- @Override
- void setTitle(String title);
-
- @Override
- void pushTitle();
-
- @Override
- void popTitle();
-
- @Override
- void iconify();
-
- @Override
- void deiconify();
-
- @Override
- void maximize();
-
- @Override
- void unmaximize();
-
- @Override
- void setMouseCaptureMode(MouseCaptureMode mouseCaptureMode);
-
- @Override
- void scrollLines(int firstLine, int lastLine, int distance);
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.input.KeyStroke;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Interface extending Terminal that removes the IOException throw clause. You can for example use this instead of
- * Terminal if you use an implementation that doesn't throw any IOExceptions or if you wrap your terminal in an
- * IOSafeTerminalAdapter. Please note that readInput() still throws IOException when it is interrupted, in order to fit
- * better in with what normal terminal do when they are blocked on input and you interrupt them.
- * @author Martin
- */
-public interface IOSafeTerminal extends Terminal {
- @Override
- void enterPrivateMode();
- @Override
- void exitPrivateMode();
- @Override
- void clearScreen();
- @Override
- void setCursorPosition(int x, int y);
- @Override
- void setCursorVisible(boolean visible);
- @Override
- void putCharacter(char c);
- @Override
- void enableSGR(SGR sgr);
- @Override
- void disableSGR(SGR sgr);
- @Override
- void resetColorAndSGR();
- @Override
- void setForegroundColor(TextColor color);
- @Override
- void setBackgroundColor(TextColor color);
- @Override
- TerminalSize getTerminalSize();
- @Override
- byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit);
- @Override
- void flush();
- @Override
- KeyStroke pollInput();
- @Override
- KeyStroke readInput() throws IOException;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.KeyStroke;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class exposes methods for converting a terminal into an IOSafeTerminal. There are two options available, either
- * one that will convert any IOException to a RuntimeException (and re-throw it) or one that will silently swallow any
- * IOException (and return null in those cases the method has a non-void return type).
- * @author Martin
- */
-public class IOSafeTerminalAdapter implements IOSafeTerminal {
- private interface ExceptionHandler {
- void onException(IOException e);
- }
-
- private static class ConvertToRuntimeException implements ExceptionHandler {
- @Override
- public void onException(IOException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static class DoNothingAndOrReturnNull implements ExceptionHandler {
- @Override
- public void onException(IOException e) { }
- }
-
- /**
- * Creates a wrapper around a Terminal that exposes it as a IOSafeTerminal. If any IOExceptions occur, they will be
- * wrapped by a RuntimeException and re-thrown.
- * @param terminal Terminal to wrap
- * @return IOSafeTerminal wrapping the supplied terminal
- */
- public static IOSafeTerminal createRuntimeExceptionConvertingAdapter(Terminal terminal) {
- if (terminal instanceof ExtendedTerminal) { // also handle Runtime-type:
- return createRuntimeExceptionConvertingAdapter((ExtendedTerminal)terminal);
- } else {
- return new IOSafeTerminalAdapter(terminal, new ConvertToRuntimeException());
- }
- }
-
- /**
- * Creates a wrapper around an ExtendedTerminal that exposes it as a IOSafeExtendedTerminal.
- * If any IOExceptions occur, they will be wrapped by a RuntimeException and re-thrown.
- * @param terminal Terminal to wrap
- * @return IOSafeTerminal wrapping the supplied terminal
- */
- public static IOSafeExtendedTerminal createRuntimeExceptionConvertingAdapter(ExtendedTerminal terminal) {
- return new IOSafeTerminalAdapter.Extended(terminal, new ConvertToRuntimeException());
- }
-
- /**
- * Creates a wrapper around a Terminal that exposes it as a IOSafeTerminal. If any IOExceptions occur, they will be
- * silently ignored and for those method with a non-void return type, null will be returned.
- * @param terminal Terminal to wrap
- * @return IOSafeTerminal wrapping the supplied terminal
- */
- public static IOSafeTerminal createDoNothingOnExceptionAdapter(Terminal terminal) {
- if (terminal instanceof ExtendedTerminal) { // also handle Runtime-type:
- return createDoNothingOnExceptionAdapter((ExtendedTerminal)terminal);
- } else {
- return new IOSafeTerminalAdapter(terminal, new DoNothingAndOrReturnNull());
- }
- }
-
- /**
- * Creates a wrapper around an ExtendedTerminal that exposes it as a IOSafeExtendedTerminal.
- * If any IOExceptions occur, they will be silently ignored and for those method with a
- * non-void return type, null will be returned.
- * @param terminal Terminal to wrap
- * @return IOSafeTerminal wrapping the supplied terminal
- */
- public static IOSafeExtendedTerminal createDoNothingOnExceptionAdapter(ExtendedTerminal terminal) {
- return new IOSafeTerminalAdapter.Extended(terminal, new DoNothingAndOrReturnNull());
- }
-
- private final Terminal backend;
- final ExceptionHandler exceptionHandler;
-
- @SuppressWarnings("WeakerAccess")
- public IOSafeTerminalAdapter(Terminal backend, ExceptionHandler exceptionHandler) {
- this.backend = backend;
- this.exceptionHandler = exceptionHandler;
- }
-
- @Override
- public void enterPrivateMode() {
- try {
- backend.enterPrivateMode();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void exitPrivateMode() {
- try {
- backend.exitPrivateMode();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void clearScreen() {
- try {
- backend.clearScreen();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void setCursorPosition(int x, int y) {
- try {
- backend.setCursorPosition(x, y);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void setCursorVisible(boolean visible) {
- try {
- backend.setCursorVisible(visible);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void putCharacter(char c) {
- try {
- backend.putCharacter(c);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- return backend.newTextGraphics();
- }
-
- @Override
- public void enableSGR(SGR sgr) {
- try {
- backend.enableSGR(sgr);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void disableSGR(SGR sgr) {
- try {
- backend.disableSGR(sgr);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void resetColorAndSGR() {
- try {
- backend.resetColorAndSGR();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void setForegroundColor(TextColor color) {
- try {
- backend.setForegroundColor(color);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void setBackgroundColor(TextColor color) {
- try {
- backend.setBackgroundColor(color);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- backend.addResizeListener(listener);
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- backend.removeResizeListener(listener);
- }
-
- @Override
- public TerminalSize getTerminalSize() {
- try {
- return backend.getTerminalSize();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- return null;
- }
-
- @Override
- public byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) {
- try {
- return backend.enquireTerminal(timeout, timeoutUnit);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- return null;
- }
-
- @Override
- public void flush() {
- try {
- backend.flush();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public KeyStroke pollInput() {
- try {
- return backend.pollInput();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- return null;
- }
-
- @Override
- public KeyStroke readInput() {
- try {
- return backend.readInput();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- return null;
- }
-
- /**
- * This class exposes methods for converting an extended terminal into an IOSafeExtendedTerminal.
- */
- public static class Extended extends IOSafeTerminalAdapter implements IOSafeExtendedTerminal {
- private final ExtendedTerminal backend;
-
- public Extended(ExtendedTerminal backend, ExceptionHandler exceptionHandler) {
- super(backend, exceptionHandler);
- this.backend = backend;
- }
-
- @Override
- public void setTerminalSize(int columns, int rows) {
- try {
- backend.setTerminalSize(columns, rows);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void setTitle(String title) {
- try {
- backend.setTitle(title);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void pushTitle() {
- try {
- backend.pushTitle();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void popTitle() {
- try {
- backend.popTitle();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void iconify() {
- try {
- backend.iconify();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void deiconify() {
- try {
- backend.deiconify();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void maximize() {
- try {
- backend.maximize();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void unmaximize() {
- try {
- backend.unmaximize();
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void setMouseCaptureMode(MouseCaptureMode mouseCaptureMode) {
- try {
- backend.setMouseCaptureMode(mouseCaptureMode);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- @Override
- public void scrollLines(int firstLine, int lastLine, int distance) {
- try {
- backend.scrollLines(firstLine, lastLine, distance);
- }
- catch(IOException e) {
- exceptionHandler.onException(e);
- }
- }
-
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal;
-
-/**
- * Constant describing different modes for capturing mouse input. By default, no mouse capturing is enabled (unless
- * previously enabled before starting the Lanterna application. These are the different modes of input capturing
- * supported. Please note that terminal emulators vary widely in how these are implemented!
- * Created by martin on 26/07/15.
- */
-public enum MouseCaptureMode {
- /**
- * Mouse clicks are captured on the down-motion but not the up-motion. This corresponds to the X10 xterm protocol.
- * KDE's Konsole (tested with 15.04) does not implement this extension, but xfce4-terminal, gnome-terminal and
- * xterm does.
- */
- CLICK,
- /**
- * Mouse clicks are captured both on down and up, this is the normal mode for capturing mouse input. KDE's konsole
- * interprets this as CLICK_RELEASE_DRAG.
- */
- CLICK_RELEASE,
- /**
- * Mouse clicks are captured both on down and up and if the mouse if moved while holding down one of the button, a
- * drag event is generated.
- */
- CLICK_RELEASE_DRAG,
- /**
- * Mouse clicks are captured both on down and up and also all mouse movements, no matter if any button is held down
- * or not.
- */
- CLICK_RELEASE_DRAG_MOVE,
- ;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * Listener interface that can be used to be alerted on terminal resizing
- */
-public interface ResizeListener {
-
- /**
- * The terminal has changed its size, most likely because the user has resized the window. This callback is
- * invoked by something inside the lanterna library, it could be a signal handler thread, it could be the AWT
- * thread, it could be something else, so please be careful with what kind of operation you do in here. Also,
- * make sure not to take too long before returning. Best practice would be to update an internal status in your
- * program to mark that the terminal has been resized (possibly along with the new size) and then in your main
- * loop you deal with this at the beginning of each redraw.
- * @param terminal Terminal that was resized
- * @param newSize Size of the terminal after the resize
- */
- @SuppressWarnings("UnusedParameters")
- void onResized(Terminal terminal, TerminalSize newSize);
-
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * This class is a simple implementation of Terminal.ResizeListener which will keep track of the size of the terminal
- * and let you know if the terminal has been resized since you last checked. This can be useful to avoid threading
- * problems with the resize callback when your application is using a main event loop.
- *
- * @author martin
- */
-@SuppressWarnings("WeakerAccess")
-public class SimpleTerminalResizeListener implements ResizeListener {
-
- boolean wasResized;
- TerminalSize lastKnownSize;
-
- /**
- * Creates a new SimpleTerminalResizeListener
- * @param initialSize Before any resize event, this listener doesn't know the size of the terminal. By supplying a
- * value here, you control what getLastKnownSize() will return if invoked before any resize events has reached us.
- */
- public SimpleTerminalResizeListener(TerminalSize initialSize) {
- this.wasResized = false;
- this.lastKnownSize = initialSize;
- }
-
- /**
- * Checks if the terminal was resized since the last time this method was called. If this is the first time calling
- * this method, the result is going to be based on if the terminal has been resized since this listener was attached
- * to the Terminal.
- *
- * @return true if the terminal was resized, false otherwise
- */
- public synchronized boolean isTerminalResized() {
- if(wasResized) {
- wasResized = false;
- return true;
- }
- else {
- return false;
- }
- }
-
- /**
- * Returns the last known size the Terminal is supposed to have.
- *
- * @return Size of the terminal, as of the last resize update
- */
- public TerminalSize getLastKnownSize() {
- return lastKnownSize;
- }
-
- @Override
- public synchronized void onResized(Terminal terminal, TerminalSize newSize) {
- this.wasResized = true;
- this.lastKnownSize = newSize;
- }
-
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.InputProvider;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This is the main terminal interface, at the lowest level supported by Lanterna. You can write your own
- * implementation of this if you want to target an exotic text terminal specification or another graphical environment
- * (like SWT), but you should probably extend {@code AbstractTerminal} instead of implementing this interface directly.
- * <p>
- * The normal way you interact in Java with a terminal is through the standard output (System.out) and standard error
- * (System.err) and it's usually through printing text only. This interface abstracts a terminal at a more fundamental
- * level, expressing methods for not only printing text but also changing colors, moving the cursor new positions,
- * enable special modifiers and get notified when the terminal's size has changed.
- * <p>
- * If you want to write an application that has a very precise control of the terminal, this is the
- * interface you should be programming against.
- *
- * @author Martin
- */
-public interface Terminal extends InputProvider {
-
- /**
- * Calling this method will, where supported, give your terminal a private area to use, separate from what was there
- * before. Some terminal emulators will preserve the terminal history and restore it when you exit private mode.
- * Some terminals will just clear the screen and put the cursor in the top-left corner. Typically, if you terminal
- * supports scrolling, going into private mode will disable the scrolling and leave you with a fixed screen, which
- * can be useful if you don't want to deal with what the terminal buffer will look like if the user scrolls up.
- *
- * @throws java.io.IOException If there was an underlying I/O error
- * @throws IllegalStateException If you are already in private mode
- */
- void enterPrivateMode() throws IOException;
-
- /**
- * If you have previously entered private mode, this method will exit this and, depending on implementation, maybe
- * restore what the terminal looked like before private mode was entered. If the terminal doesn't support a
- * secondary buffer for private mode, it will probably make a new line below the private mode and place the cursor
- * there.
- *
- * @throws java.io.IOException If there was an underlying I/O error
- * @throws IllegalStateException If you are not in private mode
- */
- void exitPrivateMode() throws IOException;
-
- /**
- * Removes all the characters, colors and graphics from the screen and leaves you with a big empty space. Text
- * cursor position is undefined after this call (depends on platform and terminal) so you should always call
- * {@code moveCursor} next. Some terminal implementations doesn't reset color and modifier state so it's also good
- * practise to call {@code resetColorAndSGR()} after this.
- * @throws java.io.IOException If there was an underlying I/O error
- */
- void clearScreen() throws IOException;
-
- /**
- * Moves the text cursor to a new location on the terminal. The top-left corner has coordinates 0 x 0 and the bottom-
- * right corner has coordinates terminal_width-1 x terminal_height-1. You can retrieve the size of the terminal by
- * calling getTerminalSize().
- *
- * @param x The 0-indexed column to place the cursor at
- * @param y The 0-indexed row to place the cursor at
- * @throws java.io.IOException If there was an underlying I/O error
- */
- void setCursorPosition(int x, int y) throws IOException;
-
- /**
- * Hides or shows the text cursor, but not all terminal (-emulators) supports this. The text cursor is normally a
- * text block or an underscore, sometimes blinking, which shows the user where keyboard-entered text is supposed to
- * show up.
- *
- * @param visible Hides the text cursor if {@code false} and shows it if {@code true}
- * @throws java.io.IOException If there was an underlying I/O error
- */
- void setCursorVisible(boolean visible) throws IOException;
-
- /**
- * Prints one character to the terminal at the current cursor location. Please note that the cursor will then move
- * one column to the right, so multiple calls to {@code putCharacter} will print out a text string without the need
- * to reposition the text cursor. If you reach the end of the line while putting characters using this method, you
- * can expect the text cursor to move to the beginning of the next line.
- * <p>
- * You can output CJK (Chinese, Japanese, Korean) characters (as well as other regional scripts) but remember that
- * the terminal that the user is using might not have the required font to render it. Also worth noticing is that
- * CJK (and some others) characters tend to take up 2 columns per character, simply because they are a square in
- * their construction as opposed to the somewhat rectangular shape we fit latin characters in. As it's very
- * difficult to create a monospace font for CJK with a 2:1 height-width proportion, it seems like the implementers
- * back in the days simply gave up and made each character take 2 column. It causes issues for the random terminal
- * programmer because you can't really trust 1 character = 1 column, but I suppose it's "しょうがない".
- *
- * @param c Character to place on the terminal
- * @throws java.io.IOException If there was an underlying I/O error
- */
- void putCharacter(char c) throws IOException;
-
- /**
- * Creates a new TextGraphics object that uses this Terminal directly when outputting. Keep in mind that you are
- * probably better off to switch to a Screen to make advanced text graphics more efficient. Also, this TextGraphics
- * implementation will not call {@code .flush()} after any operation, so you'll need to do that on your own.
- * @return TextGraphics implementation that draws directly using this Terminal interface
- */
- TextGraphics newTextGraphics() throws IOException;
-
- /**
- * Activates an {@code SGR} (Selected Graphic Rendition) code. This code modifies a state inside the terminal
- * that will apply to all characters written afterwards, such as bold, italic, blinking code and so on.
- *
- * @param sgr SGR code to apply
- * @throws java.io.IOException If there was an underlying I/O error
- * @see SGR
- * @see <a href="http://www.vt100.net/docs/vt510-rm/SGR">http://www.vt100.net/docs/vt510-rm/SGR</a>
- */
- void enableSGR(SGR sgr) throws IOException;
-
- /**
- * Deactivates an {@code SGR} (Selected Graphic Rendition) code which has previously been activated through {@code
- * enableSGR(..)}.
- *
- * @param sgr SGR code to apply
- * @throws java.io.IOException If there was an underlying I/O error
- * @see SGR
- * @see <a href="http://www.vt100.net/docs/vt510-rm/SGR">http://www.vt100.net/docs/vt510-rm/SGR</a>
- */
- void disableSGR(SGR sgr) throws IOException;
-
- /**
- * Removes all currently active SGR codes and sets foreground and background colors back to default.
- *
- * @throws java.io.IOException If there was an underlying I/O error
- * @see SGR
- * @see <a href="http://www.vt100.net/docs/vt510-rm/SGR">http://www.vt100.net/docs/vt510-rm/SGR</a>
- */
- void resetColorAndSGR() throws IOException;
-
- /**
- * Changes the foreground color for all the following characters put to the terminal. The foreground color is what
- * color to draw the text in, as opposed to the background color which is the color surrounding the characters.
- * <p>
- * This overload is using the TextColor class to define a color, which is a layer of abstraction above the three
- * different color formats supported (ANSI, indexed and RGB). The other setForegroundColor(..) overloads gives
- * you direct access to set one of those three.
- * <p>
- * Note to implementers of this interface, just make this method call <b>color.applyAsForeground(this);</b>
- *
- * @param color Color to use for foreground
- * @throws java.io.IOException If there was an underlying I/O error
- */
- void setForegroundColor(TextColor color) throws IOException;
-
- /**
- * Changes the background color for all the following characters put to the terminal. The background color is the
- * color surrounding the text being printed.
- * <p>
- * This overload is using the TextColor class to define a color, which is a layer of abstraction above the three
- * different color formats supported (ANSI, indexed and RGB). The other setBackgroundColor(..) overloads gives
- * you direct access to set one of those three.
- * <p>
- * Note to implementers of this interface, just make this method call <b>color.applyAsBackground(this);</b>
- *
- * @param color Color to use for the background
- * @throws java.io.IOException If there was an underlying I/O error
- */
- void setBackgroundColor(TextColor color) throws IOException;
-
- /**
- * Adds a {@code ResizeListener} to be called when the terminal has changed size. There is no guarantee that this
- * listener will really be invoked when the terminal has changed size, at all depends on the terminal emulator
- * implementation. Normally on Unix systems the WINCH signal will be sent to the process and lanterna can intercept
- * this.
- * <p>
- * There are no guarantees on what thread the call will be made on, so please be careful with what kind of operation
- * you perform in this callback. You should probably not take too long to return.
- *
- * @see ResizeListener
- * @param listener Listener object to be called when the terminal has been changed
- */
- void addResizeListener(ResizeListener listener);
-
- /**
- * Removes a {@code ResizeListener} from the list of listeners to be notified when the terminal has changed size
- *
- * @see ResizeListener
- * @param listener Listener object to remove
- */
- void removeResizeListener(ResizeListener listener);
-
- /**
- * Returns the size of the terminal, expressed as a {@code TerminalSize} object. Please bear in mind that depending
- * on the {@code Terminal} implementation, this may or may not be accurate. See the implementing classes for more
- * information. Most commonly, calling getTerminalSize() will involve some kind of hack to retrieve the size of the
- * terminal, like moving the cursor to position 5000x5000 and then read back the location, unless the terminal
- * implementation has a more smooth way of getting this data. Keep this in mind and see if you can avoid calling
- * this method too often. There is a helper class, SimpleTerminalResizeListener, that you can use to cache the size
- * and update it only when resize events are received (which depends on if a resize is detectable, which they are not
- * on all platforms).
- *
- * @return Size of the terminal
- * @throws java.io.IOException if there was an I/O error trying to retrieve the size of the terminal
- */
- TerminalSize getTerminalSize() throws IOException;
-
- /**
- * Retrieves optional information from the terminal by printing the ENQ ({@literal \}u005) character. Terminals and terminal
- * emulators may or may not respond to this command, sometimes it's configurable.
- *
- * @param timeout How long to wait for the talk-back message, if there's nothing immediately available on the input
- * stream, you should probably set this to a somewhat small value to prevent unnecessary blockage on the input stream
- * but large enough to accommodate a round-trip to the user's terminal (~300 ms if you are connection across the globe).
- * @param timeoutUnit What unit to use when interpreting the {@code timeout} parameter
- * @return Answer-back message from the terminal or empty if there was nothing
- * @throws java.io.IOException If there was an I/O error while trying to read the enquiry reply
- */
- byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) throws IOException;
-
- /**
- * Calls {@code flush()} on the underlying {@code OutputStream} object, or whatever other implementation this
- * terminal is built around. Some implementing classes of this interface (like SwingTerminal) doesn't do anything
- * as it doesn't really apply to them.
- * @throws java.io.IOException If there was an underlying I/O error
- */
- void flush() throws IOException;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import java.io.IOException;
-
-/**
- * This interface is for abstracting the creation of your Terminal object. The bundled implementation is
- * DefaultTerminalFactory, which will use a simple auto-detection mechanism for figuring out which terminal
- * implementation to create based on characteristics of the system the program is running on.
- * <p>
- * @author martin
- */
-@SuppressWarnings("WeakerAccess")
-public interface TerminalFactory {
- /**
- * Instantiates a Terminal according to the factory implementation.
- * @return Terminal implementation
- * @throws IOException If there was an I/O error with the underlying input/output system
- */
- Terminal createTerminal() throws IOException;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.AbstractTextGraphics;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.graphics.TextGraphics;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * This is the terminal's implementation of TextGraphics. Upon creation it takes a snapshot for the terminal's size, so
- * that it won't require to do an expensive lookup on every call to {@code getSize()}, but this also means that it can
- * go stale quickly if the terminal is resized. You should try to use the object quickly and then let it be GC:ed. It
- * will not pick up on terminal resize! Also, the state of the Terminal after an operation performed by this
- * TextGraphics implementation is undefined and you should probably re-initialize colors and modifiers.
- * <p/>
- * Any write operation that results in an IOException will be wrapped by a RuntimeException since the TextGraphics
- * interface doesn't allow throwing IOException
- */
-class TerminalTextGraphics extends AbstractTextGraphics {
-
- private final Terminal terminal;
- private final TerminalSize terminalSize;
-
- private final Map<TerminalPosition, TextCharacter> writeHistory;
-
- private AtomicInteger manageCallStackSize;
- private TextCharacter lastCharacter;
- private TerminalPosition lastPosition;
-
- TerminalTextGraphics(Terminal terminal) throws IOException {
- this.terminal = terminal;
- this.terminalSize = terminal.getTerminalSize();
- this.manageCallStackSize = new AtomicInteger(0);
- this.writeHistory = new HashMap<TerminalPosition, TextCharacter>();
- this.lastCharacter = null;
- this.lastPosition = null;
- }
-
- @Override
- public TextGraphics setCharacter(int columnIndex, int rowIndex, TextCharacter textCharacter) {
- return setCharacter(new TerminalPosition(columnIndex, rowIndex), textCharacter);
- }
-
- @Override
- public synchronized TextGraphics setCharacter(TerminalPosition position, TextCharacter textCharacter) {
- try {
- if(manageCallStackSize.get() > 0) {
- if(lastCharacter == null || !lastCharacter.equals(textCharacter)) {
- applyGraphicState(textCharacter);
- lastCharacter = textCharacter;
- }
- if(lastPosition == null || !lastPosition.equals(position)) {
- terminal.setCursorPosition(position.getColumn(), position.getRow());
- lastPosition = position;
- }
- }
- else {
- terminal.setCursorPosition(position.getColumn(), position.getRow());
- applyGraphicState(textCharacter);
- }
- terminal.putCharacter(textCharacter.getCharacter());
- if(manageCallStackSize.get() > 0) {
- lastPosition = position.withRelativeColumn(1);
- }
- writeHistory.put(position, textCharacter);
- }
- catch(IOException e) {
- throw new RuntimeException(e);
- }
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(int column, int row) {
- return getCharacter(new TerminalPosition(column, row));
- }
-
- @Override
- public synchronized TextCharacter getCharacter(TerminalPosition position) {
- return writeHistory.get(position);
- }
-
- private void applyGraphicState(TextCharacter textCharacter) throws IOException {
- terminal.resetColorAndSGR();
- terminal.setForegroundColor(textCharacter.getForegroundColor());
- terminal.setBackgroundColor(textCharacter.getBackgroundColor());
- for(SGR sgr: textCharacter.getModifiers()) {
- terminal.enableSGR(sgr);
- }
- }
-
- @Override
- public TerminalSize getSize() {
- return terminalSize;
- }
-
- @Override
- public synchronized TextGraphics drawLine(TerminalPosition fromPoint, TerminalPosition toPoint, char character) {
- try {
- enterAtomic();
- super.drawLine(fromPoint, toPoint, character);
- return this;
- }
- finally {
- leaveAtomic();
- }
- }
-
- @Override
- public synchronized TextGraphics drawTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- try {
- enterAtomic();
- super.drawTriangle(p1, p2, p3, character);
- return this;
- }
- finally {
- leaveAtomic();
- }
- }
-
- @Override
- public synchronized TextGraphics fillTriangle(TerminalPosition p1, TerminalPosition p2, TerminalPosition p3, char character) {
- try {
- enterAtomic();
- super.fillTriangle(p1, p2, p3, character);
- return this;
- }
- finally {
- leaveAtomic();
- }
- }
-
- @Override
- public synchronized TextGraphics fillRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- try {
- enterAtomic();
- super.fillRectangle(topLeft, size, character);
- return this;
- }
- finally {
- leaveAtomic();
- }
- }
-
- @Override
- public synchronized TextGraphics drawRectangle(TerminalPosition topLeft, TerminalSize size, char character) {
- try {
- enterAtomic();
- super.drawRectangle(topLeft, size, character);
- return this;
- }
- finally {
- leaveAtomic();
- }
- }
-
- @Override
- public synchronized TextGraphics putString(int column, int row, String string) {
- try {
- enterAtomic();
- return super.putString(column, row, string);
- }
- finally {
- leaveAtomic();
- }
- }
-
- /**
- * It's tricky with this implementation because we can't rely on any state in between two calls to setCharacter
- * since the caller might modify the terminal's state outside of this writer. However, many calls inside
- * TextGraphics will indeed make multiple calls in setCharacter where we know that the state won't change (actually,
- * we can't be 100% sure since the caller might create a separate thread and maliciously write directly to the
- * terminal while call one of the draw/fill/put methods in here). We could just set the state before writing every
- * single character but that would be inefficient. Rather, we keep a counter of if we are inside an 'atomic'
- * (meaning we know multiple calls to setCharacter will have the same state). Some drawing methods call other
- * drawing methods internally for their implementation so that's why this is implemented with an integer value
- * instead of a boolean; when the counter reaches zero we remove the memory of what state the terminal is in.
- */
- private void enterAtomic() {
- manageCallStackSize.incrementAndGet();
- }
-
- private void leaveAtomic() {
- if(manageCallStackSize.decrementAndGet() == 0) {
- lastPosition = null;
- lastCharacter = null;
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.ansi;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.input.*;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.terminal.ExtendedTerminal;
-import com.googlecode.lanterna.terminal.MouseCaptureMode;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.charset.Charset;
-
-/**
- * Class containing graphics code for ANSI compliant text terminals and terminal emulators. All the methods inside of
- * this class uses ANSI escape codes written to the underlying output stream.
- *
- * @see <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">Wikipedia</a>
- * @author Martin
- */
-public abstract class ANSITerminal extends StreamBasedTerminal implements ExtendedTerminal {
-
- private MouseCaptureMode mouseCaptureMode;
- private boolean inPrivateMode;
-
- @SuppressWarnings("WeakerAccess")
- protected ANSITerminal(InputStream terminalInput, OutputStream terminalOutput, Charset terminalCharset) {
- super(terminalInput, terminalOutput, terminalCharset);
- this.inPrivateMode = false;
- this.mouseCaptureMode = null;
- getInputDecoder().addProfile(getDefaultKeyDecodingProfile());
- }
-
- /**
- * This method can be overridden in a custom terminal implementation to change the default key decoders.
- * @return The KeyDecodingProfile used by the terminal when translating character sequences to keystrokes
- */
- protected KeyDecodingProfile getDefaultKeyDecodingProfile() {
- return new DefaultKeyDecodingProfile();
- }
-
- private void writeCSISequenceToTerminal(byte... tail) throws IOException {
- byte[] completeSequence = new byte[tail.length + 2];
- completeSequence[0] = (byte)0x1b;
- completeSequence[1] = (byte)'[';
- System.arraycopy(tail, 0, completeSequence, 2, tail.length);
- writeToTerminal(completeSequence);
- }
-
- private void writeSGRSequenceToTerminal(byte... sgrParameters) throws IOException {
- byte[] completeSequence = new byte[sgrParameters.length + 3];
- completeSequence[0] = (byte)0x1b;
- completeSequence[1] = (byte)'[';
- completeSequence[completeSequence.length - 1] = (byte)'m';
- System.arraycopy(sgrParameters, 0, completeSequence, 2, sgrParameters.length);
- writeToTerminal(completeSequence);
- }
-
- private void writeOSCSequenceToTerminal(byte... tail) throws IOException {
- byte[] completeSequence = new byte[tail.length + 2];
- completeSequence[0] = (byte)0x1b;
- completeSequence[1] = (byte)']';
- System.arraycopy(tail, 0, completeSequence, 2, tail.length);
- writeToTerminal(completeSequence);
- }
-
- @Override
- public TerminalSize getTerminalSize() throws IOException {
- saveCursorPosition();
- setCursorPosition(5000, 5000);
- reportPosition();
- restoreCursorPosition();
- return waitForTerminalSizeReport();
- }
-
- @Override
- public void setTerminalSize(int columns, int rows) throws IOException {
- writeCSISequenceToTerminal(("8;" + rows + ";" + columns + "t").getBytes());
-
- //We can't trust that the previous call was honoured by the terminal so force a re-query here, which will
- //trigger a resize event if one actually took place
- getTerminalSize();
- }
-
- @Override
- public void setTitle(String title) throws IOException {
- //The bell character is our 'null terminator', make sure there's none in the title
- title = title.replace("\007", "");
- writeOSCSequenceToTerminal(("2;" + title + "\007").getBytes());
- }
-
- @Override
- public void setForegroundColor(TextColor color) throws IOException {
- writeSGRSequenceToTerminal(color.getForegroundSGRSequence());
- }
-
- @Override
- public void setBackgroundColor(TextColor color) throws IOException {
- writeSGRSequenceToTerminal(color.getBackgroundSGRSequence());
- }
-
- @Override
- public void enableSGR(SGR sgr) throws IOException {
- switch(sgr) {
- case BLINK:
- writeCSISequenceToTerminal((byte) '5', (byte) 'm');
- break;
- case BOLD:
- writeCSISequenceToTerminal((byte) '1', (byte) 'm');
- break;
- case BORDERED:
- writeCSISequenceToTerminal((byte) '5', (byte) '1', (byte) 'm');
- break;
- case CIRCLED:
- writeCSISequenceToTerminal((byte) '5', (byte) '2', (byte) 'm');
- break;
- case CROSSED_OUT:
- writeCSISequenceToTerminal((byte) '9', (byte) 'm');
- break;
- case FRAKTUR:
- writeCSISequenceToTerminal((byte) '2', (byte) '0', (byte) 'm');
- break;
- case REVERSE:
- writeCSISequenceToTerminal((byte) '7', (byte) 'm');
- break;
- case UNDERLINE:
- writeCSISequenceToTerminal((byte) '4', (byte) 'm');
- break;
- }
- }
-
- @Override
- public void disableSGR(SGR sgr) throws IOException {
- switch(sgr) {
- case BLINK:
- writeCSISequenceToTerminal((byte) '2', (byte) '5', (byte) 'm');
- break;
- case BOLD:
- writeCSISequenceToTerminal((byte) '2', (byte) '2', (byte) 'm');
- break;
- case BORDERED:
- writeCSISequenceToTerminal((byte) '5', (byte) '4', (byte) 'm');
- break;
- case CIRCLED:
- writeCSISequenceToTerminal((byte) '5', (byte) '4', (byte) 'm');
- break;
- case CROSSED_OUT:
- writeCSISequenceToTerminal((byte) '2', (byte) '9', (byte) 'm');
- break;
- case FRAKTUR:
- writeCSISequenceToTerminal((byte) '2', (byte) '3', (byte) 'm');
- break;
- case REVERSE:
- writeCSISequenceToTerminal((byte) '2', (byte) '7', (byte) 'm');
- break;
- case UNDERLINE:
- writeCSISequenceToTerminal((byte) '2', (byte) '4', (byte) 'm');
- break;
- }
- }
-
- @Override
- public void resetColorAndSGR() throws IOException {
- writeCSISequenceToTerminal((byte) '0', (byte) 'm');
- }
-
- @Override
- public void clearScreen() throws IOException {
- writeCSISequenceToTerminal((byte) '2', (byte) 'J');
- }
-
- @Override
- public void enterPrivateMode() throws IOException {
- if(inPrivateMode) {
- throw new IllegalStateException("Cannot call enterPrivateMode() when already in private mode");
- }
- writeCSISequenceToTerminal((byte) '?', (byte) '1', (byte) '0', (byte) '4', (byte) '9', (byte) 'h');
- inPrivateMode = true;
- }
-
- @Override
- public void exitPrivateMode() throws IOException {
- if(!inPrivateMode) {
- throw new IllegalStateException("Cannot call exitPrivateMode() when not in private mode");
- }
- resetColorAndSGR();
- setCursorVisible(true);
- writeCSISequenceToTerminal((byte) '?', (byte) '1', (byte) '0', (byte) '4', (byte) '9', (byte) 'l');
- inPrivateMode = false;
- }
-
- @Override
- public void setCursorPosition(int x, int y) throws IOException {
- writeCSISequenceToTerminal(((y + 1) + ";" + (x + 1) + "H").getBytes());
- }
-
- @Override
- public void setCursorVisible(boolean visible) throws IOException {
- writeCSISequenceToTerminal(("?25" + (visible ? "h" : "l")).getBytes());
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- KeyStroke keyStroke;
- do {
- keyStroke = filterMouseEvents(super.readInput());
- } while(keyStroke == null);
- return keyStroke;
- }
-
- @Override
- public KeyStroke pollInput() throws IOException {
- return filterMouseEvents(super.pollInput());
- }
-
- private KeyStroke filterMouseEvents(KeyStroke keyStroke) {
- //Remove bad input events from terminals that are not following the xterm protocol properly
- if(keyStroke == null || keyStroke.getKeyType() != KeyType.MouseEvent) {
- return keyStroke;
- }
-
- MouseAction mouseAction = (MouseAction)keyStroke;
- switch(mouseAction.getActionType()) {
- case CLICK_RELEASE:
- if(mouseCaptureMode == MouseCaptureMode.CLICK) {
- return null;
- }
- break;
- case DRAG:
- if(mouseCaptureMode == MouseCaptureMode.CLICK ||
- mouseCaptureMode == MouseCaptureMode.CLICK_RELEASE) {
- return null;
- }
- break;
- case MOVE:
- if(mouseCaptureMode == MouseCaptureMode.CLICK ||
- mouseCaptureMode == MouseCaptureMode.CLICK_RELEASE ||
- mouseCaptureMode == MouseCaptureMode.CLICK_RELEASE_DRAG) {
- return null;
- }
- break;
- default:
- }
- return mouseAction;
- }
-
- @Override
- public void pushTitle() throws IOException {
- throw new UnsupportedOperationException("Not implemented yet");
- }
-
- @Override
- public void popTitle() throws IOException {
- throw new UnsupportedOperationException("Not implemented yet");
- }
-
- @Override
- public void iconify() throws IOException {
- writeCSISequenceToTerminal((byte)'2', (byte)'t');
- }
-
- @Override
- public void deiconify() throws IOException {
- writeCSISequenceToTerminal((byte)'1', (byte)'t');
- }
-
- @Override
- public void maximize() throws IOException {
- writeCSISequenceToTerminal((byte)'9', (byte)';', (byte)'1', (byte)'t');
- }
-
- @Override
- public void unmaximize() throws IOException {
- writeCSISequenceToTerminal((byte)'9', (byte)';', (byte)'0', (byte)'t');
- }
-
- @Override
- public void setMouseCaptureMode(MouseCaptureMode mouseCaptureMode) throws IOException {
- if(this.mouseCaptureMode != null) {
- switch(this.mouseCaptureMode) {
- case CLICK:
- writeCSISequenceToTerminal((byte)'?', (byte)'9', (byte)'l');
- break;
- case CLICK_RELEASE:
- writeCSISequenceToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'0', (byte)'0', (byte)'l');
- break;
- case CLICK_RELEASE_DRAG:
- writeCSISequenceToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'0', (byte)'2', (byte)'l');
- break;
- case CLICK_RELEASE_DRAG_MOVE:
- writeCSISequenceToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'0', (byte)'3', (byte)'l');
- break;
- }
- if(getCharset().equals(Charset.forName("UTF-8"))) {
- writeCSISequenceToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'0', (byte)'5', (byte)'l');
- }
- }
- this.mouseCaptureMode = mouseCaptureMode;
- if(this.mouseCaptureMode != null) {
- switch(this.mouseCaptureMode) {
- case CLICK:
- writeCSISequenceToTerminal((byte)'?', (byte)'9', (byte)'h');
- break;
- case CLICK_RELEASE:
- writeCSISequenceToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'0', (byte)'0', (byte)'h');
- break;
- case CLICK_RELEASE_DRAG:
- writeCSISequenceToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'0', (byte)'2', (byte)'h');
- break;
- case CLICK_RELEASE_DRAG_MOVE:
- writeCSISequenceToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'0', (byte)'3', (byte)'h');
- break;
- }
- if(getCharset().equals(Charset.forName("UTF-8"))) {
- writeCSISequenceToTerminal((byte)'?', (byte)'1', (byte)'0', (byte)'0', (byte)'5', (byte)'h');
- }
- }
- }
-
- /**
- * Method to test if the terminal (as far as the library knows) is in private mode.
- *
- * @return True if there has been a call to enterPrivateMode() but not yet exitPrivateMode()
- */
- boolean isInPrivateMode() {
- return inPrivateMode;
- }
-
- void reportPosition() throws IOException {
- writeCSISequenceToTerminal("6n".getBytes());
- }
-
- void restoreCursorPosition() throws IOException {
- writeCSISequenceToTerminal("u".getBytes());
- }
-
- void saveCursorPosition() throws IOException {
- writeCSISequenceToTerminal("s".getBytes());
- }
-
- @Override
- public void scrollLines(int firstLine, int lastLine, int distance) throws IOException {
- final String CSI = "\033[";
-
- // some sanity checks:
- if (distance == 0) { return; }
- if (firstLine < 0) { firstLine = 0; }
- if (lastLine < firstLine) { return; }
- StringBuilder sb = new StringBuilder();
-
- // define range:
- sb.append(CSI).append(firstLine+1)
- .append(';').append(lastLine+1).append('r');
-
- // place cursor on line to scroll away from:
- int target = distance > 0 ? lastLine : firstLine;
- sb.append(CSI).append(target+1).append(";1H");
-
- // do scroll:
- if (distance > 0) {
- int num = Math.min( distance, lastLine - firstLine + 1);
- for (int i = 0; i < num; i++) { sb.append('\n'); }
- } else { // distance < 0
- int num = Math.min( -distance, lastLine - firstLine + 1);
- for (int i = 0; i < num; i++) { sb.append("\033M"); }
- }
-
- // reset range:
- sb.append(CSI).append('r');
-
- // off we go!
- writeToTerminal(sb.toString().getBytes());
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.ansi;
-
-import com.googlecode.lanterna.TerminalSize;
-
-import java.io.*;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This class extends UnixLikeTerminal and implements the Cygwin-specific implementations. This means, running a Java
- * application using Lanterna inside the Cygwin Terminal application. The standard Windows command prompt (cmd.exe) is
- * not supported by this class.<p>
- * <p>
- * <b>NOTE:</b> This class is experimental and does not fully work! Some of the operations, like disabling echo and
- * changing cbreak seems to be impossible to do without resorting to native code. Running "stty raw" before starting the
- * JVM will improve compatibility.
- *
- * @author Martin
- * @author Andreas
- */
-public class CygwinTerminal extends UnixLikeTerminal {
-
- private static final Pattern STTY_SIZE_PATTERN = Pattern.compile(".*rows ([0-9]+);.*columns ([0-9]+);.*");
- private static final String STTY_LOCATION = findProgram("stty.exe");
-
- /**
- * Creates a new CygwinTerminal based off input and output streams and a character set to use
- * @param terminalInput Input stream to read input from
- * @param terminalOutput Output stream to write output to
- * @param terminalCharset Character set to use when writing to the output stream
- * @throws IOException If there was an I/O error when trying to initialize the class and setup the terminal
- */
- public CygwinTerminal(
- InputStream terminalInput,
- OutputStream terminalOutput,
- Charset terminalCharset) throws IOException {
- super(terminalInput, terminalOutput, terminalCharset,
- CtrlCBehaviour.TRAP, null);
-
- //Make sure to set an initial size
- onResized(80, 24);
-
- saveSTTY();
- setCBreak(true);
- setEcho(false);
- sttyMinimum1CharacterForRead();
- setupShutdownHook();
- }
-
- @Override
- public TerminalSize getTerminalSize() {
- try {
- String stty = exec(findSTTY(), "-F", getPseudoTerminalDevice(), "-a");
- Matcher matcher = STTY_SIZE_PATTERN.matcher(stty);
- if(matcher.matches()) {
- return new TerminalSize(Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(1)));
- }
- else {
- return new TerminalSize(80, 24);
- }
- }
- catch(Throwable e) {
- return new TerminalSize(80, 24);
- }
- }
-
- @Override
- protected void sttyKeyEcho(final boolean enable) throws IOException {
- runSTTYCommand(enable ? "echo" : "-echo");
- }
-
- @Override
- protected void sttyMinimum1CharacterForRead() throws IOException {
- runSTTYCommand("min", "1");
- }
-
- @Override
- protected void sttyICanon(final boolean enable) throws IOException {
- runSTTYCommand(enable ? "icanon" : "cbreak");
- }
-
- @Override
- protected String sttySave() throws IOException {
- return runSTTYCommand("-g").trim();
- }
-
- @Override
- protected void sttyRestore(String tok) throws IOException {
- runSTTYCommand(tok);
- }
-
- protected String findSTTY() {
- return STTY_LOCATION;
- }
-
- private String runSTTYCommand(String... parameters) throws IOException {
- List<String> commandLine = new ArrayList<String>(Arrays.asList(
- findSTTY(),
- "-F",
- getPseudoTerminalDevice()));
- commandLine.addAll(Arrays.asList(parameters));
- return exec(commandLine.toArray(new String[commandLine.size()]));
- }
-
- private String getPseudoTerminalDevice() {
- //This will only work if you only have one terminal window open, otherwise we'll need to figure out somehow
- //which pty to use, which could be very tricky...
- return "/dev/pty0";
- }
-
- private static String findProgram(String programName) {
- String[] paths = System.getProperty("java.library.path").split(";");
- for(String path : paths) {
- File shBin = new File(path, programName);
- if(shBin.exists()) {
- return shBin.getAbsolutePath();
- }
- }
- return programName;
- }
-
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-
-package com.googlecode.lanterna.terminal.ansi;
-
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * Using this terminal size provider, your terminal will be set to a fixed size and will never receive any resize
- * events. Of course if the physical terminal is resized, in reality it will have a different size, but the application
- * won't know about it. The size reported to the user is always the size attached to this object.
- * @author martin
- */
-public class FixedTerminalSizeProvider implements UnixTerminalSizeQuerier {
- private final TerminalSize size;
-
- /**
- * Creating a {@code FixedTerminalSizeProvider} set to a particular size that it will always report whenever the
- * associated {@code Terminal} interface queries.
- * @param size Size the terminal should be statically initialized to
- */
- public FixedTerminalSizeProvider(TerminalSize size) {
- this.size = size;
- }
-
- @Override
- public TerminalSize queryTerminalSize() {
- return size;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.ansi;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.nio.CharBuffer;
-import java.nio.charset.Charset;
-
-import com.googlecode.lanterna.Symbols;
-import com.googlecode.lanterna.input.InputDecoder;
-import com.googlecode.lanterna.input.KeyDecodingProfile;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.ScreenInfoAction;
-import com.googlecode.lanterna.input.ScreenInfoCharacterPattern;
-import com.googlecode.lanterna.terminal.AbstractTerminal;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import java.io.ByteArrayOutputStream;
-import java.util.LinkedList;
-import java.util.Queue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * An abstract terminal implementing functionality for terminals using OutputStream/InputStream. You can extend from
- * this class if your terminal implementation is using standard input and standard output but not ANSI escape codes (in
- * which case you should extend ANSITerminal). This class also contains some automatic UTF-8 to VT100 character
- * conversion when the terminal is not set to read UTF-8.
- *
- * @author Martin
- */
-public abstract class StreamBasedTerminal extends AbstractTerminal {
-
- private static final Charset UTF8_REFERENCE = Charset.forName("UTF-8");
-
- private final InputStream terminalInput;
- private final OutputStream terminalOutput;
- private final Charset terminalCharset;
-
- private final InputDecoder inputDecoder;
- private final Queue<KeyStroke> keyQueue;
- private final Lock readLock;
-
- @SuppressWarnings("WeakerAccess")
- public StreamBasedTerminal(InputStream terminalInput, OutputStream terminalOutput, Charset terminalCharset) {
- this.terminalInput = terminalInput;
- this.terminalOutput = terminalOutput;
- if(terminalCharset == null) {
- this.terminalCharset = Charset.defaultCharset();
- }
- else {
- this.terminalCharset = terminalCharset;
- }
- this.inputDecoder = new InputDecoder(new InputStreamReader(this.terminalInput, this.terminalCharset));
- this.keyQueue = new LinkedList<KeyStroke>();
- this.readLock = new ReentrantLock();
- //noinspection ConstantConditions
- }
-
- /**
- * {@inheritDoc}
- *
- * The {@code StreamBasedTerminal} class will attempt to translate some unicode characters to VT100 if the encoding
- * attached to this {@code Terminal} isn't UTF-8.
- */
- @Override
- public void putCharacter(char c) throws IOException {
- writeToTerminal(translateCharacter(c));
- }
-
- /**
- * This method will write a list of bytes directly to the output stream of the terminal.
- * @param bytes Bytes to write to the terminal (synchronized)
- * @throws java.io.IOException If there was an underlying I/O error
- */
- @SuppressWarnings("WeakerAccess")
- protected void writeToTerminal(byte... bytes) throws IOException {
- synchronized(terminalOutput) {
- terminalOutput.write(bytes);
- }
- }
-
- @Override
- public byte[] enquireTerminal(int timeout, TimeUnit timeoutTimeUnit) throws IOException {
- synchronized(terminalOutput) {
- terminalOutput.write(5); //ENQ
- flush();
- }
-
- //Wait for input
- long startTime = System.currentTimeMillis();
- while(terminalInput.available() == 0) {
- if(System.currentTimeMillis() - startTime > timeoutTimeUnit.toMillis(timeout)) {
- return new byte[0];
- }
- try {
- Thread.sleep(1);
- }
- catch(InterruptedException e) {
- return new byte[0];
- }
- }
-
- //We have at least one character, read as far as we can and return
- ByteArrayOutputStream buffer = new ByteArrayOutputStream();
- while(terminalInput.available() > 0) {
- buffer.write(terminalInput.read());
- }
- return buffer.toByteArray();
- }
-
- /**
- * Adds a KeyDecodingProfile to be used when converting raw user input characters to {@code Key} objects.
- *
- * @see KeyDecodingProfile
- * @param profile Decoding profile to add
- * @deprecated Use {@code getInputDecoder().addProfile(profile)} instead
- */
- @Deprecated
- @SuppressWarnings("WeakerAccess")
- public void addKeyDecodingProfile(KeyDecodingProfile profile) {
- inputDecoder.addProfile(profile);
- }
-
- /**
- * Returns the {@code InputDecoder} attached to this {@code StreamBasedTerminal}. Can be used to add additional
- * character patterns to recognize and tune the way input is turned in {@code KeyStroke}:s.
- * @return {@code InputDecoder} attached to this {@code StreamBasedTerminal}
- */
- public InputDecoder getInputDecoder() {
- return inputDecoder;
- }
-
- @SuppressWarnings("ConstantConditions")
- TerminalSize waitForTerminalSizeReport() throws IOException {
- long startTime = System.currentTimeMillis();
- readLock.lock();
- try {
- while(true) {
- KeyStroke key = inputDecoder.getNextCharacter(false);
- if(key == null) {
- if(System.currentTimeMillis() - startTime > 1000) { //Wait 1 second for the terminal size report to come, is this reasonable?
- throw new IOException(
- "Timeout while waiting for terminal size report! Your terminal may have refused to go into cbreak mode.");
- }
- try {
- Thread.sleep(1);
- }
- catch(InterruptedException ignored) {}
- continue;
- }
-
- // check both: real ScreenInfoActions and F3 keystrokes with modifiers:
- ScreenInfoAction report = ScreenInfoCharacterPattern.tryToAdopt(key);
- if (report == null) {
- keyQueue.add(key);
- }
- else {
- TerminalPosition reportedTerminalPosition = report.getPosition();
- onResized(reportedTerminalPosition.getColumn(), reportedTerminalPosition.getRow());
- return new TerminalSize(reportedTerminalPosition.getColumn(), reportedTerminalPosition.getRow());
- }
- }
- }
- finally {
- readLock.unlock();
- }
- }
-
- @Override
- public KeyStroke pollInput() throws IOException {
- return readInput(false);
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- return readInput(true);
- }
-
- private KeyStroke readInput(boolean blocking) throws IOException {
- readLock.lock();
- try {
- if(!keyQueue.isEmpty()) {
- return keyQueue.poll();
- }
- KeyStroke key = inputDecoder.getNextCharacter(blocking);
- if (key instanceof ScreenInfoAction) {
- TerminalPosition reportedTerminalPosition = ((ScreenInfoAction)key).getPosition();
- onResized(reportedTerminalPosition.getColumn(), reportedTerminalPosition.getRow());
- return pollInput();
- } else {
- return key;
- }
- }
- finally {
- readLock.unlock();
- }
- }
-
- @Override
- public void flush() throws IOException {
- synchronized(terminalOutput) {
- terminalOutput.flush();
- }
- }
-
- protected Charset getCharset() {
- return terminalCharset;
- }
-
- @SuppressWarnings("WeakerAccess")
- protected byte[] translateCharacter(char input) {
- if(UTF8_REFERENCE != null && UTF8_REFERENCE == terminalCharset) {
- return convertToCharset(input);
- }
- //Convert ACS to ordinary terminal codes
- switch(input) {
- case Symbols.ARROW_DOWN:
- return convertToVT100('v');
- case Symbols.ARROW_LEFT:
- return convertToVT100('<');
- case Symbols.ARROW_RIGHT:
- return convertToVT100('>');
- case Symbols.ARROW_UP:
- return convertToVT100('^');
- case Symbols.BLOCK_DENSE:
- case Symbols.BLOCK_MIDDLE:
- case Symbols.BLOCK_SOLID:
- case Symbols.BLOCK_SPARSE:
- return convertToVT100((char) 97);
- case Symbols.HEART:
- case Symbols.CLUB:
- case Symbols.SPADES:
- return convertToVT100('?');
- case Symbols.FACE_BLACK:
- case Symbols.FACE_WHITE:
- case Symbols.DIAMOND:
- return convertToVT100((char) 96);
- case Symbols.BULLET:
- return convertToVT100((char) 102);
- case Symbols.DOUBLE_LINE_CROSS:
- case Symbols.SINGLE_LINE_CROSS:
- return convertToVT100((char) 110);
- case Symbols.DOUBLE_LINE_HORIZONTAL:
- case Symbols.SINGLE_LINE_HORIZONTAL:
- return convertToVT100((char) 113);
- case Symbols.DOUBLE_LINE_BOTTOM_LEFT_CORNER:
- case Symbols.SINGLE_LINE_BOTTOM_LEFT_CORNER:
- return convertToVT100((char) 109);
- case Symbols.DOUBLE_LINE_BOTTOM_RIGHT_CORNER:
- case Symbols.SINGLE_LINE_BOTTOM_RIGHT_CORNER:
- return convertToVT100((char) 106);
- case Symbols.DOUBLE_LINE_T_DOWN:
- case Symbols.SINGLE_LINE_T_DOWN:
- case Symbols.DOUBLE_LINE_T_SINGLE_DOWN:
- case Symbols.SINGLE_LINE_T_DOUBLE_DOWN:
- return convertToVT100((char) 119);
- case Symbols.DOUBLE_LINE_T_LEFT:
- case Symbols.SINGLE_LINE_T_LEFT:
- case Symbols.DOUBLE_LINE_T_SINGLE_LEFT:
- case Symbols.SINGLE_LINE_T_DOUBLE_LEFT:
- return convertToVT100((char) 117);
- case Symbols.DOUBLE_LINE_T_RIGHT:
- case Symbols.SINGLE_LINE_T_RIGHT:
- case Symbols.DOUBLE_LINE_T_SINGLE_RIGHT:
- case Symbols.SINGLE_LINE_T_DOUBLE_RIGHT:
- return convertToVT100((char) 116);
- case Symbols.DOUBLE_LINE_T_UP:
- case Symbols.SINGLE_LINE_T_UP:
- case Symbols.DOUBLE_LINE_T_SINGLE_UP:
- case Symbols.SINGLE_LINE_T_DOUBLE_UP:
- return convertToVT100((char) 118);
- case Symbols.DOUBLE_LINE_TOP_LEFT_CORNER:
- case Symbols.SINGLE_LINE_TOP_LEFT_CORNER:
- return convertToVT100((char) 108);
- case Symbols.DOUBLE_LINE_TOP_RIGHT_CORNER:
- case Symbols.SINGLE_LINE_TOP_RIGHT_CORNER:
- return convertToVT100((char) 107);
- case Symbols.DOUBLE_LINE_VERTICAL:
- case Symbols.SINGLE_LINE_VERTICAL:
- return convertToVT100((char) 120);
- default:
- return convertToCharset(input);
- }
- }
-
- private byte[] convertToVT100(char code) {
- //Warning! This might be terminal type specific!!!!
- //So far it's worked everywhere I've tried it (xterm, gnome-terminal, putty)
- return new byte[]{27, 40, 48, (byte) code, 27, 40, 66};
- }
-
- private byte[] convertToCharset(char input) {
- return terminalCharset.encode(Character.toString(input)).array();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.ansi;
-
-import java.lang.reflect.Field;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Contains the telnet protocol commands, although not a complete set.
- * @author Martin
- */
-class TelnetProtocol {
- public static final byte COMMAND_SUBNEGOTIATION_END = (byte)0xf0; //SE
- public static final byte COMMAND_NO_OPERATION = (byte)0xf1; //NOP
- public static final byte COMMAND_DATA_MARK = (byte)0xf2; //DM
- public static final byte COMMAND_BREAK = (byte)0xf3; //BRK
- public static final byte COMMAND_INTERRUPT_PROCESS = (byte)0xf4; //IP
- public static final byte COMMAND_ABORT_OUTPUT = (byte)0xf5; //AO
- public static final byte COMMAND_ARE_YOU_THERE = (byte)0xf6; //AYT
- public static final byte COMMAND_ERASE_CHARACTER = (byte)0xf7; //EC
- public static final byte COMMAND_ERASE_LINE = (byte)0xf8; //WL
- public static final byte COMMAND_GO_AHEAD = (byte)0xf9; //GA
- public static final byte COMMAND_SUBNEGOTIATION = (byte)0xfa; //SB
- public static final byte COMMAND_WILL = (byte)0xfb;
- public static final byte COMMAND_WONT = (byte)0xfc;
- public static final byte COMMAND_DO = (byte)0xfd;
- public static final byte COMMAND_DONT = (byte)0xfe;
- public static final byte COMMAND_IAC = (byte)0xff;
-
- public static final byte OPTION_TRANSMIT_BINARY = (byte)0x00;
- public static final byte OPTION_ECHO = (byte)0x01;
- public static final byte OPTION_SUPPRESS_GO_AHEAD = (byte)0x03;
- public static final byte OPTION_STATUS = (byte)0x05;
- public static final byte OPTION_TIMING_MARK = (byte)0x06;
- public static final byte OPTION_NAOCRD = (byte)0x0a;
- public static final byte OPTION_NAOHTS = (byte)0x0b;
- public static final byte OPTION_NAOHTD = (byte)0x0c;
- public static final byte OPTION_NAOFFD = (byte)0x0d;
- public static final byte OPTION_NAOVTS = (byte)0x0e;
- public static final byte OPTION_NAOVTD = (byte)0x0f;
- public static final byte OPTION_NAOLFD = (byte)0x10;
- public static final byte OPTION_EXTEND_ASCII = (byte)0x01;
- public static final byte OPTION_TERMINAL_TYPE = (byte)0x18;
- public static final byte OPTION_NAWS = (byte)0x1f;
- public static final byte OPTION_TERMINAL_SPEED = (byte)0x20;
- public static final byte OPTION_TOGGLE_FLOW_CONTROL = (byte)0x21;
- public static final byte OPTION_LINEMODE = (byte)0x22;
- public static final byte OPTION_AUTHENTICATION = (byte)0x25;
-
- public static final Map<String, Byte> NAME_TO_CODE = createName2CodeMap();
- public static final Map<Byte, String> CODE_TO_NAME = reverseMap(NAME_TO_CODE);
-
- private static Map<String, Byte> createName2CodeMap() {
- Map<String, Byte> result = new HashMap<String, Byte>();
- for(Field field: TelnetProtocol.class.getDeclaredFields()) {
- if(field.getType() != byte.class || (!field.getName().startsWith("COMMAND_") && !field.getName().startsWith("OPTION_"))) {
- continue;
- }
- try {
- String namePart = field.getName().substring(field.getName().indexOf("_") + 1);
- result.put(namePart, (Byte)field.get(null));
- }
- catch(IllegalAccessException ignored) {
- }
- catch(IllegalArgumentException ignored) {
- }
- }
- return Collections.unmodifiableMap(result);
- }
-
- private static <V,K> Map<V,K> reverseMap(Map<K,V> n2c) {
- Map<V, K> result = new HashMap<V,K>();
- for (Map.Entry<K, V> e : n2c.entrySet()) {
- result.put(e.getValue(), e.getKey());
- }
- return Collections.unmodifiableMap(result);
- }
- /** Cannot instantiate. */
- private TelnetProtocol() {}
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.ansi;
-
-import static com.googlecode.lanterna.terminal.ansi.TelnetProtocol.*;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.nio.charset.Charset;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This class is used by the {@code TelnetTerminalServer} class when a client has connected in; this class will be the
- * interaction point for that client. All operations are sent to the client over the network socket and some of the
- * meta-operations (like echo mode) are communicated using Telnet negotiation language. You can't create objects of this
- * class directly; they are created for you when you are listening for incoming connections using a
- * {@code TelnetTerminalServer} and a client connects.
- * <p>
- * A good resource on telnet communication is http://www.tcpipguide.com/free/t_TelnetProtocol.htm<br>
- * Also here: http://support.microsoft.com/kb/231866
- * @see TelnetTerminalServer
- * @author martin
- */
-public class TelnetTerminal extends ANSITerminal {
-
- private final Socket socket;
- private final NegotiationState negotiationState;
-
- TelnetTerminal(Socket socket, Charset terminalCharset) throws IOException {
- this(socket, new TelnetClientIACFilterer(socket.getInputStream()), socket.getOutputStream(), terminalCharset);
- }
-
- //This weird construction is just so that we can access the input filter without changing the visibility in StreamBasedTerminal
- private TelnetTerminal(Socket socket, TelnetClientIACFilterer inputStream, OutputStream outputStream, Charset terminalCharset) throws IOException {
- super(inputStream, outputStream, terminalCharset);
- this.socket = socket;
- this.negotiationState = inputStream.negotiationState;
- inputStream.setEventListener(new TelnetClientEventListener() {
- @Override
- public void onResize(int columns, int rows) {
- TelnetTerminal.this.onResized(columns, rows);
- }
-
- @Override
- public void requestReply(boolean will, byte option) throws IOException {
- writeToTerminal(COMMAND_IAC, will ? COMMAND_WILL : COMMAND_WONT, option);
- }
- });
- setLineMode0();
- setEchoOff();
- setResizeNotificationOn();
- }
-
- /**
- * Returns the socket address for the remote endpoint of the telnet connection
- * @return SocketAddress representing the remote client
- */
- public SocketAddress getRemoteSocketAddress() {
- return socket.getRemoteSocketAddress();
- }
-
- private void setEchoOff() throws IOException {
- writeToTerminal(COMMAND_IAC, COMMAND_WILL, OPTION_ECHO);
- flush();
- }
-
- private void setLineMode0() throws IOException {
- writeToTerminal(
- COMMAND_IAC, COMMAND_DO, OPTION_LINEMODE,
- COMMAND_IAC, COMMAND_SUBNEGOTIATION, OPTION_LINEMODE, (byte)1, (byte)0, COMMAND_IAC, COMMAND_SUBNEGOTIATION_END);
- flush();
- }
-
- private void setResizeNotificationOn() throws IOException {
- writeToTerminal(
- COMMAND_IAC, COMMAND_DO, OPTION_NAWS);
- flush();
- }
-
- /**
- * Retrieves the current negotiation state with the client, containing details on what options have been enabled
- * and what the client has said it supports.
- * @return The current negotiation state for this client
- */
- public NegotiationState getNegotiationState() {
- return negotiationState;
- }
-
- /**
- * Closes the socket to the client, effectively ending the telnet session and the terminal.
- * @throws IOException If there was an underlying I/O error
- */
- public void close() throws IOException {
- socket.close();
- }
-
- /**
- * This class contains some of the various states that the Telnet negotiation protocol defines. Lanterna doesn't
- * support all of them but the more common ones are represented.
- */
- public static class NegotiationState {
- private boolean clientEcho;
- private boolean clientLineMode0;
- private boolean clientResizeNotification;
- private boolean suppressGoAhead;
- private boolean extendedAscii;
-
- NegotiationState() {
- this.clientEcho = true;
- this.clientLineMode0 = false;
- this.clientResizeNotification = false;
- this.suppressGoAhead = true;
- this.extendedAscii = true;
- }
-
- /**
- * Is the telnet client echo mode turned on (client is echoing characters locally)
- * @return {@code true} if client echo is enabled
- */
- public boolean isClientEcho() {
- return clientEcho;
- }
-
- /**
- * Is the telnet client line mode 0 turned on (client sends character by character instead of line by line)
- * @return {@code true} if client line mode 0 is enabled
- */
- public boolean isClientLineMode0() {
- return clientLineMode0;
- }
-
- /**
- * Is the telnet client resize notification turned on (client notifies server when the terminal window has
- * changed size)
- * @return {@code true} if client resize notification is enabled
- */
- public boolean isClientResizeNotification() {
- return clientResizeNotification;
- }
-
-
- /**
- * Is the telnet client suppress go-ahead turned on
- * @return {@code true} if client suppress go-ahead is enabled
- */
- public boolean isSuppressGoAhead() {
- return suppressGoAhead;
- }
-
- /**
- * Is the telnet client extended ascii turned on
- * @return {@code true} if client extended ascii is enabled
- */
- public boolean isExtendedAscii() {
- return extendedAscii;
- }
-
- private void onUnsupportedStateCommand(boolean enabling, byte value) {
- System.err.println("Unsupported operation: Client says it " + (enabling ? "will" : "won't") + " do " + TelnetProtocol.CODE_TO_NAME.get(value));
- }
-
- private void onUnsupportedRequestCommand(boolean askedToDo, byte value) {
- System.err.println("Unsupported request: Client asks us, " + (askedToDo ? "do" : "don't") + " " + TelnetProtocol.CODE_TO_NAME.get(value));
- }
-
- private void onUnsupportedSubnegotiation(byte option, byte[] additionalData) {
- System.err.println("Unsupported subnegotiation: Client send " + TelnetProtocol.CODE_TO_NAME.get(option) + " with extra data " +
- toList(additionalData));
- }
-
- private static List<String> toList(byte[] array) {
- List<String> list = new ArrayList<String>(array.length);
- for(byte b: array) {
- list.add(String.format("%02X ", b));
- }
- return list;
- }
- }
-
- private interface TelnetClientEventListener {
- void onResize(int columns, int rows);
- void requestReply(boolean will, byte option) throws IOException;
- }
-
- private static class TelnetClientIACFilterer extends InputStream {
- private final NegotiationState negotiationState;
- private final InputStream inputStream;
- private final byte[] buffer;
- private final byte[] workingBuffer;
- private int bytesInBuffer;
- private TelnetClientEventListener eventListener;
-
- TelnetClientIACFilterer(InputStream inputStream) {
- this.negotiationState = new NegotiationState();
- this.inputStream = inputStream;
- this.buffer = new byte[64 * 1024];
- this.workingBuffer = new byte[1024];
- this.bytesInBuffer = 0;
- this.eventListener = null;
- }
-
- private void setEventListener(TelnetClientEventListener eventListener) {
- this.eventListener = eventListener;
- }
-
- @Override
- public int read() throws IOException {
- throw new UnsupportedOperationException("TelnetClientIACFilterer doesn't support .read()");
- }
-
- @Override
- public void close() throws IOException {
- inputStream.close();
- }
-
- @Override
- public int available() throws IOException {
- int underlyingStreamAvailable = inputStream.available();
- if(underlyingStreamAvailable == 0 && bytesInBuffer == 0) {
- return 0;
- }
- else if(underlyingStreamAvailable == 0) {
- return bytesInBuffer;
- }
- else if(bytesInBuffer == buffer.length) {
- return bytesInBuffer;
- }
- fillBuffer();
- return bytesInBuffer;
- }
-
- @Override
- @SuppressWarnings("NullableProblems") //I can't find the correct way to fix this!
- public int read(byte[] b, int off, int len) throws IOException {
- if(inputStream.available() > 0) {
- fillBuffer();
- }
- if(bytesInBuffer == 0) {
- return -1;
- }
- int bytesToCopy = Math.min(len, bytesInBuffer);
- System.arraycopy(buffer, 0, b, off, bytesToCopy);
- System.arraycopy(buffer, bytesToCopy, buffer, 0, buffer.length - bytesToCopy);
- bytesInBuffer -= bytesToCopy;
- return bytesToCopy;
- }
-
- private void fillBuffer() throws IOException {
- int readBytes = inputStream.read(workingBuffer, 0, Math.min(workingBuffer.length, buffer.length - bytesInBuffer));
- if(readBytes == -1) {
- return;
- }
- for(int i = 0; i < readBytes; i++) {
- if(workingBuffer[i] == COMMAND_IAC) {
- i++;
- if(Arrays.asList(COMMAND_DO, COMMAND_DONT, COMMAND_WILL, COMMAND_WONT).contains(workingBuffer[i])) {
- parseCommand(workingBuffer, i, readBytes);
- ++i;
- continue;
- }
- else if(workingBuffer[i] == COMMAND_SUBNEGOTIATION) { //0xFA = SB = Subnegotiation
- i += parseSubNegotiation(workingBuffer, ++i, readBytes);
- continue;
- }
- else if(workingBuffer[i] != COMMAND_IAC) { //Double IAC = 255
- System.err.println("Unknown Telnet command: " + workingBuffer[i]);
- }
- }
- buffer[bytesInBuffer++] = workingBuffer[i];
- }
- }
-
- private void parseCommand(byte[] buffer, int position, int max) throws IOException {
- if(position + 1 >= max) {
- throw new IllegalStateException("State error, we got a command signal from the remote telnet client but "
- + "not enough characters available in the stream");
- }
- byte command = buffer[position];
- byte value = buffer[position + 1];
- switch(command) {
- case COMMAND_DO:
- case COMMAND_DONT:
- if(value == OPTION_SUPPRESS_GO_AHEAD) {
- negotiationState.suppressGoAhead = (command == COMMAND_DO);
- eventListener.requestReply(command == COMMAND_DO, value);
- }
- else if(value == OPTION_EXTEND_ASCII) {
- negotiationState.extendedAscii = (command == COMMAND_DO);
- eventListener.requestReply(command == COMMAND_DO, value);
- }
- else {
- negotiationState.onUnsupportedRequestCommand(command == COMMAND_DO, value);
- }
- break;
- case COMMAND_WILL:
- case COMMAND_WONT:
- if(value == OPTION_ECHO) {
- negotiationState.clientEcho = (command == COMMAND_WILL);
- }
- else if(value == OPTION_LINEMODE) {
- negotiationState.clientLineMode0 = (command == COMMAND_WILL);
- }
- else if(value == OPTION_NAWS) {
- negotiationState.clientResizeNotification = (command == COMMAND_WILL);
- }
- else {
- negotiationState.onUnsupportedStateCommand(command == COMMAND_WILL, value);
- }
- break;
- default:
- throw new UnsupportedOperationException("No command handler implemented for " + TelnetProtocol.CODE_TO_NAME.get(command));
- }
- }
-
- private int parseSubNegotiation(byte[] buffer, int position, int max) {
- int originalPosition = position;
-
- //Read operation
- byte operation = buffer[position++];
-
- //Read until [IAC SE]
- ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
- while(position < max) {
- byte read = buffer[position];
- if(read != COMMAND_IAC) {
- outputBuffer.write(read);
- }
- else {
- if(position + 1 == max) {
- throw new IllegalStateException("State error, unexpected end of buffer when reading subnegotiation");
- }
- position++;
- if(buffer[position] == COMMAND_IAC) {
- outputBuffer.write(COMMAND_IAC); //Escaped IAC
- }
- else if(buffer[position] == COMMAND_SUBNEGOTIATION_END) {
- parseSubNegotiation(operation, outputBuffer.toByteArray());
- return ++position - originalPosition;
- }
- }
- position++;
- }
- throw new IllegalStateException("State error, unexpected end of buffer when reading subnegotiation, no IAC SE");
- }
-
- private void parseSubNegotiation(byte option, byte[] additionalData) {
- switch(option) {
- case OPTION_NAWS:
- eventListener.onResize(
- convertTwoBytesToInt2(additionalData[1], additionalData[0]),
- convertTwoBytesToInt2(additionalData[3], additionalData[2]));
- break;
- case OPTION_LINEMODE:
- //We don't parse this, as this is a very complicated command :(
- //Let's leave it for now, fingers crossed
- break;
- default:
- negotiationState.onUnsupportedSubnegotiation(option, additionalData);
- break;
- }
- }
- }
-
- private static int convertTwoBytesToInt2(byte b1, byte b2) {
- return ( (b2 & 0xFF) << 8) | (b1 & 0xFF);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.ansi;
-
-import java.io.IOException;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.nio.charset.Charset;
-import javax.net.ServerSocketFactory;
-
-/**
- * This class implements a Telnet server, capable of accepting multiple clients and presenting each one as their own
- * Terminal. You need to tell it at least what port to listen on and then it create a Server socket listening for
- * incoming connections. Use {@code acceptConnection()} to wait for the next incoming connection, it will be returned as
- * a {@code TelnetTerminal} object that represents the client and which will be the way for the server to send content
- * to this client. Next connecting client (through {@code acceptConnection()} will get a different
- * {@code TelnetTerminal}, i.e. their content will not be in sync automatically but considered as two different
- * terminals.
- * @author martin
- * @see TelnetTerminal
- * @see <a href="http://en.wikipedia.org/wiki/Telnet">Wikipedia</a>
- */
-@SuppressWarnings("WeakerAccess")
-public class TelnetTerminalServer {
- private final Charset charset;
- private final ServerSocket serverSocket;
-
- /**
- * Creates a new TelnetTerminalServer on a specific port
- * @param port Port to listen for incoming telnet connections
- * @throws IOException If there was an underlying I/O exception
- */
- public TelnetTerminalServer(int port) throws IOException {
- this(ServerSocketFactory.getDefault(), port);
- }
-
- /**
- * Creates a new TelnetTerminalServer on a specific port, using a certain character set
- * @param port Port to listen for incoming telnet connections
- * @param charset Character set to use
- * @throws IOException If there was an underlying I/O exception
- */
- public TelnetTerminalServer(int port, Charset charset) throws IOException {
- this(ServerSocketFactory.getDefault(), port, charset);
- }
-
- /**
- * Creates a new TelnetTerminalServer on a specific port through a ServerSocketFactory
- * @param port Port to listen for incoming telnet connections
- * @param serverSocketFactory ServerSocketFactory to use when creating the ServerSocket
- * @throws IOException If there was an underlying I/O exception
- */
- public TelnetTerminalServer(ServerSocketFactory serverSocketFactory, int port) throws IOException {
- this(serverSocketFactory, port, Charset.defaultCharset());
- }
-
- /**
- * Creates a new TelnetTerminalServer on a specific port through a ServerSocketFactory with a certain Charset
- * @param serverSocketFactory ServerSocketFactory to use when creating the ServerSocket
- * @param port Port to listen for incoming telnet connections
- * @param charset Character set to use
- * @throws IOException If there was an underlying I/O exception
- */
- public TelnetTerminalServer(ServerSocketFactory serverSocketFactory, int port, Charset charset) throws IOException {
- this.serverSocket = serverSocketFactory.createServerSocket(port);
- this.charset = charset;
- }
-
- /**
- * Returns the actual server socket used by this object. Can be used to tweak settings but be careful!
- * @return Underlying ServerSocket
- */
- public ServerSocket getServerSocket() {
- return serverSocket;
- }
-
- /**
- * Waits for the next client to connect in to our server and returns a Terminal implementation, TelnetTerminal, that
- * represents the remote terminal this client is running. The terminal can be used just like any other Terminal, but
- * keep in mind that all operations are sent over the network.
- * @return TelnetTerminal for the remote client's terminal
- * @throws IOException If there was an underlying I/O exception
- */
- public TelnetTerminal acceptConnection() throws IOException {
- Socket clientSocket = serverSocket.accept();
- clientSocket.setTcpNoDelay(true);
- return new TelnetTerminal(clientSocket, charset);
- }
-
- /**
- * Closes the server socket, accepting no new connection. Any call to acceptConnection() after this will fail.
- * @throws IOException If there was an underlying I/O exception
- */
- public void close() throws IOException {
- serverSocket.close();
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal.ansi;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-import java.nio.charset.Charset;
-
-import com.googlecode.lanterna.input.KeyStroke;
-
-/**
- * UnixLikeTerminal extends from ANSITerminal and defines functionality that is common to
- * {@code UnixTerminal} and {@code CygwinTerminal}, like setting tty modes; echo, cbreak
- * and minimum characters for reading as well as a shutdown hook to set the tty back to
- * original state at the end.
- * <p>
- * If requested, it handles Control-C input to terminate the program, and hooks
- * into Unix WINCH signal to detect when the user has resized the terminal,
- * if supported by the JVM.
- *
- * @author Andreas
- * @author Martin
- */
-public abstract class UnixLikeTerminal extends ANSITerminal {
-
- /**
- * This enum lets you control how Lanterna will handle a ctrl+c keystroke from the user.
- */
- public enum CtrlCBehaviour {
- /**
- * Pressing ctrl+c doesn't kill the application, it will be added to the input queue as any other key stroke
- */
- TRAP,
- /**
- * Pressing ctrl+c will restore the terminal and kill the application as it normally does with terminal
- * applications. Lanterna will restore the terminal and then call {@code System.exit(1)} for this.
- */
- CTRL_C_KILLS_APPLICATION,
- }
-
- protected final CtrlCBehaviour terminalCtrlCBehaviour;
- protected final File ttyDev;
- private String sttyStatusToRestore;
-
- /**
- * Creates a UnixTerminal using a specified input stream, output stream and character set, with a custom size
- * querier instead of using the default one. This way you can override size detection (if you want to force the
- * terminal to a fixed size, for example). You also choose how you want ctrl+c key strokes to be handled.
- *
- * @param terminalInput Input stream to read terminal input from
- * @param terminalOutput Output stream to write terminal output to
- * @param terminalCharset Character set to use when converting characters to bytes
- * @param terminalCtrlCBehaviour Special settings on how the terminal will behave, see {@code UnixTerminalMode} for more
- * details
- * @param ttyDev File to redirect standard input from in exec(), if not null.
- */
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
- public UnixLikeTerminal(
- InputStream terminalInput,
- OutputStream terminalOutput,
- Charset terminalCharset,
- CtrlCBehaviour terminalCtrlCBehaviour,
- File ttyDev) {
- super(terminalInput, terminalOutput, terminalCharset);
- this.terminalCtrlCBehaviour = terminalCtrlCBehaviour;
- this.sttyStatusToRestore = null;
- this.ttyDev = ttyDev;
- }
-
- protected String exec(String... cmd) throws IOException {
- if (ttyDev != null) {
- //Here's what we try to do, but that is Java 7+ only:
- // processBuilder.redirectInput(ProcessBuilder.Redirect.from(ttyDev));
- //instead, for Java 6, we join the cmd into a scriptlet with redirection
- //and replace cmd by a call to sh with the scriptlet:
- StringBuilder sb = new StringBuilder();
- for (String arg : cmd) { sb.append(arg).append(' '); }
- sb.append("< ").append(ttyDev);
- cmd = new String[] { "sh", "-c", sb.toString() };
- }
- ProcessBuilder pb = new ProcessBuilder(cmd);
- Process process = pb.start();
- ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream();
- InputStream stdout = process.getInputStream();
- int readByte = stdout.read();
- while(readByte >= 0) {
- stdoutBuffer.write(readByte);
- readByte = stdout.read();
- }
- ByteArrayInputStream stdoutBufferInputStream = new ByteArrayInputStream(stdoutBuffer.toByteArray());
- BufferedReader reader = new BufferedReader(new InputStreamReader(stdoutBufferInputStream));
- StringBuilder builder = new StringBuilder();
- String line;
- while((line = reader.readLine()) != null) {
- builder.append(line);
- }
- reader.close();
- return builder.toString();
- }
-
- @Override
- public KeyStroke pollInput() throws IOException {
- //Check if we have ctrl+c coming
- KeyStroke key = super.pollInput();
- isCtrlC(key);
- return key;
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- //Check if we have ctrl+c coming
- KeyStroke key = super.readInput();
- isCtrlC(key);
- return key;
- }
-
- private void isCtrlC(KeyStroke key) throws IOException {
- if(key != null
- && terminalCtrlCBehaviour == CtrlCBehaviour.CTRL_C_KILLS_APPLICATION
- && key.getCharacter() != null
- && key.getCharacter() == 'c'
- && !key.isAltDown()
- && key.isCtrlDown()) {
-
- exitPrivateMode();
- System.exit(1);
- }
- }
-
- protected void setupWinResizeHandler() {
- try {
- Class<?> signalClass = Class.forName("sun.misc.Signal");
- for(Method m : signalClass.getDeclaredMethods()) {
- if("handle".equals(m.getName())) {
- Object windowResizeHandler = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Class.forName("sun.misc.SignalHandler")}, new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- if("handle".equals(method.getName())) {
- getTerminalSize();
- }
- return null;
- }
- });
- m.invoke(null, signalClass.getConstructor(String.class).newInstance("WINCH"), windowResizeHandler);
- }
- }
- } catch(Throwable e) {
- System.err.println(e.getMessage());
- }
- }
-
- protected void setupShutdownHook() {
- Runtime.getRuntime().addShutdownHook(new Thread("Lanterna STTY restore") {
- @Override
- public void run() {
- try {
- if (isInPrivateMode()) {
- exitPrivateMode();
- }
- }
- catch(IOException ignored) {}
- catch(IllegalStateException ignored) {} // still possible!
-
- try {
- restoreSTTY();
- }
- catch(IOException ignored) {}
- }
- });
- }
-
- /**
- * Enabling cbreak mode will allow you to read user input immediately as the user enters the characters, as opposed
- * to reading the data in lines as the user presses enter. If you want your program to respond to user input by the
- * keyboard, you probably want to enable cbreak mode.
- *
- * @see <a href="http://en.wikipedia.org/wiki/POSIX_terminal_interface">POSIX terminal interface</a>
- * @param cbreakOn Should cbreak be turned on or not
- * @throws IOException
- */
- public void setCBreak(boolean cbreakOn) throws IOException {
- sttyICanon(!cbreakOn);
- }
-
- /**
- * Enables or disables keyboard echo, meaning the immediate output of the characters you type on your keyboard. If
- * your users are going to interact with this application through the keyboard, you probably want to disable echo
- * mode.
- *
- * @param echoOn true if keyboard input will immediately echo, false if it's hidden
- * @throws IOException
- */
- public void setEcho(boolean echoOn) throws IOException {
- sttyKeyEcho(echoOn);
- }
-
- protected void saveSTTY() throws IOException {
- if(sttyStatusToRestore == null) {
- sttyStatusToRestore = sttySave();
- }
- }
-
- protected synchronized void restoreSTTY() throws IOException {
- if(sttyStatusToRestore != null) {
- sttyRestore( sttyStatusToRestore );
- sttyStatusToRestore = null;
- }
- }
-
- // A couple of system-dependent helpers:
- protected abstract void sttyKeyEcho(final boolean enable) throws IOException;
- protected abstract void sttyMinimum1CharacterForRead() throws IOException;
- protected abstract void sttyICanon(final boolean enable) throws IOException;
- protected abstract String sttySave() throws IOException;
- protected abstract void sttyRestore(String tok) throws IOException;
-
-}
\ No newline at end of file
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.ansi;
-
-
-import java.io.*;
-import java.nio.charset.Charset;
-
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * This class extends UnixLikeTerminal and implements the Unix-specific parts.
- * <p>
- * If you need to have Lanterna to call stty at a different location, you'll need to
- * subclass this and override {@code getSTTYCommand()}.
- *
- * @author Martin
- */
-@SuppressWarnings("WeakerAccess")
-public class UnixTerminal extends UnixLikeTerminal {
-
- protected final UnixTerminalSizeQuerier terminalSizeQuerier;
- private final boolean catchSpecialCharacters;
-
- /**
- * Creates a UnixTerminal with default settings, using System.in and System.out for input/output, using the default
- * character set on the system as the encoding and trap ctrl+c signal instead of killing the application.
- * @throws IOException If there was an I/O error initializing the terminal
- */
- public UnixTerminal() throws IOException {
- this(System.in, System.out, Charset.defaultCharset());
- }
-
- /**
- * Creates a UnixTerminal using a specified input stream, output stream and character set. Ctrl+c signal will be
- * trapped instead of killing the application.
- *
- * @param terminalInput Input stream to read terminal input from
- * @param terminalOutput Output stream to write terminal output to
- * @param terminalCharset Character set to use when converting characters to bytes
- * @throws java.io.IOException If there was an I/O error initializing the terminal
- */
- public UnixTerminal(
- InputStream terminalInput,
- OutputStream terminalOutput,
- Charset terminalCharset) throws IOException {
- this(terminalInput, terminalOutput, terminalCharset, null);
- }
-
- /**
- * Creates a UnixTerminal using a specified input stream, output stream and character set, with a custom size
- * querier instead of using the default one. This way you can override size detection (if you want to force the
- * terminal to a fixed size, for example). Ctrl+c signal will be trapped instead of killing the application.
- *
- * @param terminalInput Input stream to read terminal input from
- * @param terminalOutput Output stream to write terminal output to
- * @param terminalCharset Character set to use when converting characters to bytes
- * @param customSizeQuerier Object to use for looking up the size of the terminal, or null to use the built-in
- * method
- * @throws java.io.IOException If there was an I/O error initializing the terminal
- */
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
- public UnixTerminal(
- InputStream terminalInput,
- OutputStream terminalOutput,
- Charset terminalCharset,
- UnixTerminalSizeQuerier customSizeQuerier) throws IOException {
- this(terminalInput, terminalOutput, terminalCharset, customSizeQuerier, CtrlCBehaviour.CTRL_C_KILLS_APPLICATION);
- }
-
- /**
- * Creates a UnixTerminal using a specified input stream, output stream and character set, with a custom size
- * querier instead of using the default one. This way you can override size detection (if you want to force the
- * terminal to a fixed size, for example). You also choose how you want ctrl+c key strokes to be handled.
- *
- * @param terminalInput Input stream to read terminal input from
- * @param terminalOutput Output stream to write terminal output to
- * @param terminalCharset Character set to use when converting characters to bytes
- * @param customSizeQuerier Object to use for looking up the size of the terminal, or null to use the built-in
- * method
- * @param terminalCtrlCBehaviour Special settings on how the terminal will behave, see {@code UnixTerminalMode} for more
- * details
- * @throws java.io.IOException If there was an I/O error initializing the terminal
- */
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
- public UnixTerminal(
- InputStream terminalInput,
- OutputStream terminalOutput,
- Charset terminalCharset,
- UnixTerminalSizeQuerier customSizeQuerier,
- CtrlCBehaviour terminalCtrlCBehaviour) throws IOException {
- super(terminalInput,
- terminalOutput,
- terminalCharset,
- terminalCtrlCBehaviour,
- new File("/dev/tty"));
-
- this.terminalSizeQuerier = customSizeQuerier;
-
- //Make sure to set an initial size
- onResized(80, 24);
-
- setupWinResizeHandler();
- saveSTTY();
- setCBreak(true);
- setEcho(false);
- sttyMinimum1CharacterForRead();
- if("false".equals(System.getProperty("com.googlecode.lanterna.terminal.UnixTerminal.catchSpecialCharacters", "").trim().toLowerCase())) {
- catchSpecialCharacters = false;
- }
- else {
- catchSpecialCharacters = true;
- disableSpecialCharacters();
- }
- setupShutdownHook();
- }
-
- @Override
- public TerminalSize getTerminalSize() throws IOException {
- if(terminalSizeQuerier != null) {
- return terminalSizeQuerier.queryTerminalSize();
- }
-
- return super.getTerminalSize();
- }
-
- @Override
- protected void sttyKeyEcho(final boolean enable) throws IOException {
- exec(getSTTYCommand(), enable ? "echo" : "-echo");
- }
-
- @Override
- protected void sttyMinimum1CharacterForRead() throws IOException {
- exec(getSTTYCommand(), "min", "1");
- }
-
- @Override
- protected void sttyICanon(final boolean enable) throws IOException {
- exec(getSTTYCommand(), enable ? "icanon" : "-icanon");
- }
-
- @Override
- protected String sttySave() throws IOException {
- return exec(getSTTYCommand(), "-g").trim();
- }
-
- @Override
- protected void sttyRestore(String tok) throws IOException {
- exec(getSTTYCommand(), tok);
- }
-
- /*
- //What was the problem with this one? I don't remember... Restoring ctrl+c for now (see below)
- private void restoreEOFCtrlD() throws IOException {
- exec(getShellCommand(), "-c", getSTTYCommand() + " eof ^d < /dev/tty");
- }
-
- private void disableSpecialCharacters() throws IOException {
- exec(getShellCommand(), "-c", getSTTYCommand() + " intr undef < /dev/tty");
- exec(getShellCommand(), "-c", getSTTYCommand() + " start undef < /dev/tty");
- exec(getShellCommand(), "-c", getSTTYCommand() + " stop undef < /dev/tty");
- exec(getShellCommand(), "-c", getSTTYCommand() + " susp undef < /dev/tty");
- }
-
- private void restoreSpecialCharacters() throws IOException {
- exec(getShellCommand(), "-c", getSTTYCommand() + " intr ^C < /dev/tty");
- exec(getShellCommand(), "-c", getSTTYCommand() + " start ^Q < /dev/tty");
- exec(getShellCommand(), "-c", getSTTYCommand() + " stop ^S < /dev/tty");
- exec(getShellCommand(), "-c", getSTTYCommand() + " susp ^Z < /dev/tty");
- }
- */
-
-
- /**
- * This method causes certain keystrokes (at the moment only ctrl+c) to be passed in to the program instead of
- * interpreted by the shell and affect the program. For example, ctrl+c will send an interrupt that causes the
- * JVM to shut down, but this method will make it pass in ctrl+c as a normal KeyStroke instead (you can still make
- * ctrl+c kill the application, but Lanterna can do this for you after having restored the terminal).
- * <p>
- * Please note that this method is generally called automatically (i.e. it's turned on by default), unless you
- * define a system property "com.googlecode.lanterna.terminal.UnixTerminal.catchSpecialCharacters" and set it to
- * the string "false".
- * @throws IOException If there was an I/O error when attempting to disable special characters
- * @see com.googlecode.lanterna.terminal.ansi.UnixLikeTerminal.CtrlCBehaviour
- */
- public void disableSpecialCharacters() throws IOException {
- exec(getSTTYCommand(), "intr", "undef");
- }
-
- /**
- * This method restores the special characters disabled by {@code disableSpecialCharacters()}, if it has been
- * called.
- * @throws IOException If there was an I/O error when attempting to restore special characters
- */
- public void restoreSpecialCharacters() throws IOException {
- exec(getSTTYCommand(), "intr", "^C");
- }
-
- @Override
- protected synchronized void restoreSTTY() throws IOException {
- super.restoreSTTY();
- if(catchSpecialCharacters) {
- restoreSpecialCharacters();
- }
- }
-
- protected String getSTTYCommand() {
- return "/bin/stty";
- }
-
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.ansi;
-
-import com.googlecode.lanterna.TerminalSize;
-
-/**
- * This class allows you to override by what means Lanterna detects the size of
- * the terminal. You can implement this interface and pass it to the
- * UnixTerminal constructor in order to use it.
- * @author martin
- */
-@SuppressWarnings("WeakerAccess")
-public interface UnixTerminalSizeQuerier {
- /**
- * Checks what the size of the terminal is, measured in number of rows and columns. The implementer of this
- * interface is expected to know which terminal we are querying for and have all it needs to figure out the size.
- * One way of implementing this could be to read of an external value or variable or calling IPCs or just return
- * a static size at all times.
- * @return Size of the terminal at this point in time
- */
- TerminalSize queryTerminalSize();
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.terminal.IOSafeTerminal;
-import com.googlecode.lanterna.terminal.ResizeListener;
-
-import java.awt.*;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-
-/**
- * This class provides an AWT implementation of the Terminal interface that is an embeddable component you can put into
- * an AWT container. The class has static helper methods for opening a new frame with an AWTTerminal as its content,
- * similar to how the SwingTerminal used to work in earlier versions of lanterna. This version supports private mode and
- * non-private mode with a scrollback history. You can customize many of the properties by supplying device
- * configuration, font configuration and color configuration when you construct the object.
- * @author martin
- */
-@SuppressWarnings("serial")
-public class AWTTerminal extends Panel implements IOSafeTerminal {
-
- private final AWTTerminalImplementation terminalImplementation;
-
- /**
- * Creates a new AWTTerminal with all the defaults set and no scroll controller connected.
- */
- public AWTTerminal() {
- this(new TerminalScrollController.Null());
- }
-
-
- /**
- * Creates a new AWTTerminal with a particular scrolling controller that will be notified when the terminals
- * history size grows and will be called when this class needs to figure out the current scrolling position.
- * @param scrollController Controller for scrolling the terminal history
- */
- @SuppressWarnings("WeakerAccess")
- public AWTTerminal(TerminalScrollController scrollController) {
- this(TerminalEmulatorDeviceConfiguration.getDefault(),
- AWTTerminalFontConfiguration.getDefault(),
- TerminalEmulatorColorConfiguration.getDefault(),
- scrollController);
- }
-
- /**
- * Creates a new AWTTerminal component using custom settings and no scroll controller.
- * @param deviceConfiguration Device configuration to use for this AWTTerminal
- * @param fontConfiguration Font configuration to use for this AWTTerminal
- * @param colorConfiguration Color configuration to use for this AWTTerminal
- */
- public AWTTerminal(
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- AWTTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration) {
-
- this(null, deviceConfiguration, fontConfiguration, colorConfiguration);
- }
-
- /**
- * Creates a new AWTTerminal component using custom settings and no scroll controller.
- * @param initialTerminalSize Initial size of the terminal, which will be used when calculating the preferred size
- * of the component. If null, it will default to 80x25. If the AWT layout manager forces
- * the component to a different size, the value of this parameter won't have any meaning
- * @param deviceConfiguration Device configuration to use for this AWTTerminal
- * @param fontConfiguration Font configuration to use for this AWTTerminal
- * @param colorConfiguration Color configuration to use for this AWTTerminal
- */
- public AWTTerminal(
- TerminalSize initialTerminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- AWTTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration) {
-
- this(initialTerminalSize,
- deviceConfiguration,
- fontConfiguration,
- colorConfiguration,
- new TerminalScrollController.Null());
- }
-
- /**
- * Creates a new AWTTerminal component using custom settings and a custom scroll controller. The scrolling
- * controller will be notified when the terminal's history size grows and will be called when this class needs to
- * figure out the current scrolling position.
- * @param deviceConfiguration Device configuration to use for this AWTTerminal
- * @param fontConfiguration Font configuration to use for this AWTTerminal
- * @param colorConfiguration Color configuration to use for this AWTTerminal
- * @param scrollController Controller to use for scrolling, the object passed in will be notified whenever the
- * scrollable area has changed
- */
- public AWTTerminal(
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- AWTTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalScrollController scrollController) {
-
- this(null, deviceConfiguration, fontConfiguration, colorConfiguration, scrollController);
- }
-
-
-
- /**
- * Creates a new AWTTerminal component using custom settings and a custom scroll controller. The scrolling
- * controller will be notified when the terminal's history size grows and will be called when this class needs to
- * figure out the current scrolling position.
- * @param initialTerminalSize Initial size of the terminal, which will be used when calculating the preferred size
- * of the component. If null, it will default to 80x25. If the AWT layout manager forces
- * the component to a different size, the value of this parameter won't have any meaning
- * @param deviceConfiguration Device configuration to use for this AWTTerminal
- * @param fontConfiguration Font configuration to use for this AWTTerminal
- * @param colorConfiguration Color configuration to use for this AWTTerminal
- * @param scrollController Controller to use for scrolling, the object passed in will be notified whenever the
- * scrollable area has changed
- */
- public AWTTerminal(
- TerminalSize initialTerminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- AWTTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalScrollController scrollController) {
-
- //Enforce valid values on the input parameters
- if(deviceConfiguration == null) {
- deviceConfiguration = TerminalEmulatorDeviceConfiguration.getDefault();
- }
- if(fontConfiguration == null) {
- fontConfiguration = SwingTerminalFontConfiguration.getDefault();
- }
- if(colorConfiguration == null) {
- colorConfiguration = TerminalEmulatorColorConfiguration.getDefault();
- }
-
- terminalImplementation = new AWTTerminalImplementation(
- this,
- fontConfiguration,
- initialTerminalSize,
- deviceConfiguration,
- colorConfiguration,
- scrollController);
- }
-
- /**
- * Overridden method from AWT's {@code Component} class that returns the preferred size of the terminal (in pixels)
- * @return The terminal's preferred size in pixels
- */
- @Override
- public synchronized Dimension getPreferredSize() {
- return terminalImplementation.getPreferredSize();
- }
-
- /**
- * Overridden method from AWT's {@code Component} class that is called by OS window system when the component needs
- * to be redrawn
- * @param {@code Graphics} object to use when drawing the component
- */
- @Override
- public synchronized void paint(Graphics componentGraphics) {
- // Flicker-free AWT!
- // Extend Panel and do the drawing work in both update(..) and paint(..)
- terminalImplementation.paintComponent(componentGraphics);
- }
-
- /**
- * Overridden method from AWT's {@code Component} class that is called by OS window system when the component needs
- * to be updated (the size has changed) and redrawn
- * @param {@code Graphics} object to use when drawing the component
- */
- @Override
- public synchronized void update(Graphics componentGraphics) {
- // Flicker-free AWT!
- // Extend Panel and do the drawing work in both update(..) and paint(..)
- terminalImplementation.paintComponent(componentGraphics);
- }
-
- // Terminal methods below here, just forward to the implementation
-
- @Override
- public void enterPrivateMode() {
- terminalImplementation.enterPrivateMode();
- }
-
- @Override
- public void exitPrivateMode() {
- terminalImplementation.exitPrivateMode();
- }
-
- @Override
- public void clearScreen() {
- terminalImplementation.clearScreen();
- }
-
- @Override
- public void setCursorPosition(int x, int y) {
- terminalImplementation.setCursorPosition(x, y);
- }
-
- @Override
- public void setCursorVisible(boolean visible) {
- terminalImplementation.setCursorVisible(visible);
- }
-
- @Override
- public void putCharacter(char c) {
- terminalImplementation.putCharacter(c);
- }
-
- @Override
- public void enableSGR(SGR sgr) {
- terminalImplementation.enableSGR(sgr);
- }
-
- @Override
- public void disableSGR(SGR sgr) {
- terminalImplementation.disableSGR(sgr);
- }
-
- @Override
- public void resetColorAndSGR() {
- terminalImplementation.resetColorAndSGR();
- }
-
- @Override
- public void setForegroundColor(TextColor color) {
- terminalImplementation.setForegroundColor(color);
- }
-
- @Override
- public void setBackgroundColor(TextColor color) {
- terminalImplementation.setBackgroundColor(color);
- }
-
- @Override
- public TerminalSize getTerminalSize() {
- return terminalImplementation.getTerminalSize();
- }
-
- @Override
- public byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) {
- return terminalImplementation.enquireTerminal(timeout, timeoutUnit);
- }
-
- @Override
- public void flush() {
- terminalImplementation.flush();
- }
-
- @Override
- public KeyStroke pollInput() {
- return terminalImplementation.pollInput();
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- return terminalImplementation.readInput();
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- return terminalImplementation.newTextGraphics();
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- terminalImplementation.addResizeListener(listener);
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- terminalImplementation.removeResizeListener(listener);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.Symbols;
-import com.googlecode.lanterna.TextCharacter;
-
-import java.awt.*;
-import java.awt.font.FontRenderContext;
-import java.awt.geom.Rectangle2D;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.*;
-import java.util.List;
-
-/**
- * This class encapsulates the font information used by an {@link AWTTerminal}. By customizing this class, you can
- * choose which fonts are going to be used by an {@link AWTTerminal} component and some other related settings.
- * @author martin
- */
-public class AWTTerminalFontConfiguration {
-
- /**
- * Controls how the SGR bold will take effect when enabled on a character. Mainly this is controlling if the
- * character should be rendered with a bold font or not. The reason for this is that some characters, notably the
- * lines and double-lines in defined in Symbol, usually doesn't look very good with bold font when you try to
- * construct a GUI.
- */
- public enum BoldMode {
- /**
- * All characters with SGR Bold enabled will be rendered using a bold font
- */
- EVERYTHING,
- /**
- * All characters with SGR Bold enabled, except for the characters defined as constants in Symbols class, will
- * be rendered using a bold font
- */
- EVERYTHING_BUT_SYMBOLS,
- /**
- * Bold font will not be used for characters with SGR bold enabled
- */
- NOTHING,
- ;
- }
-
- private static final Set<String> MONOSPACE_CHECK_OVERRIDE = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
- "VL Gothic Regular",
- "NanumGothic",
- "WenQuanYi Zen Hei Mono",
- "WenQuanYi Zen Hei",
- "AR PL UMing TW",
- "AR PL UMing HK",
- "AR PL UMing CN"
- )));
-
- private static List<Font> getDefaultWindowsFonts() {
- return Collections.unmodifiableList(Arrays.asList(
- new Font("Courier New", Font.PLAIN, 14), //Monospaced can look pretty bad on Windows, so let's override it
- new Font("Monospaced", Font.PLAIN, 14)));
- }
-
- private static List<Font> getDefaultLinuxFonts() {
- return Collections.unmodifiableList(Arrays.asList(
- new Font("DejaVu Sans Mono", Font.PLAIN, 14),
- new Font("Monospaced", Font.PLAIN, 14),
- //Below, these should be redundant (Monospaced is supposed to catch-all)
- // but Java 6 seems to have issues with finding monospaced fonts sometimes
- new Font("Ubuntu Mono", Font.PLAIN, 14),
- new Font("FreeMono", Font.PLAIN, 14),
- new Font("Liberation Mono", Font.PLAIN, 14),
- new Font("VL Gothic Regular", Font.PLAIN, 14),
- new Font("NanumGothic", Font.PLAIN, 14),
- new Font("WenQuanYi Zen Hei Mono", Font.PLAIN, 14),
- new Font("WenQuanYi Zen Hei", Font.PLAIN, 14),
- new Font("AR PL UMing TW", Font.PLAIN, 14),
- new Font("AR PL UMing HK", Font.PLAIN, 14),
- new Font("AR PL UMing CN", Font.PLAIN, 14)));
- }
-
- private static List<Font> getDefaultFonts() {
- return Collections.unmodifiableList(Collections.singletonList(
- new Font("Monospaced", Font.PLAIN, 14)));
- }
-
- protected static Font[] selectDefaultFont() {
- String osName = System.getProperty("os.name", "").toLowerCase();
- if(osName.contains("win")) {
- List<Font> windowsFonts = getDefaultWindowsFonts();
- return windowsFonts.toArray(new Font[windowsFonts.size()]);
- }
- else if(osName.contains("linux")) {
- List<Font> linuxFonts = getDefaultLinuxFonts();
- return linuxFonts.toArray(new Font[linuxFonts.size()]);
- }
- else {
- List<Font> defaultFonts = getDefaultFonts();
- return defaultFonts.toArray(new Font[defaultFonts.size()]);
- }
- }
-
- /**
- * This is the default font settings that will be used if you don't specify anything
- */
- public static AWTTerminalFontConfiguration getDefault() {
- return newInstance(filterMonospaced(selectDefaultFont()));
- }
-
- /**
- * Given an array of fonts, returns another array with only the ones that are monospaced. The fonts in the result
- * will have the same order as in which they came in. A font is considered monospaced if the width of 'i' and 'W' is
- * the same.
- * @param fonts Fonts to filter monospaced fonts from
- * @return Array with the fonts from the input parameter that were monospaced
- */
- public static Font[] filterMonospaced(Font... fonts) {
- List<Font> result = new ArrayList<Font>(fonts.length);
- for(Font font: fonts) {
- if (isFontMonospaced(font)) {
- result.add(font);
- }
- }
- return result.toArray(new Font[result.size()]);
- }
-
- /**
- * Creates a new font configuration from a list of fonts in order of priority. This works by having the terminal
- * attempt to draw each character with the fonts in the order they are specified in and stop once we find a font
- * that can actually draw the character. For ASCII characters, it's very likely that the first font will always be
- * used.
- * @param fontsInOrderOfPriority Fonts to use when drawing text, in order of priority
- * @return Font configuration built from the font list
- */
- @SuppressWarnings("WeakerAccess")
- public static AWTTerminalFontConfiguration newInstance(Font... fontsInOrderOfPriority) {
- return new AWTTerminalFontConfiguration(true, BoldMode.EVERYTHING_BUT_SYMBOLS, fontsInOrderOfPriority);
- }
-
- private final List<Font> fontPriority;
- private final int fontWidth;
- private final int fontHeight;
- private final boolean useAntiAliasing;
- private final BoldMode boldMode;
-
- @SuppressWarnings("WeakerAccess")
- protected AWTTerminalFontConfiguration(boolean useAntiAliasing, BoldMode boldMode, Font... fontsInOrderOfPriority) {
- if(fontsInOrderOfPriority == null || fontsInOrderOfPriority.length == 0) {
- throw new IllegalArgumentException("Must pass in a valid list of fonts to SwingTerminalFontConfiguration");
- }
- this.useAntiAliasing = useAntiAliasing;
- this.boldMode = boldMode;
- this.fontPriority = new ArrayList<Font>(Arrays.asList(fontsInOrderOfPriority));
- this.fontWidth = getFontWidth(fontPriority.get(0));
- this.fontHeight = getFontHeight(fontPriority.get(0));
-
- //Make sure all the fonts are monospace
- for(Font font: fontPriority) {
- if(!isFontMonospaced(font)) {
- throw new IllegalArgumentException("Font " + font + " isn't monospaced!");
- }
- }
-
- //Make sure all lower-priority fonts are less or equal in width and height, shrink if necessary
- for(int i = 1; i < fontPriority.size(); i++) {
- Font font = fontPriority.get(i);
- while(getFontWidth(font) > fontWidth || getFontHeight(font) > fontHeight) {
- float newSize = font.getSize2D() - 0.5f;
- if(newSize < 0.01) {
- throw new IllegalStateException("Unable to shrink font " + (i+1) + " to fit the size of highest priority font " + fontPriority.get(0));
- }
- font = font.deriveFont(newSize);
- fontPriority.set(i, font);
- }
- }
- }
-
- Font getFontForCharacter(TextCharacter character) {
- Font normalFont = getFontForCharacter(character.getCharacter());
- if(boldMode == BoldMode.EVERYTHING || (boldMode == BoldMode.EVERYTHING_BUT_SYMBOLS && isNotASymbol(character.getCharacter()))) {
- if(character.isBold()) {
- normalFont = normalFont.deriveFont(Font.BOLD);
- }
- }
- return normalFont;
- }
-
- private Font getFontForCharacter(char c) {
- for(Font font: fontPriority) {
- if(font.canDisplay(c)) {
- return font;
- }
- }
- //No available font here, what to do...?
- return fontPriority.get(0);
- }
-
- int getFontWidth() {
- return fontWidth;
- }
-
- int getFontHeight() {
- return fontHeight;
- }
-
- boolean isAntiAliased() {
- return useAntiAliasing;
- }
-
- private static boolean isFontMonospaced(Font font) {
- if(MONOSPACE_CHECK_OVERRIDE.contains(font.getName())) {
- return true;
- }
- FontRenderContext frc = new FontRenderContext(
- null,
- RenderingHints.VALUE_TEXT_ANTIALIAS_OFF,
- RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT);
- Rectangle2D iBounds = font.getStringBounds("i", frc);
- Rectangle2D mBounds = font.getStringBounds("W", frc);
- return iBounds.getWidth() == mBounds.getWidth();
- }
-
- private int getFontWidth(Font font) {
- return (int)font.getStringBounds("W", getFontRenderContext()).getWidth();
- }
-
- private int getFontHeight(Font font) {
- return (int)font.getStringBounds("W", getFontRenderContext()).getHeight();
- }
-
- private FontRenderContext getFontRenderContext() {
- return new FontRenderContext(
- null,
- useAntiAliasing ?
- RenderingHints.VALUE_TEXT_ANTIALIAS_ON : RenderingHints.VALUE_TEXT_ANTIALIAS_OFF,
- RenderingHints.VALUE_FRACTIONALMETRICS_DEFAULT);
- }
-
-
- private static final Set<Character> SYMBOLS_CACHE = new HashSet<Character>();
- static {
- for(Field field: Symbols.class.getFields()) {
- if(field.getType() == char.class &&
- (field.getModifiers() & Modifier.FINAL) != 0 &&
- (field.getModifiers() & Modifier.STATIC) != 0) {
- try {
- SYMBOLS_CACHE.add(field.getChar(null));
- }
- catch(IllegalArgumentException ignore) {
- //Should never happen!
- }
- catch(IllegalAccessException ignore) {
- //Should never happen!
- }
- }
- }
- }
-
- private boolean isNotASymbol(char character) {
- return !SYMBOLS_CACHE.contains(character);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-import com.googlecode.lanterna.terminal.IOSafeTerminal;
-import com.googlecode.lanterna.terminal.ResizeListener;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-
-import java.awt.*;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class is similar to what SwingTerminal used to be before Lanterna 3.0; a Frame that contains a terminal
- * emulator. In Lanterna 3, this class is just an AWT Frame containing a {@link AWTTerminal} component, but it also
- * implements the {@link com.googlecode.lanterna.terminal.Terminal} interface and delegates all calls to the internal
- * {@link AWTTerminal}. You can tweak the class a bit to have special behaviours when exiting private mode or when the
- * user presses ESC key.
- *
- * <p>Please note that this is the AWT version and there is a Swing counterpart: {@link SwingTerminalFrame}
- * @see AWTTerminal
- * @see SwingTerminalFrame
- * @author martin
- */
-@SuppressWarnings("serial")
-public class AWTTerminalFrame extends Frame implements IOSafeTerminal {
- private final AWTTerminal awtTerminal;
- private TerminalEmulatorAutoCloseTrigger autoCloseTrigger;
- private boolean disposed;
-
- /**
- * Creates a new AWTTerminalFrame that doesn't automatically close.
- */
- public AWTTerminalFrame() throws HeadlessException {
- this(TerminalEmulatorAutoCloseTrigger.DoNotAutoClose);
- }
-
- /**
- * Creates a new AWTTerminalFrame with a specified auto-close behaviour
- * @param autoCloseTrigger What to trigger automatic disposal of the Frame
- */
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
- public AWTTerminalFrame(TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this("AwtTerminalFrame", autoCloseTrigger);
- }
-
- /**
- * Creates a new AWTTerminalFrame with a given title and no automatic closing.
- * @param title Title to use for the window
- */
- public AWTTerminalFrame(String title) throws HeadlessException {
- this(title, TerminalEmulatorAutoCloseTrigger.DoNotAutoClose);
- }
-
- /**
- * Creates a new AWTTerminalFrame with a specified auto-close behaviour and specific title
- * @param title Title to use for the window
- * @param autoCloseTrigger What to trigger automatic disposal of the Frame
- */
- @SuppressWarnings("WeakerAccess")
- public AWTTerminalFrame(String title, TerminalEmulatorAutoCloseTrigger autoCloseTrigger) throws HeadlessException {
- this(title, new AWTTerminal(), autoCloseTrigger);
- }
-
- /**
- * Creates a new AWTTerminalFrame using a specified title and a series of AWT terminal configuration objects
- * @param title What title to use for the window
- * @param deviceConfiguration Device configuration for the embedded AWTTerminal
- * @param fontConfiguration Font configuration for the embedded AWTTerminal
- * @param colorConfiguration Color configuration for the embedded AWTTerminal
- */
- public AWTTerminalFrame(String title,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- AWTTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration) {
- this(title, deviceConfiguration, fontConfiguration, colorConfiguration, TerminalEmulatorAutoCloseTrigger.DoNotAutoClose);
- }
-
- /**
- * Creates a new AWTTerminalFrame using a specified title and a series of AWT terminal configuration objects
- * @param title What title to use for the window
- * @param deviceConfiguration Device configuration for the embedded AWTTerminal
- * @param fontConfiguration Font configuration for the embedded AWTTerminal
- * @param colorConfiguration Color configuration for the embedded AWTTerminal
- * @param autoCloseTrigger What to trigger automatic disposal of the Frame
- */
- public AWTTerminalFrame(String title,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- AWTTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this(title, null, deviceConfiguration, fontConfiguration, colorConfiguration, autoCloseTrigger);
- }
-
- /**
- * Creates a new AWTTerminalFrame using a specified title and a series of AWT terminal configuration objects
- * @param title What title to use for the window
- * @param terminalSize Initial size of the terminal, in rows and columns. If null, it will default to 80x25.
- * @param deviceConfiguration Device configuration for the embedded AWTTerminal
- * @param fontConfiguration Font configuration for the embedded AWTTerminal
- * @param colorConfiguration Color configuration for the embedded AWTTerminal
- * @param autoCloseTrigger What to trigger automatic disposal of the Frame
- */
- public AWTTerminalFrame(String title,
- TerminalSize terminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- AWTTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this(title,
- new AWTTerminal(terminalSize, deviceConfiguration, fontConfiguration, colorConfiguration),
- autoCloseTrigger);
- }
-
- private AWTTerminalFrame(String title, AWTTerminal awtTerminal, TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- super(title != null ? title : "AWTTerminalFrame");
- this.awtTerminal = awtTerminal;
- this.autoCloseTrigger = autoCloseTrigger;
- this.disposed = false;
-
- setLayout(new BorderLayout());
- add(awtTerminal, BorderLayout.CENTER);
- setBackground(Color.BLACK); //This will reduce white flicker when resizing the window
- pack();
-
- //Put input focus on the terminal component by default
- awtTerminal.requestFocusInWindow();
- }
-
- /**
- * Returns the auto-close trigger used by the AWTTerminalFrame
- * @return Current auto-close trigger
- */
- public TerminalEmulatorAutoCloseTrigger getAutoCloseTrigger() {
- return autoCloseTrigger;
- }
-
- /**
- * Changes the current auto-close trigger used by this AWTTerminalFrame
- * @param autoCloseTrigger New auto-close trigger to use
- */
- public void setAutoCloseTrigger(TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this.autoCloseTrigger = autoCloseTrigger;
- }
-
- @Override
- public void dispose() {
- super.dispose();
- disposed = true;
- }
-
- ///////////
- // Delegate all Terminal interface implementations to AWTTerminal
- ///////////
- @Override
- public KeyStroke pollInput() {
- if(disposed) {
- return new KeyStroke(KeyType.EOF);
- }
- KeyStroke keyStroke = awtTerminal.pollInput();
- if(autoCloseTrigger == TerminalEmulatorAutoCloseTrigger.CloseOnEscape &&
- keyStroke != null &&
- keyStroke.getKeyType() == KeyType.Escape) {
- dispose();
- }
- return keyStroke;
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- return awtTerminal.readInput();
- }
-
- @Override
- public void enterPrivateMode() {
- awtTerminal.enterPrivateMode();
- }
-
- @Override
- public void exitPrivateMode() {
- awtTerminal.exitPrivateMode();
- if(autoCloseTrigger == TerminalEmulatorAutoCloseTrigger.CloseOnExitPrivateMode) {
- dispose();
- }
- }
-
- @Override
- public void clearScreen() {
- awtTerminal.clearScreen();
- }
-
- @Override
- public void setCursorPosition(int x, int y) {
- awtTerminal.setCursorPosition(x, y);
- }
-
- @Override
- public void setCursorVisible(boolean visible) {
- awtTerminal.setCursorVisible(visible);
- }
-
- @Override
- public void putCharacter(char c) {
- awtTerminal.putCharacter(c);
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- return awtTerminal.newTextGraphics();
- }
-
- @Override
- public void enableSGR(SGR sgr) {
- awtTerminal.enableSGR(sgr);
- }
-
- @Override
- public void disableSGR(SGR sgr) {
- awtTerminal.disableSGR(sgr);
- }
-
- @Override
- public void resetColorAndSGR() {
- awtTerminal.resetColorAndSGR();
- }
-
- @Override
- public void setForegroundColor(TextColor color) {
- awtTerminal.setForegroundColor(color);
- }
-
- @Override
- public void setBackgroundColor(TextColor color) {
- awtTerminal.setBackgroundColor(color);
- }
-
- @Override
- public TerminalSize getTerminalSize() {
- return awtTerminal.getTerminalSize();
- }
-
- @Override
- public byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) {
- return awtTerminal.enquireTerminal(timeout, timeoutUnit);
- }
-
- @Override
- public void flush() {
- awtTerminal.flush();
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- awtTerminal.addResizeListener(listener);
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- awtTerminal.removeResizeListener(listener);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.input.KeyStroke;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.io.IOException;
-import java.util.Collections;
-
-/**
- * AWT implementation of {@link GraphicalTerminalImplementation} that contains all the overrides for AWT
- * Created by martin on 08/02/16.
- */
-class AWTTerminalImplementation extends GraphicalTerminalImplementation {
- private final Component component;
- private final AWTTerminalFontConfiguration fontConfiguration;
-
- /**
- * Creates a new {@code AWTTerminalImplementation}
- * @param component Component that is the AWT terminal surface
- * @param fontConfiguration Font configuration to use
- * @param initialTerminalSize Initial size of the terminal
- * @param deviceConfiguration Device configuration
- * @param colorConfiguration Color configuration
- * @param scrollController Controller to be used when inspecting scroll status
- */
- AWTTerminalImplementation(
- Component component,
- AWTTerminalFontConfiguration fontConfiguration,
- TerminalSize initialTerminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalScrollController scrollController) {
-
- super(initialTerminalSize, deviceConfiguration, colorConfiguration, scrollController);
- this.component = component;
- this.fontConfiguration = fontConfiguration;
-
- //Prevent us from shrinking beyond one character
- component.setMinimumSize(new Dimension(fontConfiguration.getFontWidth(), fontConfiguration.getFontHeight()));
-
- //noinspection unchecked
- component.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.<AWTKeyStroke>emptySet());
- //noinspection unchecked
- component.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Collections.<AWTKeyStroke>emptySet());
-
- component.addKeyListener(new TerminalInputListener());
- component.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- AWTTerminalImplementation.this.component.requestFocusInWindow();
- }
- });
-
- component.addHierarchyListener(new HierarchyListener() {
- @Override
- public void hierarchyChanged(HierarchyEvent e) {
- if(e.getChangeFlags() == HierarchyEvent.DISPLAYABILITY_CHANGED) {
- if(e.getChanged().isDisplayable()) {
- startBlinkTimer();
- }
- else {
- stopBlinkTimer();
- }
- }
- }
- });
- }
-
-
- /**
- * Returns the current font configuration. Note that it is immutable and cannot be changed.
- * @return This {@link AWTTerminal}'s current font configuration
- */
- public AWTTerminalFontConfiguration getFontConfiguration() {
- return fontConfiguration;
- }
-
- @Override
- protected int getFontHeight() {
- return fontConfiguration.getFontHeight();
- }
-
- @Override
- protected int getFontWidth() {
- return fontConfiguration.getFontWidth();
- }
-
- @Override
- protected int getHeight() {
- return component.getHeight();
- }
-
- @Override
- protected int getWidth() {
- return component.getWidth();
- }
-
- @Override
- protected Font getFontForCharacter(TextCharacter character) {
- return fontConfiguration.getFontForCharacter(character);
- }
-
- @Override
- protected boolean isTextAntiAliased() {
- return fontConfiguration.isAntiAliased();
- }
-
- @Override
- protected void repaint() {
- if(EventQueue.isDispatchThread()) {
- component.repaint();
- }
- else {
- EventQueue.invokeLater(new Runnable() {
- @Override
- public void run() {
- component.repaint();
- }
- });
- }
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- if(EventQueue.isDispatchThread()) {
- throw new UnsupportedOperationException("Cannot call SwingTerminal.readInput() on the AWT thread");
- }
- return super.readInput();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.*;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-import com.googlecode.lanterna.terminal.IOSafeTerminal;
-import com.googlecode.lanterna.terminal.ResizeListener;
-
-import java.awt.*;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.util.*;
-import java.util.List;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This is the class that does the heavy lifting for both {@link AWTTerminal} and {@link SwingTerminal}. It maintains
- * most of the external terminal state and also the main back buffer that is copied to the components area on draw
- * operations.
- *
- * @author martin
- */
-@SuppressWarnings("serial")
-abstract class GraphicalTerminalImplementation implements IOSafeTerminal {
- private final TerminalEmulatorDeviceConfiguration deviceConfiguration;
- private final TerminalEmulatorColorConfiguration colorConfiguration;
- private final VirtualTerminal virtualTerminal;
- private final BlockingQueue<KeyStroke> keyQueue;
- private final List<ResizeListener> resizeListeners;
-
- private final String enquiryString;
- private final EnumSet<SGR> activeSGRs;
- private TextColor foregroundColor;
- private TextColor backgroundColor;
-
- private volatile boolean cursorIsVisible;
- private volatile Timer blinkTimer;
- private volatile boolean hasBlinkingText;
- private volatile boolean blinkOn;
- private volatile boolean flushed;
-
- // We use two different data structures to optimize drawing
- // * A map (as a two-dimensional array) of all characters currently visible inside this component
- // * A backbuffer with the graphics content
- //
- // The buffer is the most important one as it allows us to re-use what was drawn earlier. It is not reset on every
- // drawing operation but updates just in those places where the map tells us the character has changed. Note that
- // when the component is resized, we always update the whole buffer.
- //
- // DON'T RELY ON THESE FOR SIZE! We make it a big bigger than necessary to make resizing smoother. Use the AWT/Swing
- // methods to get the correct dimensions or use {@code getTerminalSize()} to get the size in terminal space.
- private CharacterState[][] visualState;
- private BufferedImage backbuffer;
-
- /**
- * Creates a new GraphicalTerminalImplementation component using custom settings and a custom scroll controller. The
- * scrolling controller will be notified when the terminal's history size grows and will be called when this class
- * needs to figure out the current scrolling position.
- * @param initialTerminalSize Initial size of the terminal, which will be used when calculating the preferred size
- * of the component. If null, it will default to 80x25. If the AWT layout manager forces
- * the component to a different size, the value of this parameter won't have any meaning
- * @param deviceConfiguration Device configuration to use for this SwingTerminal
- * @param colorConfiguration Color configuration to use for this SwingTerminal
- * @param scrollController Controller to use for scrolling, the object passed in will be notified whenever the
- * scrollable area has changed
- */
- public GraphicalTerminalImplementation(
- TerminalSize initialTerminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalScrollController scrollController) {
-
- //This is kind of meaningless since we don't know how large the
- //component is at this point, but we should set it to something
- if(initialTerminalSize == null) {
- initialTerminalSize = new TerminalSize(80, 24);
- }
- this.virtualTerminal = new VirtualTerminal(
- deviceConfiguration.getLineBufferScrollbackSize(),
- initialTerminalSize,
- scrollController);
- this.keyQueue = new LinkedBlockingQueue<KeyStroke>();
- this.resizeListeners = new CopyOnWriteArrayList<ResizeListener>();
- this.deviceConfiguration = deviceConfiguration;
- this.colorConfiguration = colorConfiguration;
-
- this.activeSGRs = EnumSet.noneOf(SGR.class);
- this.foregroundColor = TextColor.ANSI.DEFAULT;
- this.backgroundColor = TextColor.ANSI.DEFAULT;
- this.cursorIsVisible = true; //Always start with an activate and visible cursor
- this.enquiryString = "TerminalEmulator";
- this.visualState = new CharacterState[48][160];
- this.backbuffer = null; // We don't know the dimensions yet
- this.blinkTimer = null;
- this.hasBlinkingText = false; // Assume initial content doesn't have any blinking text
- this.blinkOn = true;
- this.flushed = false;
-
- //Set the initial scrollable size
- //scrollObserver.newScrollableLength(fontConfiguration.getFontHeight() * terminalSize.getRows());
- }
-
- ///////////
- // First abstract methods that are implemented in AWTTerminalImplementation and SwingTerminalImplementation
- ///////////
-
- /**
- * Used to find out the font height, in pixels
- * @return Terminal font height in pixels
- */
- protected abstract int getFontHeight();
-
- /**
- * Used to find out the font width, in pixels
- * @return Terminal font width in pixels
- */
- protected abstract int getFontWidth();
-
- /**
- * Used when requiring the total height of the terminal component, in pixels
- * @return Height of the terminal component, in pixels
- */
- protected abstract int getHeight();
-
- /**
- * Used when requiring the total width of the terminal component, in pixels
- * @return Width of the terminal component, in pixels
- */
- protected abstract int getWidth();
-
- /**
- * Returning the AWT font to use for the specific character. This might not always be the same, in case a we are
- * trying to draw an unusual character (probably CJK) which isn't contained in the standard terminal font.
- * @param character Character to get the font for
- * @return Font to be used for this character
- */
- protected abstract Font getFontForCharacter(TextCharacter character);
-
- /**
- * Returns {@code true} if anti-aliasing is enabled, {@code false} otherwise
- * @return {@code true} if anti-aliasing is enabled, {@code false} otherwise
- */
- protected abstract boolean isTextAntiAliased();
-
- /**
- * Called by the {@code GraphicalTerminalImplementation} when it would like the OS to schedule a repaint of the
- * window
- */
- protected abstract void repaint();
-
- /**
- * Start the timer that triggers blinking
- */
- protected synchronized void startBlinkTimer() {
- if(blinkTimer != null) {
- // Already on!
- return;
- }
- blinkTimer = new Timer("LanternaTerminalBlinkTimer", true);
- blinkTimer.schedule(new TimerTask() {
- @Override
- public void run() {
- blinkOn = !blinkOn;
- if(hasBlinkingText) {
- repaint();
- }
- }
- }, deviceConfiguration.getBlinkLengthInMilliSeconds(), deviceConfiguration.getBlinkLengthInMilliSeconds());
- }
-
- /**
- * Stops the timer the triggers blinking
- */
- protected synchronized void stopBlinkTimer() {
- if(blinkTimer == null) {
- // Already off!
- return;
- }
- blinkTimer.cancel();
- blinkTimer = null;
- }
-
- ///////////
- // First implement all the Swing-related methods
- ///////////
- /**
- * Calculates the preferred size of this terminal
- * @return Preferred size of this terminal
- */
- synchronized Dimension getPreferredSize() {
- return new Dimension(getFontWidth() * virtualTerminal.getSize().getColumns(),
- getFontHeight() * virtualTerminal.getSize().getRows());
- }
-
- /**
- * Updates the back buffer (if necessary) and draws it to the component's surface
- * @param componentGraphics Object to use when drawing to the component's surface
- */
- protected synchronized void paintComponent(Graphics componentGraphics) {
- //First, resize the buffer width/height if necessary
- int fontWidth = getFontWidth();
- int fontHeight = getFontHeight();
- //boolean antiAliasing = fontConfiguration.isAntiAliased();
- int widthInNumberOfCharacters = getWidth() / fontWidth;
- int visibleRows = getHeight() / fontHeight;
- boolean terminalResized = false;
-
- //Don't let size be less than 1
- widthInNumberOfCharacters = Math.max(1, widthInNumberOfCharacters);
- visibleRows = Math.max(1, visibleRows);
-
- //scrollObserver.updateModel(currentBuffer.getNumberOfLines(), visibleRows);
- TerminalSize terminalSize = virtualTerminal.getSize().withColumns(widthInNumberOfCharacters).withRows(visibleRows);
- if(!terminalSize.equals(virtualTerminal.getSize())) {
- virtualTerminal.resize(terminalSize);
- for(ResizeListener listener: resizeListeners) {
- listener.onResized(this, terminalSize);
- }
- terminalResized = true;
- ensureVisualStateHasRightSize(terminalSize);
- }
- ensureBackbufferHasRightSize();
-
- // At this point, if the user hasn't asked for an explicit flush, just paint the backbuffer. It's prone to
- // problems if the user isn't flushing properly but it reduces flickering when resizing the window and the code
- // is asynchronously responding to the resize
- if(flushed) {
- updateBackBuffer(fontWidth, fontHeight, terminalResized, terminalSize);
- flushed = false;
- }
-
- componentGraphics.drawImage(backbuffer, 0, 0, getWidth(), getHeight(), 0, 0, getWidth(), getHeight(), null);
-
- // Dispose the graphic objects
- componentGraphics.dispose();
-
- // Tell anyone waiting on us that drawing is complete
- notifyAll();
- }
-
- private void updateBackBuffer(int fontWidth, int fontHeight, boolean terminalResized, TerminalSize terminalSize) {
- //Retrieve the position of the cursor, relative to the scrolling state
- TerminalPosition translatedCursorPosition = virtualTerminal.getTranslatedCursorPosition();
-
- //Setup the graphics object
- Graphics2D backbufferGraphics = backbuffer.createGraphics();
-
- if(isTextAntiAliased()) {
- backbufferGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
- backbufferGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
- }
-
- // Draw line by line, character by character
- // Initiate the blink state to whatever the cursor is using, since if the cursor is blinking then we always want
- // to do the blink repaint
- boolean foundBlinkingCharacters = deviceConfiguration.isCursorBlinking();
- int rowIndex = 0;
- for(List<TextCharacter> row: virtualTerminal.getLines()) {
- for(int columnIndex = 0; columnIndex < row.size(); columnIndex++) {
- //Any extra characters from the virtual terminal that doesn't fit can be discarded
- if(columnIndex >= terminalSize.getColumns()) {
- continue;
- }
-
- TextCharacter character = row.get(columnIndex);
- boolean atCursorLocation = translatedCursorPosition.equals(columnIndex, rowIndex);
- //If next position is the cursor location and this is a CJK character (i.e. cursor is on the padding),
- //consider this location the cursor position since otherwise the cursor will be skipped
- if(!atCursorLocation &&
- translatedCursorPosition.getColumn() == columnIndex + 1 &&
- translatedCursorPosition.getRow() == rowIndex &&
- TerminalTextUtils.isCharCJK(character.getCharacter())) {
- atCursorLocation = true;
- }
- int characterWidth = fontWidth * (TerminalTextUtils.isCharCJK(character.getCharacter()) ? 2 : 1);
-
- Color foregroundColor = deriveTrueForegroundColor(character, atCursorLocation);
- Color backgroundColor = deriveTrueBackgroundColor(character, atCursorLocation);
-
- boolean drawCursor = atCursorLocation &&
- (!deviceConfiguration.isCursorBlinking() || //Always draw if the cursor isn't blinking
- (deviceConfiguration.isCursorBlinking() && blinkOn)); //If the cursor is blinking, only draw when blinkOn is true
-
- CharacterState characterState = new CharacterState(character, foregroundColor, backgroundColor, drawCursor);
- if(!characterState.equals(visualState[rowIndex][columnIndex]) || terminalResized) {
- drawCharacter(backbufferGraphics,
- character,
- columnIndex,
- rowIndex,
- foregroundColor,
- backgroundColor,
- fontWidth,
- fontHeight,
- characterWidth,
- drawCursor);
- visualState[rowIndex][columnIndex] = characterState;
- if(TerminalTextUtils.isCharCJK(character.getCharacter())) {
- visualState[rowIndex][columnIndex+1] = characterState;
- }
- }
-
- if(character.getModifiers().contains(SGR.BLINK)) {
- foundBlinkingCharacters = true;
- }
- if(TerminalTextUtils.isCharCJK(character.getCharacter())) {
- columnIndex++; //Skip the trailing space after a CJK character
- }
- }
- rowIndex++;
- }
-
- // Take care of the left-over area at the bottom and right of the component where no character can fit
- int leftoverHeight = getHeight() % fontHeight;
- int leftoverWidth = getWidth() % fontWidth;
- backbufferGraphics.setColor(Color.BLACK);
- if(leftoverWidth > 0) {
- backbufferGraphics.fillRect(getWidth() - leftoverWidth, 0, leftoverWidth, getHeight());
- }
- if(leftoverHeight > 0) {
- backbufferGraphics.fillRect(0, getHeight() - leftoverHeight, getWidth(), leftoverHeight);
- }
- backbufferGraphics.dispose();
-
- // Update the blink status according to if there were any blinking characters or not
- this.hasBlinkingText = foundBlinkingCharacters;
- }
-
- private void ensureBackbufferHasRightSize() {
- if(backbuffer == null) {
- backbuffer = new BufferedImage(getWidth() * 2, getHeight() * 2, BufferedImage.TYPE_INT_RGB);
- }
- if(backbuffer.getWidth() < getWidth() || backbuffer.getWidth() > getWidth() * 4 ||
- backbuffer.getHeight() < getHeight() || backbuffer.getHeight() > getHeight() * 4) {
- BufferedImage newBackbuffer = new BufferedImage(Math.max(getWidth(), 1) * 2, Math.max(getHeight(), 1) * 2, BufferedImage.TYPE_INT_RGB);
- Graphics2D graphics = newBackbuffer.createGraphics();
- graphics.drawImage(backbuffer, 0, 0, null);
- graphics.dispose();
- backbuffer = newBackbuffer;
- }
- }
-
- private void ensureVisualStateHasRightSize(TerminalSize terminalSize) {
- if(visualState == null) {
- visualState = new CharacterState[terminalSize.getRows() * 2][terminalSize.getColumns() * 2];
- }
- if(visualState.length < terminalSize.getRows() || visualState.length > Math.max(terminalSize.getRows(), 1) * 4) {
- visualState = Arrays.copyOf(visualState, terminalSize.getRows() * 2);
- }
- for(int rowIndex = 0; rowIndex < visualState.length; rowIndex++) {
- CharacterState[] row = visualState[rowIndex];
- if(row == null) {
- row = new CharacterState[terminalSize.getColumns() * 2];
- visualState[rowIndex] = row;
- }
- if(row.length < terminalSize.getColumns() || row.length > Math.max(terminalSize.getColumns(), 1) * 4) {
- row = Arrays.copyOf(row, terminalSize.getColumns() * 2);
- visualState[rowIndex] = row;
- }
-
- // Make sure all items outside the 'real' terminal size are null
- if(rowIndex < terminalSize.getRows()) {
- Arrays.fill(row, terminalSize.getColumns(), row.length, null);
- }
- else {
- Arrays.fill(row, null);
- }
- }
- }
-
- private void drawCharacter(
- Graphics g,
- TextCharacter character,
- int columnIndex,
- int rowIndex,
- Color foregroundColor,
- Color backgroundColor,
- int fontWidth,
- int fontHeight,
- int characterWidth,
- boolean drawCursor) {
-
- int x = columnIndex * fontWidth;
- int y = rowIndex * fontHeight;
- g.setColor(backgroundColor);
- g.setClip(x, y, characterWidth, fontHeight);
- g.fillRect(x, y, characterWidth, fontHeight);
-
- g.setColor(foregroundColor);
- Font font = getFontForCharacter(character);
- g.setFont(font);
- FontMetrics fontMetrics = g.getFontMetrics();
- g.drawString(Character.toString(character.getCharacter()), x, ((rowIndex + 1) * fontHeight) - fontMetrics.getDescent());
-
- if(character.isCrossedOut()) {
- int lineStartX = x;
- int lineStartY = y + (fontHeight / 2);
- int lineEndX = lineStartX + characterWidth;
- g.drawLine(lineStartX, lineStartY, lineEndX, lineStartY);
- }
- if(character.isUnderlined()) {
- int lineStartX = x;
- int lineStartY = ((rowIndex + 1) * fontHeight) - fontMetrics.getDescent() + 1;
- int lineEndX = lineStartX + characterWidth;
- g.drawLine(lineStartX, lineStartY, lineEndX, lineStartY);
- }
-
- if(drawCursor) {
- if(deviceConfiguration.getCursorColor() == null) {
- g.setColor(foregroundColor);
- }
- else {
- g.setColor(colorConfiguration.toAWTColor(deviceConfiguration.getCursorColor(), false, false));
- }
- if(deviceConfiguration.getCursorStyle() == TerminalEmulatorDeviceConfiguration.CursorStyle.UNDER_BAR) {
- g.fillRect(x, y + fontHeight - 3, characterWidth, 2);
- }
- else if(deviceConfiguration.getCursorStyle() == TerminalEmulatorDeviceConfiguration.CursorStyle.VERTICAL_BAR) {
- g.fillRect(x, y + 1, 2, fontHeight - 2);
- }
- }
- }
-
-
- private Color deriveTrueForegroundColor(TextCharacter character, boolean atCursorLocation) {
- TextColor foregroundColor = character.getForegroundColor();
- TextColor backgroundColor = character.getBackgroundColor();
- boolean reverse = character.isReversed();
- boolean blink = character.isBlinking();
-
- if(cursorIsVisible && atCursorLocation) {
- if(deviceConfiguration.getCursorStyle() == TerminalEmulatorDeviceConfiguration.CursorStyle.REVERSED &&
- (!deviceConfiguration.isCursorBlinking() || !blinkOn)) {
- reverse = true;
- }
- }
-
- if(reverse && (!blink || !blinkOn)) {
- return colorConfiguration.toAWTColor(backgroundColor, backgroundColor != TextColor.ANSI.DEFAULT, character.isBold());
- }
- else if(!reverse && blink && blinkOn) {
- return colorConfiguration.toAWTColor(backgroundColor, false, character.isBold());
- }
- else {
- return colorConfiguration.toAWTColor(foregroundColor, true, character.isBold());
- }
- }
-
- private Color deriveTrueBackgroundColor(TextCharacter character, boolean atCursorLocation) {
- TextColor foregroundColor = character.getForegroundColor();
- TextColor backgroundColor = character.getBackgroundColor();
- boolean reverse = character.isReversed();
-
- if(cursorIsVisible && atCursorLocation) {
- if(deviceConfiguration.getCursorStyle() == TerminalEmulatorDeviceConfiguration.CursorStyle.REVERSED &&
- (!deviceConfiguration.isCursorBlinking() || !blinkOn)) {
- reverse = true;
- }
- else if(deviceConfiguration.getCursorStyle() == TerminalEmulatorDeviceConfiguration.CursorStyle.FIXED_BACKGROUND) {
- backgroundColor = deviceConfiguration.getCursorColor();
- }
- }
-
- if(reverse) {
- return colorConfiguration.toAWTColor(foregroundColor, backgroundColor == TextColor.ANSI.DEFAULT, character.isBold());
- }
- else {
- return colorConfiguration.toAWTColor(backgroundColor, false, false);
- }
- }
-
- ///////////
- // Then delegate all Terminal interface methods to the virtual terminal implementation
- //
- // Some of these methods we need to pass to the AWT-thread, which makes the call asynchronous. Hopefully this isn't
- // causing too much problem...
- ///////////
- @Override
- public KeyStroke pollInput() {
- return keyQueue.poll();
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- try {
- return keyQueue.take();
- }
- catch(InterruptedException ignore) {
- throw new IOException("Blocking input was interrupted");
- }
- }
-
- @Override
- public synchronized void enterPrivateMode() {
- virtualTerminal.switchToPrivateMode();
- clearBackBufferAndVisualState();
- flush();
- }
-
- @Override
- public synchronized void exitPrivateMode() {
- virtualTerminal.switchToNormalMode();
- clearBackBufferAndVisualState();
- flush();
- }
-
- @Override
- public synchronized void clearScreen() {
- virtualTerminal.clear();
- clearBackBufferAndVisualState();
- flush();
- }
-
- /**
- * Clears out the back buffer and the resets the visual state so next paint operation will do a full repaint of
- * everything
- */
- protected void clearBackBufferAndVisualState() {
- // Manually clear the backbuffer and visual state
- if(backbuffer != null) {
- Graphics2D graphics = backbuffer.createGraphics();
- Color foregroundColor = colorConfiguration.toAWTColor(TextColor.ANSI.DEFAULT, true, false);
- Color backgroundColor = colorConfiguration.toAWTColor(TextColor.ANSI.DEFAULT, false, false);
- graphics.setColor(backgroundColor);
- graphics.fillRect(0, 0, getWidth(), getHeight());
- graphics.dispose();
-
- for(CharacterState[] line : visualState) {
- Arrays.fill(line, new CharacterState(new TextCharacter(' '), foregroundColor, backgroundColor, false));
- }
- }
- }
-
- @Override
- public synchronized void setCursorPosition(final int x, final int y) {
- virtualTerminal.setCursorPosition(new TerminalPosition(x, y));
- }
-
- @Override
- public void setCursorVisible(final boolean visible) {
- cursorIsVisible = visible;
- }
-
- @Override
- public synchronized void putCharacter(final char c) {
- virtualTerminal.putCharacter(new TextCharacter(c, foregroundColor, backgroundColor, activeSGRs));
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- return new VirtualTerminalTextGraphics(virtualTerminal);
- }
-
- @Override
- public void enableSGR(final SGR sgr) {
- activeSGRs.add(sgr);
- }
-
- @Override
- public void disableSGR(final SGR sgr) {
- activeSGRs.remove(sgr);
- }
-
- @Override
- public void resetColorAndSGR() {
- foregroundColor = TextColor.ANSI.DEFAULT;
- backgroundColor = TextColor.ANSI.DEFAULT;
- activeSGRs.clear();
- }
-
- @Override
- public void setForegroundColor(final TextColor color) {
- foregroundColor = color;
- }
-
- @Override
- public void setBackgroundColor(final TextColor color) {
- backgroundColor = color;
- }
-
- @Override
- public synchronized TerminalSize getTerminalSize() {
- return virtualTerminal.getSize();
- }
-
- @Override
- public byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) {
- return enquiryString.getBytes();
- }
-
- @Override
- public void flush() {
- flushed = true;
- repaint();
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- resizeListeners.add(listener);
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- resizeListeners.remove(listener);
- }
-
- ///////////
- // Remaining are private internal classes used by SwingTerminal
- ///////////
- private static final Set<Character> TYPED_KEYS_TO_IGNORE = new HashSet<Character>(Arrays.asList('\n', '\t', '\r', '\b', '\33', (char)127));
-
- /**
- * Class that translates AWT key events into Lanterna {@link KeyStroke}
- */
- protected class TerminalInputListener extends KeyAdapter {
- @Override
- public void keyTyped(KeyEvent e) {
- char character = e.getKeyChar();
- boolean altDown = (e.getModifiersEx() & InputEvent.ALT_DOWN_MASK) != 0;
- boolean ctrlDown = (e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0;
-
- if(!TYPED_KEYS_TO_IGNORE.contains(character)) {
- if(ctrlDown) {
- //We need to re-adjust the character if ctrl is pressed, just like for the AnsiTerminal
- character = (char) ('a' - 1 + character);
- }
- keyQueue.add(new KeyStroke(character, ctrlDown, altDown));
- }
- }
-
- @Override
- public void keyPressed(KeyEvent e) {
- boolean altDown = (e.getModifiersEx() & InputEvent.ALT_DOWN_MASK) != 0;
- boolean ctrlDown = (e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0;
- if(e.getKeyCode() == KeyEvent.VK_ENTER) {
- keyQueue.add(new KeyStroke(KeyType.Enter, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_ESCAPE) {
- keyQueue.add(new KeyStroke(KeyType.Escape, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
- keyQueue.add(new KeyStroke(KeyType.Backspace, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_LEFT) {
- keyQueue.add(new KeyStroke(KeyType.ArrowLeft, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_RIGHT) {
- keyQueue.add(new KeyStroke(KeyType.ArrowRight, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_UP) {
- keyQueue.add(new KeyStroke(KeyType.ArrowUp, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_DOWN) {
- keyQueue.add(new KeyStroke(KeyType.ArrowDown, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_INSERT) {
- keyQueue.add(new KeyStroke(KeyType.Insert, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_DELETE) {
- keyQueue.add(new KeyStroke(KeyType.Delete, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_HOME) {
- keyQueue.add(new KeyStroke(KeyType.Home, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_END) {
- keyQueue.add(new KeyStroke(KeyType.End, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_PAGE_UP) {
- keyQueue.add(new KeyStroke(KeyType.PageUp, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
- keyQueue.add(new KeyStroke(KeyType.PageDown, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F1) {
- keyQueue.add(new KeyStroke(KeyType.F1, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F2) {
- keyQueue.add(new KeyStroke(KeyType.F2, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F3) {
- keyQueue.add(new KeyStroke(KeyType.F3, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F4) {
- keyQueue.add(new KeyStroke(KeyType.F4, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F5) {
- keyQueue.add(new KeyStroke(KeyType.F5, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F6) {
- keyQueue.add(new KeyStroke(KeyType.F6, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F7) {
- keyQueue.add(new KeyStroke(KeyType.F7, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F8) {
- keyQueue.add(new KeyStroke(KeyType.F8, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F9) {
- keyQueue.add(new KeyStroke(KeyType.F9, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F10) {
- keyQueue.add(new KeyStroke(KeyType.F10, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F11) {
- keyQueue.add(new KeyStroke(KeyType.F11, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_F12) {
- keyQueue.add(new KeyStroke(KeyType.F12, ctrlDown, altDown));
- }
- else if(e.getKeyCode() == KeyEvent.VK_TAB) {
- if(e.isShiftDown()) {
- keyQueue.add(new KeyStroke(KeyType.ReverseTab, ctrlDown, altDown));
- }
- else {
- keyQueue.add(new KeyStroke(KeyType.Tab, ctrlDown, altDown));
- }
- }
- else {
- //keyTyped doesn't catch this scenario (for whatever reason...) so we have to do it here
- if(altDown && ctrlDown && e.getKeyCode() >= 'A' && e.getKeyCode() <= 'Z') {
- char asLowerCase = Character.toLowerCase((char) e.getKeyCode());
- keyQueue.add(new KeyStroke(asLowerCase, true, true));
- }
- }
- }
- }
-
- private static class CharacterState {
- private final TextCharacter textCharacter;
- private final Color foregroundColor;
- private final Color backgroundColor;
- private final boolean drawCursor;
-
- CharacterState(TextCharacter textCharacter, Color foregroundColor, Color backgroundColor, boolean drawCursor) {
- this.textCharacter = textCharacter;
- this.foregroundColor = foregroundColor;
- this.backgroundColor = backgroundColor;
- this.drawCursor = drawCursor;
- }
-
- @Override
- public boolean equals(Object o) {
- if(this == o) {
- return true;
- }
- if(o == null || getClass() != o.getClass()) {
- return false;
- }
- CharacterState that = (CharacterState) o;
- if(drawCursor != that.drawCursor) {
- return false;
- }
- if(!textCharacter.equals(that.textCharacter)) {
- return false;
- }
- if(!foregroundColor.equals(that.foregroundColor)) {
- return false;
- }
- return backgroundColor.equals(that.backgroundColor);
- }
-
- @Override
- public int hashCode() {
- int result = textCharacter.hashCode();
- result = 31 * result + foregroundColor.hashCode();
- result = 31 * result + backgroundColor.hashCode();
- result = 31 * result + (drawCursor ? 1 : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "CharacterState{" +
- "textCharacter=" + textCharacter +
- ", foregroundColor=" + foregroundColor +
- ", backgroundColor=" + backgroundColor +
- ", drawCursor=" + drawCursor +
- '}';
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.terminal.IOSafeTerminal;
-import com.googlecode.lanterna.terminal.ResizeListener;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import java.awt.BorderLayout;
-import java.awt.Container;
-import java.awt.Scrollbar;
-import java.awt.event.AdjustmentEvent;
-import java.awt.event.AdjustmentListener;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This is a AWT Container that carries an {@link AWTTerminal} with a scrollbar, effectively implementing a
- * pseudo-terminal with scrollback history. You can choose the same parameters are for {@link AWTTerminal}, they are
- * forwarded, this class mostly deals with linking the {@link AWTTerminal} with the scrollbar and having them update
- * each other.
- * @author Martin
- */
-@SuppressWarnings("serial")
-public class ScrollingAWTTerminal extends Container implements IOSafeTerminal {
-
- private final AWTTerminal awtTerminal;
- private final Scrollbar scrollBar;
-
- /**
- * Creates a new {@code ScrollingAWTTerminal} with all default options
- */
- public ScrollingAWTTerminal() {
- this(TerminalEmulatorDeviceConfiguration.getDefault(),
- SwingTerminalFontConfiguration.getDefault(),
- TerminalEmulatorColorConfiguration.getDefault());
- }
-
- /**
- * Creates a new {@code ScrollingAWTTerminal} with customizable settings.
- * @param deviceConfiguration How to configure the terminal virtual device
- * @param fontConfiguration What kind of fonts to use
- * @param colorConfiguration Which color schema to use for ANSI colors
- */
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
- public ScrollingAWTTerminal(
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration) {
-
- this.scrollBar = new Scrollbar(Scrollbar.VERTICAL);
- this.awtTerminal = new AWTTerminal(
- deviceConfiguration,
- fontConfiguration,
- colorConfiguration,
- new ScrollController());
-
- setLayout(new BorderLayout());
- add(awtTerminal, 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());
- }
-
- private class ScrollController implements TerminalScrollController {
- @Override
- public void updateModel(int totalSize, int screenSize) {
- if(scrollBar.getMaximum() != totalSize) {
- int lastMaximum = scrollBar.getMaximum();
- scrollBar.setMaximum(totalSize);
- if(lastMaximum < totalSize &&
- lastMaximum - scrollBar.getVisibleAmount() - scrollBar.getValue() == 0) {
- int adjustedValue = scrollBar.getValue() + (totalSize - lastMaximum);
- scrollBar.setValue(adjustedValue);
- }
- }
- if(scrollBar.getVisibleAmount() != screenSize) {
- if(scrollBar.getValue() + screenSize > scrollBar.getMaximum()) {
- scrollBar.setValue(scrollBar.getMaximum() - screenSize);
- }
- scrollBar.setVisibleAmount(screenSize);
- }
- }
-
- @Override
- public int getScrollingOffset() {
- return scrollBar.getMaximum() - scrollBar.getVisibleAmount() - scrollBar.getValue();
- }
- }
-
- private class ScrollbarListener implements AdjustmentListener {
- @Override
- public synchronized void adjustmentValueChanged(AdjustmentEvent e) {
- awtTerminal.repaint();
- }
- }
-
- ///////////
- // Delegate all Terminal interface implementations to SwingTerminal
- ///////////
- @Override
- public KeyStroke pollInput() {
- return awtTerminal.pollInput();
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- return awtTerminal.readInput();
- }
-
- @Override
- public void enterPrivateMode() {
- awtTerminal.enterPrivateMode();
- }
-
- @Override
- public void exitPrivateMode() {
- awtTerminal.exitPrivateMode();
- }
-
- @Override
- public void clearScreen() {
- awtTerminal.clearScreen();
- }
-
- @Override
- public void setCursorPosition(int x, int y) {
- awtTerminal.setCursorPosition(x, y);
- }
-
- @Override
- public void setCursorVisible(boolean visible) {
- awtTerminal.setCursorVisible(visible);
- }
-
- @Override
- public void putCharacter(char c) {
- awtTerminal.putCharacter(c);
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- return awtTerminal.newTextGraphics();
- }
-
- @Override
- public void enableSGR(SGR sgr) {
- awtTerminal.enableSGR(sgr);
- }
-
- @Override
- public void disableSGR(SGR sgr) {
- awtTerminal.disableSGR(sgr);
- }
-
- @Override
- public void resetColorAndSGR() {
- awtTerminal.resetColorAndSGR();
- }
-
- @Override
- public void setForegroundColor(TextColor color) {
- awtTerminal.setForegroundColor(color);
- }
-
- @Override
- public void setBackgroundColor(TextColor color) {
- awtTerminal.setBackgroundColor(color);
- }
-
- @Override
- public TerminalSize getTerminalSize() {
- return awtTerminal.getTerminalSize();
- }
-
- @Override
- public byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) {
- return awtTerminal.enquireTerminal(timeout, timeoutUnit);
- }
-
- @Override
- public void flush() {
- awtTerminal.flush();
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- awtTerminal.addResizeListener(listener);
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- awtTerminal.removeResizeListener(listener);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.terminal.IOSafeTerminal;
-import com.googlecode.lanterna.terminal.ResizeListener;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import java.awt.BorderLayout;
-import java.awt.event.AdjustmentEvent;
-import java.awt.event.AdjustmentListener;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import javax.swing.JComponent;
-import javax.swing.JScrollBar;
-
-/**
- * This is a Swing JComponent that carries a {@link SwingTerminal} with a scrollbar, effectively implementing a
- * pseudo-terminal with scrollback history. You can choose the same parameters are for {@link SwingTerminal}, they are
- * forwarded, this class mostly deals with linking the {@link SwingTerminal} with the scrollbar and having them update
- * each other.
- * @author Martin
- */
-@SuppressWarnings("serial")
-public class ScrollingSwingTerminal extends JComponent implements IOSafeTerminal {
-
- private final SwingTerminal swingTerminal;
- private final JScrollBar scrollBar;
-
- /**
- * Creates a new {@code ScrollingSwingTerminal} with all default options
- */
- public ScrollingSwingTerminal() {
- this(TerminalEmulatorDeviceConfiguration.getDefault(),
- SwingTerminalFontConfiguration.getDefault(),
- TerminalEmulatorColorConfiguration.getDefault());
- }
-
- /**
- * Creates a new {@code ScrollingSwingTerminal} with customizable settings.
- * @param deviceConfiguration How to configure the terminal virtual device
- * @param fontConfiguration What kind of fonts to use
- * @param colorConfiguration Which color schema to use for ANSI colors
- */
- @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());
- }
-
- private class ScrollController implements TerminalScrollController {
- @Override
- public void updateModel(int totalSize, int screenSize) {
- if(scrollBar.getMaximum() != totalSize) {
- int lastMaximum = scrollBar.getMaximum();
- scrollBar.setMaximum(totalSize);
- if(lastMaximum < totalSize &&
- lastMaximum - scrollBar.getVisibleAmount() - scrollBar.getValue() == 0) {
- int adjustedValue = scrollBar.getValue() + (totalSize - lastMaximum);
- scrollBar.setValue(adjustedValue);
- }
- }
- if(scrollBar.getVisibleAmount() != screenSize) {
- if(scrollBar.getValue() + screenSize > scrollBar.getMaximum()) {
- scrollBar.setValue(scrollBar.getMaximum() - screenSize);
- }
- scrollBar.setVisibleAmount(screenSize);
- }
- }
-
- @Override
- public int getScrollingOffset() {
- return scrollBar.getMaximum() - scrollBar.getVisibleAmount() - scrollBar.getValue();
- }
- }
-
- private class ScrollbarListener implements AdjustmentListener {
- @Override
- public synchronized void adjustmentValueChanged(AdjustmentEvent e) {
- swingTerminal.repaint();
- }
- }
-
- ///////////
- // Delegate all Terminal interface implementations to SwingTerminal
- ///////////
- @Override
- public KeyStroke pollInput() {
- return swingTerminal.pollInput();
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- 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 setCursorVisible(boolean visible) {
- swingTerminal.setCursorVisible(visible);
- }
-
- @Override
- public void putCharacter(char c) {
- swingTerminal.putCharacter(c);
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- 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 flush() {
- swingTerminal.flush();
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- swingTerminal.addResizeListener(listener);
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- swingTerminal.removeResizeListener(listener);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.*;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.terminal.IOSafeTerminal;
-import com.googlecode.lanterna.terminal.ResizeListener;
-
-import javax.swing.*;
-import java.awt.*;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This class provides an Swing implementation of the {@link com.googlecode.lanterna.terminal.Terminal} interface that
- * is an embeddable component you can put into a Swing container. The class has static helper methods for opening a new
- * frame with a {@link SwingTerminal} as its content, similar to how the SwingTerminal used to work in earlier versions
- * of lanterna. This version supports private mode and non-private mode with a scrollback history. You can customize
- * many of the properties by supplying device configuration, font configuration and color configuration when you
- * construct the object.
- * @author martin
- */
-public class SwingTerminal extends JComponent implements IOSafeTerminal {
-
- private final SwingTerminalImplementation terminalImplementation;
-
- /**
- * Creates a new SwingTerminal with all the defaults set and no scroll controller connected.
- */
- public SwingTerminal() {
- this(new TerminalScrollController.Null());
- }
-
-
- /**
- * Creates a new SwingTerminal with a particular scrolling controller that will be notified when the terminals
- * history size grows and will be called when this class needs to figure out the current scrolling position.
- * @param scrollController Controller for scrolling the terminal history
- */
- @SuppressWarnings("WeakerAccess")
- public SwingTerminal(TerminalScrollController scrollController) {
- this(TerminalEmulatorDeviceConfiguration.getDefault(),
- SwingTerminalFontConfiguration.getDefault(),
- TerminalEmulatorColorConfiguration.getDefault(),
- scrollController);
- }
-
- /**
- * Creates a new SwingTerminal component using custom settings and no scroll controller.
- * @param deviceConfiguration Device configuration to use for this SwingTerminal
- * @param fontConfiguration Font configuration to use for this SwingTerminal
- * @param colorConfiguration Color configuration to use for this SwingTerminal
- */
- public SwingTerminal(
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration) {
-
- this(null, deviceConfiguration, fontConfiguration, colorConfiguration);
- }
-
- /**
- * Creates a new SwingTerminal component using custom settings and no scroll controller.
- * @param initialTerminalSize Initial size of the terminal, which will be used when calculating the preferred size
- * of the component. If null, it will default to 80x25. If the AWT layout manager forces
- * the component to a different size, the value of this parameter won't have any meaning
- * @param deviceConfiguration Device configuration to use for this SwingTerminal
- * @param fontConfiguration Font configuration to use for this SwingTerminal
- * @param colorConfiguration Color configuration to use for this SwingTerminal
- */
- public SwingTerminal(
- TerminalSize initialTerminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration) {
-
- this(initialTerminalSize,
- deviceConfiguration,
- fontConfiguration,
- colorConfiguration,
- new TerminalScrollController.Null());
- }
-
- /**
- * Creates a new SwingTerminal component using custom settings and a custom scroll controller. The scrolling
- * controller will be notified when the terminal's history size grows and will be called when this class needs to
- * figure out the current scrolling position.
- * @param deviceConfiguration Device configuration to use for this SwingTerminal
- * @param fontConfiguration Font configuration to use for this SwingTerminal
- * @param colorConfiguration Color configuration to use for this SwingTerminal
- * @param scrollController Controller to use for scrolling, the object passed in will be notified whenever the
- * scrollable area has changed
- */
- public SwingTerminal(
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalScrollController scrollController) {
-
- this(null, deviceConfiguration, fontConfiguration, colorConfiguration, scrollController);
- }
-
-
-
- /**
- * Creates a new SwingTerminal component using custom settings and a custom scroll controller. The scrolling
- * controller will be notified when the terminal's history size grows and will be called when this class needs to
- * figure out the current scrolling position.
- * @param initialTerminalSize Initial size of the terminal, which will be used when calculating the preferred size
- * of the component. If null, it will default to 80x25. If the AWT layout manager forces
- * the component to a different size, the value of this parameter won't have any meaning
- * @param deviceConfiguration Device configuration to use for this SwingTerminal
- * @param fontConfiguration Font configuration to use for this SwingTerminal
- * @param colorConfiguration Color configuration to use for this SwingTerminal
- * @param scrollController Controller to use for scrolling, the object passed in will be notified whenever the
- * scrollable area has changed
- */
- public SwingTerminal(
- TerminalSize initialTerminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalScrollController scrollController) {
-
- //Enforce valid values on the input parameters
- if(deviceConfiguration == null) {
- deviceConfiguration = TerminalEmulatorDeviceConfiguration.getDefault();
- }
- if(fontConfiguration == null) {
- fontConfiguration = SwingTerminalFontConfiguration.getDefault();
- }
- if(colorConfiguration == null) {
- colorConfiguration = TerminalEmulatorColorConfiguration.getDefault();
- }
-
- terminalImplementation = new SwingTerminalImplementation(
- this,
- fontConfiguration,
- initialTerminalSize,
- deviceConfiguration,
- colorConfiguration,
- scrollController);
- }
-
- /**
- * Overridden method from Swing's {@code JComponent} class that returns the preferred size of the terminal (in
- * pixels)
- * @return The terminal's preferred size in pixels
- */
- @Override
- public synchronized Dimension getPreferredSize() {
- return terminalImplementation.getPreferredSize();
- }
-
- /**
- * Overridden method from Swing's {@code JComponent} class that is called by OS window system when the component
- * needs to be redrawn
- * @param {@code Graphics} object to use when drawing the component
- */
- @Override
- protected synchronized void paintComponent(Graphics componentGraphics) {
- terminalImplementation.paintComponent(componentGraphics);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Terminal methods below here, just forward to the implementation
-
- @Override
- public void enterPrivateMode() {
- terminalImplementation.enterPrivateMode();
- }
-
- @Override
- public void exitPrivateMode() {
- terminalImplementation.exitPrivateMode();
- }
-
- @Override
- public void clearScreen() {
- terminalImplementation.clearScreen();
- }
-
- @Override
- public void setCursorPosition(int x, int y) {
- terminalImplementation.setCursorPosition(x, y);
- }
-
- @Override
- public void setCursorVisible(boolean visible) {
- terminalImplementation.setCursorVisible(visible);
- }
-
- @Override
- public void putCharacter(char c) {
- terminalImplementation.putCharacter(c);
- }
-
- @Override
- public void enableSGR(SGR sgr) {
- terminalImplementation.enableSGR(sgr);
- }
-
- @Override
- public void disableSGR(SGR sgr) {
- terminalImplementation.disableSGR(sgr);
- }
-
- @Override
- public void resetColorAndSGR() {
- terminalImplementation.resetColorAndSGR();
- }
-
- @Override
- public void setForegroundColor(TextColor color) {
- terminalImplementation.setForegroundColor(color);
- }
-
- @Override
- public void setBackgroundColor(TextColor color) {
- terminalImplementation.setBackgroundColor(color);
- }
-
- @Override
- public TerminalSize getTerminalSize() {
- return terminalImplementation.getTerminalSize();
- }
-
- @Override
- public byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) {
- return terminalImplementation.enquireTerminal(timeout, timeoutUnit);
- }
-
- @Override
- public void flush() {
- terminalImplementation.flush();
- }
-
- @Override
- public KeyStroke pollInput() {
- return terminalImplementation.pollInput();
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- return terminalImplementation.readInput();
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- return terminalImplementation.newTextGraphics();
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- terminalImplementation.addResizeListener(listener);
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- terminalImplementation.removeResizeListener(listener);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal.swing;
-
-import java.awt.*;
-
-/**
- * Font configuration class for {@link SwingTerminal} that is extending from {@link AWTTerminalFontConfiguration}
- */
-public class SwingTerminalFontConfiguration extends AWTTerminalFontConfiguration {
- /**
- * This is the default font settings that will be used if you don't specify anything
- */
- public static SwingTerminalFontConfiguration getDefault() {
- return newInstance(filterMonospaced(selectDefaultFont()));
- }
-
- /**
- * Creates a new font configuration from a list of fonts in order of priority. This works by having the terminal
- * attempt to draw each character with the fonts in the order they are specified in and stop once we find a font
- * that can actually draw the character. For ASCII characters, it's very likely that the first font will always be
- * used.
- * @param fontsInOrderOfPriority Fonts to use when drawing text, in order of priority
- * @return Font configuration built from the font list
- */
- @SuppressWarnings("WeakerAccess")
- public static SwingTerminalFontConfiguration newInstance(Font... fontsInOrderOfPriority) {
- return new SwingTerminalFontConfiguration(true, BoldMode.EVERYTHING_BUT_SYMBOLS, fontsInOrderOfPriority);
- }
-
- /**
- * Creates a new font configuration from a list of fonts in order of priority. This works by having the terminal
- * attempt to draw each character with the fonts in the order they are specified in and stop once we find a font
- * that can actually draw the character. For ASCII characters, it's very likely that the first font will always be
- * used.
- * @param useAntiAliasing If {@code true} then anti-aliasing should be enabled when drawing text
- * @param boldMode Option to control what to do when drawing text with the bold SGR enabled
- * @param fontsInOrderOfPriority Fonts to use when drawing text, in order of priority
- */
- public SwingTerminalFontConfiguration(boolean useAntiAliasing, BoldMode boldMode, Font... fontsInOrderOfPriority) {
- super(useAntiAliasing, boldMode, fontsInOrderOfPriority);
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.SGR;
-import com.googlecode.lanterna.graphics.TextGraphics;
-import com.googlecode.lanterna.input.KeyStroke;
-import com.googlecode.lanterna.input.KeyType;
-import com.googlecode.lanterna.terminal.IOSafeTerminal;
-import com.googlecode.lanterna.terminal.ResizeListener;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextColor;
-
-import java.awt.*;
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-import javax.swing.*;
-
-/**
- * This class is similar to what SwingTerminal used to be before Lanterna 3.0; a JFrame that contains a terminal
- * emulator. In Lanterna 3, this class is just a JFrame containing a SwingTerminal component, but it also implements
- * the Terminal interface and delegates all calls to the internal SwingTerminal. You can tweak the class a bit to have
- * special behaviours when exiting private mode or when the user presses ESC key.
- * @author martin
- */
-@SuppressWarnings("serial")
-public class SwingTerminalFrame extends JFrame implements IOSafeTerminal {
- private final SwingTerminal swingTerminal;
- private TerminalEmulatorAutoCloseTrigger autoCloseTrigger;
- private boolean disposed;
-
- /**
- * Creates a new SwingTerminalFrame that doesn't automatically close.
- */
- public SwingTerminalFrame() throws HeadlessException {
- this(TerminalEmulatorAutoCloseTrigger.DoNotAutoClose);
- }
-
- /**
- * Creates a new SwingTerminalFrame with a specified auto-close behaviour
- * @param autoCloseTrigger What to trigger automatic disposal of the JFrame
- */
- @SuppressWarnings({"SameParameterValue", "WeakerAccess"})
- public SwingTerminalFrame(TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this("SwingTerminalFrame", autoCloseTrigger);
- }
-
- /**
- * Creates a new SwingTerminalFrame with a given title and no automatic closing.
- * @param title Title to use for the window
- */
- public SwingTerminalFrame(String title) throws HeadlessException {
- this(title, TerminalEmulatorAutoCloseTrigger.DoNotAutoClose);
- }
-
- /**
- * Creates a new SwingTerminalFrame with a specified auto-close behaviour and specific title
- * @param title Title to use for the window
- * @param autoCloseTrigger What to trigger automatic disposal of the JFrame
- */
- @SuppressWarnings("WeakerAccess")
- public SwingTerminalFrame(String title, TerminalEmulatorAutoCloseTrigger autoCloseTrigger) throws HeadlessException {
- this(title, new SwingTerminal(), autoCloseTrigger);
- }
-
- /**
- * Creates a new SwingTerminalFrame using a specified title and a series of swing terminal configuration objects
- * @param title What title to use for the window
- * @param deviceConfiguration Device configuration for the embedded SwingTerminal
- * @param fontConfiguration Font configuration for the embedded SwingTerminal
- * @param colorConfiguration Color configuration for the embedded SwingTerminal
- */
- public SwingTerminalFrame(String title,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration) {
- this(title, deviceConfiguration, fontConfiguration, colorConfiguration, TerminalEmulatorAutoCloseTrigger.DoNotAutoClose);
- }
-
- /**
- * Creates a new SwingTerminalFrame using a specified title and a series of swing terminal configuration objects
- * @param title What title to use for the window
- * @param deviceConfiguration Device configuration for the embedded SwingTerminal
- * @param fontConfiguration Font configuration for the embedded SwingTerminal
- * @param colorConfiguration Color configuration for the embedded SwingTerminal
- * @param autoCloseTrigger What to trigger automatic disposal of the JFrame
- */
- public SwingTerminalFrame(String title,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this(title, null, deviceConfiguration, fontConfiguration, colorConfiguration, autoCloseTrigger);
- }
-
- /**
- * Creates a new SwingTerminalFrame using a specified title and a series of swing terminal configuration objects
- * @param title What title to use for the window
- * @param terminalSize Initial size of the terminal, in rows and columns. If null, it will default to 80x25.
- * @param deviceConfiguration Device configuration for the embedded SwingTerminal
- * @param fontConfiguration Font configuration for the embedded SwingTerminal
- * @param colorConfiguration Color configuration for the embedded SwingTerminal
- * @param autoCloseTrigger What to trigger automatic disposal of the JFrame
- */
- public SwingTerminalFrame(String title,
- TerminalSize terminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this(title,
- new SwingTerminal(terminalSize, deviceConfiguration, fontConfiguration, colorConfiguration),
- autoCloseTrigger);
- }
-
- private SwingTerminalFrame(String title, SwingTerminal swingTerminal, TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- super(title != null ? title : "SwingTerminalFrame");
- this.swingTerminal = swingTerminal;
- this.autoCloseTrigger = autoCloseTrigger;
- this.disposed = false;
-
- getContentPane().setLayout(new BorderLayout());
- getContentPane().add(swingTerminal, BorderLayout.CENTER);
- setBackground(Color.BLACK); //This will reduce white flicker when resizing the window
- pack();
-
- //Put input focus on the terminal component by default
- swingTerminal.requestFocusInWindow();
- }
-
- /**
- * Returns the auto-close trigger used by the SwingTerminalFrame
- * @return Current auto-close trigger
- */
- public TerminalEmulatorAutoCloseTrigger getAutoCloseTrigger() {
- return autoCloseTrigger;
- }
-
- /**
- * Changes the current auto-close trigger used by this SwingTerminalFrame
- * @param autoCloseTrigger New auto-close trigger to use
- */
- public void setAutoCloseTrigger(TerminalEmulatorAutoCloseTrigger autoCloseTrigger) {
- this.autoCloseTrigger = autoCloseTrigger;
- }
-
- @Override
- public void dispose() {
- super.dispose();
- disposed = true;
- }
-
- ///////////
- // Delegate all Terminal interface implementations to SwingTerminal
- ///////////
- @Override
- public KeyStroke pollInput() {
- if(disposed) {
- return new KeyStroke(KeyType.EOF);
- }
- KeyStroke keyStroke = swingTerminal.pollInput();
- if(autoCloseTrigger == TerminalEmulatorAutoCloseTrigger.CloseOnEscape &&
- keyStroke != null &&
- keyStroke.getKeyType() == KeyType.Escape) {
- dispose();
- }
- return keyStroke;
- }
-
- @Override
- public KeyStroke readInput() throws IOException {
- return swingTerminal.readInput();
- }
-
- @Override
- public void enterPrivateMode() {
- swingTerminal.enterPrivateMode();
- }
-
- @Override
- public void exitPrivateMode() {
- swingTerminal.exitPrivateMode();
- if(autoCloseTrigger == TerminalEmulatorAutoCloseTrigger.CloseOnExitPrivateMode) {
- dispose();
- }
- }
-
- @Override
- public void clearScreen() {
- swingTerminal.clearScreen();
- }
-
- @Override
- public void setCursorPosition(int x, int y) {
- swingTerminal.setCursorPosition(x, y);
- }
-
- @Override
- public void setCursorVisible(boolean visible) {
- swingTerminal.setCursorVisible(visible);
- }
-
- @Override
- public void putCharacter(char c) {
- swingTerminal.putCharacter(c);
- }
-
- @Override
- public TextGraphics newTextGraphics() throws IOException {
- 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 flush() {
- swingTerminal.flush();
- }
-
- @Override
- public void addResizeListener(ResizeListener listener) {
- swingTerminal.addResizeListener(listener);
- }
-
- @Override
- public void removeResizeListener(ResizeListener listener) {
- swingTerminal.removeResizeListener(listener);
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.TextCharacter;
-
-import javax.swing.*;
-import java.awt.*;
-import java.awt.event.HierarchyEvent;
-import java.awt.event.HierarchyListener;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.io.IOException;
-import java.util.Collections;
-
-/**
- * Concrete implementation of {@link GraphicalTerminalImplementation} that adapts it to Swing
- */
-class SwingTerminalImplementation extends GraphicalTerminalImplementation {
-
- private final JComponent component;
- private final SwingTerminalFontConfiguration fontConfiguration;
-
- /**
- * Creates a new {@code SwingTerminalImplementation}
- * @param component JComponent that is the Swing terminal surface
- * @param fontConfiguration Font configuration to use
- * @param initialTerminalSize Initial size of the terminal
- * @param deviceConfiguration Device configuration
- * @param colorConfiguration Color configuration
- * @param scrollController Controller to be used when inspecting scroll status
- */
- SwingTerminalImplementation(
- JComponent component,
- SwingTerminalFontConfiguration fontConfiguration,
- TerminalSize initialTerminalSize,
- TerminalEmulatorDeviceConfiguration deviceConfiguration,
- TerminalEmulatorColorConfiguration colorConfiguration,
- TerminalScrollController scrollController) {
-
- super(initialTerminalSize, deviceConfiguration, colorConfiguration, scrollController);
- this.component = component;
- this.fontConfiguration = fontConfiguration;
-
- //Prevent us from shrinking beyond one character
- component.setMinimumSize(new Dimension(fontConfiguration.getFontWidth(), fontConfiguration.getFontHeight()));
-
- //noinspection unchecked
- component.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Collections.<AWTKeyStroke>emptySet());
- //noinspection unchecked
- component.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Collections.<AWTKeyStroke>emptySet());
-
- //Make sure the component is double-buffered to prevent flickering
- component.setDoubleBuffered(true);
-
- component.addKeyListener(new TerminalInputListener());
- component.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- SwingTerminalImplementation.this.component.requestFocusInWindow();
- }
- });
- component.addHierarchyListener(new HierarchyListener() {
- @Override
- public void hierarchyChanged(HierarchyEvent e) {
- if(e.getChangeFlags() == HierarchyEvent.DISPLAYABILITY_CHANGED) {
- if(e.getChanged().isDisplayable()) {
- startBlinkTimer();
- }
- else {
- stopBlinkTimer();
- }
- }
- }
- });
- }
-
-
- /**
- * Returns the current font configuration. Note that it is immutable and cannot be changed.
- * @return This SwingTerminal's current font configuration
- */
- public SwingTerminalFontConfiguration getFontConfiguration() {
- return fontConfiguration;
- }
-
- @Override
- protected int getFontHeight() {
- return fontConfiguration.getFontHeight();
- }
-
- @Override
- protected int getFontWidth() {
- return fontConfiguration.getFontWidth();
- }
-
- @Override
- protected int getHeight() {
- return component.getHeight();
- }
-
- @Override
- protected int getWidth() {
- return component.getWidth();
- }
-
- @Override
- protected Font getFontForCharacter(TextCharacter character) {
- return fontConfiguration.getFontForCharacter(character);
- }
-
- @Override
- protected boolean isTextAntiAliased() {
- return fontConfiguration.isAntiAliased();
- }
-
- @Override
- protected void repaint() {
- if(SwingUtilities.isEventDispatchThread()) {
- component.repaint();
- }
- else {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- component.repaint();
- }
- });
- }
- }
-
- @Override
- public com.googlecode.lanterna.input.KeyStroke readInput() throws IOException {
- if(SwingUtilities.isEventDispatchThread()) {
- throw new UnsupportedOperationException("Cannot call SwingTerminal.readInput() on the AWT thread");
- }
- return super.readInput();
- }
-}
+++ /dev/null
-package com.googlecode.lanterna.terminal.swing;
-
-/**
- * This enum stored various ways the AWTTerminalFrame and SwingTerminalFrame can automatically close (hide and dispose)
- * themselves when a certain condition happens. By default, auto-close is not active.
- */
-public enum TerminalEmulatorAutoCloseTrigger {
- /**
- * Auto-close disabled
- */
- DoNotAutoClose,
- /**
- * Close the frame when exiting from private mode
- */
- CloseOnExitPrivateMode,
- /**
- * Close if the user presses ESC key on the keyboard
- */
- CloseOnEscape,
- ;
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.TextColor;
-import java.awt.Color;
-
-/**
- * Color configuration settings to be using with SwingTerminal. This class contains color-related settings that is used
- * by SwingTerminal when it renders the component.
- * @author martin
- */
-public class TerminalEmulatorColorConfiguration {
-
- /**
- * This is the default settings that is used when you create a new SwingTerminal without specifying any color
- * configuration. It will use classic VGA colors for the ANSI palette and bright colors on bold text.
- */
- public static TerminalEmulatorColorConfiguration getDefault() {
- return newInstance(TerminalEmulatorPalette.STANDARD_VGA);
- }
-
- /**
- * Creates a new color configuration based on a particular palette and with using brighter colors on bold text.
- * @param colorPalette Palette to use for this color configuration
- * @return The resulting color configuration
- */
- @SuppressWarnings("SameParameterValue")
- public static TerminalEmulatorColorConfiguration newInstance(TerminalEmulatorPalette colorPalette) {
- return new TerminalEmulatorColorConfiguration(colorPalette, true);
- }
-
- private final TerminalEmulatorPalette colorPalette;
- private final boolean useBrightColorsOnBold;
-
- private TerminalEmulatorColorConfiguration(TerminalEmulatorPalette colorPalette, boolean useBrightColorsOnBold) {
- this.colorPalette = colorPalette;
- this.useBrightColorsOnBold = useBrightColorsOnBold;
- }
-
- /**
- * Given a TextColor and a hint as to if the color is to be used as foreground or not and if we currently have
- * bold text enabled or not, it returns the closest AWT color that matches this.
- * @param color What text color to convert
- * @param isForeground Is the color intended to be used as foreground color
- * @param inBoldContext Is the color intended to be used for on a character this is bold
- * @return The AWT color that represents this text color
- */
- public Color toAWTColor(TextColor color, boolean isForeground, boolean inBoldContext) {
- if(color instanceof TextColor.ANSI) {
- return colorPalette.get((TextColor.ANSI)color, isForeground, inBoldContext && useBrightColorsOnBold);
- }
- return color.toColor();
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.TextColor;
-
-/**
- * Object that encapsulates the configuration parameters for the terminal 'device' that a SwingTerminal is emulating.
- * This includes properties such as the shape of the cursor, the color of the cursor, how large scrollback is available
- * and if the cursor should blink or not.
- * @author martin
- */
-public class TerminalEmulatorDeviceConfiguration {
-
- /**
- * This is a static reference to the default terminal device configuration. Use this one if you are unsure.
- */
- public static TerminalEmulatorDeviceConfiguration getDefault() {
- return new TerminalEmulatorDeviceConfiguration();
- }
-
- private final int lineBufferScrollbackSize;
- private final int blinkLengthInMilliSeconds;
- private final CursorStyle cursorStyle;
- private final TextColor cursorColor;
- private final boolean cursorBlinking;
-
- /**
- * Creates a new terminal device configuration object with all the defaults set
- */
- @SuppressWarnings("WeakerAccess")
- public TerminalEmulatorDeviceConfiguration() {
- this(2000, 500, CursorStyle.REVERSED, new TextColor.RGB(255, 255, 255), false);
- }
-
- /**
- * Creates a new terminal device configuration object with all configurable values specified.
- * @param lineBufferScrollbackSize How many lines of scrollback buffer should the terminal save?
- * @param blinkLengthInMilliSeconds How many milliseconds does a 'blink' last
- * @param cursorStyle Style of the terminal text cursor
- * @param cursorColor Color of the terminal text cursor
- * @param cursorBlinking Should the terminal text cursor blink?
- */
- @SuppressWarnings("WeakerAccess")
- public TerminalEmulatorDeviceConfiguration(int lineBufferScrollbackSize, int blinkLengthInMilliSeconds, CursorStyle cursorStyle, TextColor cursorColor, boolean cursorBlinking) {
- this.lineBufferScrollbackSize = lineBufferScrollbackSize;
- this.blinkLengthInMilliSeconds = blinkLengthInMilliSeconds;
- this.cursorStyle = cursorStyle;
- this.cursorColor = cursorColor;
- this.cursorBlinking = cursorBlinking;
- }
-
- /**
- * Returns the length of a 'blink', which is the interval time a character with the blink SGR enabled with be drawn
- * with foreground color and background color set to the same.
- * @return Milliseconds of a blink interval
- */
- public int getBlinkLengthInMilliSeconds() {
- return blinkLengthInMilliSeconds;
- }
-
- /**
- * How many lines of history should be saved so the user can scroll back to them?
- * @return Number of lines in the scrollback buffer
- */
- public int getLineBufferScrollbackSize() {
- return lineBufferScrollbackSize;
- }
-
- /**
- * Style the text cursor should take
- * @return Text cursor style
- * @see TerminalEmulatorDeviceConfiguration.CursorStyle
- */
- public CursorStyle getCursorStyle() {
- return cursorStyle;
- }
-
- /**
- * What color to draw the text cursor color in
- * @return Color of the text cursor
- */
- public TextColor getCursorColor() {
- return cursorColor;
- }
-
- /**
- * Should the text cursor be blinking
- * @return {@code true} if the text cursor should be blinking
- */
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- public boolean isCursorBlinking() {
- return cursorBlinking;
- }
-
- /**
- * Returns a copy of this device configuration but with a different size of the scrollback buffer
- * @param lineBufferScrollbackSize Size of the scrollback buffer (in number of lines) the copy should have
- * @return Copy of this device configuration with a specified size for the scrollback buffer
- */
- public TerminalEmulatorDeviceConfiguration withLineBufferScrollbackSize(int lineBufferScrollbackSize) {
- if(this.lineBufferScrollbackSize == lineBufferScrollbackSize) {
- return this;
- }
- else {
- return new TerminalEmulatorDeviceConfiguration(
- lineBufferScrollbackSize,
- blinkLengthInMilliSeconds,
- cursorStyle,
- cursorColor,
- cursorBlinking);
- }
- }
-
- /**
- * Different cursor styles supported by SwingTerminal
- */
- public enum CursorStyle {
- /**
- * The cursor is drawn by inverting the front- and background colors of the cursor position
- */
- REVERSED,
- /**
- * The cursor is drawn by using the cursor color as the background color for the character at the cursor position
- */
- FIXED_BACKGROUND,
- /**
- * The cursor is rendered as a thick horizontal line at the bottom of the character
- */
- UNDER_BAR,
- /**
- * The cursor is rendered as a left-side aligned vertical line
- */
- VERTICAL_BAR,
- ;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.TextColor;
-import java.awt.Color;
-
-/**
- * This class specifies the palette of colors the terminal will use for the normally available 8 + 1 ANSI colors but
- * also their 'bright' versions with are normally enabled through bold mode. There are several palettes available, all
- * based on popular terminal emulators. All colors are defined in the AWT format.
- * @author Martin
- */
-@SuppressWarnings("WeakerAccess")
-public class TerminalEmulatorPalette {
- /**
- * Values taken from gnome-terminal on Ubuntu
- */
- public static final TerminalEmulatorPalette GNOME_TERMINAL =
- new TerminalEmulatorPalette(
- new java.awt.Color(211, 215, 207),
- new java.awt.Color(238, 238, 236),
- new java.awt.Color(46, 52, 54),
- new java.awt.Color(46, 52, 54),
- new java.awt.Color(85, 87, 83),
- new java.awt.Color(204, 0, 0),
- new java.awt.Color(239, 41, 41),
- new java.awt.Color(78, 154, 6),
- new java.awt.Color(138, 226, 52),
- new java.awt.Color(196, 160, 0),
- new java.awt.Color(252, 233, 79),
- new java.awt.Color(52, 101, 164),
- new java.awt.Color(114, 159, 207),
- new java.awt.Color(117, 80, 123),
- new java.awt.Color(173, 127, 168),
- new java.awt.Color(6, 152, 154),
- new java.awt.Color(52, 226, 226),
- new java.awt.Color(211, 215, 207),
- new java.awt.Color(238, 238, 236));
-
- /**
- * Values taken from <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">
- * wikipedia</a>, these are supposed to be the standard VGA palette.
- */
- public static final TerminalEmulatorPalette STANDARD_VGA =
- new TerminalEmulatorPalette(
- new java.awt.Color(170, 170, 170),
- new java.awt.Color(255, 255, 255),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(85, 85, 85),
- new java.awt.Color(170, 0, 0),
- new java.awt.Color(255, 85, 85),
- new java.awt.Color(0, 170, 0),
- new java.awt.Color(85, 255, 85),
- new java.awt.Color(170, 85, 0),
- new java.awt.Color(255, 255, 85),
- new java.awt.Color(0, 0, 170),
- new java.awt.Color(85, 85, 255),
- new java.awt.Color(170, 0, 170),
- new java.awt.Color(255, 85, 255),
- new java.awt.Color(0, 170, 170),
- new java.awt.Color(85, 255, 255),
- new java.awt.Color(170, 170, 170),
- new java.awt.Color(255, 255, 255));
-
- /**
- * Values taken from <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">
- * wikipedia</a>, these are supposed to be what Windows XP cmd is using.
- */
- public static final TerminalEmulatorPalette WINDOWS_XP_COMMAND_PROMPT =
- new TerminalEmulatorPalette(
- new java.awt.Color(192, 192, 192),
- new java.awt.Color(255, 255, 255),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(128, 128, 128),
- new java.awt.Color(128, 0, 0),
- new java.awt.Color(255, 0, 0),
- new java.awt.Color(0, 128, 0),
- new java.awt.Color(0, 255, 0),
- new java.awt.Color(128, 128, 0),
- new java.awt.Color(255, 255, 0),
- new java.awt.Color(0, 0, 128),
- new java.awt.Color(0, 0, 255),
- new java.awt.Color(128, 0, 128),
- new java.awt.Color(255, 0, 255),
- new java.awt.Color(0, 128, 128),
- new java.awt.Color(0, 255, 255),
- new java.awt.Color(192, 192, 192),
- new java.awt.Color(255, 255, 255));
-
- /**
- * Values taken from <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">
- * wikipedia</a>, these are supposed to be what terminal.app on MacOSX is using.
- */
- public static final TerminalEmulatorPalette MAC_OS_X_TERMINAL_APP =
- new TerminalEmulatorPalette(
- new java.awt.Color(203, 204, 205),
- new java.awt.Color(233, 235, 235),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(129, 131, 131),
- new java.awt.Color(194, 54, 33),
- new java.awt.Color(252,57,31),
- new java.awt.Color(37, 188, 36),
- new java.awt.Color(49, 231, 34),
- new java.awt.Color(173, 173, 39),
- new java.awt.Color(234, 236, 35),
- new java.awt.Color(73, 46, 225),
- new java.awt.Color(88, 51, 255),
- new java.awt.Color(211, 56, 211),
- new java.awt.Color(249, 53, 248),
- new java.awt.Color(51, 187, 200),
- new java.awt.Color(20, 240, 240),
- new java.awt.Color(203, 204, 205),
- new java.awt.Color(233, 235, 235));
-
- /**
- * Values taken from <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">
- * wikipedia</a>, these are supposed to be what putty is using.
- */
- public static final TerminalEmulatorPalette PUTTY =
- new TerminalEmulatorPalette(
- new java.awt.Color(187, 187, 187),
- new java.awt.Color(255, 255, 255),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(85, 85, 85),
- new java.awt.Color(187, 0, 0),
- new java.awt.Color(255, 85, 85),
- new java.awt.Color(0, 187, 0),
- new java.awt.Color(85, 255, 85),
- new java.awt.Color(187, 187, 0),
- new java.awt.Color(255, 255, 85),
- new java.awt.Color(0, 0, 187),
- new java.awt.Color(85, 85, 255),
- new java.awt.Color(187, 0, 187),
- new java.awt.Color(255, 85, 255),
- new java.awt.Color(0, 187, 187),
- new java.awt.Color(85, 255, 255),
- new java.awt.Color(187, 187, 187),
- new java.awt.Color(255, 255, 255));
-
- /**
- * Values taken from <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">
- * wikipedia</a>, these are supposed to be what xterm is using.
- */
- public static final TerminalEmulatorPalette XTERM =
- new TerminalEmulatorPalette(
- new java.awt.Color(229, 229, 229),
- new java.awt.Color(255, 255, 255),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(0, 0, 0),
- new java.awt.Color(127, 127, 127),
- new java.awt.Color(205, 0, 0),
- new java.awt.Color(255, 0, 0),
- new java.awt.Color(0, 205, 0),
- new java.awt.Color(0, 255, 0),
- new java.awt.Color(205, 205, 0),
- new java.awt.Color(255, 255, 0),
- new java.awt.Color(0, 0, 238),
- new java.awt.Color(92, 92, 255),
- new java.awt.Color(205, 0, 205),
- new java.awt.Color(255, 0, 255),
- new java.awt.Color(0, 205, 205),
- new java.awt.Color(0, 255, 255),
- new java.awt.Color(229, 229, 229),
- new java.awt.Color(255, 255, 255));
-
- /**
- * Default colors the SwingTerminal is using if you don't specify anything
- */
- public static final TerminalEmulatorPalette DEFAULT = GNOME_TERMINAL;
-
- private final Color defaultColor;
- private final Color defaultBrightColor;
- private final Color defaultBackgroundColor;
- private final Color normalBlack;
- private final Color brightBlack;
- private final Color normalRed;
- private final Color brightRed;
- private final Color normalGreen;
- private final Color brightGreen;
- private final Color normalYellow;
- private final Color brightYellow;
- private final Color normalBlue;
- private final Color brightBlue;
- private final Color normalMagenta;
- private final Color brightMagenta;
- private final Color normalCyan;
- private final Color brightCyan;
- private final Color normalWhite;
- private final Color brightWhite;
-
- /**
- * Creates a new palette with all colors specified up-front
- * @param defaultColor Default color which no specific color has been selected
- * @param defaultBrightColor Default color which no specific color has been selected but bold is enabled
- * @param defaultBackgroundColor Default color to use for the background when no specific color has been selected
- * @param normalBlack Color for normal black
- * @param brightBlack Color for bright black
- * @param normalRed Color for normal red
- * @param brightRed Color for bright red
- * @param normalGreen Color for normal green
- * @param brightGreen Color for bright green
- * @param normalYellow Color for normal yellow
- * @param brightYellow Color for bright yellow
- * @param normalBlue Color for normal blue
- * @param brightBlue Color for bright blue
- * @param normalMagenta Color for normal magenta
- * @param brightMagenta Color for bright magenta
- * @param normalCyan Color for normal cyan
- * @param brightCyan Color for bright cyan
- * @param normalWhite Color for normal white
- * @param brightWhite Color for bright white
- */
- public TerminalEmulatorPalette(
- Color defaultColor,
- Color defaultBrightColor,
- Color defaultBackgroundColor,
- Color normalBlack,
- Color brightBlack,
- Color normalRed,
- Color brightRed,
- Color normalGreen,
- Color brightGreen,
- Color normalYellow,
- Color brightYellow,
- Color normalBlue,
- Color brightBlue,
- Color normalMagenta,
- Color brightMagenta,
- Color normalCyan,
- Color brightCyan,
- Color normalWhite,
- Color brightWhite) {
- this.defaultColor = defaultColor;
- this.defaultBrightColor = defaultBrightColor;
- this.defaultBackgroundColor = defaultBackgroundColor;
- this.normalBlack = normalBlack;
- this.brightBlack = brightBlack;
- this.normalRed = normalRed;
- this.brightRed = brightRed;
- this.normalGreen = normalGreen;
- this.brightGreen = brightGreen;
- this.normalYellow = normalYellow;
- this.brightYellow = brightYellow;
- this.normalBlue = normalBlue;
- this.brightBlue = brightBlue;
- this.normalMagenta = normalMagenta;
- this.brightMagenta = brightMagenta;
- this.normalCyan = normalCyan;
- this.brightCyan = brightCyan;
- this.normalWhite = normalWhite;
- this.brightWhite = brightWhite;
- }
-
- /**
- * Returns the AWT color from this palette given an ANSI color and two hints for if we are looking for a background
- * color and if we want to use the bright version.
- * @param color Which ANSI color we want to extract
- * @param isForeground Is this color we extract going to be used as a background color?
- * @param useBrightTones If true, we should return the bright version of the color
- * @return AWT color extracted from this palette for the input parameters
- */
- public Color get(TextColor.ANSI color, boolean isForeground, boolean useBrightTones) {
- if(useBrightTones) {
- switch(color) {
- case BLACK:
- return brightBlack;
- case BLUE:
- return brightBlue;
- case CYAN:
- return brightCyan;
- case DEFAULT:
- return isForeground ? defaultBrightColor : defaultBackgroundColor;
- case GREEN:
- return brightGreen;
- case MAGENTA:
- return brightMagenta;
- case RED:
- return brightRed;
- case WHITE:
- return brightWhite;
- case YELLOW:
- return brightYellow;
- }
- }
- else {
- switch(color) {
- case BLACK:
- return normalBlack;
- case BLUE:
- return normalBlue;
- case CYAN:
- return normalCyan;
- case DEFAULT:
- return isForeground ? defaultColor : defaultBackgroundColor;
- case GREEN:
- return normalGreen;
- case MAGENTA:
- return normalMagenta;
- case RED:
- return normalRed;
- case WHITE:
- return normalWhite;
- case YELLOW:
- return normalYellow;
- }
- }
- throw new IllegalArgumentException("Unknown text color " + color);
- }
-
- @SuppressWarnings({"SimplifiableIfStatement", "ConstantConditions"})
- @Override
- public boolean equals(Object obj) {
- if(obj == null) {
- return false;
- }
- if(getClass() != obj.getClass()) {
- return false;
- }
- final TerminalEmulatorPalette other = (TerminalEmulatorPalette) obj;
- if(this.defaultColor != other.defaultColor && (this.defaultColor == null || !this.defaultColor.equals(other.defaultColor))) {
- return false;
- }
- if(this.defaultBrightColor != other.defaultBrightColor && (this.defaultBrightColor == null || !this.defaultBrightColor.equals(other.defaultBrightColor))) {
- return false;
- }
- if(this.defaultBackgroundColor != other.defaultBackgroundColor && (this.defaultBackgroundColor == null || !this.defaultBackgroundColor.equals(other.defaultBackgroundColor))) {
- return false;
- }
- if(this.normalBlack != other.normalBlack && (this.normalBlack == null || !this.normalBlack.equals(other.normalBlack))) {
- return false;
- }
- if(this.brightBlack != other.brightBlack && (this.brightBlack == null || !this.brightBlack.equals(other.brightBlack))) {
- return false;
- }
- if(this.normalRed != other.normalRed && (this.normalRed == null || !this.normalRed.equals(other.normalRed))) {
- return false;
- }
- if(this.brightRed != other.brightRed && (this.brightRed == null || !this.brightRed.equals(other.brightRed))) {
- return false;
- }
- if(this.normalGreen != other.normalGreen && (this.normalGreen == null || !this.normalGreen.equals(other.normalGreen))) {
- return false;
- }
- if(this.brightGreen != other.brightGreen && (this.brightGreen == null || !this.brightGreen.equals(other.brightGreen))) {
- return false;
- }
- if(this.normalYellow != other.normalYellow && (this.normalYellow == null || !this.normalYellow.equals(other.normalYellow))) {
- return false;
- }
- if(this.brightYellow != other.brightYellow && (this.brightYellow == null || !this.brightYellow.equals(other.brightYellow))) {
- return false;
- }
- if(this.normalBlue != other.normalBlue && (this.normalBlue == null || !this.normalBlue.equals(other.normalBlue))) {
- return false;
- }
- if(this.brightBlue != other.brightBlue && (this.brightBlue == null || !this.brightBlue.equals(other.brightBlue))) {
- return false;
- }
- if(this.normalMagenta != other.normalMagenta && (this.normalMagenta == null || !this.normalMagenta.equals(other.normalMagenta))) {
- return false;
- }
- if(this.brightMagenta != other.brightMagenta && (this.brightMagenta == null || !this.brightMagenta.equals(other.brightMagenta))) {
- return false;
- }
- if(this.normalCyan != other.normalCyan && (this.normalCyan == null || !this.normalCyan.equals(other.normalCyan))) {
- return false;
- }
- if(this.brightCyan != other.brightCyan && (this.brightCyan == null || !this.brightCyan.equals(other.brightCyan))) {
- return false;
- }
- if(this.normalWhite != other.normalWhite && (this.normalWhite == null || !this.normalWhite.equals(other.normalWhite))) {
- return false;
- }
- return !(this.brightWhite != other.brightWhite && (this.brightWhite == null || !this.brightWhite.equals(other.brightWhite)));
- }
-
- @SuppressWarnings("ConstantConditions")
- @Override
- public int hashCode() {
- int hash = 5;
- hash = 47 * hash + (this.defaultColor != null ? this.defaultColor.hashCode() : 0);
- hash = 47 * hash + (this.defaultBrightColor != null ? this.defaultBrightColor.hashCode() : 0);
- hash = 47 * hash + (this.defaultBackgroundColor != null ? this.defaultBackgroundColor.hashCode() : 0);
- hash = 47 * hash + (this.normalBlack != null ? this.normalBlack.hashCode() : 0);
- hash = 47 * hash + (this.brightBlack != null ? this.brightBlack.hashCode() : 0);
- hash = 47 * hash + (this.normalRed != null ? this.normalRed.hashCode() : 0);
- hash = 47 * hash + (this.brightRed != null ? this.brightRed.hashCode() : 0);
- hash = 47 * hash + (this.normalGreen != null ? this.normalGreen.hashCode() : 0);
- hash = 47 * hash + (this.brightGreen != null ? this.brightGreen.hashCode() : 0);
- hash = 47 * hash + (this.normalYellow != null ? this.normalYellow.hashCode() : 0);
- hash = 47 * hash + (this.brightYellow != null ? this.brightYellow.hashCode() : 0);
- hash = 47 * hash + (this.normalBlue != null ? this.normalBlue.hashCode() : 0);
- hash = 47 * hash + (this.brightBlue != null ? this.brightBlue.hashCode() : 0);
- hash = 47 * hash + (this.normalMagenta != null ? this.normalMagenta.hashCode() : 0);
- hash = 47 * hash + (this.brightMagenta != null ? this.brightMagenta.hashCode() : 0);
- hash = 47 * hash + (this.normalCyan != null ? this.normalCyan.hashCode() : 0);
- hash = 47 * hash + (this.brightCyan != null ? this.brightCyan.hashCode() : 0);
- hash = 47 * hash + (this.normalWhite != null ? this.normalWhite.hashCode() : 0);
- hash = 47 * hash + (this.brightWhite != null ? this.brightWhite.hashCode() : 0);
- return hash;
- }
-
- @Override
- public String toString() {
- return "SwingTerminalPalette{" +
- "defaultColor=" + defaultColor +
- ", defaultBrightColor=" + defaultBrightColor +
- ", defaultBackgroundColor=" + defaultBackgroundColor +
- ", normalBlack=" + normalBlack +
- ", brightBlack=" + brightBlack +
- ", normalRed=" + normalRed +
- ", brightRed=" + brightRed +
- ", normalGreen=" + normalGreen +
- ", brightGreen=" + brightGreen +
- ", normalYellow=" + normalYellow +
- ", brightYellow=" + brightYellow +
- ", normalBlue=" + normalBlue +
- ", brightBlue=" + brightBlue +
- ", normalMagenta=" + normalMagenta +
- ", brightMagenta=" + brightMagenta +
- ", normalCyan=" + normalCyan +
- ", brightCyan=" + brightCyan +
- ", normalWhite=" + normalWhite +
- ", brightWhite=" + brightWhite + '}';
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-/**
- * This interface can be used to control the backlog scrolling of a SwingTerminal. It's used as a callback by the
- * {@code SwingTerminal} when it needs to fetch the scroll position and also used whenever the backlog changes to that
- * some view class, like a scrollbar for example, can update its view accordingly.
- * @author Martin
- */
-public interface TerminalScrollController {
- /**
- * Called by the SwingTerminal when the terminal has changed or more lines are entered into the terminal
- * @param totalSize Total number of lines in the backlog currently
- * @param screenSize Number of lines covered by the terminal window at its current size
- */
- void updateModel(int totalSize, int screenSize);
-
- /**
- * Called by the SwingTerminal to know the 'offset' into the backlog. Returning 0 here will always draw the latest
- * lines; if you return 5, it will draw from five lines into the backlog and skip the 5 most recent lines.
- * @return According to this scroll controller, how far back into the backlog are we?
- */
- int getScrollingOffset();
-
- /**
- * Implementation of {@link TerminalScrollController} that does nothing
- */
- final class Null implements TerminalScrollController {
- @Override
- public void updateModel(int totalSize, int screenSize) {
- }
-
- @Override
- public int getScrollingOffset() {
- return 0;
- }
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * Contains an entire text buffer used by Swing terminal
- * @author martin
- */
-class TextBuffer {
- private final int backlog;
- private final LinkedList<List<TextCharacter>> lineBuffer;
- private final TextCharacter fillCharacter;
-
- TextBuffer(int backlog) {
- this.backlog = backlog;
- this.lineBuffer = new LinkedList<List<TextCharacter>>();
- this.fillCharacter = TextCharacter.DEFAULT_CHARACTER;
-
- //Initialize the content to one line
- newLine();
- }
-
- void clear() {
- lineBuffer.clear();
- newLine();
- }
-
- void newLine() {
- ArrayList<TextCharacter> line = new ArrayList<TextCharacter>(200);
- line.add(fillCharacter);
- lineBuffer.addFirst(line);
- }
-
-
- Iterable<List<TextCharacter>> getVisibleLines(final int visibleRows, final int scrollOffset) {
- final int length = Math.min(visibleRows, lineBuffer.size());
- return new Iterable<List<TextCharacter>>() {
- @Override
- public Iterator<List<TextCharacter>> iterator() {
- return new Iterator<List<TextCharacter>>() {
- private final ListIterator<List<TextCharacter>> listIterator = lineBuffer.subList(scrollOffset, scrollOffset + length).listIterator(length);
- @Override
- public boolean hasNext() { return listIterator.hasPrevious(); }
- @Override
- public List<TextCharacter> next() { return listIterator.previous(); }
- @Override
- public void remove() { listIterator.remove(); }
- };
- }
- };
- }
-
- int getNumberOfLines() {
- return lineBuffer.size();
- }
-
- void trimBacklog(int terminalHeight) {
- while(lineBuffer.size() - terminalHeight > backlog) {
- lineBuffer.removeLast();
- }
- }
-
- void ensurePosition(TerminalSize terminalSize, TerminalPosition position) {
- getLine(terminalSize, position);
- }
-
- public TextCharacter getCharacter(TerminalSize terminalSize, TerminalPosition position) {
- return getLine(terminalSize, position).get(position.getColumn());
- }
-
- void setCharacter(TerminalSize terminalSize, TerminalPosition currentPosition, TextCharacter terminalCharacter) {
- List<TextCharacter> line = getLine(terminalSize, currentPosition);
-
- //If we are replacing a CJK character with a non-CJK character, make the following character empty
- if(TerminalTextUtils.isCharCJK(line.get(currentPosition.getColumn()).getCharacter()) &&
- !TerminalTextUtils.isCharCJK(terminalCharacter.getCharacter())) {
- line.set(currentPosition.getColumn() + 1, terminalCharacter.withCharacter(' '));
- }
-
- //Set the character in the buffer
- line.set(currentPosition.getColumn(), terminalCharacter);
-
- //Pad CJK character with a trailing space
- if(TerminalTextUtils.isCharCJK(terminalCharacter.getCharacter()) && currentPosition.getColumn() + 1 < line.size()) {
- ensurePosition(terminalSize, currentPosition.withRelativeColumn(1));
- line.set(currentPosition.getColumn() + 1, terminalCharacter.withCharacter(' '));
- }
- //If there's a CJK character immediately to our left, reset it
- if(currentPosition.getColumn() > 0 && TerminalTextUtils.isCharCJK(line.get(currentPosition.getColumn() - 1).getCharacter())) {
- line.set(currentPosition.getColumn() - 1, line.get(currentPosition.getColumn() - 1).withCharacter(' '));
- }
- }
-
- private List<TextCharacter> getLine(TerminalSize terminalSize, TerminalPosition position) {
- while(position.getRow() >= lineBuffer.size()) {
- newLine();
- }
- int lineIndex = Math.min(terminalSize.getRows(), lineBuffer.size()) - 1 - position.getRow();
- List<TextCharacter> line = lineBuffer.get(lineIndex);
- while(line.size() <= position.getColumn()) {
- line.add(fillCharacter);
- }
- return line;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.TerminalTextUtils;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.screen.TabBehaviour;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Contains the internal state of the Swing terminal
- * @author martin
- */
-class VirtualTerminal {
-
- private final TextBuffer mainTextBuffer;
- private final TextBuffer privateModeTextBuffer;
- private final TerminalScrollController terminalScrollController;
-
- private TextBuffer currentBuffer;
- private TerminalSize size;
- private TerminalPosition cursorPosition;
-
- //To avoid adding more synchronization and locking, we'll store a copy of all visible lines in this list. This is
- //also the list we return (as an iterable) so it may not be reliable as each call to getLines will change it. This
- //isn't 100% safe but hopefully a good trade-off
- private final List<List<TextCharacter>> visibleLinesBuffer;
-
- VirtualTerminal(
- int backlog,
- TerminalSize initialSize,
- TerminalScrollController scrollController) {
-
- this.mainTextBuffer = new TextBuffer(backlog);
- this.privateModeTextBuffer = new TextBuffer(0);
- this.terminalScrollController = scrollController;
-
- this.currentBuffer = mainTextBuffer;
- this.size = initialSize;
- this.cursorPosition = TerminalPosition.TOP_LEFT_CORNER;
-
- this.visibleLinesBuffer = new ArrayList<List<TextCharacter>>(120);
- }
-
- void resize(TerminalSize newSize) {
- if(size.getRows() < newSize.getRows()) {
- cursorPosition = cursorPosition.withRelativeRow(newSize.getRows() - size.getRows());
- }
- this.size = newSize;
- updateScrollingController();
- correctCursor();
- }
-
- private void updateScrollingController() {
- int totalSize = Math.max(currentBuffer.getNumberOfLines(), size.getRows());
- int visibleSize = size.getRows();
- this.terminalScrollController.updateModel(totalSize, visibleSize);
- }
-
- TerminalSize getSize() {
- return size;
- }
-
- synchronized void setCursorPosition(TerminalPosition cursorPosition) {
- //Make sure the cursor position is within the bounds
- cursorPosition = cursorPosition.withColumn(
- Math.min(Math.max(cursorPosition.getColumn(), 0), size.getColumns() - 1));
- cursorPosition = cursorPosition.withRow(
- Math.min(Math.max(cursorPosition.getRow(), 0), size.getRows() - 1));
-
- currentBuffer.ensurePosition(size, cursorPosition);
- this.cursorPosition = cursorPosition;
- correctCursor();
- }
-
- TerminalPosition getTranslatedCursorPosition() {
- return cursorPosition.withRelativeRow(terminalScrollController.getScrollingOffset());
- }
-
- private void correctCursor() {
- this.cursorPosition =
- new TerminalPosition(
- Math.min(cursorPosition.getColumn(), size.getColumns() - 1),
- Math.min(cursorPosition.getRow(), size.getRows() - 1));
- this.cursorPosition =
- new TerminalPosition(
- Math.max(cursorPosition.getColumn(), 0),
- Math.max(cursorPosition.getRow(), 0));
- }
-
- synchronized TextCharacter getCharacter(TerminalPosition position) {
- return currentBuffer.getCharacter(size, position);
- }
-
- synchronized void putCharacter(TextCharacter terminalCharacter) {
- if(terminalCharacter.getCharacter() == '\n') {
- moveCursorToNextLine();
- }
- else if(terminalCharacter.getCharacter() == '\t') {
- int nrOfSpaces = TabBehaviour.ALIGN_TO_COLUMN_4.getTabReplacement(cursorPosition.getColumn()).length();
- for(int i = 0; i < nrOfSpaces && cursorPosition.getColumn() < size.getColumns() - 1; i++) {
- putCharacter(terminalCharacter.withCharacter(' '));
- }
- }
- else {
- currentBuffer.setCharacter(size, cursorPosition, terminalCharacter);
-
- //Advance cursor
- cursorPosition = cursorPosition.withRelativeColumn(TerminalTextUtils.isCharCJK(terminalCharacter.getCharacter()) ? 2 : 1);
- if(cursorPosition.getColumn() >= size.getColumns()) {
- moveCursorToNextLine();
- }
- currentBuffer.ensurePosition(size, cursorPosition);
- }
- }
-
- /**
- * Method that updates the cursor position and puts a character atomically. This method is here for thread safety.
- * The cursor position after this call will be the following position after the one specified.
- * @param cursorPosition Position to put the character at
- * @param terminalCharacter Character to put
- */
- synchronized void setCursorAndPutCharacter(TerminalPosition cursorPosition, TextCharacter terminalCharacter) {
- setCursorPosition(cursorPosition);
- putCharacter(terminalCharacter);
- }
-
- private void moveCursorToNextLine() {
- cursorPosition = cursorPosition.withColumn(0).withRelativeRow(1);
- if(cursorPosition.getRow() >= size.getRows()) {
- cursorPosition = cursorPosition.withRelativeRow(-1);
- if(currentBuffer == mainTextBuffer) {
- currentBuffer.newLine();
- currentBuffer.trimBacklog(size.getRows());
- updateScrollingController();
- }
- }
- currentBuffer.ensurePosition(size, cursorPosition);
- }
-
- void switchToPrivateMode() {
- currentBuffer = privateModeTextBuffer;
- }
-
- void switchToNormalMode() {
- currentBuffer = mainTextBuffer;
- }
-
- void clear() {
- currentBuffer.clear();
- setCursorPosition(TerminalPosition.TOP_LEFT_CORNER);
- }
-
- synchronized Iterable<List<TextCharacter>> getLines() {
- int scrollingOffset = terminalScrollController.getScrollingOffset();
- int visibleRows = size.getRows();
- //Make sure scrolling isn't too far off (can be sometimes when the terminal is being resized and the scrollbar
- //hasn't adjusted itself yet)
- if(currentBuffer.getNumberOfLines() > visibleRows &&
- scrollingOffset + visibleRows > currentBuffer.getNumberOfLines()) {
- scrollingOffset = currentBuffer.getNumberOfLines() - visibleRows;
- }
-
- visibleLinesBuffer.clear();
- for(List<TextCharacter> line: currentBuffer.getVisibleLines(visibleRows, scrollingOffset)) {
- visibleLinesBuffer.add(line);
- }
- return visibleLinesBuffer;
- }
-}
+++ /dev/null
-/*
- * This file is part of lanterna (http://code.google.com/p/lanterna/).
- *
- * lanterna is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- * Copyright (C) 2010-2015 Martin
- */
-package com.googlecode.lanterna.terminal.swing;
-
-import com.googlecode.lanterna.graphics.AbstractTextGraphics;
-import com.googlecode.lanterna.TextCharacter;
-import com.googlecode.lanterna.TerminalPosition;
-import com.googlecode.lanterna.TerminalSize;
-import com.googlecode.lanterna.graphics.TextGraphics;
-
-/**
- * Implementation of TextGraphics for the SwingTerminal, which is able to access directly into the TextBuffer and set
- * values in there directly.
- * @author Martin
- */
-class VirtualTerminalTextGraphics extends AbstractTextGraphics {
- private final VirtualTerminal virtualTerminal;
-
- VirtualTerminalTextGraphics(VirtualTerminal virtualTerminal) {
- this.virtualTerminal = virtualTerminal;
- }
-
- @Override
- public TextGraphics setCharacter(int columnIndex, int rowIndex, TextCharacter textCharacter) {
- TerminalSize size = getSize();
- if(columnIndex < 0 || columnIndex >= size.getColumns() ||
- rowIndex < 0 || rowIndex >= size.getRows()) {
- return this;
- }
- virtualTerminal.setCursorAndPutCharacter(new TerminalPosition(columnIndex, rowIndex), textCharacter);
- return this;
- }
-
- @Override
- public TextCharacter getCharacter(TerminalPosition position) {
- return virtualTerminal.getCharacter(position);
- }
-
- @Override
- public TextCharacter getCharacter(int column, int row) {
- return getCharacter(new TerminalPosition(column, row));
- }
-
- @Override
- public TerminalSize getSize() {
- return virtualTerminal.getSize();
- }
-}
+++ /dev/null
-# This is the default properties
-
-foreground = white
-background = black
-sgr =
-foreground[SELECTED] = yellow
-background[SELECTED] = blue
-sgr[SELECTED] = bold
-
-com.googlecode.lanterna.foreground = black
-com.googlecode.lanterna.background = white
-com.googlecode.lanterna.sgr =
-com.googlecode.lanterna.foreground[SELECTED] = yellow
-com.googlecode.lanterna.background[SELECTED] = blue
-com.googlecode.lanterna.sgr[SELECTED] = bold
-
-# Default color and style for the window decoration renderer
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.foreground = black
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.background = white
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.foreground[PRELIGHT] = white
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.sgr[PRELIGHT] = bold
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.char[HORIZONTAL_LINE] = \u2500
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.char[VERTICAL_LINE] = \u2502
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.char[BOTTOM_LEFT_CORNER] = \u2514
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.char[TOP_LEFT_CORNER] = \u250c
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.char[BOTTOM_RIGHT_CORNER] = \u2518
-com.googlecode.lanterna.gui2.DefaultWindowDecorationRenderer.char[TOP_RIGHT_CORNER] = \u2510
-
-#Borders
-com.googlecode.lanterna.gui2.Borders$StandardBorder.foreground = black
-com.googlecode.lanterna.gui2.Borders$StandardBorder.background = white
-com.googlecode.lanterna.gui2.Borders$StandardBorder.sgr =
-com.googlecode.lanterna.gui2.Borders$StandardBorder.foreground[PRELIGHT] = white
-com.googlecode.lanterna.gui2.Borders$StandardBorder.sgr[PRELIGHT] = bold
-com.googlecode.lanterna.gui2.Borders$SingleLine.char[HORIZONTAL_LINE] = \u2500
-com.googlecode.lanterna.gui2.Borders$SingleLine.char[VERTICAL_LINE] = \u2502
-com.googlecode.lanterna.gui2.Borders$SingleLine.char[BOTTOM_LEFT_CORNER] = \u2514
-com.googlecode.lanterna.gui2.Borders$SingleLine.char[TOP_LEFT_CORNER] = \u250c
-com.googlecode.lanterna.gui2.Borders$SingleLine.char[BOTTOM_RIGHT_CORNER] = \u2518
-com.googlecode.lanterna.gui2.Borders$SingleLine.char[TOP_RIGHT_CORNER] = \u2510
-com.googlecode.lanterna.gui2.Borders$DoubleLine.char[HORIZONTAL_LINE] = \u2550
-com.googlecode.lanterna.gui2.Borders$DoubleLine.char[VERTICAL_LINE] = \u2551
-com.googlecode.lanterna.gui2.Borders$DoubleLine.char[BOTTOM_LEFT_CORNER] = \u255a
-com.googlecode.lanterna.gui2.Borders$DoubleLine.char[TOP_LEFT_CORNER] = \u2554
-com.googlecode.lanterna.gui2.Borders$DoubleLine.char[BOTTOM_RIGHT_CORNER] = \u255d
-com.googlecode.lanterna.gui2.Borders$DoubleLine.char[TOP_RIGHT_CORNER] = \u2557
-
-#Button
-com.googlecode.lanterna.gui2.Button.renderer = com.googlecode.lanterna.gui2.Button$DefaultButtonRenderer
-com.googlecode.lanterna.gui2.Button.foreground = black
-com.googlecode.lanterna.gui2.Button.background = white
-com.googlecode.lanterna.gui2.Button.sgr = bold
-com.googlecode.lanterna.gui2.Button.foreground[SELECTED] = yellow
-com.googlecode.lanterna.gui2.Button.background[SELECTED] = blue
-com.googlecode.lanterna.gui2.Button.sgr[SELECTED] = bold
-com.googlecode.lanterna.gui2.Button.foreground[ACTIVE] = white
-com.googlecode.lanterna.gui2.Button.background[ACTIVE] = blue
-com.googlecode.lanterna.gui2.Button.sgr[ACTIVE] = bold
-com.googlecode.lanterna.gui2.Button.foreground[PRELIGHT] = red
-com.googlecode.lanterna.gui2.Button.background[PRELIGHT] = white
-com.googlecode.lanterna.gui2.Button.sgr[PRELIGHT] =
-com.googlecode.lanterna.gui2.Button.foreground[INSENSITIVE] = black
-com.googlecode.lanterna.gui2.Button.background[INSENSITIVE] = white
-com.googlecode.lanterna.gui2.Button.sgr[INSENSITIVE] =
-com.googlecode.lanterna.gui2.Button.char[LEFT_BORDER] = <
-com.googlecode.lanterna.gui2.Button.char[RIGHT_BORDER] = >
-
-# List boxes default
-com.googlecode.lanterna.gui2.AbstractListBox.foreground = black
-com.googlecode.lanterna.gui2.AbstractListBox.background = white
-com.googlecode.lanterna.gui2.AbstractListBox.foreground[SELECTED] = white
-com.googlecode.lanterna.gui2.AbstractListBox.background[SELECTED] = blue
-com.googlecode.lanterna.gui2.AbstractListBox.foreground[INSENSITIVE] = white
-com.googlecode.lanterna.gui2.AbstractListBox.background[INSENSITIVE] = black
-
-# TextBox
-com.googlecode.lanterna.gui2.TextBox.foreground = white
-com.googlecode.lanterna.gui2.TextBox.background = blue
-com.googlecode.lanterna.gui2.TextBox.foreground[ACTIVE] = yellow
-com.googlecode.lanterna.gui2.TextBox.background[ACTIVE] = blue
-com.googlecode.lanterna.gui2.TextBox.sgr[ACTIVE] = bold
-
-# CheckBox
-com.googlecode.lanterna.gui2.CheckBox.foreground = black
-com.googlecode.lanterna.gui2.CheckBox.background = white
-com.googlecode.lanterna.gui2.CheckBox.foreground[PRELIGHT] = white
-com.googlecode.lanterna.gui2.CheckBox.background[PRELIGHT] = blue
-com.googlecode.lanterna.gui2.CheckBox.sgr[PRELIGHT] = bold
-com.googlecode.lanterna.gui2.CheckBox.foreground[ACTIVE] = yellow
-com.googlecode.lanterna.gui2.CheckBox.background[ACTIVE] = blue
-com.googlecode.lanterna.gui2.CheckBox.sgr[ACTIVE] = bold
-com.googlecode.lanterna.gui2.CheckBox.char[MARKER] = x
-
-# Separator
-com.googlecode.lanterna.gui2.Separator.foreground = black
-com.googlecode.lanterna.gui2.Separator.background = white
-com.googlecode.lanterna.gui2.Separator.sgr = bold
-
-# ScrollBar
-com.googlecode.lanterna.gui2.ScrollBar.char[UP_ARROW]=\u2191
-com.googlecode.lanterna.gui2.ScrollBar.char[DOWN_ARROW]=\u2193
-com.googlecode.lanterna.gui2.ScrollBar.char[LEFT_ARROW]=\u2190
-com.googlecode.lanterna.gui2.ScrollBar.char[RIGHT_ARROW]=\u2192
-
-com.googlecode.lanterna.gui2.ScrollBar.char[VERTICAL_BACKGROUND]=\u2502
-com.googlecode.lanterna.gui2.ScrollBar.char[VERTICAL_SMALL_TRACKER]=\u2503
-com.googlecode.lanterna.gui2.ScrollBar.char[VERTICAL_TRACKER_BACKGROUND]=\u2503
-com.googlecode.lanterna.gui2.ScrollBar.char[VERTICAL_TRACKER_TOP]=\u257d
-com.googlecode.lanterna.gui2.ScrollBar.char[VERTICAL_TRACKER_BOTTOM]=\u257f
-
-com.googlecode.lanterna.gui2.ScrollBar.char[HORIZONTAL_BACKGROUND]=\u2500
-com.googlecode.lanterna.gui2.ScrollBar.char[HORIZONTAL_SMALL_TRACKER]=\u2501
-com.googlecode.lanterna.gui2.ScrollBar.char[HORIZONTAL_TRACKER_BACKGROUND]=\u2501
-com.googlecode.lanterna.gui2.ScrollBar.char[HORIZONTAL_TRACKER_LEFT]=\u257c
-com.googlecode.lanterna.gui2.ScrollBar.char[HORIZONTAL_TRACKER_RIGHT]=\u257e
-
-# com.googlecode.lanterna.gui2.ScrollBar.char[VERTICAL_BACKGROUND]=\u2592
-# com.googlecode.lanterna.gui2.ScrollBar.char[HORIZONTAL_BACKGROUND]=\u2592
-# com.googlecode.lanterna.gui2.ScrollBar.char[SMALL_TRACKER]=\u25aa
-# com.googlecode.lanterna.gui2.ScrollBar.char[TRACKER_MIDDLE]=\u25aa
-# com.googlecode.lanterna.gui2.ScrollBar.char[TRACKER_BACKGROUND]=\u0020
-# com.googlecode.lanterna.gui2.ScrollBar.char[TRACKER_TOP]=\u028c
-# com.googlecode.lanterna.gui2.ScrollBar.char[TRACKER_BOTTOM]=\u0076
-# com.googlecode.lanterna.gui2.ScrollBar.char[TRACKER_LEFT]=\u003c
-# com.googlecode.lanterna.gui2.ScrollBar.char[TRACKER_RIGHT]=\u003e
-
-# Table
-com.googlecode.lanterna.gui2.table.Table.foreground = black
-com.googlecode.lanterna.gui2.table.Table.background = white
-com.googlecode.lanterna.gui2.table.Table.sgr[HEADER] = underline,bold
-com.googlecode.lanterna.gui2.table.Table.foreground[SELECTED] = white
-com.googlecode.lanterna.gui2.table.Table.background[SELECTED] = blue
-com.googlecode.lanterna.gui2.table.Table.foreground[ACTIVE] = yellow
-com.googlecode.lanterna.gui2.table.Table.background[ACTIVE] = blue
-com.googlecode.lanterna.gui2.table.Table.sgr[ACTIVE] = bold
\ No newline at end of file
+++ /dev/null
-short.label.ok=OK
-short.label.cancel=Cancel
-short.label.yes=Yes
-short.label.no=No
-short.label.close=Close
-short.label.abort=Abort
-short.label.ignore=Ignore
-short.label.retry=Retry
-short.label.continue=Continue
-short.label.open=Open
-short.label.save=Save
\ No newline at end of file
+++ /dev/null
-short.label.ok=OK
-short.label.cancel=Annullér
-short.label.yes=Ja
-short.label.no=Nej
-short.label.close=Luk
-short.label.abort=Afbryd
-short.label.ignore=Ignorér
-short.label.retry=Prøv igen
-short.label.continue=Fortsæt
-short.label.open=Åbn
-short.label.save=Gem
\ No newline at end of file
+++ /dev/null
-short.label.ok=Ok
-short.label.cancel=Abbrechen
-short.label.yes=Ja
-short.label.no=Nein
-short.label.close=Schließen
-short.label.abort=Beenden
-short.label.ignore=Ignorieren
-short.label.retry=Wiederholen
-short.label.continue=Weiter
-short.label.open=Öffnen
-short.label.save=Speichern
\ No newline at end of file
+++ /dev/null
-short.label.ok=OK
-short.label.cancel=Peru
-short.label.yes=Kyllä
-short.label.no=Ei
-short.label.close=Sulje
-short.label.abort=Keskeytä
-short.label.ignore=Ohita
-short.label.retry=Yritä uudelleen
-short.label.continue=Jatka
-short.label.open=Avaa
-short.label.save=Tallenna
\ No newline at end of file
+++ /dev/null
-short.label.ok=Ok
-short.label.cancel=Annuler
-short.label.yes=Oui
-short.label.no=Non
-short.label.close=Fermer
-short.label.abort=Abandonner
-short.label.ignore=Ignorer
-short.label.retry=Réessayer
-short.label.continue=Continuer
-short.label.open=Ouvrir
-short.label.save=Enregistrer
\ No newline at end of file
+++ /dev/null
-short.label.ok=OK
-short.label.cancel=キャンセル
-short.label.yes=はい
-short.label.no=いいえ
-short.label.close=閉じる
-short.label.abort=中断
-short.label.ignore=無視
-short.label.retry=再試行
-short.label.continue=続ける
-short.label.open=オープン
-short.label.save=保存
\ No newline at end of file
+++ /dev/null
-short.label.ok=OK
-short.label.cancel=Avbryt
-short.label.yes=Ja
-short.label.no=Nei
-short.label.close=Lukk
-short.label.abort=Avbryt
-short.label.ignore=Ignorer
-short.label.retry=Prøv igjen
-short.label.continue=Fortsett
-short.label.open=Åpne
-short.label.save=Lagre
\ No newline at end of file
+++ /dev/null
-short.label.ok=OK
-short.label.cancel=Avbryt
-short.label.yes=Ja
-short.label.no=Nei
-short.label.close=Lukk
-short.label.abort=Avbryt
-short.label.ignore=Ignorer
-short.label.retry=Prøv igjen
-short.label.continue=Fortsett
-short.label.open=Åpne
-short.label.save=Lagre
\ No newline at end of file
+++ /dev/null
-short.label.ok=OK
-short.label.cancel=Avbryt
-short.label.yes=Ja
-short.label.no=Nei
-short.label.close=Lukk
-short.label.abort=Avbryt
-short.label.ignore=Ignorer
-short.label.retry=Prøv igjen
-short.label.continue=Fortsett
-short.label.open=Åpne
-short.label.save=Lagre
\ No newline at end of file
+++ /dev/null
-short.label.ok=OK
-short.label.cancel=Avbryt
-short.label.yes=Ja
-short.label.no=Nej
-short.label.close=Stäng
-short.label.abort=Avbryt
-short.label.ignore=Ignorera
-short.label.retry=Försök igen
-short.label.continue=Fortsätt
-short.label.open=Öppna
-short.label.save=Spara
\ No newline at end of file