summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrien Hopkins <ahopk127@my.yorku.ca>2022-04-19 16:35:43 -0500
committerAdrien Hopkins <ahopk127@my.yorku.ca>2022-04-19 16:35:43 -0500
commit8cc60583134a4d01e9967424e5a51332de6cc38b (patch)
treeaf3bb8662dd4203fcfbd071f005e0619096f61ee
parent0aacba9fc8a9140fdf331172ad66afe280d09b5e (diff)
Finalized version 0.4.0-alpha.1
-rw-r--r--README.org2
-rw-r--r--build.gradle2
-rw-r--r--src/main/java/sevenUnits/ProgramInfo.java2
-rw-r--r--src/main/java/sevenUnits/converterGUI/DefaultPrefixRepetitionRule.java95
-rw-r--r--src/main/java/sevenUnits/converterGUI/DelegateListModel.java242
-rw-r--r--src/main/java/sevenUnits/converterGUI/FilterComparator.java129
-rw-r--r--src/main/java/sevenUnits/converterGUI/GridBagBuilder.java479
-rw-r--r--src/main/java/sevenUnits/converterGUI/MutablePredicate.java70
-rw-r--r--src/main/java/sevenUnits/converterGUI/SearchBoxList.java320
-rw-r--r--src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java1506
-rw-r--r--src/main/java/sevenUnitsGUI/Main.java (renamed from src/main/java/sevenUnits/converterGUI/package-info.java)24
-rw-r--r--src/main/java/sevenUnitsGUI/Presenter.java2
-rw-r--r--src/main/java/sevenUnitsGUI/TabbedView.java76
-rw-r--r--src/main/java/sevenUnitsGUI/View.java8
-rw-r--r--src/main/java/sevenUnitsGUI/ViewBot.java3
-rw-r--r--src/test/java/sevenUnitsGUI/PresenterTest.java4
-rw-r--r--src/test/resources/test-settings.txt2
17 files changed, 39 insertions, 2927 deletions
diff --git a/README.org b/README.org
index 2b4ffa0..f891bdc 100644
--- a/README.org
+++ b/README.org
@@ -1,4 +1,4 @@
-* 7Units v0.3.2
+* 7Units v0.4.0a1
(this project uses Semantic Versioning)
** What is it?
This is a unit converter, which allows you to convert between different units, and includes a GUI which can read unit data from a file (using some unit math) and convert between units that you type in, and has a unit and prefix viewer to check the units that have been loaded in.
diff --git a/build.gradle b/build.gradle
index 4484e9d..8b90060 100644
--- a/build.gradle
+++ b/build.gradle
@@ -8,7 +8,7 @@ java {
sourceCompatibility = JavaVersion.VERSION_11
}
-mainClassName = "sevenUnits.converterGUI.SevenUnitsGUI"
+mainClassName = "sevenUnitsGUI.Main"
repositories {
mavenCentral()
diff --git a/src/main/java/sevenUnits/ProgramInfo.java b/src/main/java/sevenUnits/ProgramInfo.java
index 6407d7c..f32d2c7 100644
--- a/src/main/java/sevenUnits/ProgramInfo.java
+++ b/src/main/java/sevenUnits/ProgramInfo.java
@@ -28,7 +28,7 @@ public final class ProgramInfo {
/** The version number (0.4.0-alpha+dev) */
public static final SemanticVersionNumber VERSION = SemanticVersionNumber
- .builder(0, 4, 0).preRelease("alpha").buildMetadata("dev").build();
+ .preRelease(0, 4, 0, "alpha", 1);
private ProgramInfo() {
// this class is only for static variables, you shouldn't be able to
diff --git a/src/main/java/sevenUnits/converterGUI/DefaultPrefixRepetitionRule.java b/src/main/java/sevenUnits/converterGUI/DefaultPrefixRepetitionRule.java
deleted file mode 100644
index 6b6abf0..0000000
--- a/src/main/java/sevenUnits/converterGUI/DefaultPrefixRepetitionRule.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * @since 2020-08-26
- */
-package sevenUnits.converterGUI;
-
-import java.util.List;
-import java.util.function.Predicate;
-
-import sevenUnits.unit.Metric;
-import sevenUnits.unit.UnitPrefix;
-
-/**
- * A rule that specifies whether prefix repetition is allowed
- *
- * @since 2020-08-26
- */
-enum DefaultPrefixRepetitionRule implements Predicate<List<UnitPrefix>> {
- NO_REPETITION {
- @Override
- public boolean test(List<UnitPrefix> prefixes) {
- return prefixes.size() <= 1;
- }
- },
- NO_RESTRICTION {
- @Override
- public boolean test(List<UnitPrefix> prefixes) {
- return true;
- }
- },
- /**
- * You are allowed to have any number of Yotta/Yocto followed by possibly one
- * Kilo-Zetta/Milli-Zepto followed by possibly one Deca/Hecto. Same for
- * reducing prefixes, don't mix magnifying and reducing. Non-metric
- * (including binary) prefixes can't be repeated.
- */
- COMPLEX_REPETITION {
- @Override
- public boolean test(List<UnitPrefix> prefixes) {
- // determine whether we are magnifying or reducing
- final boolean magnifying;
- if (prefixes.isEmpty())
- return true;
- else if (prefixes.get(0).getMultiplier() > 1) {
- magnifying = true;
- } else {
- magnifying = false;
- }
-
- // if the first prefix is non-metric (including binary prefixes),
- // assume we are using non-metric prefixes
- // non-metric prefixes are allowed, but can't be repeated.
- if (!Metric.DECIMAL_PREFIXES.contains(prefixes.get(0)))
- return NO_REPETITION.test(prefixes);
-
- int part = 0; // 0=yotta/yoctos, 1=kilo-zetta/milli-zepto,
- // 2=deka,hecto,deci,centi
-
- for (final UnitPrefix prefix : prefixes) {
- // check that the current prefix is metric and appropriately
- // magnifying/reducing
- if (!Metric.DECIMAL_PREFIXES.contains(prefix))
- return false;
- if (magnifying != prefix.getMultiplier() > 1)
- return false;
-
- // check if the current prefix is correct
- // since part is set *after* this check, part designates the state
- // of the *previous* prefix
- switch (part) {
- case 0:
- // do nothing, any prefix is valid after a yotta
- break;
- case 1:
- // after a kilo-zetta, only deka/hecto are valid
- if (Metric.THOUSAND_PREFIXES.contains(prefix))
- return false;
- break;
- case 2:
- // deka/hecto must be the last prefix, so this is always invalid
- return false;
- }
-
- // set part
- if (Metric.YOTTA.equals(prefix) || Metric.YOCTO.equals(prefix)) {
- part = 0;
- } else if (Metric.THOUSAND_PREFIXES.contains(prefix)) {
- part = 1;
- } else {
- part = 2;
- }
- }
- return true;
- }
- };
-}
diff --git a/src/main/java/sevenUnits/converterGUI/DelegateListModel.java b/src/main/java/sevenUnits/converterGUI/DelegateListModel.java
deleted file mode 100644
index dd8cc97..0000000
--- a/src/main/java/sevenUnits/converterGUI/DelegateListModel.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/**
- * Copyright (C) 2018 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.util.ArrayList;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-
-import javax.swing.AbstractListModel;
-
-/**
- * A list model that delegates to a list.
- * <p>
- * It is recommended to use the delegate methods in DelegateListModel instead of the delegated list's methods because
- * the delegate methods handle updating the list.
- * </p>
- *
- * @author Adrien Hopkins
- * @since 2019-01-14
- * @since v0.1.0
- */
-final class DelegateListModel<E> extends AbstractListModel<E> implements List<E> {
- /**
- * @since 2019-01-14
- * @since v0.1.0
- */
- private static final long serialVersionUID = 8985494428224810045L;
-
- /**
- * The list that this model is a delegate to.
- *
- * @since 2019-01-14
- * @since v0.1.0
- */
- private final List<E> delegate;
-
- /**
- * Creates an empty {@code DelegateListModel}.
- *
- * @since 2019-04-13
- */
- public DelegateListModel() {
- this(new ArrayList<>());
- }
-
- /**
- * Creates the {@code DelegateListModel}.
- *
- * @param delegate
- * list to delegate
- * @since 2019-01-14
- * @since v0.1.0
- */
- public DelegateListModel(final List<E> delegate) {
- this.delegate = delegate;
- }
-
- @Override
- public boolean add(final E element) {
- final int index = this.delegate.size();
- final boolean success = this.delegate.add(element);
- this.fireIntervalAdded(this, index, index);
- return success;
- }
-
- @Override
- public void add(final int index, final E element) {
- this.delegate.add(index, element);
- this.fireIntervalAdded(this, index, index);
- }
-
- @Override
- public boolean addAll(final Collection<? extends E> c) {
- boolean changed = false;
- for (final E e : c) {
- if (this.add(e)) {
- changed = true;
- }
- }
- return changed;
- }
-
- @Override
- public boolean addAll(final int index, final Collection<? extends E> c) {
- for (final E e : c) {
- this.add(index, e);
- }
- return !c.isEmpty(); // Since this is a list, it will always change if c has elements.
- }
-
- @Override
- public void clear() {
- final int oldSize = this.delegate.size();
- this.delegate.clear();
- if (oldSize >= 1) {
- this.fireIntervalRemoved(this, 0, oldSize - 1);
- }
- }
-
- @Override
- public boolean contains(final Object elem) {
- return this.delegate.contains(elem);
- }
-
- @Override
- public boolean containsAll(final Collection<?> c) {
- for (final Object e : c) {
- if (!c.contains(e))
- return false;
- }
- return true;
- }
-
- @Override
- public E get(final int index) {
- return this.delegate.get(index);
- }
-
- @Override
- public E getElementAt(final int index) {
- return this.delegate.get(index);
- }
-
- @Override
- public int getSize() {
- return this.delegate.size();
- }
-
- @Override
- public int indexOf(final Object elem) {
- return this.delegate.indexOf(elem);
- }
-
- @Override
- public boolean isEmpty() {
- return this.delegate.isEmpty();
- }
-
- @Override
- public Iterator<E> iterator() {
- return this.delegate.iterator();
- }
-
- @Override
- public int lastIndexOf(final Object elem) {
- return this.delegate.lastIndexOf(elem);
- }
-
- @Override
- public ListIterator<E> listIterator() {
- return this.delegate.listIterator();
- }
-
- @Override
- public ListIterator<E> listIterator(final int index) {
- return this.delegate.listIterator(index);
- }
-
- @Override
- public E remove(final int index) {
- final E returnValue = this.delegate.get(index);
- this.delegate.remove(index);
- this.fireIntervalRemoved(this, index, index);
- return returnValue;
- }
-
- @Override
- public boolean remove(final Object o) {
- final int index = this.delegate.indexOf(o);
- final boolean returnValue = this.delegate.remove(o);
- this.fireIntervalRemoved(this, index, index);
- return returnValue;
- }
-
- @Override
- public boolean removeAll(final Collection<?> c) {
- boolean changed = false;
- for (final Object e : c) {
- if (this.remove(e)) {
- changed = true;
- }
- }
- return changed;
- }
-
- @Override
- public boolean retainAll(final Collection<?> c) {
- final int oldSize = this.size();
- final boolean returnValue = this.delegate.retainAll(c);
- this.fireIntervalRemoved(this, this.size(), oldSize - 1);
- return returnValue;
- }
-
- @Override
- public E set(final int index, final E element) {
- final E returnValue = this.delegate.get(index);
- this.delegate.set(index, element);
- this.fireContentsChanged(this, index, index);
- return returnValue;
- }
-
- @Override
- public int size() {
- return this.delegate.size();
- }
-
- @Override
- public List<E> subList(final int fromIndex, final int toIndex) {
- return this.delegate.subList(fromIndex, toIndex);
- }
-
- @Override
- public Object[] toArray() {
- return this.delegate.toArray();
- }
-
- @Override
- public <T> T[] toArray(final T[] a) {
- return this.delegate.toArray(a);
- }
-
- @Override
- public String toString() {
- return this.delegate.toString();
- }
-}
diff --git a/src/main/java/sevenUnits/converterGUI/FilterComparator.java b/src/main/java/sevenUnits/converterGUI/FilterComparator.java
deleted file mode 100644
index edd00e2..0000000
--- a/src/main/java/sevenUnits/converterGUI/FilterComparator.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/**
- * Copyright (C) 2018 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.util.Comparator;
-import java.util.Objects;
-
-/**
- * A comparator that compares strings using a filter.
- *
- * @author Adrien Hopkins
- * @since 2019-01-15
- * @since v0.1.0
- */
-final class FilterComparator implements Comparator<String> {
- /**
- * The filter that the comparator is filtered by.
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- private final String filter;
- /**
- * The comparator to use if the arguments are otherwise equal.
- *
- * @since 2019-01-15
- * @since v0.1.0
- */
- private final Comparator<String> comparator;
- /**
- * Whether or not the comparison is case-sensitive.
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- private final boolean caseSensitive;
-
- /**
- * Creates the {@code FilterComparator}.
- *
- * @param filter
- * @since 2019-01-15
- * @since v0.1.0
- */
- public FilterComparator(final String filter) {
- this(filter, null);
- }
-
- /**
- * Creates the {@code FilterComparator}.
- *
- * @param filter
- * string to filter by
- * @param comparator
- * comparator to fall back to if all else fails, null is compareTo.
- * @throws NullPointerException
- * if filter is null
- * @since 2019-01-15
- * @since v0.1.0
- */
- public FilterComparator(final String filter, final Comparator<String> comparator) {
- this(filter, comparator, false);
- }
-
- /**
- * Creates the {@code FilterComparator}.
- *
- * @param filter
- * string to filter by
- * @param comparator
- * comparator to fall back to if all else fails, null is compareTo.
- * @param caseSensitive
- * whether or not the comparator is case-sensitive
- * @throws NullPointerException
- * if filter is null
- * @since 2019-04-14
- * @since v0.2.0
- */
- public FilterComparator(final String filter, final Comparator<String> comparator, final boolean caseSensitive) {
- this.filter = Objects.requireNonNull(filter, "filter must not be null.");
- this.comparator = comparator;
- this.caseSensitive = caseSensitive;
- }
-
- @Override
- public int compare(final String arg0, final String arg1) {
- // if this is case insensitive, make them lowercase
- final String str0, str1;
- if (this.caseSensitive) {
- str0 = arg0;
- str1 = arg1;
- } else {
- str0 = arg0.toLowerCase();
- str1 = arg1.toLowerCase();
- }
-
- // elements that start with the filter always go first
- if (str0.startsWith(this.filter) && !str1.startsWith(this.filter))
- return -1;
- else if (!str0.startsWith(this.filter) && str1.startsWith(this.filter))
- return 1;
-
- // elements that contain the filter but don't start with them go next
- if (str0.contains(this.filter) && !str1.contains(this.filter))
- return -1;
- else if (!str0.contains(this.filter) && !str1.contains(this.filter))
- return 1;
-
- // other elements go last
- if (this.comparator == null)
- return str0.compareTo(str1);
- else
- return this.comparator.compare(str0, str1);
- }
-}
diff --git a/src/main/java/sevenUnits/converterGUI/GridBagBuilder.java b/src/main/java/sevenUnits/converterGUI/GridBagBuilder.java
deleted file mode 100644
index 0b71d78..0000000
--- a/src/main/java/sevenUnits/converterGUI/GridBagBuilder.java
+++ /dev/null
@@ -1,479 +0,0 @@
-/**
- * Copyright (C) 2018 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.GridBagConstraints;
-import java.awt.Insets;
-
-/**
- * A builder for Java's {@link java.awt.GridBagConstraints} class.
- *
- * @author Adrien Hopkins
- * @since 2018-11-30
- * @since v0.1.0
- */
-final class GridBagBuilder {
- /**
- * The built {@code GridBagConstraints}'s {@code gridx} property.
- * <p>
- * Specifies the cell containing the leading edge of the component's display area, where the first cell in a row has
- * <code>gridx=0</code>. The leading edge of a component's display area is its left edge for a horizontal,
- * left-to-right container and its right edge for a horizontal, right-to-left container. The value
- * <code>RELATIVE</code> specifies that the component be placed immediately following the component that was added
- * to the container just before this component was added.
- * <p>
- * The default value is <code>RELATIVE</code>. <code>gridx</code> should be a non-negative value.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#gridy
- * @see java.awt.ComponentOrientation
- */
- private final int gridx;
-
- /**
- * The built {@code GridBagConstraints}'s {@code gridy} property.
- * <p>
- * Specifies the cell at the top of the component's display area, where the topmost cell has <code>gridy=0</code>.
- * The value <code>RELATIVE</code> specifies that the component be placed just below the component that was added to
- * the container just before this component was added.
- * <p>
- * The default value is <code>RELATIVE</code>. <code>gridy</code> should be a non-negative value.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#gridx
- */
- private final int gridy;
-
- /**
- * The built {@code GridBagConstraints}'s {@code gridwidth} property.
- * <p>
- * Specifies the number of cells in a row for the component's display area.
- * <p>
- * Use <code>REMAINDER</code> to specify that the component's display area will be from <code>gridx</code> to the
- * last cell in the row. Use <code>RELATIVE</code> to specify that the component's display area will be from
- * <code>gridx</code> to the next to the last one in its row.
- * <p>
- * <code>gridwidth</code> should be non-negative and the default value is 1.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#gridheight
- */
- private final int gridwidth;
-
- /**
- * The built {@code GridBagConstraints}'s {@code gridheight} property.
- * <p>
- * Specifies the number of cells in a column for the component's display area.
- * <p>
- * Use <code>REMAINDER</code> to specify that the component's display area will be from <code>gridy</code> to the
- * last cell in the column. Use <code>RELATIVE</code> to specify that the component's display area will be from
- * <code>gridy</code> to the next to the last one in its column.
- * <p>
- * <code>gridheight</code> should be a non-negative value and the default value is 1.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#gridwidth
- */
- private final int gridheight;
-
- /**
- * The built {@code GridBagConstraints}'s {@code weightx} property.
- * <p>
- * Specifies how to distribute extra horizontal space.
- * <p>
- * The grid bag layout manager calculates the weight of a column to be the maximum <code>weightx</code> of all the
- * components in a column. If the resulting layout is smaller horizontally than the area it needs to fill, the extra
- * space is distributed to each column in proportion to its weight. A column that has a weight of zero receives no
- * extra space.
- * <p>
- * If all the weights are zero, all the extra space appears between the grids of the cell and the left and right
- * edges.
- * <p>
- * The default value of this field is <code>0</code>. <code>weightx</code> should be a non-negative value.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#weighty
- */
- private double weightx;
-
- /**
- * The built {@code GridBagConstraints}'s {@code weighty} property.
- * <p>
- * Specifies how to distribute extra vertical space.
- * <p>
- * The grid bag layout manager calculates the weight of a row to be the maximum <code>weighty</code> of all the
- * components in a row. If the resulting layout is smaller vertically than the area it needs to fill, the extra
- * space is distributed to each row in proportion to its weight. A row that has a weight of zero receives no extra
- * space.
- * <p>
- * If all the weights are zero, all the extra space appears between the grids of the cell and the top and bottom
- * edges.
- * <p>
- * The default value of this field is <code>0</code>. <code>weighty</code> should be a non-negative value.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#weightx
- */
- private double weighty;
-
- /**
- * The built {@code GridBagConstraints}'s {@code anchor} property.
- * <p>
- * This field is used when the component is smaller than its display area. It determines where, within the display
- * area, to place the component.
- * <p>
- * There are three kinds of possible values: orientation relative, baseline relative and absolute. Orientation
- * relative values are interpreted relative to the container's component orientation property, baseline relative
- * values are interpreted relative to the baseline and absolute values are not. The absolute values are:
- * <code>CENTER</code>, <code>NORTH</code>, <code>NORTHEAST</code>, <code>EAST</code>, <code>SOUTHEAST</code>,
- * <code>SOUTH</code>, <code>SOUTHWEST</code>, <code>WEST</code>, and <code>NORTHWEST</code>. The orientation
- * relative values are: <code>PAGE_START</code>, <code>PAGE_END</code>, <code>LINE_START</code>,
- * <code>LINE_END</code>, <code>FIRST_LINE_START</code>, <code>FIRST_LINE_END</code>, <code>LAST_LINE_START</code>
- * and <code>LAST_LINE_END</code>. The baseline relative values are: <code>BASELINE</code>,
- * <code>BASELINE_LEADING</code>, <code>BASELINE_TRAILING</code>, <code>ABOVE_BASELINE</code>,
- * <code>ABOVE_BASELINE_LEADING</code>, <code>ABOVE_BASELINE_TRAILING</code>, <code>BELOW_BASELINE</code>,
- * <code>BELOW_BASELINE_LEADING</code>, and <code>BELOW_BASELINE_TRAILING</code>. The default value is
- * <code>CENTER</code>.
- *
- * @serial
- * @see #clone()
- * @see java.awt.ComponentOrientation
- */
- private int anchor;
-
- /**
- * The built {@code GridBagConstraints}'s {@code fill} property.
- * <p>
- * This field is used when the component's display area is larger than the component's requested size. It determines
- * whether to resize the component, and if so, how.
- * <p>
- * The following values are valid for <code>fill</code>:
- *
- * <ul>
- * <li><code>NONE</code>: Do not resize the component.
- * <li><code>HORIZONTAL</code>: Make the component wide enough to fill its display area horizontally, but do not
- * change its height.
- * <li><code>VERTICAL</code>: Make the component tall enough to fill its display area vertically, but do not change
- * its width.
- * <li><code>BOTH</code>: Make the component fill its display area entirely.
- * </ul>
- * <p>
- * The default value is <code>NONE</code>.
- *
- * @serial
- * @see #clone()
- */
- private int fill;
-
- /**
- * The built {@code GridBagConstraints}'s {@code insets} property.
- * <p>
- * This field specifies the external padding of the component, the minimum amount of space between the component and
- * the edges of its display area.
- * <p>
- * The default value is <code>new Insets(0, 0, 0, 0)</code>.
- *
- * @serial
- * @see #clone()
- */
- private Insets insets;
-
- /**
- * The built {@code GridBagConstraints}'s {@code ipadx} property.
- * <p>
- * This field specifies the internal padding of the component, how much space to add to the minimum width of the
- * component. The width of the component is at least its minimum width plus <code>ipadx</code> pixels.
- * <p>
- * The default value is <code>0</code>.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#ipady
- */
- private int ipadx;
-
- /**
- * The built {@code GridBagConstraints}'s {@code ipady} property.
- * <p>
- * This field specifies the internal padding, that is, how much space to add to the minimum height of the component.
- * The height of the component is at least its minimum height plus <code>ipady</code> pixels.
- * <p>
- * The default value is 0.
- *
- * @serial
- * @see #clone()
- * @see java.awt.GridBagConstraints#ipadx
- */
- private int ipady;
-
- /**
- * @param gridx
- * x position
- * @param gridy
- * y position
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder(final int gridx, final int gridy) {
- this(gridx, gridy, 1, 1);
- }
-
- /**
- * @param gridx
- * x position
- * @param gridy
- * y position
- * @param gridwidth
- * number of cells occupied horizontally
- * @param gridheight
- * number of cells occupied vertically
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder(final int gridx, final int gridy, final int gridwidth, final int gridheight) {
- this(gridx, gridy, gridwidth, gridheight, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.NONE,
- new Insets(0, 0, 0, 0), 0, 0);
- }
-
- /**
- * @param gridx
- * x position
- * @param gridy
- * y position
- * @param gridwidth
- * number of cells occupied horizontally
- * @param gridheight
- * number of cells occupied vertically
- * @param weightx
- * @param weighty
- * @param anchor
- * @param fill
- * @param insets
- * @param ipadx
- * @param ipady
- * @since 2018-11-30
- * @since v0.1.0
- */
- private GridBagBuilder(final int gridx, final int gridy, final int gridwidth, final int gridheight,
- final double weightx, final double weighty, final int anchor, final int fill, final Insets insets,
- final int ipadx, final int ipady) {
- super();
- this.gridx = gridx;
- this.gridy = gridy;
- this.gridwidth = gridwidth;
- this.gridheight = gridheight;
- this.weightx = weightx;
- this.weighty = weighty;
- this.anchor = anchor;
- this.fill = fill;
- this.insets = (Insets) insets.clone();
- this.ipadx = ipadx;
- this.ipady = ipady;
- }
-
- /**
- * @return {@code GridBagConstraints} created by this builder
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagConstraints build() {
- return new GridBagConstraints(this.gridx, this.gridy, this.gridwidth, this.gridheight, this.weightx,
- this.weighty, this.anchor, this.fill, this.insets, this.ipadx, this.ipady);
- }
-
- /**
- * @return anchor
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getAnchor() {
- return this.anchor;
- }
-
- /**
- * @return fill
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getFill() {
- return this.fill;
- }
-
- /**
- * @return gridheight
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getGridheight() {
- return this.gridheight;
- }
-
- /**
- * @return gridwidth
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getGridwidth() {
- return this.gridwidth;
- }
-
- /**
- * @return gridx
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getGridx() {
- return this.gridx;
- }
-
- /**
- * @return gridy
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getGridy() {
- return this.gridy;
- }
-
- /**
- * @return insets
- * @since 2018-11-30
- * @since v0.1.0
- */
- public Insets getInsets() {
- return this.insets;
- }
-
- /**
- * @return ipadx
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getIpadx() {
- return this.ipadx;
- }
-
- /**
- * @return ipady
- * @since 2018-11-30
- * @since v0.1.0
- */
- public int getIpady() {
- return this.ipady;
- }
-
- /**
- * @return weightx
- * @since 2018-11-30
- * @since v0.1.0
- */
- public double getWeightx() {
- return this.weightx;
- }
-
- /**
- * @return weighty
- * @since 2018-11-30
- * @since v0.1.0
- */
- public double getWeighty() {
- return this.weighty;
- }
-
- /**
- * @param anchor
- * anchor to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setAnchor(final int anchor) {
- this.anchor = anchor;
- return this;
- }
-
- /**
- * @param fill
- * fill to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setFill(final int fill) {
- this.fill = fill;
- return this;
- }
-
- /**
- * @param insets
- * insets to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setInsets(final Insets insets) {
- this.insets = insets;
- return this;
- }
-
- /**
- * @param ipadx
- * ipadx to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setIpadx(final int ipadx) {
- this.ipadx = ipadx;
- return this;
- }
-
- /**
- * @param ipady
- * ipady to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setIpady(final int ipady) {
- this.ipady = ipady;
- return this;
- }
-
- /**
- * @param weightx
- * weightx to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setWeightx(final double weightx) {
- this.weightx = weightx;
- return this;
- }
-
- /**
- * @param weighty
- * weighty to set
- * @since 2018-11-30
- * @since v0.1.0
- */
- public GridBagBuilder setWeighty(final double weighty) {
- this.weighty = weighty;
- return this;
- }
-}
diff --git a/src/main/java/sevenUnits/converterGUI/MutablePredicate.java b/src/main/java/sevenUnits/converterGUI/MutablePredicate.java
deleted file mode 100644
index ae6b7a1..0000000
--- a/src/main/java/sevenUnits/converterGUI/MutablePredicate.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * Copyright (C) 2019 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.util.function.Predicate;
-
-/**
- * A container for a predicate, which can be changed later.
- *
- * @author Adrien Hopkins
- * @since 2019-04-13
- * @since v0.2.0
- */
-final class MutablePredicate<T> implements Predicate<T> {
- /**
- * The predicate stored in this {@code MutablePredicate}
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private Predicate<T> predicate;
-
- /**
- * Creates the {@code MutablePredicate}.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public MutablePredicate(final Predicate<T> predicate) {
- this.predicate = predicate;
- }
-
- /**
- * @return predicate
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final Predicate<T> getPredicate() {
- return this.predicate;
- }
-
- /**
- * @param predicate
- * new value of predicate
- * @since 2019-04-13
- * @since v0.2.0
- */
- public final void setPredicate(final Predicate<T> predicate) {
- this.predicate = predicate;
- }
-
- @Override
- public boolean test(final T t) {
- return this.predicate.test(t);
- }
-}
diff --git a/src/main/java/sevenUnits/converterGUI/SearchBoxList.java b/src/main/java/sevenUnits/converterGUI/SearchBoxList.java
deleted file mode 100644
index 2aa9fce..0000000
--- a/src/main/java/sevenUnits/converterGUI/SearchBoxList.java
+++ /dev/null
@@ -1,320 +0,0 @@
-/**
- * Copyright (C) 2019 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.Color;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.function.Predicate;
-
-import javax.swing.JList;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.JTextField;
-
-/**
- * @author Adrien Hopkins
- * @since 2019-04-13
- * @since v0.2.0
- */
-final class SearchBoxList extends JPanel {
-
- /**
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final long serialVersionUID = 6226930279415983433L;
-
- /**
- * The text to place in an empty search box.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final String EMPTY_TEXT = "Search...";
-
- /**
- * The color to use for an empty foreground.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- private static final Color EMPTY_FOREGROUND = new Color(192, 192, 192);
-
- // the components
- private final Collection<String> itemsToFilter;
- private final DelegateListModel<String> listModel;
- private final JTextField searchBox;
- private final JList<String> searchItems;
-
- private boolean searchBoxEmpty = true;
-
- // I need to do this because, for some reason, Swing is auto-focusing my
- // search box without triggering a focus
- // event.
- private boolean searchBoxFocused = false;
-
- private Predicate<String> customSearchFilter = o -> true;
- private final Comparator<String> defaultOrdering;
- private final boolean caseSensitive;
-
- /**
- * Creates the {@code SearchBoxList}.
- *
- * @param itemsToFilter items to put in the list
- * @since 2019-04-14
- */
- public SearchBoxList(final Collection<String> itemsToFilter) {
- this(itemsToFilter, null, false);
- }
-
- /**
- * Creates the {@code SearchBoxList}.
- *
- * @param itemsToFilter items to put in the list
- * @param defaultOrdering default ordering of items after filtration
- * (null=Comparable)
- * @param caseSensitive whether or not the filtration is case-sensitive
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public SearchBoxList(final Collection<String> itemsToFilter,
- final Comparator<String> defaultOrdering,
- final boolean caseSensitive) {
- super(new BorderLayout(), true);
- this.itemsToFilter = new ArrayList<>(itemsToFilter);
- this.defaultOrdering = defaultOrdering;
- this.caseSensitive = caseSensitive;
-
- // create the components
- this.listModel = new DelegateListModel<>(new ArrayList<>(itemsToFilter));
- this.searchItems = new JList<>(this.listModel);
-
- this.searchBox = new JTextField(EMPTY_TEXT);
- this.searchBox.setForeground(EMPTY_FOREGROUND);
-
- // add them to the panel
- this.add(this.searchBox, BorderLayout.PAGE_START);
- this.add(new JScrollPane(this.searchItems), BorderLayout.CENTER);
-
- // set up the search box
- this.searchBox.addFocusListener(new FocusListener() {
- @Override
- public void focusGained(final FocusEvent e) {
- SearchBoxList.this.searchBoxFocusGained(e);
- }
-
- @Override
- public void focusLost(final FocusEvent e) {
- SearchBoxList.this.searchBoxFocusLost(e);
- }
- });
-
- this.searchBox.addCaretListener(e -> this.searchBoxTextChanged());
- this.searchBoxEmpty = true;
- }
-
- /**
- * Adds an additional filter for searching.
- *
- * @param filter filter to add.
- * @since 2019-04-13
- * @since v0.2.0
- */
- public void addSearchFilter(final Predicate<String> filter) {
- this.customSearchFilter = this.customSearchFilter.and(filter);
- }
-
- /**
- * Resets the search filter.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public void clearSearchFilters() {
- this.customSearchFilter = o -> true;
- }
-
- /**
- * @return this component's search box component
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final JTextField getSearchBox() {
- return this.searchBox;
- }
-
- /**
- * @param searchText text to search for
- * @return a filter that filters out that text, based on this list's case
- * sensitive setting
- * @since 2019-04-14
- * @since v0.2.0
- */
- private Predicate<String> getSearchFilter(final String searchText) {
- if (this.caseSensitive)
- return string -> string.contains(searchText);
- else
- return string -> string.toLowerCase()
- .contains(searchText.toLowerCase());
- }
-
- /**
- * @return this component's list component
- * @since 2019-04-14
- * @since v0.2.0
- */
- public final JList<String> getSearchList() {
- return this.searchItems;
- }
-
- /**
- * @return index selected in item list
- * @since 2019-04-14
- * @since v0.2.0
- */
- public int getSelectedIndex() {
- return this.searchItems.getSelectedIndex();
- }
-
- /**
- * @return value selected in item list
- * @since 2019-04-13
- * @since v0.2.0
- */
- public String getSelectedValue() {
- return this.searchItems.getSelectedValue();
- }
-
- /**
- * Re-applies the filters.
- *
- * @since 2019-04-13
- * @since v0.2.0
- */
- public void reapplyFilter() {
- final String searchText = this.searchBoxEmpty ? ""
- : this.searchBox.getText();
- final FilterComparator comparator = new FilterComparator(searchText,
- this.defaultOrdering, this.caseSensitive);
- final Predicate<String> searchFilter = this.getSearchFilter(searchText);
-
- this.listModel.clear();
- this.itemsToFilter.forEach(string -> {
- if (searchFilter.test(string)) {
- this.listModel.add(string);
- }
- });
-
- // applies the custom filters
- this.listModel.removeIf(this.customSearchFilter.negate());
-
- // sorts the remaining items
- this.listModel.sort(comparator);
- }
-
- /**
- * Runs whenever the search box gains focus.
- *
- * @param e focus event
- * @since 2019-04-13
- * @since v0.2.0
- */
- private void searchBoxFocusGained(final FocusEvent e) {
- this.searchBoxFocused = true;
- if (this.searchBoxEmpty) {
- this.searchBox.setText("");
- this.searchBox.setForeground(Color.BLACK);
- }
- }
-
- /**
- * Runs whenever the search box loses focus.
- *
- * @param e focus event
- * @since 2019-04-13
- * @since v0.2.0
- */
- private void searchBoxFocusLost(final FocusEvent e) {
- this.searchBoxFocused = false;
- if (this.searchBoxEmpty) {
- this.searchBox.setText(EMPTY_TEXT);
- this.searchBox.setForeground(EMPTY_FOREGROUND);
- }
- }
-
- /**
- * Runs whenever the text in the search box is changed.
- * <p>
- * Reapplies the search filter, and custom filters.
- * </p>
- *
- * @since 2019-04-14
- * @since v0.2.0
- */
- private void searchBoxTextChanged() {
- if (this.searchBoxFocused) {
- this.searchBoxEmpty = this.searchBox.getText().equals("");
- }
- final String searchText = this.searchBoxEmpty ? ""
- : this.searchBox.getText();
- final FilterComparator comparator = new FilterComparator(searchText,
- this.defaultOrdering, this.caseSensitive);
- final Predicate<String> searchFilter = this.getSearchFilter(searchText);
-
- // initialize list with items that match the filter then sort
- this.listModel.clear();
- this.itemsToFilter.forEach(string -> {
- if (searchFilter.test(string)) {
- this.listModel.add(string);
- }
- });
-
- // applies the custom filters
- this.listModel.removeIf(this.customSearchFilter.negate());
-
- // sorts the remaining items
- this.listModel.sort(comparator);
- }
-
- /**
- * Resets the search box list's contents to the provided items, removing any
- * old items
- *
- * @param newItems new items to put in list
- * @since 2021-05-22
- */
- public void setItems(Collection<String> newItems) {
- this.itemsToFilter.clear();
- this.itemsToFilter.addAll(newItems);
- this.reapplyFilter();
- }
-
- /**
- * Manually updates the search box's item list.
- *
- * @since 2020-08-27
- */
- public void updateList() {
- this.searchBoxTextChanged();
- }
-}
diff --git a/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java b/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java
deleted file mode 100644
index 309bdb9..0000000
--- a/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java
+++ /dev/null
@@ -1,1506 +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.Metric;
-import sevenUnits.unit.Unit;
-import sevenUnits.unit.UnitDatabase;
-import sevenUnits.unit.UnitPrefix;
-import sevenUnits.unit.UnitValue;
-import sevenUnits.utils.ConditionalExistenceCollections;
-import sevenUnits.utils.NameSymbol;
-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(true).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, RoundingMode.HALF_EVEN);
- 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(true).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.toString());
- 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();
- }
-}
diff --git a/src/main/java/sevenUnits/converterGUI/package-info.java b/src/main/java/sevenUnitsGUI/Main.java
index 784664f..b5a896f 100644
--- a/src/main/java/sevenUnits/converterGUI/package-info.java
+++ b/src/main/java/sevenUnitsGUI/Main.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2019 Adrien Hopkins
+ * Copyright (C) 2022 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
@@ -14,11 +14,21 @@
* 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 sevenUnitsGUI;
+
/**
- * The GUI interface of the Unit Converter.
- *
- * @author Adrien Hopkins
- * @since 2019-01-25
- * @since v0.2.0
+ * The main code for the 7Units GUI
+ *
+ * @since 2022-04-19
*/
-package sevenUnits.converterGUI; \ No newline at end of file
+public final class Main {
+
+ /**
+ * @param args
+ * @since 2022-04-19
+ */
+ public static void main(String[] args) {
+ View.createTabbedView();
+ }
+
+}
diff --git a/src/main/java/sevenUnitsGUI/Presenter.java b/src/main/java/sevenUnitsGUI/Presenter.java
index fd050b7..4feea44 100644
--- a/src/main/java/sevenUnitsGUI/Presenter.java
+++ b/src/main/java/sevenUnitsGUI/Presenter.java
@@ -567,7 +567,7 @@ public final class Presenter {
* @return true iff the One-Way Conversion feature is available (views that
* show units as a list will have metric units removed from the From
* unit list and imperial/USC units removed from the To unit list)
- *
+ *
* @since 2022-03-30
*/
public boolean oneWayConversionEnabled() {
diff --git a/src/main/java/sevenUnitsGUI/TabbedView.java b/src/main/java/sevenUnitsGUI/TabbedView.java
index c8e69ee..be80ccb 100644
--- a/src/main/java/sevenUnitsGUI/TabbedView.java
+++ b/src/main/java/sevenUnitsGUI/TabbedView.java
@@ -124,87 +124,17 @@ final class TabbedView implements ExpressionConversionView, UnitConversionView {
* Rounds to a fixed number of significant digits. Precision is used,
* representing the number of significant digits to round to.
*/
- SIGNIFICANT_DIGITS(true) {
- @Override
- public Function<UncertainDouble, String> getRuleFromPrecision(
- int precision) {
- return StandardDisplayRules.fixedPrecision(precision);
- }
- },
+ SIGNIFICANT_DIGITS,
/**
* Rounds to a fixed number of decimal places. Precision is used,
* representing the number of decimal places to round to.
*/
- DECIMAL_PLACES(true) {
- @Override
- public Function<UncertainDouble, String> getRuleFromPrecision(
- int precision) {
- return StandardDisplayRules.fixedDecimals(precision);
- }
- },
+ DECIMAL_PLACES,
/**
* Rounds according to UncertainDouble's toString method. The specified
* precision is ignored.
*/
- UNCERTAINTY(false) {
- @Override
- public Function<UncertainDouble, String> getRuleFromPrecision(
- int precision) {
- return StandardDisplayRules.uncertaintyBased();
- }
- };
-
- /**
- * If true, this type of rounding rule requires you to specify a
- * precision.
- */
- private final boolean requiresPrecision;
-
- /**
- * @param canCustomizePrecision
- * @since 2022-04-18
- */
- private StandardRoundingType(boolean requiresPrecision) {
- this.requiresPrecision = requiresPrecision;
- }
-
- /**
- * Gets a rounding rule of this type.
- *
- * @param precision the rounding type's precision. If
- * {@link #requiresPrecision} is false, this field will
- * be ignored.
- * @return rounding rule
- * @since 2022-04-18
- */
- public abstract Function<UncertainDouble, String> getRuleFromPrecision(
- int precision);
-
- /**
- * Tries to get this rule without specifying precision.
- *
- * @throws UnsupportedOperationException if this rule requires specifying
- * precision
- * @since 2022-04-18
- */
- public final Function<UncertainDouble, String> getRuleWithoutPrecision() {
- if (this.requiresPrecision())
- throw new UnsupportedOperationException("Rounding type " + this
- + " requires you to specify precision.");
- else
- // random number to mess with anyone who lies about whether or not
- // precision is required
- return this.getRuleFromPrecision(-623546735);
- }
-
- /**
- * @return whether or not this rounding type requires you to specify an
- * integer precision
- * @since 2022-04-18
- */
- public boolean requiresPrecision() {
- return this.requiresPrecision;
- }
+ UNCERTAINTY;
}
/**
diff --git a/src/main/java/sevenUnitsGUI/View.java b/src/main/java/sevenUnitsGUI/View.java
index 011e87f..b2d2b94 100644
--- a/src/main/java/sevenUnitsGUI/View.java
+++ b/src/main/java/sevenUnitsGUI/View.java
@@ -30,6 +30,14 @@ import sevenUnits.utils.NameSymbol;
*/
public interface View {
/**
+ * @return a new tabbed view
+ * @since 2022-04-19
+ */
+ static View createTabbedView() {
+ return new TabbedView();
+ }
+
+ /**
* @return the presenter associated with this view
* @since 2022-04-19
*/
diff --git a/src/main/java/sevenUnitsGUI/ViewBot.java b/src/main/java/sevenUnitsGUI/ViewBot.java
index 9253ae5..a3ba7a2 100644
--- a/src/main/java/sevenUnitsGUI/ViewBot.java
+++ b/src/main/java/sevenUnitsGUI/ViewBot.java
@@ -34,7 +34,8 @@ import sevenUnits.utils.Nameable;
* @author Adrien Hopkins
* @since 2022-01-29
*/
-final class ViewBot implements UnitConversionView, ExpressionConversionView {
+public final class ViewBot
+ implements UnitConversionView, ExpressionConversionView {
/**
* A record of the parameters given to
* {@link View#showPrefix(NameSymbol, String)}, for testing.
diff --git a/src/test/java/sevenUnitsGUI/PresenterTest.java b/src/test/java/sevenUnitsGUI/PresenterTest.java
index f639329..3364e83 100644
--- a/src/test/java/sevenUnitsGUI/PresenterTest.java
+++ b/src/test/java/sevenUnitsGUI/PresenterTest.java
@@ -148,6 +148,7 @@ public final class PresenterTest {
presenter.database.clear();
presenter.database.addUnit("metre", metre);
presenter.database.addUnit("meter", meter);
+ presenter.setOneWayConversionEnabled(false);
// test that only one of them is included if duplicate units disabled
presenter.setShowDuplicates(false);
@@ -265,6 +266,8 @@ public final class PresenterTest {
presenter.setOneWayConversionEnabled(true);
presenter.setShowDuplicates(true);
presenter.setNumberDisplayRule(StandardDisplayRules.fixedPrecision(11));
+ presenter.setPrefixRepetitionRule(
+ DefaultPrefixRepetitionRule.COMPLEX_REPETITION);
presenter.saveSettings(TEST_SETTINGS);
// overwrite custom settings
@@ -323,6 +326,7 @@ public final class PresenterTest {
// setup
final ViewBot viewBot = new ViewBot();
final Presenter presenter = new Presenter(viewBot);
+ presenter.setOneWayConversionEnabled(false);
// override default database units
presenter.database.clear();
diff --git a/src/test/resources/test-settings.txt b/src/test/resources/test-settings.txt
index 932221e..a0f494a 100644
--- a/src/test/resources/test-settings.txt
+++ b/src/test/resources/test-settings.txt
@@ -1,4 +1,4 @@
number_display_rule=Round to 11 significant figures
-prefix_rule=NO_RESTRICTION
+prefix_rule=COMPLEX_REPETITION
one_way=true
include_duplicates=true