summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAdrien Hopkins <ahopk127@my.yorku.ca>2022-02-19 16:59:26 -0500
committerAdrien Hopkins <ahopk127@my.yorku.ca>2022-02-19 16:59:26 -0500
commit540b798e397fb787fd81c8e6e636a2343655a42f (patch)
tree8370b7f70da0be6284fe648d0bfa85d063ef48b9 /src
parentb179f3720fcd569c07f5fe95ee00d7ccfe12639d (diff)
Made barebones GUI (TabbedView)
Diffstat (limited to 'src')
-rw-r--r--src/main/java/sevenUnits/ProgramInfo.java2
-rw-r--r--src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java2
-rw-r--r--src/main/java/sevenUnitsGUI/DelegateListModel.java242
-rw-r--r--src/main/java/sevenUnitsGUI/FilterComparator.java128
-rw-r--r--src/main/java/sevenUnitsGUI/GridBagBuilder.java479
-rw-r--r--src/main/java/sevenUnitsGUI/MutablePredicate.java70
-rw-r--r--src/main/java/sevenUnitsGUI/Presenter.java88
-rw-r--r--src/main/java/sevenUnitsGUI/SearchBoxList.java331
-rw-r--r--src/main/java/sevenUnitsGUI/TabbedView.java605
-rw-r--r--src/main/java/sevenUnitsGUI/UnitConversionView.java10
-rw-r--r--src/main/java/sevenUnitsGUI/ViewBot.java23
-rw-r--r--src/main/resources/about.txt2
-rw-r--r--src/test/java/sevenUnitsGUI/PresenterTest.java12
13 files changed, 1968 insertions, 26 deletions
diff --git a/src/main/java/sevenUnits/ProgramInfo.java b/src/main/java/sevenUnits/ProgramInfo.java
index ba6bc7a..0d67824 100644
--- a/src/main/java/sevenUnits/ProgramInfo.java
+++ b/src/main/java/sevenUnits/ProgramInfo.java
@@ -24,7 +24,7 @@ package sevenUnits;
*/
public final class ProgramInfo {
- public static final String VERSION = "0.3.2";
+ public static final String VERSION = "0.4.0-dev";
private ProgramInfo() {
// this class is only for static variables, you shouldn't be able to
diff --git a/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java b/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java
index bfd5974..e21c25f 100644
--- a/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java
+++ b/src/main/java/sevenUnits/converterGUI/SevenUnitsGUI.java
@@ -69,8 +69,8 @@ import sevenUnits.unit.BaseDimension;
import sevenUnits.unit.BritishImperial;
import sevenUnits.unit.LinearUnit;
import sevenUnits.unit.LinearUnitValue;
-import sevenUnits.unit.NameSymbol;
import sevenUnits.unit.Metric;
+import sevenUnits.unit.NameSymbol;
import sevenUnits.unit.Unit;
import sevenUnits.unit.UnitDatabase;
import sevenUnits.unit.UnitPrefix;
diff --git a/src/main/java/sevenUnitsGUI/DelegateListModel.java b/src/main/java/sevenUnitsGUI/DelegateListModel.java
new file mode 100644
index 0000000..5938b59
--- /dev/null
+++ b/src/main/java/sevenUnitsGUI/DelegateListModel.java
@@ -0,0 +1,242 @@
+/**
+ * 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 sevenUnitsGUI;
+
+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/sevenUnitsGUI/FilterComparator.java b/src/main/java/sevenUnitsGUI/FilterComparator.java
new file mode 100644
index 0000000..f34d0c0
--- /dev/null
+++ b/src/main/java/sevenUnitsGUI/FilterComparator.java
@@ -0,0 +1,128 @@
+/**
+ * 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 sevenUnitsGUI;
+
+import java.util.Comparator;
+import java.util.Objects;
+
+/**
+ * A comparator that compares strings using a filter.
+ *
+ * @param <T> type of element being compared
+ *
+ * @author Adrien Hopkins
+ * @since 2019-01-15
+ * @since v0.1.0
+ */
+final class FilterComparator<T> implements Comparator<T> {
+ /**
+ * 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<T> 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<T> 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<T> 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 T arg0, final T arg1) {
+ // if this is case insensitive, make them lowercase
+ final String str0, str1;
+ if (this.caseSensitive) {
+ str0 = arg0.toString();
+ str1 = arg1.toString();
+ } else {
+ str0 = arg0.toString().toLowerCase();
+ str1 = arg1.toString().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(arg0, arg1);
+ }
+}
diff --git a/src/main/java/sevenUnitsGUI/GridBagBuilder.java b/src/main/java/sevenUnitsGUI/GridBagBuilder.java
new file mode 100644
index 0000000..32e94d7
--- /dev/null
+++ b/src/main/java/sevenUnitsGUI/GridBagBuilder.java
@@ -0,0 +1,479 @@
+/**
+ * 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 sevenUnitsGUI;
+
+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/sevenUnitsGUI/MutablePredicate.java b/src/main/java/sevenUnitsGUI/MutablePredicate.java
new file mode 100644
index 0000000..6cb8689
--- /dev/null
+++ b/src/main/java/sevenUnitsGUI/MutablePredicate.java
@@ -0,0 +1,70 @@
+/**
+ * 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 sevenUnitsGUI;
+
+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/sevenUnitsGUI/Presenter.java b/src/main/java/sevenUnitsGUI/Presenter.java
index 4373049..07671e4 100644
--- a/src/main/java/sevenUnitsGUI/Presenter.java
+++ b/src/main/java/sevenUnitsGUI/Presenter.java
@@ -16,12 +16,21 @@
*/
package sevenUnitsGUI;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Scanner;
import java.util.function.Function;
import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import sevenUnits.ProgramInfo;
+import sevenUnits.unit.BaseDimension;
+import sevenUnits.unit.Unit;
import sevenUnits.unit.UnitDatabase;
import sevenUnits.unit.UnitPrefix;
+import sevenUnits.utils.ObjectProduct;
import sevenUnits.utils.UncertainDouble;
/**
@@ -32,6 +41,62 @@ import sevenUnits.utils.UncertainDouble;
*/
public final class Presenter {
/**
+ * @return text in About file
+ * @since 2022-02-19
+ */
+ static final String getAboutText() {
+ return Presenter.getLinesFromResource("/about.txt").stream()
+ .map(Presenter::withoutComments).collect(Collectors.joining("\n"))
+ .replaceAll("\\[VERSION\\]", ProgramInfo.VERSION);
+ }
+
+ /**
+ * 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
+ */
+ private 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 Presenter.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);
+ }
+
+ // ====== SETTINGS ======
+
+ /**
* The view that this presenter communicates with
*/
private final View view;
@@ -41,8 +106,6 @@ public final class Presenter {
*/
private final UnitDatabase database;
- // ====== SETTINGS ======
-
/**
* The rule used for parsing input numbers. Any number-string inputted into
* this program will be parsed using this method.
@@ -136,6 +199,8 @@ public final class Presenter {
*/
public void loadSettings() {}
+ void prefixSelected() {}
+
/**
* Gets user settings from the view then saves them to the user's settings
* file.
@@ -143,4 +208,23 @@ public final class Presenter {
* @since 2021-12-15
*/
public void saveSettings() {}
+
+ /**
+ * 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
+ */
+ boolean unitMatchesDimension(String unitName, String dimensionName) {
+ final Unit unit = this.database.getUnit(unitName);
+ final ObjectProduct<BaseDimension> dimension = this.database
+ .getDimension(dimensionName);
+ return unit.getDimension().equals(dimension);
+ }
+
+ void unitNameSelected() {}
}
diff --git a/src/main/java/sevenUnitsGUI/SearchBoxList.java b/src/main/java/sevenUnitsGUI/SearchBoxList.java
new file mode 100644
index 0000000..2b935d0
--- /dev/null
+++ b/src/main/java/sevenUnitsGUI/SearchBoxList.java
@@ -0,0 +1,331 @@
+/**
+ * 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 sevenUnitsGUI;
+
+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.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+
+/**
+ * @param <E> type of element in list
+ * @author Adrien Hopkins
+ * @since 2019-04-13
+ * @since v0.2.0
+ */
+final class SearchBoxList<E> 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<E> itemsToFilter;
+ private final DelegateListModel<E> listModel;
+ private final JTextField searchBox;
+ private final JList<E> 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<E> customSearchFilter = o -> true;
+ private final Comparator<E> defaultOrdering;
+ private final boolean caseSensitive;
+
+ /**
+ * Creates an empty SearchBoxList
+ *
+ * @since 2022-02-19
+ */
+ public SearchBoxList() {
+ this(List.of(), null, false);
+ }
+
+ /**
+ * Creates the {@code SearchBoxList}.
+ *
+ * @param itemsToFilter items to put in the list
+ * @since 2019-04-14
+ */
+ public SearchBoxList(final Collection<E> 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<E> itemsToFilter,
+ final Comparator<E> 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<E> 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<E> getSearchFilter(final String searchText) {
+ if (this.caseSensitive)
+ return item -> item.toString().contains(searchText);
+ else
+ return item -> item.toString().toLowerCase()
+ .contains(searchText.toLowerCase());
+ }
+
+ /**
+ * @return this component's list component
+ * @since 2019-04-14
+ * @since v0.2.0
+ */
+ public final JList<E> getSearchList() {
+ return this.searchItems;
+ }
+
+ /**
+ * @return index selected in item list, -1 if no selection
+ * @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 Optional<E> getSelectedValue() {
+ return Optional.ofNullable(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<E> comparator = new FilterComparator<>(searchText,
+ this.defaultOrdering, this.caseSensitive);
+ final Predicate<E> searchFilter = this.getSearchFilter(searchText);
+
+ this.listModel.clear();
+ this.itemsToFilter.forEach(item -> {
+ if (searchFilter.test(item)) {
+ this.listModel.add(item);
+ }
+ });
+
+ // 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<E> comparator = new FilterComparator<>(searchText,
+ this.defaultOrdering, this.caseSensitive);
+ final Predicate<E> 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<? extends E> 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/sevenUnitsGUI/TabbedView.java b/src/main/java/sevenUnitsGUI/TabbedView.java
new file mode 100644
index 0000000..e92b661
--- /dev/null
+++ b/src/main/java/sevenUnitsGUI/TabbedView.java
@@ -0,0 +1,605 @@
+/**
+ * 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
+ * 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 sevenUnitsGUI;
+
+import java.awt.BorderLayout;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.event.KeyEvent;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Optional;
+import java.util.Set;
+
+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.SwingConstants;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.WindowConstants;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.TitledBorder;
+
+import sevenUnits.ProgramInfo;
+import sevenUnits.unit.BaseDimension;
+import sevenUnits.unit.Unit;
+import sevenUnits.unit.UnitPrefix;
+import sevenUnits.utils.NamedObjectProduct;
+import sevenUnits.utils.ObjectProduct;
+
+/**
+ * A View that separates its functions into multiple tabs
+ *
+ * @since 2022-02-19
+ */
+final class TabbedView implements ExpressionConversionView, UnitConversionView {
+ /**
+ * A List-like view of a JComboBox's items
+ *
+ * @param <E> type of item in list
+ *
+ * @since 2022-02-19
+ */
+ private static final class JComboBoxItemSet<E> extends AbstractSet<E> {
+ private final JComboBox<E> comboBox;
+
+ /**
+ * @param comboBox combo box to get items from
+ * @since 2022-02-19
+ */
+ public JComboBoxItemSet(JComboBox<E> comboBox) {
+ this.comboBox = comboBox;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new Iterator<>() {
+ private int index = 0;
+
+ @Override
+ public boolean hasNext() {
+ return this.index < JComboBoxItemSet.this.size();
+ }
+
+ @Override
+ public E next() {
+ if (this.hasNext())
+ return JComboBoxItemSet.this.comboBox.getItemAt(this.index++);
+ else
+ throw new NoSuchElementException(
+ "Iterator has finished iteration");
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ return this.comboBox.getItemCount();
+ }
+
+ }
+
+ private static final NumberFormat NUMBER_FORMATTER = new DecimalFormat();
+
+ /**
+ * Creates a TabbedView.
+ *
+ * @param args command line arguments
+ * @since 2022-02-19
+ */
+ public static void main(String[] args) {
+ // This view doesn't need to do anything, the side effects of creating it
+ // are enough to start the program
+ @SuppressWarnings("unused")
+ final View view = new TabbedView();
+ }
+
+ /** The Presenter that handles this View */
+ final Presenter presenter;
+ /** The frame that this view lives on */
+ final JFrame frame;
+ /** The tabbed pane that contains all of the components */
+ final JTabbedPane masterPane;
+
+ // DIMENSION-BASED CONVERTER
+ /** The combo box that selects dimensions */
+ private final JComboBox<NamedObjectProduct<BaseDimension>> dimensionSelector;
+ /** 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<Unit> fromSearch;
+ /** The panel for "To" in the dimension-based converter */
+ private final SearchBoxList<Unit> toSearch;
+ /** The output area in the dimension-based converter */
+ private final JTextArea unitOutput;
+
+ // 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 expressionOutput;
+
+ // UNIT AND PREFIX VIEWERS
+ /** The searchable list of unit names in the unit viewer */
+ private final SearchBoxList<Unit> unitNameList;
+ /** The searchable list of prefix names in the prefix viewer */
+ private final SearchBoxList<UnitPrefix> 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 view and makes it visible to the user
+ *
+ * @since 2022-02-19
+ */
+ public TabbedView() {
+ // 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();
+ }
+
+ // initialize important components
+ this.presenter = new Presenter(this);
+ this.frame = new JFrame("7Units " + ProgramInfo.VERSION);
+ this.frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+ // master components (those that contain everything else within them)
+ this.masterPane = new JTabbedPane();
+ this.frame.add(this.masterPane);
+
+ // ============ UNIT CONVERSION TAB ============
+ 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));
+ inputPanel.setBorder(new EmptyBorder(6, 6, 3, 6));
+
+ this.fromSearch = new SearchBoxList<>();
+ inputPanel.add(this.fromSearch);
+
+ final JPanel inBetweenPanel = new JPanel();
+ inputPanel.add(inBetweenPanel);
+ inBetweenPanel.setLayout(new BorderLayout());
+
+ this.dimensionSelector = new JComboBox<>();
+ inBetweenPanel.add(this.dimensionSelector, BorderLayout.PAGE_START);
+ this.dimensionSelector
+ .addItemListener(e -> this.presenter.applyDimensionFilter());
+
+ final JLabel arrowLabel = new JLabel("-->");
+ inBetweenPanel.add(arrowLabel, BorderLayout.CENTER);
+ arrowLabel.setHorizontalAlignment(SwingConstants.CENTER);
+
+ this.toSearch = new SearchBoxList<>();
+ inputPanel.add(this.toSearch);
+ }
+
+ { // panel for submit and output, and also value entry
+ final JPanel outputPanel = new JPanel();
+ convertUnitPanel.add(outputPanel, BorderLayout.PAGE_END);
+ outputPanel.setLayout(new BorderLayout());
+ outputPanel.setBorder(new EmptyBorder(3, 6, 6, 6));
+
+ final JLabel valuePrompt = new JLabel("Value to convert: ");
+ outputPanel.add(valuePrompt, BorderLayout.LINE_START);
+
+ this.valueInput = new JFormattedTextField(NUMBER_FORMATTER);
+ outputPanel.add(this.valueInput, BorderLayout.CENTER);
+
+ // conversion button
+ final JButton convertButton = new JButton("Convert");
+ outputPanel.add(convertButton, BorderLayout.LINE_END);
+ convertButton.addActionListener(e -> this.presenter.convertUnits());
+ convertButton.setMnemonic(KeyEvent.VK_ENTER);
+
+ // conversion output
+ this.unitOutput = new JTextArea(2, 32);
+ outputPanel.add(this.unitOutput, BorderLayout.PAGE_END);
+ this.unitOutput.setEditable(false);
+ }
+
+ // ============ EXPRESSION CONVERSION TAB ============
+ 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));
+
+ // from and to expressions
+ this.fromEntry = new JTextField();
+ convertExpressionPanel.add(this.fromEntry);
+ this.fromEntry.setBorder(BorderFactory.createTitledBorder("From"));
+
+ this.toEntry = new JTextField();
+ convertExpressionPanel.add(this.toEntry);
+ this.toEntry.setBorder(BorderFactory.createTitledBorder("To"));
+
+ // 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
+ this.expressionOutput = new JTextArea(2, 32);
+ convertExpressionPanel.add(this.expressionOutput);
+ this.expressionOutput
+ .setBorder(BorderFactory.createTitledBorder("Output"));
+ this.expressionOutput.setEditable(false);
+
+ // =========== UNIT VIEWER ===========
+ final JPanel unitLookupPanel = new JPanel();
+ this.masterPane.addTab("Unit Viewer", unitLookupPanel);
+ this.masterPane.setMnemonicAt(2, KeyEvent.VK_V);
+ unitLookupPanel.setLayout(new GridLayout());
+
+ this.unitNameList = new SearchBoxList<>();
+ unitLookupPanel.add(this.unitNameList);
+ this.unitNameList.getSearchList()
+ .addListSelectionListener(e -> this.presenter.unitNameSelected());
+
+ // the text box for unit's toString
+ this.unitTextBox = new JTextArea();
+ unitLookupPanel.add(this.unitTextBox);
+ this.unitTextBox.setEditable(false);
+ this.unitTextBox.setLineWrap(true);
+
+ // ============ PREFIX VIEWER =============
+ final JPanel prefixLookupPanel = new JPanel();
+ this.masterPane.addTab("Prefix Viewer", prefixLookupPanel);
+ this.masterPane.setMnemonicAt(3, KeyEvent.VK_P);
+ prefixLookupPanel.setLayout(new GridLayout(1, 2));
+
+ this.prefixNameList = new SearchBoxList<>();
+ prefixLookupPanel.add(this.prefixNameList);
+ this.prefixNameList.getSearchList()
+ .addListSelectionListener(e -> this.presenter.prefixSelected());
+
+ // the text box for prefix's toString
+ this.prefixTextBox = new JTextArea();
+ prefixLookupPanel.add(this.prefixTextBox);
+ this.prefixTextBox.setEditable(false);
+ this.prefixTextBox.setLineWrap(true);
+
+ 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);
+ infoTextArea.setText(Presenter.getAboutText());
+
+ // ============ SETTINGS PANEL ============
+ this.masterPane.addTab("\u2699",
+ new JScrollPane(this.createSettingsPanel()));
+ this.masterPane.setMnemonicAt(5, KeyEvent.VK_S);
+
+ // ============ FINALIZE CREATION OF VIEW ============
+ this.frame.pack();
+ this.frame.setVisible(true);
+
+ }
+
+ /**
+ * Creates and returns the settings panel (in its own function to make this
+ * code more organized, as this function is massive!)
+ *
+ * @since 2022-02-19
+ */
+ private JPanel createSettingsPanel() {
+ final JPanel settingsPanel = new JPanel();
+
+ 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());
+ }
+
+ // ============ OTHER SETTINGS ============
+ {
+ final JPanel miscPanel = new JPanel();
+ settingsPanel.add(miscPanel);
+ 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());
+ }
+
+ return settingsPanel;
+ }
+
+ @Override
+ public Set<NamedObjectProduct<BaseDimension>> getDimensions() {
+ return Collections
+ .unmodifiableSet(new JComboBoxItemSet<>(this.dimensionSelector));
+ }
+
+ @Override
+ public String getFromExpression() {
+ return this.fromEntry.getText();
+ }
+
+ @Override
+ public Optional<Unit> getFromSelection() {
+ return this.fromSearch.getSelectedValue();
+ }
+
+ @Override
+ public String getInputValue() {
+ return this.valueInput.getText();
+ }
+
+ @Override
+ public Optional<? extends ObjectProduct<BaseDimension>> getSelectedDimension() {
+ // this must work because this function can only return items that are in
+ // the selector, which are all of type ObjectProduct<BaseDimension>
+ @SuppressWarnings("unchecked")
+ final ObjectProduct<BaseDimension> selectedItem = (ObjectProduct<BaseDimension>) this.dimensionSelector
+ .getSelectedItem();
+ return Optional.ofNullable(selectedItem);
+ }
+
+ @Override
+ public String getToExpression() {
+ return this.toEntry.getText();
+ }
+
+ @Override
+ public Optional<Unit> getToSelection() {
+ return this.toSearch.getSelectedValue();
+ }
+
+ @Override
+ public void setDimensions(
+ Set<NamedObjectProduct<BaseDimension>> dimensions) {
+ this.dimensionSelector.removeAllItems();
+ for (final NamedObjectProduct<BaseDimension> d : dimensions) {
+ this.dimensionSelector.addItem(d);
+ }
+ }
+
+ @Override
+ public void setFromUnits(Set<? extends Unit> units) {
+ this.fromSearch.setItems(units);
+ }
+
+ @Override
+ public void setToUnits(Set<? extends Unit> units) {
+ this.toSearch.setItems(units);
+ }
+
+ @Override
+ public void showErrorMessage(String title, String message) {
+ JOptionPane.showMessageDialog(this.frame, message, title,
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ @Override
+ public void showExpressionConversionOutput(String fromExpression,
+ String toExpression, double value) {
+ this.expressionOutput.setText(
+ String.format("%s = %s %s", fromExpression, value, toExpression));
+ }
+
+ @Override
+ public void showUnitConversionOutput(String outputString) {
+ this.unitOutput.setText(outputString);
+ }
+
+}
diff --git a/src/main/java/sevenUnitsGUI/UnitConversionView.java b/src/main/java/sevenUnitsGUI/UnitConversionView.java
index 97ec30f..5fd5a82 100644
--- a/src/main/java/sevenUnitsGUI/UnitConversionView.java
+++ b/src/main/java/sevenUnitsGUI/UnitConversionView.java
@@ -16,8 +16,8 @@
*/
package sevenUnitsGUI;
-import java.util.List;
import java.util.Optional;
+import java.util.Set;
import sevenUnits.unit.BaseDimension;
import sevenUnits.unit.Unit;
@@ -35,7 +35,7 @@ public interface UnitConversionView extends View {
* @return dimensions available for filtering
* @since 2022-01-29
*/
- List<NamedObjectProduct<BaseDimension>> getDimensions();
+ Set<NamedObjectProduct<BaseDimension>> getDimensions();
/**
* @return unit to convert <em>from</em>
@@ -68,7 +68,7 @@ public interface UnitConversionView extends View {
* @param dimensions dimensions to use
* @since 2021-12-15
*/
- void setDimensions(List<NamedObjectProduct<BaseDimension>> dimensions);
+ void setDimensions(Set<NamedObjectProduct<BaseDimension>> dimensions);
/**
* Sets the available units to convert from. {@link #getFromSelection} is not
@@ -78,7 +78,7 @@ public interface UnitConversionView extends View {
* @param units units to convert from
* @since 2021-12-15
*/
- void setFromUnits(List<? extends Unit> units);
+ void setFromUnits(Set<? extends Unit> units);
/**
* Sets the available units to convert to. {@link #getToSelection} is not
@@ -88,7 +88,7 @@ public interface UnitConversionView extends View {
* @param units units to convert to
* @since 2021-12-15
*/
- void setToUnits(List<? extends Unit> units);
+ void setToUnits(Set<? extends Unit> units);
/**
* Shows the output of a unit conversion.
diff --git a/src/main/java/sevenUnitsGUI/ViewBot.java b/src/main/java/sevenUnitsGUI/ViewBot.java
index cc070e2..0c0d189 100644
--- a/src/main/java/sevenUnitsGUI/ViewBot.java
+++ b/src/main/java/sevenUnitsGUI/ViewBot.java
@@ -20,6 +20,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import sevenUnits.unit.BaseDimension;
import sevenUnits.unit.Unit;
@@ -38,7 +39,7 @@ final class ViewBot implements UnitConversionView, ExpressionConversionView {
private final Presenter presenter;
/** The dimensions available to select from */
- private List<NamedObjectProduct<BaseDimension>> dimensions;
+ private Set<NamedObjectProduct<BaseDimension>> dimensions;
/** The expression in the From field */
private String fromExpression;
/** The expression in the To field */
@@ -54,9 +55,9 @@ final class ViewBot implements UnitConversionView, ExpressionConversionView {
/** The currently selected dimension */
private Optional<? extends ObjectProduct<BaseDimension>> selectedDimension;
/** The units available in the From selection */
- private List<? extends Unit> fromUnits;
+ private Set<? extends Unit> fromUnits;
/** The units available in the To selection */
- private List<? extends Unit> toUnits;
+ private Set<? extends Unit> toUnits;
/** Saved output values of all unit conversions */
private List<String> unitConversionOutputValues;
@@ -74,7 +75,7 @@ final class ViewBot implements UnitConversionView, ExpressionConversionView {
* @since 2022-01-29
*/
@Override
- public List<NamedObjectProduct<BaseDimension>> getDimensions() {
+ public Set<NamedObjectProduct<BaseDimension>> getDimensions() {
return this.dimensions;
}
@@ -96,8 +97,8 @@ final class ViewBot implements UnitConversionView, ExpressionConversionView {
* @return the units available for selection in From
* @since 2022-01-29
*/
- public List<Unit> getFromUnits() {
- return Collections.unmodifiableList(this.fromUnits);
+ public Set<Unit> getFromUnits() {
+ return Collections.unmodifiableSet(this.fromUnits);
}
@Override
@@ -132,8 +133,8 @@ final class ViewBot implements UnitConversionView, ExpressionConversionView {
* @return the units available for selection in To
* @since 2022-01-29
*/
- public List<Unit> getToUnits() {
- return Collections.unmodifiableList(this.toUnits);
+ public Set<Unit> getToUnits() {
+ return Collections.unmodifiableSet(this.toUnits);
}
/**
@@ -146,7 +147,7 @@ final class ViewBot implements UnitConversionView, ExpressionConversionView {
@Override
public void setDimensions(
- List<NamedObjectProduct<BaseDimension>> dimensions) {
+ Set<NamedObjectProduct<BaseDimension>> dimensions) {
this.dimensions = Objects.requireNonNull(dimensions,
"dimensions may not be null");
}
@@ -181,7 +182,7 @@ final class ViewBot implements UnitConversionView, ExpressionConversionView {
}
@Override
- public void setFromUnits(List<? extends Unit> units) {
+ public void setFromUnits(Set<? extends Unit> units) {
this.fromUnits = Objects.requireNonNull(units, "units may not be null");
}
@@ -233,7 +234,7 @@ final class ViewBot implements UnitConversionView, ExpressionConversionView {
}
@Override
- public void setToUnits(List<? extends Unit> units) {
+ public void setToUnits(Set<? extends Unit> units) {
this.toUnits = Objects.requireNonNull(units, "units may not be null");
}
diff --git a/src/main/resources/about.txt b/src/main/resources/about.txt
index f175396..7780db3 100644
--- a/src/main/resources/about.txt
+++ b/src/main/resources/about.txt
@@ -2,7 +2,7 @@ About 7Units v[VERSION]
Copyright Notice:
-Unit Converter Copyright (C) 2018-2021 Adrien Hopkins
+Unit Converter Copyright (C) 2018-2022 Adrien Hopkins
This program comes with ABSOLUTELY NO WARRANTY;
for details read the LICENSE file, section 15
diff --git a/src/test/java/sevenUnitsGUI/PresenterTest.java b/src/test/java/sevenUnitsGUI/PresenterTest.java
index 675e3ab..3e7c2b5 100644
--- a/src/test/java/sevenUnitsGUI/PresenterTest.java
+++ b/src/test/java/sevenUnitsGUI/PresenterTest.java
@@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import org.junit.jupiter.api.Test;
@@ -36,9 +37,9 @@ import sevenUnits.utils.NamedObjectProduct;
* @since 2022-02-10
*/
public final class PresenterTest {
- List<Unit> testUnits = List.of(Metric.METRE, Metric.KILOMETRE,
+ Set<Unit> testUnits = Set.of(Metric.METRE, Metric.KILOMETRE,
Metric.METRE_PER_SECOND, Metric.KILOMETRE_PER_HOUR);
- List<NamedObjectProduct<BaseDimension>> testDimensions = List.of(
+ Set<NamedObjectProduct<BaseDimension>> testDimensions = Set.of(
Metric.Dimensions.LENGTH.withName(NameSymbol.ofName("Length")),
Metric.Dimensions.VELOCITY.withName(NameSymbol.ofName("Velocity")));
@@ -56,12 +57,13 @@ public final class PresenterTest {
viewBot.setFromUnits(this.testUnits);
viewBot.setToUnits(this.testUnits);
viewBot.setDimensions(this.testDimensions);
- viewBot.setSelectedDimension(Optional.of(this.testDimensions.get(0)));
+ viewBot.setSelectedDimension(
+ Optional.of(this.testDimensions.iterator().next()));
// filter to length units only, then get the filtered sets of units
presenter.applyDimensionFilter();
- final List<Unit> fromUnits = viewBot.getFromUnits();
- final List<Unit> toUnits = viewBot.getToUnits();
+ final Set<Unit> fromUnits = viewBot.getFromUnits();
+ final Set<Unit> toUnits = viewBot.getToUnits();
// test that fromUnits/toUnits is [METRE, KILOMETRE]
// HOWEVER I don't care about the order so I'm testing it this way