summaryrefslogtreecommitdiff
path: root/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java')
-rw-r--r--src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java1505
1 files changed, 0 insertions, 1505 deletions
diff --git a/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java b/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java
deleted file mode 100644
index bfd5974..0000000
--- a/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java
+++ /dev/null
@@ -1,1505 +0,0 @@
-/**
- * Copyright (C) 2018-2021 Adrien Hopkins
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-package sevenUnits.converterGUI;
-
-import java.awt.BorderLayout;
-import java.awt.GridBagConstraints;
-import java.awt.GridBagLayout;
-import java.awt.GridLayout;
-import java.awt.event.KeyEvent;
-import java.io.BufferedWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.math.BigDecimal;
-import java.math.MathContext;
-import java.math.RoundingMode;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.NoSuchElementException;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.function.Predicate;
-import java.util.stream.Collectors;
-
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.ButtonGroup;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JComboBox;
-import javax.swing.JFormattedTextField;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JRadioButton;
-import javax.swing.JScrollPane;
-import javax.swing.JSlider;
-import javax.swing.JTabbedPane;
-import javax.swing.JTextArea;
-import javax.swing.JTextField;
-import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
-import javax.swing.WindowConstants;
-import javax.swing.border.TitledBorder;
-
-import sevenUnits.ProgramInfo;
-import sevenUnits.unit.BaseDimension;
-import sevenUnits.unit.BritishImperial;
-import sevenUnits.unit.LinearUnit;
-import sevenUnits.unit.LinearUnitValue;
-import sevenUnits.unit.NameSymbol;
-import sevenUnits.unit.Metric;
-import sevenUnits.unit.Unit;
-import sevenUnits.unit.UnitDatabase;
-import sevenUnits.unit.UnitPrefix;
-import sevenUnits.unit.UnitValue;
-import sevenUnits.utils.ConditionalExistenceCollections;
-import sevenUnits.utils.ObjectProduct;
-
-/**
- * @author Adrien Hopkins
- * @since 2018-12-27
- * @since v0.1.0
- */
-final class SevenUnitsGUI {
- /**
- * A tab in the View.
- */
- private enum Pane {
- UNIT_CONVERTER, EXPRESSION_CONVERTER, UNIT_VIEWER, PREFIX_VIEWER, ABOUT,
- SETTINGS;
- }
-
- private static class Presenter {
- /** The default place where settings are stored. */
- private static final String DEFAULT_SETTINGS_FILEPATH = "settings.txt";
- /** The default place where units are stored. */
- private static final String DEFAULT_UNITS_FILEPATH = "/unitsfile.txt";
- /** The default place where dimensions are stored. */
- private static final String DEFAULT_DIMENSIONS_FILEPATH = "/dimensionfile.txt";
- /** The default place where exceptions are stored. */
- private static final String DEFAULT_EXCEPTIONS_FILEPATH = "/metric_exceptions.txt";
-
- /**
- * Adds default units and dimensions to a database.
- *
- * @param database database to add to
- * @since 2019-04-14
- * @since v0.2.0
- */
- private static void addDefaults(final UnitDatabase database) {
- database.addUnit("metre", Metric.METRE);
- database.addUnit("kilogram", Metric.KILOGRAM);
- database.addUnit("gram", Metric.KILOGRAM.dividedBy(1000));
- database.addUnit("second", Metric.SECOND);
- database.addUnit("ampere", Metric.AMPERE);
- database.addUnit("kelvin", Metric.KELVIN);
- database.addUnit("mole", Metric.MOLE);
- database.addUnit("candela", Metric.CANDELA);
- database.addUnit("bit", Metric.BIT);
- database.addUnit("unit", Metric.ONE);
- // nonlinear units - must be loaded manually
- database.addUnit("tempCelsius", Metric.CELSIUS);
- database.addUnit("tempFahrenheit", BritishImperial.FAHRENHEIT);
-
- // load initial dimensions
- database.addDimension("LENGTH", Metric.Dimensions.LENGTH);
- database.addDimension("MASS", Metric.Dimensions.MASS);
- database.addDimension("TIME", Metric.Dimensions.TIME);
- database.addDimension("TEMPERATURE", Metric.Dimensions.TEMPERATURE);
- }
-
- /**
- * Gets the text of a resource file as a set of strings (each one is one
- * line of the text).
- *
- * @param filename filename to get resource from
- * @return contents of file
- * @since 2021-03-27
- */
- public static final List<String> getLinesFromResource(String filename) {
- final List<String> lines = new ArrayList<>();
-
- try (InputStream stream = inputStream(filename);
- Scanner scanner = new Scanner(stream)) {
- while (scanner.hasNextLine()) {
- lines.add(scanner.nextLine());
- }
- } catch (final IOException e) {
- throw new AssertionError(
- "Error occurred while loading file " + filename, e);
- }
-
- return lines;
- }
-
- /**
- * Gets an input stream for a resource file.
- *
- * @param filepath file to use as resource
- * @return obtained Path
- * @since 2021-03-27
- */
- private static final InputStream inputStream(String filepath) {
- return SevenUnitsGUI.class.getResourceAsStream(filepath);
- }
-
- /**
- * @return {@code line} with any comments removed.
- * @since 2021-03-13
- */
- private static final String withoutComments(String line) {
- final int index = line.indexOf('#');
- return index == -1 ? line : line.substring(index);
- }
-
- /** The presenter's associated view. */
- private final View view;
-
- /** The units known by the program. */
- private final UnitDatabase database;
-
- /** The names of all of the units */
- private final List<String> unitNames;
-
- /** The names of all of the prefixes */
- private final List<String> prefixNames;
-
- /** The names of all of the dimensions */
- private final List<String> dimensionNames;
-
- /** Unit names that are ignored by the metric-only/imperial-only filter */
- private final Set<String> metricExceptions;
-
- private final Comparator<String> prefixNameComparator;
-
- /** A boolean remembering whether or not one-way conversion is on */
- private boolean oneWay = true;
- /** The prefix rule */
- private DefaultPrefixRepetitionRule prefixRule = null;
-
- // conditions for existence of From and To entries
- // used for one-way conversion
- private final MutablePredicate<String> fromExistenceCondition = new MutablePredicate<>(
- s -> true);
-
- private final MutablePredicate<String> toExistenceCondition = new MutablePredicate<>(
- s -> true);
-
- /*
- * Rounding-related settings. I am using my own system, and not
- * MathContext, because MathContext does not support decimal place based
- * or scientific rounding, only significant digit based rounding.
- */
- private int precision = 6;
-
- private RoundingType roundingType = RoundingType.SIGNIFICANT_DIGITS;
-
- // The "include duplicate units" setting
- private boolean includeDuplicateUnits = true;
-
- /**
- * Creates the presenter.
- *
- * @param view presenter's associated view
- * @since 2018-12-27
- * @since v0.1.0
- */
- Presenter(final View view) {
- this.view = view;
-
- // load initial units
- this.database = new UnitDatabase(
- DefaultPrefixRepetitionRule.NO_RESTRICTION);
- Presenter.addDefaults(this.database);
-
- // load units and prefixes
- try (final InputStream units = inputStream(DEFAULT_UNITS_FILEPATH)) {
- this.database.loadUnitsFromStream(units);
- } catch (final IOException e) {
- throw new AssertionError("Loading of unitsfile.txt failed.", e);
- }
-
- // load dimensions
- try (final InputStream dimensions = inputStream(
- DEFAULT_DIMENSIONS_FILEPATH)) {
- this.database.loadDimensionsFromStream(dimensions);
- } catch (final IOException e) {
- throw new AssertionError("Loading of dimensionfile.txt failed.", e);
- }
-
- // load metric exceptions
- try {
- this.metricExceptions = new HashSet<>();
- try (InputStream exceptions = inputStream(
- DEFAULT_EXCEPTIONS_FILEPATH);
- Scanner scanner = new Scanner(exceptions)) {
- while (scanner.hasNextLine()) {
- final String line = Presenter
- .withoutComments(scanner.nextLine());
- if (!line.isBlank()) {
- this.metricExceptions.add(line);
- }
- }
- }
- } catch (final IOException e) {
- throw new AssertionError("Loading of metric_exceptions.txt failed.",
- e);
- }
-
- // load settings - requires database to exist
- if (Files.exists(this.getSettingsFile())) {
- this.loadSettings();
- }
-
- // a comparator that can be used to compare prefix names
- // any name that does not exist is less than a name that does.
- // otherwise, they are compared by value
- this.prefixNameComparator = (o1, o2) -> {
- if (!Presenter.this.database.containsPrefixName(o1))
- return -1;
- else if (!Presenter.this.database.containsPrefixName(o2))
- return 1;
-
- final UnitPrefix p1 = Presenter.this.database.getPrefix(o1);
- final UnitPrefix p2 = Presenter.this.database.getPrefix(o2);
-
- if (p1.getMultiplier() < p2.getMultiplier())
- return -1;
- else if (p1.getMultiplier() > p2.getMultiplier())
- return 1;
-
- return o1.compareTo(o2);
- };
-
- this.unitNames = new ArrayList<>(
- this.database.unitMapPrefixless(true).keySet());
- this.unitNames.sort(null); // sorts it using Comparable
-
- this.prefixNames = new ArrayList<>(this.database.prefixMap().keySet());
- this.prefixNames.sort(this.prefixNameComparator); // sorts it using my
- // comparator
-
- this.dimensionNames = new DelegateListModel<>(
- new ArrayList<>(this.database.dimensionMap().keySet()));
- this.dimensionNames.sort(null); // sorts it using Comparable
-
- // a Predicate that returns true iff the argument is a full base unit
- final Predicate<Unit> isFullBase = unit -> unit instanceof LinearUnit
- && ((LinearUnit) unit).isBase();
-
- // print out unit counts
- System.out.printf(
- "Successfully loaded %d units with %d unit names (%d base units).%n",
- this.database.unitMapPrefixless(false).size(),
- this.database.unitMapPrefixless(true).size(),
- this.database.unitMapPrefixless(false).values().stream()
- .filter(isFullBase).count());
- }
-
- /**
- * Converts in the dimension-based converter
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final void convertDimensionBased() {
- final String fromSelection = this.view.getFromSelection();
- if (fromSelection == null) {
- this.view.showErrorDialog("Error",
- "No unit selected in From field");
- return;
- }
- final String toSelection = this.view.getToSelection();
- if (toSelection == null) {
- this.view.showErrorDialog("Error", "No unit selected in To field");
- return;
- }
-
- final Unit from = this.database.getUnit(fromSelection);
- final Unit to = this.database.getUnit(toSelection)
- .withName(NameSymbol.ofName(toSelection));
-
- final UnitValue beforeValue;
- try {
- beforeValue = UnitValue.of(from,
- this.view.getDimensionConverterInput());
- } catch (final ParseException e) {
- this.view.showErrorDialog("Error",
- "Error in parsing: " + e.getMessage());
- return;
- }
- final UnitValue value = beforeValue.convertTo(to);
-
- final String output = this.getRoundedString(value);
-
- this.view.setDimensionConverterOutputText(
- String.format("%s = %s", beforeValue, output));
- }
-
- /**
- * Runs whenever the convert button is pressed.
- *
- * <p>
- * Reads and parses a unit expression from the from and to boxes, then
- * converts {@code from} to {@code to}. Any errors are shown in
- * JOptionPanes.
- * </p>
- *
- * @since 2019-01-26
- * @since v0.1.0
- */
- public final void convertExpressions() {
- final String fromUnitString = this.view.getFromText();
- final String toUnitString = this.view.getToText();
-
- if (fromUnitString.isEmpty()) {
- this.view.showErrorDialog("Parse Error",
- "Please enter a unit expression in the From: box.");
- return;
- }
- if (toUnitString.isEmpty()) {
- this.view.showErrorDialog("Parse Error",
- "Please enter a unit expression in the To: box.");
- return;
- }
-
- final LinearUnitValue from;
- final Unit to;
- try {
- from = this.database.evaluateUnitExpression(fromUnitString);
- } catch (final IllegalArgumentException | NoSuchElementException e) {
- this.view.showErrorDialog("Parse Error",
- "Could not recognize text in From entry: " + e.getMessage());
- return;
- }
- try {
- to = this.database.getUnitFromExpression(toUnitString);
- } catch (final IllegalArgumentException | NoSuchElementException e) {
- this.view.showErrorDialog("Parse Error",
- "Could not recognize text in To entry: " + e.getMessage());
- return;
- }
-
- if (to instanceof LinearUnit) {
- // convert to LinearUnitValue
- final LinearUnitValue from2;
- final LinearUnit to2 = ((LinearUnit) to)
- .withName(NameSymbol.ofName(toUnitString));
- final boolean useSlash;
-
- if (from.canConvertTo(to2)) {
- from2 = from;
- useSlash = false;
- } else if (LinearUnitValue.ONE.dividedBy(from).canConvertTo(to2)) {
- from2 = LinearUnitValue.ONE.dividedBy(from);
- useSlash = true;
- } else {
- // if I can't convert, leave
- this.view.showErrorDialog("Conversion Error",
- String.format("Cannot convert between %s and %s",
- fromUnitString, toUnitString));
- return;
- }
-
- final LinearUnitValue converted = from2.convertTo(to2);
- this.view.setExpressionConverterOutputText((useSlash ? "1 / " : "")
- + String.format("%s = %s", fromUnitString,
- this.getRoundedString(converted, false)));
- return;
- } else {
- // convert to UnitValue
- final UnitValue from2 = from.asUnitValue();
- if (from2.canConvertTo(to)) {
- final UnitValue converted = from2.convertTo(to);
-
- this.view
- .setExpressionConverterOutputText(String.format("%s = %s",
- fromUnitString, this.getRoundedString(converted)));
- } else {
- // if I can't convert, leave
- this.view.showErrorDialog("Conversion Error",
- String.format("Cannot convert between %s and %s",
- fromUnitString, toUnitString));
- }
- }
- }
-
- /**
- * @return a list of all of the unit dimensions
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final List<String> dimensionNameList() {
- return this.dimensionNames;
- }
-
- /**
- * @return a list of all the entries in the dimension-based converter's
- * From box
- * @since 2020-08-27
- */
- public final Set<String> fromEntries() {
- return ConditionalExistenceCollections.conditionalExistenceSet(
- this.unitNameSet(), this.fromExistenceCondition);
- }
-
- /**
- * @return a comparator to compare prefix names
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final Comparator<String> getPrefixNameComparator() {
- return this.prefixNameComparator;
- }
-
- /**
- * Like {@link LinearUnitValue#toString(boolean)}, but obeys this unit
- * converter's rounding settings.
- *
- * @since 2020-08-04
- */
- private final String getRoundedString(final LinearUnitValue value,
- boolean showUncertainty) {
- switch (this.roundingType) {
- case DECIMAL_PLACES:
- case SIGNIFICANT_DIGITS:
- return this.getRoundedString(value.asUnitValue());
- case SCIENTIFIC:
- return value.toString(showUncertainty);
- default:
- throw new AssertionError("Invalid switch condition.");
- }
- }
-
- /**
- * Like {@link UnitValue#toString()}, but obeys this unit converter's
- * rounding settings.
- *
- * @since 2020-08-04
- */
- private final String getRoundedString(final UnitValue value) {
- final BigDecimal unrounded = new BigDecimal(value.getValue());
- final BigDecimal rounded;
- int precision = this.precision;
-
- switch (this.roundingType) {
- case DECIMAL_PLACES:
- rounded = unrounded.setScale(precision, RoundingMode.HALF_EVEN);
- break;
- case SCIENTIFIC:
- precision = 12;
- //$FALL-THROUGH$
- case SIGNIFICANT_DIGITS:
- rounded = unrounded
- .round(new MathContext(precision, RoundingMode.HALF_EVEN));
- break;
- default:
- throw new AssertionError("Invalid switch condition.");
- }
-
- String output = rounded.toString();
-
- // remove trailing zeroes
- if (output.contains(".")) {
- while (output.endsWith("0")) {
- output = output.substring(0, output.length() - 1);
- }
- if (output.endsWith(".")) {
- output = output.substring(0, output.length() - 1);
- }
- }
-
- return output + " " + value.getUnit().getPrimaryName().get();
- }
-
- /**
- * @return The file where settings are stored;
- * @since 2020-12-11
- */
- private final Path getSettingsFile() {
- return Path.of(DEFAULT_SETTINGS_FILEPATH);
- }
-
- /**
- * Loads settings from the settings file.
- *
- * @since 2021-02-17
- */
- public final void loadSettings() {
- try {
- // read file line by line
- final int lineNum = 0;
- for (final String line : Files
- .readAllLines(this.getSettingsFile())) {
- final int equalsIndex = line.indexOf('=');
- if (equalsIndex == -1)
- throw new IllegalStateException(
- "Settings file is malformed at line " + lineNum);
-
- final String param = line.substring(0, equalsIndex);
- final String value = line.substring(equalsIndex + 1);
-
- switch (param) {
- // set manually to avoid the unnecessary saving of the non-manual
- // methods
- case "precision":
- this.precision = Integer.valueOf(value);
- break;
- case "rounding_type":
- this.roundingType = RoundingType.valueOf(value);
- break;
- case "prefix_rule":
- this.prefixRule = DefaultPrefixRepetitionRule.valueOf(value);
- this.database.setPrefixRepetitionRule(this.prefixRule);
- break;
- case "one_way":
- this.oneWay = Boolean.valueOf(value);
- if (this.oneWay) {
- this.fromExistenceCondition.setPredicate(
- unitName -> this.metricExceptions.contains(unitName)
- || !this.database.getUnit(unitName)
- .isMetric());
- this.toExistenceCondition.setPredicate(
- unitName -> this.metricExceptions.contains(unitName)
- || this.database.getUnit(unitName).isMetric());
- } else {
- this.fromExistenceCondition.setPredicate(unitName -> true);
- this.toExistenceCondition.setPredicate(unitName -> true);
- }
- break;
- case "include_duplicates":
- this.includeDuplicateUnits = Boolean.valueOf(value);
- if (this.view.presenter != null) {
- this.view.update();
- }
- break;
- default:
- System.err.printf("Warning: unrecognized setting \"%s\".",
- param);
- break;
- }
- }
- } catch (final IOException e) {}
- }
-
- /**
- * @return a set of all prefix names in the database
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final Set<String> prefixNameSet() {
- return this.database.prefixMap().keySet();
- }
-
- /**
- * Runs whenever a prefix is selected in the viewer.
- * <p>
- * Shows its information in the text box to the right.
- * </p>
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- public final void prefixSelected() {
- final String prefixName = this.view.getPrefixViewerSelection();
- if (prefixName == null)
- return;
- else {
- final UnitPrefix prefix = this.database.getPrefix(prefixName);
-
- this.view.setPrefixTextBoxText(String.format("%s%nMultiplier: %s",
- prefixName, prefix.getMultiplier()));
- }
- }
-
- /**
- * Saves the settings to the settings file.
- *
- * @since 2021-02-17
- */
- public final void saveSettings() {
- try (BufferedWriter writer = Files
- .newBufferedWriter(this.getSettingsFile())) {
- writer.write(String.format("precision=%d\n", this.precision));
- writer.write(
- String.format("rounding_type=%s\n", this.roundingType));
- writer.write(String.format("prefix_rule=%s\n", this.prefixRule));
- writer.write(String.format("one_way=%s\n", this.oneWay));
- writer.write(String.format("include_duplicates=%s\n",
- this.includeDuplicateUnits));
- } catch (final IOException e) {
- e.printStackTrace();
- this.view.showErrorDialog("I/O Error",
- "Error occurred while saving settings: "
- + e.getLocalizedMessage());
- }
- }
-
- public final void setIncludeDuplicateUnits(
- boolean includeDuplicateUnits) {
- this.includeDuplicateUnits = includeDuplicateUnits;
-
- this.view.update();
- this.saveSettings();
- }
-
- /**
- * Enables or disables one-way conversion.
- *
- * @param oneWay whether one-way conversion should be on (true) or off
- * (false)
- * @since 2020-08-27
- */
- public final void setOneWay(boolean oneWay) {
- this.oneWay = oneWay;
- if (oneWay) {
- this.fromExistenceCondition.setPredicate(
- unitName -> this.metricExceptions.contains(unitName)
- || !this.database.getUnit(unitName).isMetric());
- this.toExistenceCondition.setPredicate(
- unitName -> this.metricExceptions.contains(unitName)
- || this.database.getUnit(unitName).isMetric());
- } else {
- this.fromExistenceCondition.setPredicate(unitName -> true);
- this.toExistenceCondition.setPredicate(unitName -> true);
- }
-
- this.saveSettings();
- }
-
- /**
- * @param precision new value of precision
- * @since 2019-01-15
- * @since v0.1.0
- */
- public final void setPrecision(final int precision) {
- this.precision = precision;
-
- this.saveSettings();
- }
-
- /**
- * @param prefixRepetitionRule the prefixRepetitionRule to set
- * @since 2020-08-26
- */
- public void setPrefixRepetitionRule(
- Predicate<List<UnitPrefix>> prefixRepetitionRule) {
- if (prefixRepetitionRule instanceof DefaultPrefixRepetitionRule) {
- this.prefixRule = (DefaultPrefixRepetitionRule) prefixRepetitionRule;
- } else {
- this.prefixRule = null;
- }
- this.database.setPrefixRepetitionRule(prefixRepetitionRule);
-
- this.saveSettings();
- }
-
- /**
- * @param roundingType the roundingType to set
- * @since 2020-07-16
- */
- public final void setRoundingType(RoundingType roundingType) {
- this.roundingType = roundingType;
-
- this.saveSettings();
- }
-
- /**
- * @return a list of all the entries in the dimension-based converter's To
- * box
- * @since 2020-08-27
- */
- public final Set<String> toEntries() {
- return ConditionalExistenceCollections.conditionalExistenceSet(
- this.unitNameSet(), this.toExistenceCondition);
- }
-
- /**
- * Returns true if and only if the unit represented by {@code unitName}
- * has the dimension represented by {@code dimensionName}.
- *
- * @param unitName name of unit to test
- * @param dimensionName name of dimension to test
- * @return whether unit has dimenision
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final boolean unitMatchesDimension(final String unitName,
- final String dimensionName) {
- final Unit unit = this.database.getUnit(unitName);
- final ObjectProduct<BaseDimension> dimension = this.database
- .getDimension(dimensionName);
- return unit.getDimension().equals(dimension);
- }
-
- /**
- * Runs whenever a unit is selected in the viewer.
- * <p>
- * Shows its information in the text box to the right.
- * </p>
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- public final void unitNameSelected() {
- final String unitName = this.view.getUnitViewerSelection();
- if (unitName == null)
- return;
- else {
- final Unit unit = this.database.getUnit(unitName);
-
- this.view.setUnitTextBoxText(unit.toString());
- }
- }
-
- /**
- * @return a set of all of the unit names
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final Set<String> unitNameSet() {
- return this.database.unitMapPrefixless(this.includeDuplicateUnits)
- .keySet();
- }
- }
-
- /**
- * Different types of rounding.
- *
- * Significant digits: Rounds to a number of digits. i.e. with precision 5,
- * 12345.6789 rounds to 12346. Decimal places: Rounds to a number of digits
- * after the decimal point, i.e. with precision 5, 12345.6789 rounds to
- * 12345.67890. Scientific: Rounds based on the number of digits and
- * operations, following standard scientific rounding.
- */
- private static enum RoundingType {
- SIGNIFICANT_DIGITS, DECIMAL_PLACES, SCIENTIFIC;
- }
-
- private static class View {
- private static final NumberFormat NUMBER_FORMATTER = new DecimalFormat();
-
- /** The view's frame. */
- private final JFrame frame;
- /** The view's associated presenter. */
- private final Presenter presenter;
- /** The master pane containing all of the tabs. */
- private final JTabbedPane masterPane;
-
- // DIMENSION-BASED CONVERTER
- /** The panel for inputting values in the dimension-based converter */
- private final JTextField valueInput;
- /** The panel for "From" in the dimension-based converter */
- private final SearchBoxList fromSearch;
- /** The panel for "To" in the dimension-based converter */
- private final SearchBoxList toSearch;
- /** The output area in the dimension-based converter */
- private final JTextArea dimensionBasedOutput;
-
- // EXPRESSION-BASED CONVERTER
- /** The "From" entry in the conversion panel */
- private final JTextField fromEntry;
- /** The "To" entry in the conversion panel */
- private final JTextField toEntry;
- /** The output area in the conversion panel */
- private final JTextArea output;
-
- // UNIT AND PREFIX VIEWERS
- /** The searchable list of unit names in the unit viewer */
- private final SearchBoxList unitNameList;
- /** The searchable list of prefix names in the prefix viewer */
- private final SearchBoxList prefixNameList;
- /** The text box for unit data in the unit viewer */
- private final JTextArea unitTextBox;
- /** The text box for prefix data in the prefix viewer */
- private final JTextArea prefixTextBox;
-
- /**
- * Creates the {@code View}.
- *
- * @since 2019-01-14
- * @since v0.1.0
- */
- public View() {
- this.presenter = new Presenter(this);
- this.frame = new JFrame("7Units");
- this.frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
-
- // enable system look and feel
- try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
- } catch (ClassNotFoundException | InstantiationException
- | IllegalAccessException | UnsupportedLookAndFeelException e) {
- // oh well, just use default theme
- System.err.println("Failed to enable system look-and-feel.");
- e.printStackTrace();
- }
-
- // create the components
- this.masterPane = new JTabbedPane();
- this.unitNameList = new SearchBoxList(this.presenter.unitNameSet());
- this.prefixNameList = new SearchBoxList(this.presenter.prefixNameSet(),
- this.presenter.getPrefixNameComparator(), true);
- this.unitTextBox = new JTextArea();
- this.prefixTextBox = new JTextArea();
- this.fromSearch = new SearchBoxList(this.presenter.fromEntries());
- this.toSearch = new SearchBoxList(this.presenter.toEntries());
- this.valueInput = new JFormattedTextField(NUMBER_FORMATTER);
- this.dimensionBasedOutput = new JTextArea(2, 32);
- this.fromEntry = new JTextField();
- this.toEntry = new JTextField();
- this.output = new JTextArea(2, 32);
-
- // create more components
- this.initComponents();
-
- this.frame.pack();
- }
-
- /**
- * @return the currently selected pane.
- * @throws AssertionError if no pane (or an invalid pane) is selected
- */
- public Pane getActivePane() {
- switch (this.masterPane.getSelectedIndex()) {
- case 0:
- return Pane.UNIT_CONVERTER;
- case 1:
- return Pane.EXPRESSION_CONVERTER;
- case 2:
- return Pane.UNIT_VIEWER;
- case 3:
- return Pane.PREFIX_VIEWER;
- case 4:
- return Pane.ABOUT;
- case 5:
- return Pane.SETTINGS;
- default:
- throw new AssertionError("No selected pane, or invalid pane.");
- }
- }
-
- /**
- * @return value in dimension-based converter
- * @throws ParseException
- * @since 2020-07-07
- */
- public double getDimensionConverterInput() throws ParseException {
- final Number value = NUMBER_FORMATTER.parse(this.valueInput.getText());
- if (value instanceof Double)
- return (double) value;
- else if (value instanceof Long)
- return ((Long) value).longValue();
- else
- throw new AssertionError();
- }
-
- /**
- * @return selection in "From" selector in dimension-based converter
- * @since 2019-04-13
- * @since v0.2.0
- */
- public String getFromSelection() {
- return this.fromSearch.getSelectedValue();
- }
-
- /**
- * @return text in "From" box in converter panel
- * @since 2019-01-15
- * @since v0.1.0
- */
- public String getFromText() {
- return this.fromEntry.getText();
- }
-
- /**
- * @return index of selected prefix in prefix viewer
- * @since 2019-01-15
- * @since v0.1.0
- */
- public String getPrefixViewerSelection() {
- return this.prefixNameList.getSelectedValue();
- }
-
- /**
- * @return selection in "To" selector in dimension-based converter
- * @since 2019-04-13
- * @since v0.2.0
- */
- public String getToSelection() {
- return this.toSearch.getSelectedValue();
- }
-
- /**
- * @return text in "To" box in converter panel
- * @since 2019-01-26
- * @since v0.1.0
- */
- public String getToText() {
- return this.toEntry.getText();
- }
-
- /**
- * @return index of selected unit in unit viewer
- * @since 2019-01-15
- * @since v0.1.0
- */
- public String getUnitViewerSelection() {
- return this.unitNameList.getSelectedValue();
- }
-
- /**
- * Starts up the application.
- *
- * @since 2018-12-27
- * @since v0.1.0
- */
- public final void init() {
- this.frame.setVisible(true);
- }
-
- /**
- * Initializes the view's components.
- *
- * @since 2018-12-27
- * @since v0.1.0
- */
- private final void initComponents() {
- final JPanel masterPanel = new JPanel();
- this.frame.add(masterPanel);
-
- masterPanel.setLayout(new BorderLayout());
-
- { // pane with all of the tabs
- masterPanel.add(this.masterPane, BorderLayout.CENTER);
-
- // update stuff
- this.masterPane.addChangeListener(e -> this.update());
-
- { // a panel for unit conversion using a selector
- final JPanel convertUnitPanel = new JPanel();
- this.masterPane.addTab("Convert Units", convertUnitPanel);
- this.masterPane.setMnemonicAt(0, KeyEvent.VK_U);
-
- convertUnitPanel.setLayout(new BorderLayout());
-
- { // panel for input part
- final JPanel inputPanel = new JPanel();
- convertUnitPanel.add(inputPanel, BorderLayout.CENTER);
-
- inputPanel.setLayout(new GridLayout(1, 3));
-
- final JComboBox<String> dimensionSelector = new JComboBox<>(
- this.presenter.dimensionNameList()
- .toArray(new String[0]));
- dimensionSelector.setSelectedItem("LENGTH");
-
- // handle dimension filter
- final MutablePredicate<String> dimensionFilter = new MutablePredicate<>(
- s -> true);
-
- // panel for From things
- inputPanel.add(this.fromSearch);
-
- this.fromSearch.addSearchFilter(dimensionFilter);
-
- { // for dimension selector and arrow that represents
- // conversion
- final JPanel inBetweenPanel = new JPanel();
- inputPanel.add(inBetweenPanel);
-
- inBetweenPanel.setLayout(new BorderLayout());
-
- { // dimension selector
- inBetweenPanel.add(dimensionSelector,
- BorderLayout.PAGE_START);
- }
-
- { // the arrow in the middle
- final JLabel arrowLabel = new JLabel("->");
- inBetweenPanel.add(arrowLabel, BorderLayout.CENTER);
- }
- }
-
- // panel for To things
-
- inputPanel.add(this.toSearch);
-
- this.toSearch.addSearchFilter(dimensionFilter);
-
- // code for dimension filter
- dimensionSelector.addItemListener(e -> {
- dimensionFilter.setPredicate(string -> View.this.presenter
- .unitMatchesDimension(string,
- (String) dimensionSelector.getSelectedItem()));
- this.fromSearch.reapplyFilter();
- this.toSearch.reapplyFilter();
- });
-
- // apply the item listener once because I have a default
- // selection
- dimensionFilter.setPredicate(string -> View.this.presenter
- .unitMatchesDimension(string,
- (String) dimensionSelector.getSelectedItem()));
- this.fromSearch.reapplyFilter();
- this.toSearch.reapplyFilter();
- }
-
- { // panel for submit and output, and also value entry
- final JPanel outputPanel = new JPanel();
- convertUnitPanel.add(outputPanel, BorderLayout.PAGE_END);
-
- outputPanel.setLayout(new GridLayout(3, 1));
-
- { // unit input
- final JPanel valueInputPanel = new JPanel();
- outputPanel.add(valueInputPanel);
-
- valueInputPanel.setLayout(new BorderLayout());
-
- { // prompt
- final JLabel valuePrompt = new JLabel(
- "Value to convert: ");
- valueInputPanel.add(valuePrompt,
- BorderLayout.LINE_START);
- }
-
- { // value to convert
- valueInputPanel.add(this.valueInput,
- BorderLayout.CENTER);
- }
- }
-
- { // button to convert
- final JButton convertButton = new JButton("Convert");
- outputPanel.add(convertButton);
-
- convertButton.addActionListener(
- e -> this.presenter.convertDimensionBased());
- convertButton.setMnemonic(KeyEvent.VK_ENTER);
- }
-
- { // output of conversion
- outputPanel.add(this.dimensionBasedOutput);
- this.dimensionBasedOutput.setEditable(false);
- }
- }
- }
-
- { // panel for unit conversion using expressions
- final JPanel convertExpressionPanel = new JPanel();
- this.masterPane.addTab("Convert Unit Expressions",
- convertExpressionPanel);
- this.masterPane.setMnemonicAt(1, KeyEvent.VK_E);
-
- convertExpressionPanel.setLayout(new GridLayout(4, 1));
-
- { // panel for units to convert from
- final JPanel fromPanel = new JPanel();
- convertExpressionPanel.add(fromPanel);
-
- fromPanel.setBorder(BorderFactory.createTitledBorder("From"));
- fromPanel.setLayout(new GridLayout(1, 1));
-
- { // entry for units
- fromPanel.add(this.fromEntry);
- }
- }
-
- { // panel for units to convert to
- final JPanel toPanel = new JPanel();
- convertExpressionPanel.add(toPanel);
-
- toPanel.setBorder(BorderFactory.createTitledBorder("To"));
- toPanel.setLayout(new GridLayout(1, 1));
-
- { // entry for units
- toPanel.add(this.toEntry);
- }
- }
-
- { // button to convert
- final JButton convertButton = new JButton("Convert");
- convertExpressionPanel.add(convertButton);
-
- convertButton.addActionListener(
- e -> this.presenter.convertExpressions());
- convertButton.setMnemonic(KeyEvent.VK_ENTER);
- }
-
- { // output of conversion
- final JPanel outputPanel = new JPanel();
- convertExpressionPanel.add(outputPanel);
-
- outputPanel
- .setBorder(BorderFactory.createTitledBorder("Output"));
- outputPanel.setLayout(new GridLayout(1, 1));
-
- { // output
- outputPanel.add(this.output);
- this.output.setEditable(false);
- }
- }
- }
-
- { // panel to look up units
- final JPanel unitLookupPanel = new JPanel();
- this.masterPane.addTab("Unit Viewer", unitLookupPanel);
- this.masterPane.setMnemonicAt(2, KeyEvent.VK_V);
-
- unitLookupPanel.setLayout(new GridLayout());
-
- { // search panel
- unitLookupPanel.add(this.unitNameList);
-
- this.unitNameList.getSearchList().addListSelectionListener(
- e -> this.presenter.unitNameSelected());
- }
-
- { // the text box for unit's toString
- unitLookupPanel.add(this.unitTextBox);
- this.unitTextBox.setEditable(false);
- this.unitTextBox.setLineWrap(true);
- }
- }
-
- { // panel to look up prefixes
- final JPanel prefixLookupPanel = new JPanel();
- this.masterPane.addTab("Prefix Viewer", prefixLookupPanel);
- this.masterPane.setMnemonicAt(3, KeyEvent.VK_P);
-
- prefixLookupPanel.setLayout(new GridLayout(1, 2));
-
- { // panel for listing and seaching
- prefixLookupPanel.add(this.prefixNameList);
-
- this.prefixNameList.getSearchList().addListSelectionListener(
- e -> this.presenter.prefixSelected());
- }
-
- { // the text box for prefix's toString
- prefixLookupPanel.add(this.prefixTextBox);
- this.prefixTextBox.setEditable(false);
- this.prefixTextBox.setLineWrap(true);
- }
- }
-
- { // Info panel
- final JPanel infoPanel = new JPanel();
- this.masterPane.addTab("\uD83D\uDEC8", // info (i) character
- new JScrollPane(infoPanel));
-
- final JTextArea infoTextArea = new JTextArea();
- infoTextArea.setEditable(false);
- infoTextArea.setOpaque(false);
- infoPanel.add(infoTextArea);
-
- // get info text
- final String infoText = Presenter
- .getLinesFromResource("/about.txt").stream()
- .map(Presenter::withoutComments)
- .collect(Collectors.joining("\n"))
- .replaceAll("\\[VERSION\\]", ProgramInfo.VERSION);
- infoTextArea.setText(infoText);
- }
-
- { // Settings panel
- final JPanel settingsPanel = new JPanel();
- this.masterPane.addTab("\u2699", new JScrollPane(settingsPanel));
- this.masterPane.setMnemonicAt(5, KeyEvent.VK_S);
-
- settingsPanel.setLayout(
- new BoxLayout(settingsPanel, BoxLayout.PAGE_AXIS));
-
- { // rounding settings
- final JPanel roundingPanel = new JPanel();
- settingsPanel.add(roundingPanel);
- roundingPanel
- .setBorder(new TitledBorder("Rounding Settings"));
- roundingPanel.setLayout(new GridBagLayout());
-
- // rounding rule selection
- final ButtonGroup roundingRuleButtons = new ButtonGroup();
-
- final JLabel roundingRuleLabel = new JLabel("Rounding Rule:");
- roundingPanel.add(roundingRuleLabel, new GridBagBuilder(0, 0)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton fixedPrecision = new JRadioButton(
- "Fixed Precision");
- if (this.presenter.roundingType == RoundingType.SIGNIFICANT_DIGITS) {
- fixedPrecision.setSelected(true);
- }
- fixedPrecision.addActionListener(e -> this.presenter
- .setRoundingType(RoundingType.SIGNIFICANT_DIGITS));
- roundingRuleButtons.add(fixedPrecision);
- roundingPanel.add(fixedPrecision, new GridBagBuilder(0, 1)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton fixedDecimals = new JRadioButton(
- "Fixed Decimal Places");
- if (this.presenter.roundingType == RoundingType.DECIMAL_PLACES) {
- fixedDecimals.setSelected(true);
- }
- fixedDecimals.addActionListener(e -> this.presenter
- .setRoundingType(RoundingType.DECIMAL_PLACES));
- roundingRuleButtons.add(fixedDecimals);
- roundingPanel.add(fixedDecimals, new GridBagBuilder(0, 2)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton relativePrecision = new JRadioButton(
- "Scientific Precision");
- if (this.presenter.roundingType == RoundingType.SCIENTIFIC) {
- relativePrecision.setSelected(true);
- }
- relativePrecision.addActionListener(e -> this.presenter
- .setRoundingType(RoundingType.SCIENTIFIC));
- roundingRuleButtons.add(relativePrecision);
- roundingPanel.add(relativePrecision, new GridBagBuilder(0, 3)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JLabel sliderLabel = new JLabel("Precision:");
- roundingPanel.add(sliderLabel, new GridBagBuilder(0, 4)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JSlider sigDigSlider = new JSlider(0, 12);
- roundingPanel.add(sigDigSlider, new GridBagBuilder(0, 5)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- sigDigSlider.setMajorTickSpacing(4);
- sigDigSlider.setMinorTickSpacing(1);
- sigDigSlider.setSnapToTicks(true);
- sigDigSlider.setPaintTicks(true);
- sigDigSlider.setPaintLabels(true);
- sigDigSlider.setValue(this.presenter.precision);
-
- sigDigSlider.addChangeListener(e -> this.presenter
- .setPrecision(sigDigSlider.getValue()));
- }
-
- { // prefix repetition settings
- final JPanel prefixRepetitionPanel = new JPanel();
- settingsPanel.add(prefixRepetitionPanel);
- prefixRepetitionPanel.setBorder(
- new TitledBorder("Prefix Repetition Settings"));
- prefixRepetitionPanel.setLayout(new GridBagLayout());
-
- // prefix rules
- final ButtonGroup prefixRuleButtons = new ButtonGroup();
-
- final JRadioButton noRepetition = new JRadioButton(
- "No Repetition");
- if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.NO_REPETITION) {
- noRepetition.setSelected(true);
- }
- noRepetition.addActionListener(
- e -> this.presenter.setPrefixRepetitionRule(
- DefaultPrefixRepetitionRule.NO_REPETITION));
- prefixRuleButtons.add(noRepetition);
- prefixRepetitionPanel.add(noRepetition,
- new GridBagBuilder(0, 0)
- .setAnchor(GridBagConstraints.LINE_START)
- .build());
-
- final JRadioButton noRestriction = new JRadioButton(
- "No Restriction");
- if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.NO_RESTRICTION) {
- noRestriction.setSelected(true);
- }
- noRestriction.addActionListener(
- e -> this.presenter.setPrefixRepetitionRule(
- DefaultPrefixRepetitionRule.NO_RESTRICTION));
- prefixRuleButtons.add(noRestriction);
- prefixRepetitionPanel.add(noRestriction,
- new GridBagBuilder(0, 1)
- .setAnchor(GridBagConstraints.LINE_START)
- .build());
-
- final JRadioButton customRepetition = new JRadioButton(
- "Complex Repetition");
- if (this.presenter.prefixRule == DefaultPrefixRepetitionRule.COMPLEX_REPETITION) {
- customRepetition.setSelected(true);
- }
- customRepetition.addActionListener(
- e -> this.presenter.setPrefixRepetitionRule(
- DefaultPrefixRepetitionRule.COMPLEX_REPETITION));
- prefixRuleButtons.add(customRepetition);
- prefixRepetitionPanel.add(customRepetition,
- new GridBagBuilder(0, 2)
- .setAnchor(GridBagConstraints.LINE_START)
- .build());
- }
-
- { // search settings
- final JPanel searchingPanel = new JPanel();
- settingsPanel.add(searchingPanel);
- searchingPanel.setBorder(new TitledBorder("Search Settings"));
- searchingPanel.setLayout(new GridBagLayout());
-
- // searching rules
- final ButtonGroup searchRuleButtons = new ButtonGroup();
-
- final JRadioButton noPrefixes = new JRadioButton(
- "Never Include Prefixed Units");
- noPrefixes.setEnabled(false);
- searchRuleButtons.add(noPrefixes);
- searchingPanel.add(noPrefixes, new GridBagBuilder(0, 0)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton fixedPrefixes = new JRadioButton(
- "Include Some Prefixes");
- fixedPrefixes.setEnabled(false);
- searchRuleButtons.add(fixedPrefixes);
- searchingPanel.add(fixedPrefixes, new GridBagBuilder(0, 1)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton explicitPrefixes = new JRadioButton(
- "Include Explicit Prefixes");
- explicitPrefixes.setEnabled(false);
- searchRuleButtons.add(explicitPrefixes);
- searchingPanel.add(explicitPrefixes, new GridBagBuilder(0, 2)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JRadioButton alwaysInclude = new JRadioButton(
- "Include All Single Prefixes");
- alwaysInclude.setEnabled(false);
- searchRuleButtons.add(alwaysInclude);
- searchingPanel.add(alwaysInclude, new GridBagBuilder(0, 3)
- .setAnchor(GridBagConstraints.LINE_START).build());
- }
-
- { // miscellaneous settings
- final JPanel miscPanel = new JPanel();
- settingsPanel.add(miscPanel);
- miscPanel
- .setBorder(new TitledBorder("Miscellaneous Settings"));
- miscPanel.setLayout(new GridBagLayout());
-
- final JCheckBox oneWay = new JCheckBox(
- "Convert One Way Only");
- oneWay.setSelected(this.presenter.oneWay);
- oneWay.addItemListener(
- e -> this.presenter.setOneWay(e.getStateChange() == 1));
- miscPanel.add(oneWay, new GridBagBuilder(0, 0)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JCheckBox showAllVariations = new JCheckBox(
- "Show Duplicates in \"Convert Units\"");
- showAllVariations
- .setSelected(this.presenter.includeDuplicateUnits);
- showAllVariations.addItemListener(e -> this.presenter
- .setIncludeDuplicateUnits(e.getStateChange() == 1));
- miscPanel.add(showAllVariations, new GridBagBuilder(0, 1)
- .setAnchor(GridBagConstraints.LINE_START).build());
-
- final JButton unitFileButton = new JButton(
- "Manage Unit Data Files");
- unitFileButton.setEnabled(false);
- miscPanel.add(unitFileButton, new GridBagBuilder(0, 2)
- .setAnchor(GridBagConstraints.LINE_START).build());
- }
- }
- }
- }
-
- /**
- * Sets the text in the output of the dimension-based converter.
- *
- * @param text text to set
- * @since 2019-04-13
- * @since v0.2.0
- */
- public void setDimensionConverterOutputText(final String text) {
- this.dimensionBasedOutput.setText(text);
- }
-
- /**
- * Sets the text in the output of the conversion panel.
- *
- * @param text text to set
- * @since 2019-01-15
- * @since v0.1.0
- */
- public void setExpressionConverterOutputText(final String text) {
- this.output.setText(text);
- }
-
- /**
- * Sets the text of the prefix text box in the prefix viewer.
- *
- * @param text text to set
- * @since 2019-01-15
- * @since v0.1.0
- */
- public void setPrefixTextBoxText(final String text) {
- this.prefixTextBox.setText(text);
- }
-
- /**
- * Sets the text of the unit text box in the unit viewer.
- *
- * @param text text to set
- * @since 2019-01-15
- * @since v0.1.0
- */
- public void setUnitTextBoxText(final String text) {
- this.unitTextBox.setText(text);
- }
-
- /**
- * Shows an error dialog.
- *
- * @param title title of dialog
- * @param message message in dialog
- * @since 2019-01-14
- * @since v0.1.0
- */
- public void showErrorDialog(final String title, final String message) {
- JOptionPane.showMessageDialog(this.frame, message, title,
- JOptionPane.ERROR_MESSAGE);
- }
-
- public void update() {
- this.unitNameList.setItems(this.presenter.unitNameSet());
- this.fromSearch.setItems(this.presenter.fromEntries());
- this.toSearch.setItems(this.presenter.toEntries());
-
- switch (this.getActivePane()) {
- case UNIT_CONVERTER:
- this.fromSearch.updateList();
- this.toSearch.updateList();
- break;
- default:
- // do nothing, for now
- break;
- }
- }
- }
-
- public static void main(final String[] args) {
- new View().init();
- }
-}