Skip to content

Commit 356900d

Browse files
committed
Add built-in user interface with randomize & legality options (#9 & #16)
1 parent ff86154 commit 356900d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+12549
-157
lines changed

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ dependencies {
3030
implementation 'com.formdev:flatlaf:3.1.1'
3131
implementation 'com.formdev:flatlaf-extras:3.1.1'
3232
implementation 'com.formdev:flatlaf-intellij-themes:3.1.1'
33+
implementation 'com.miglayout:miglayout-swing:4.2' // Finally, a good fucking layout manager.
34+
implementation 'org.swinglabs.swingx:swingx-autocomplete:1.6.5-1'
3335
}
3436

3537
sourceSets {

src/main/java/entralinked/Entralinked.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import com.fasterxml.jackson.databind.ObjectMapper;
1515
import com.fasterxml.jackson.databind.SerializationFeature;
1616

17-
import entralinked.gui.MainView;
17+
import entralinked.gui.view.MainView;
1818
import entralinked.model.dlc.DlcList;
1919
import entralinked.model.player.PlayerManager;
2020
import entralinked.model.user.UserManager;
@@ -119,7 +119,6 @@ public Entralinked(String[] args) {
119119

120120
if(mainView != null) {
121121
SwingUtilities.invokeLater(() -> {
122-
mainView.setDashboardButtonEnabled(true);
123122
mainView.setStatusLabelText("Configure your DS to use the following DNS server: %s".formatted(hostIpAddress));
124123
});
125124
}

src/main/java/entralinked/GameVersion.java

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import java.util.Map;
55

66
public enum GameVersion {
7-
7+
88
// ==================================
99
// Black Version & White Version
1010
// ==================================
@@ -29,21 +29,35 @@ public enum GameVersion {
2929
// Black Version 2 & White Version 2
3030
// ==================================
3131

32-
BLACK_2_JAPANESE(23, 1, "IREJ", "ブラック2", true),
33-
BLACK_2_ENGLISH(23, 2, "IREO", "Black Version 2", true),
34-
BLACK_2_FRENCH(23, 3, "IREF", "Version Noire 2", true),
35-
BLACK_2_ITALIAN(23, 4, "IREI", "Versione Nera 2", true),
36-
BLACK_2_GERMAN(23, 5, "IRED", "Schwarze Edition 2", true),
37-
BLACK_2_SPANISH(23, 7, "IRES", "Edicion Negra 2", true),
38-
BLACK_2_KOREAN(23, 8, "IREK", "블랙2", true),
39-
40-
WHITE_2_JAPANESE(22, 1, "IRDJ", "ホワイト2", true),
41-
WHITE_2_ENGLISH(22, 2, "IRDO", "White Version 2", true),
42-
WHITE_2_FRENCH(22, 3, "IRDF", "Version Blanche 2", true),
43-
WHITE_2_ITALIAN(22, 4, "IRDI", "Versione Bianca 2", true),
44-
WHITE_2_GERMAN(22, 5, "IRDD", "Weisse Edition 2", true),
45-
WHITE_2_SPANISH(22, 7, "IRDS", "Edicion Blanca 2", true),
46-
WHITE_2_KOREAN(22, 8, "IRDK", "화이트2", true);
32+
BLACK_2_JAPANESE(23, 1, "IREJ", "ブラック2"),
33+
BLACK_2_ENGLISH(23, 2, "IREO", "Black Version 2"),
34+
BLACK_2_FRENCH(23, 3, "IREF", "Version Noire 2"),
35+
BLACK_2_ITALIAN(23, 4, "IREI", "Versione Nera 2"),
36+
BLACK_2_GERMAN(23, 5, "IRED", "Schwarze Edition 2"),
37+
BLACK_2_SPANISH(23, 7, "IRES", "Edicion Negra 2"),
38+
BLACK_2_KOREAN(23, 8, "IREK", "블랙2"),
39+
40+
WHITE_2_JAPANESE(22, 1, "IRDJ", "ホワイト2"),
41+
WHITE_2_ENGLISH(22, 2, "IRDO", "White Version 2"),
42+
WHITE_2_FRENCH(22, 3, "IRDF", "Version Blanche 2"),
43+
WHITE_2_ITALIAN(22, 4, "IRDI", "Versione Bianca 2"),
44+
WHITE_2_GERMAN(22, 5, "IRDD", "Weisse Edition 2"),
45+
WHITE_2_SPANISH(22, 7, "IRDS", "Edicion Blanca 2"),
46+
WHITE_2_KOREAN(22, 8, "IRDK", "화이트2");
47+
48+
// Masks
49+
public static final int BW_MASK = 0b110011111111;
50+
public static final int B2W2_MASK = 0b001111111111;
51+
public static final int ALL_MASK = BW_MASK | B2W2_MASK;
52+
public static final int JAP_MASK = 0b111100000001;
53+
public static final int ENG_MASK = 0b111100000010;
54+
public static final int FRE_MASK = 0b111100000100;
55+
public static final int ITA_MASK = 0b111100001000;
56+
public static final int GER_MASK = 0b111100010000;
57+
public static final int SPA_MASK = 0b111101000000;
58+
public static final int KOR_MASK = 0b111110000000;
59+
public static final int JAP_KOR_MASK = JAP_MASK | KOR_MASK;
60+
public static final int NA_EUR_MASK = ENG_MASK | FRE_MASK | ITA_MASK | GER_MASK | SPA_MASK;
4761

4862
// Lookup maps
4963
private static final Map<String, GameVersion> mapBySerial = new HashMap<>();
@@ -52,34 +66,32 @@ public enum GameVersion {
5266
static {
5367
for(GameVersion version : values()) {
5468
mapBySerial.put(version.getSerial(), version);
55-
mapByCodes.put(version.getRomCode() << version.getLanguageCode(), version);
69+
mapByCodes.put(version.getBits(), version);
5670
}
5771
}
5872

5973
private final int romCode;
6074
private final int languageCode; // Values are not tested
6175
private final String serial;
6276
private final String displayName;
63-
private final boolean isVersion2;
6477

65-
private GameVersion(int romCode, int languageCode, String serial, String displayName, boolean isVersion2) {
78+
private GameVersion(int romCode, int languageCode, String serial, String displayName) {
6679
this.romCode = romCode;
6780
this.languageCode = languageCode;
6881
this.serial = serial;
6982
this.displayName = displayName;
70-
this.isVersion2 = isVersion2;
71-
}
72-
73-
private GameVersion(int romCode, int languageCode, String serial, String displayName) {
74-
this(romCode, languageCode, serial, displayName, false);
7583
}
7684

7785
public static GameVersion lookup(String serial) {
7886
return mapBySerial.get(serial);
7987
}
8088

8189
public static GameVersion lookup(int romCode, int languageCode) {
82-
return mapByCodes.get(romCode << languageCode);
90+
return mapByCodes.get(getBits(romCode, languageCode));
91+
}
92+
93+
private static int getBits(int romCode, int languageCode) {
94+
return (1 << (8 - (romCode - 23))) | (1 << languageCode - 1) & 0b111111111111;
8395
}
8496

8597
public int getRomCode() {
@@ -99,6 +111,15 @@ public String getDisplayName() {
99111
}
100112

101113
public boolean isVersion2() {
102-
return isVersion2;
114+
return checkMask(B2W2_MASK);
115+
}
116+
117+
public boolean checkMask(int mask) {
118+
int bits = getBits();
119+
return (bits & mask) == bits;
120+
}
121+
122+
public int getBits() {
123+
return getBits(romCode, languageCode);
103124
}
104125
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package entralinked.gui;
2+
3+
import java.awt.Component;
4+
import java.io.File;
5+
import java.util.Collections;
6+
import java.util.List;
7+
import java.util.function.Consumer;
8+
import java.util.function.Function;
9+
10+
import javax.swing.JFileChooser;
11+
import javax.swing.filechooser.FileFilter;
12+
13+
public class FileChooser {
14+
15+
private static final JFileChooser fileChooser = new JFileChooser(".");
16+
17+
public static void showFileOpenDialog(Component parent, Consumer<FileSelection> handler) {
18+
showFileOpenDialog(parent, Collections.emptyList(), handler);
19+
}
20+
21+
public static void showFileOpenDialog(Component parent, FileFilter fileFilter, Consumer<FileSelection> handler) {
22+
showFileOpenDialog(parent, List.of(fileFilter), handler);
23+
}
24+
25+
public static void showFileOpenDialog(Component parent, List<FileFilter> fileFilters, Consumer<FileSelection> handler) {
26+
showDialog(parent, fileFilters, fileChooser::showOpenDialog, handler);
27+
}
28+
29+
private static void showDialog(Component parent, List<FileFilter> fileFilters, Function<Component, Integer> dialogFunction, Consumer<FileSelection> handler) {
30+
FileFilter currentFilter = fileChooser.getFileFilter();
31+
fileChooser.resetChoosableFileFilters();
32+
fileChooser.setAcceptAllFileFilterUsed(fileFilters.isEmpty());
33+
fileFilters.forEach(fileChooser::addChoosableFileFilter);
34+
35+
if(fileFilters.contains(currentFilter)) {
36+
fileChooser.setFileFilter(currentFilter);
37+
}
38+
39+
if(dialogFunction.apply(parent) == JFileChooser.APPROVE_OPTION) {
40+
File file = fileChooser.getSelectedFile();
41+
handler.accept(new FileSelection(file, fileChooser.getFileFilter(), getFileExtension(file)));
42+
}
43+
}
44+
45+
public static String getFileExtension(File file) {
46+
String name = file.getName();
47+
int index = name.lastIndexOf('.');
48+
49+
if(index == -1 || index + 1 == name.length()) {
50+
return null;
51+
}
52+
53+
return name.substring(index + 1).toLowerCase();
54+
}
55+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package entralinked.gui;
2+
3+
import java.io.File;
4+
5+
import javax.swing.filechooser.FileFilter;
6+
7+
public record FileSelection(File file, FileFilter filter, String extension) {}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package entralinked.gui;
2+
3+
import java.awt.FontMetrics;
4+
import java.awt.Graphics2D;
5+
import java.awt.RenderingHints;
6+
import java.awt.image.BufferedImage;
7+
import java.util.HashMap;
8+
import java.util.Map;
9+
10+
import javax.imageio.ImageIO;
11+
12+
import entralinked.gui.data.DataManager;
13+
14+
public class ImageLoader {
15+
16+
private static final Map<String, BufferedImage> cache = new HashMap<>();
17+
18+
public static BufferedImage getImage(String path) {
19+
return cache.computeIfAbsent(path, ImageLoader::loadImage);
20+
}
21+
22+
private static BufferedImage loadImage(String path) {
23+
try {
24+
return ImageIO.read(DataManager.class.getResource(path));
25+
} catch(Exception e) {
26+
BufferedImage image = new BufferedImage(128, 128, BufferedImage.TYPE_INT_ARGB);
27+
Graphics2D graphics = image.createGraphics();
28+
graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
29+
graphics.drawString("IMAGE LOAD ERROR", 0, 16);
30+
graphics.drawString("Please report this!", 0, 124);
31+
drawWrappedString(graphics, path, 0, 32, image.getWidth());
32+
drawWrappedString(graphics, e.getMessage(), 0, 80, image.getWidth());
33+
graphics.dispose();
34+
return image;
35+
}
36+
}
37+
38+
private static void drawWrappedString(Graphics2D graphics, String string, int x, int y, int width) {
39+
int length = string.length();
40+
FontMetrics metrics = graphics.getFontMetrics();
41+
String line = "";
42+
int currentY = y;
43+
44+
for(int i = 0; i < length; i++) {
45+
char next = string.charAt(i);
46+
47+
if((!line.isEmpty() && x + metrics.stringWidth(line + next) >= width)) {
48+
graphics.drawString(line, x, currentY);
49+
line = "";
50+
currentY += metrics.getHeight();
51+
}
52+
53+
line += next;
54+
55+
if(i + 1 == length) {
56+
graphics.drawString(line, x, currentY);
57+
}
58+
}
59+
}
60+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package entralinked.gui;
2+
3+
import javax.swing.DefaultCellEditor;
4+
import javax.swing.InputVerifier;
5+
import javax.swing.JTextField;
6+
7+
@SuppressWarnings("serial")
8+
public class InputVerifierCellEditor extends DefaultCellEditor {
9+
10+
private final InputVerifier verifier;
11+
12+
public InputVerifierCellEditor(InputVerifier verifier) {
13+
this(verifier, new JTextField());
14+
}
15+
16+
public InputVerifierCellEditor(InputVerifier verifier, JTextField textField) {
17+
super(textField);
18+
this.verifier = verifier;
19+
}
20+
21+
@Override
22+
public boolean stopCellEditing() {
23+
return verifier.verify(editorComponent) && super.stopCellEditing();
24+
}
25+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package entralinked.gui;
2+
3+
import java.awt.Component;
4+
import java.awt.Font;
5+
import java.util.function.Function;
6+
7+
import javax.swing.DefaultListCellRenderer;
8+
import javax.swing.JList;
9+
10+
import entralinked.utility.SwingUtility;
11+
12+
@SuppressWarnings({"serial", "unchecked"})
13+
public class ModelListCellRenderer<T> extends DefaultListCellRenderer {
14+
15+
private final Class<T> type;
16+
private final Function<T, String> textSupplier;
17+
private final String nullValue;
18+
19+
public ModelListCellRenderer(Class<T> type, Function<T, String> textSupplier, String nullValue) {
20+
this.type = type;
21+
this.textSupplier = textSupplier;
22+
this.nullValue = nullValue;
23+
}
24+
25+
@Override
26+
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
27+
Component component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
28+
setText(value == null ? String.valueOf(nullValue) : type.isAssignableFrom(value.getClass()) ? textSupplier.apply((T)value) : String.valueOf(value));
29+
Font font = component.getFont();
30+
String text = getText();
31+
32+
if(font.canDisplayUpTo(text) != -1) {
33+
component.setFont(SwingUtility.findSupportingFont(text, font));
34+
}
35+
36+
return component;
37+
}
38+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package entralinked.gui;
2+
3+
import java.awt.Component;
4+
import java.awt.Font;
5+
import java.util.function.Function;
6+
7+
import javax.swing.JTable;
8+
import javax.swing.table.DefaultTableCellRenderer;
9+
10+
import entralinked.utility.SwingUtility;
11+
12+
@SuppressWarnings({"serial", "unchecked"})
13+
public class ModelTableCellRenderer<T> extends DefaultTableCellRenderer {
14+
15+
private final Class<T> type;
16+
private final Function<T, String> textSupplier;
17+
private final String nullValue;
18+
19+
public ModelTableCellRenderer(Class<T> type, Function<T, String> textSupplier, String nullValue) {
20+
this.type = type;
21+
this.textSupplier = textSupplier;
22+
this.nullValue = nullValue;
23+
}
24+
25+
@Override
26+
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
27+
Component component = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
28+
setText(value == null ? String.valueOf(nullValue) : type.isAssignableFrom(value.getClass()) ? textSupplier.apply((T)value) : String.valueOf(value));
29+
Font font = component.getFont();
30+
String text = getText();
31+
32+
if(font.canDisplayUpTo(text) != -1) {
33+
component.setFont(SwingUtility.findSupportingFont(text, font));
34+
}
35+
36+
return component;
37+
}
38+
}

0 commit comments

Comments
 (0)