Two-column bookmark manager with connect-on-startup
Replace the modal add/edit form with a master/detail dialog: bookmark list on the left, the selected bookmark's settings on the right. Edits are flushed back into the bookmark when the selection changes or the dialog closes. Bookmarks gain a "connect on startup" flag (only one at a time, since the client holds a single connection); MainFrame connects to it once the window is up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,8 @@ public final class Bookmark {
|
|||||||
public String channel = "";
|
public String channel = "";
|
||||||
/** Password for {@link #channel}, if it is protected. */
|
/** Password for {@link #channel}, if it is protected. */
|
||||||
public String channelPassword = "";
|
public String channelPassword = "";
|
||||||
|
/** Connect to this server automatically when the client starts. */
|
||||||
|
public boolean connectOnStartup = false;
|
||||||
|
|
||||||
public Bookmark() {
|
public Bookmark() {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,14 @@ public final class Bookmarks {
|
|||||||
if (index >= 0 && index < entries.size()) entries.remove(index);
|
if (index >= 0 && index < entries.size()) entries.remove(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** First bookmark flagged to connect at startup, or {@code null}. */
|
||||||
|
public Bookmark startupBookmark() {
|
||||||
|
for (Bookmark b : entries) {
|
||||||
|
if (b.connectOnStartup) return b;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static Bookmarks load() {
|
public static Bookmarks load() {
|
||||||
Bookmarks b = new Bookmarks();
|
Bookmarks b = new Bookmarks();
|
||||||
if (!FILE.isFile()) return b;
|
if (!FILE.isFile()) return b;
|
||||||
@@ -51,6 +59,7 @@ public final class Bookmarks {
|
|||||||
bm.identityId = p.getProperty(prefix + "identityId", "");
|
bm.identityId = p.getProperty(prefix + "identityId", "");
|
||||||
bm.channel = p.getProperty(prefix + "channel", "");
|
bm.channel = p.getProperty(prefix + "channel", "");
|
||||||
bm.channelPassword = p.getProperty(prefix + "channelPassword", "");
|
bm.channelPassword = p.getProperty(prefix + "channelPassword", "");
|
||||||
|
bm.connectOnStartup = Boolean.parseBoolean(p.getProperty(prefix + "connectOnStartup", "false"));
|
||||||
if (bm.address != null && !bm.address.isBlank()) b.entries.add(bm);
|
if (bm.address != null && !bm.address.isBlank()) b.entries.add(bm);
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
@@ -70,6 +79,7 @@ public final class Bookmarks {
|
|||||||
p.setProperty(prefix + "identityId", nullToEmpty(bm.identityId));
|
p.setProperty(prefix + "identityId", nullToEmpty(bm.identityId));
|
||||||
p.setProperty(prefix + "channel", nullToEmpty(bm.channel));
|
p.setProperty(prefix + "channel", nullToEmpty(bm.channel));
|
||||||
p.setProperty(prefix + "channelPassword", nullToEmpty(bm.channelPassword));
|
p.setProperty(prefix + "channelPassword", nullToEmpty(bm.channelPassword));
|
||||||
|
p.setProperty(prefix + "connectOnStartup", Boolean.toString(bm.connectOnStartup));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (!DIR.isDirectory()) {
|
if (!DIR.isDirectory()) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import javax.swing.Box;
|
|||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.DefaultListModel;
|
import javax.swing.DefaultListModel;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JCheckBox;
|
||||||
import javax.swing.JDialog;
|
import javax.swing.JDialog;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JList;
|
import javax.swing.JList;
|
||||||
@@ -16,63 +17,238 @@ import javax.swing.JOptionPane;
|
|||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
import javax.swing.JPasswordField;
|
import javax.swing.JPasswordField;
|
||||||
import javax.swing.JScrollPane;
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JSplitPane;
|
||||||
import javax.swing.JTextField;
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.ListSelectionModel;
|
||||||
import java.awt.BorderLayout;
|
import java.awt.BorderLayout;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridBagConstraints;
|
||||||
|
import java.awt.GridBagLayout;
|
||||||
|
import java.awt.Insets;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/** Manage saved servers: add, edit, remove and quick-connect. */
|
/**
|
||||||
|
* Manage saved servers. Two columns: the bookmark list on the left, the
|
||||||
|
* selected bookmark's settings on the right. Edits are applied to the selected
|
||||||
|
* bookmark as soon as the selection changes or the dialog is closed.
|
||||||
|
*/
|
||||||
public final class BookmarksDialog extends JDialog {
|
public final class BookmarksDialog extends JDialog {
|
||||||
|
|
||||||
private final Bookmarks bookmarks;
|
private final Bookmarks bookmarks;
|
||||||
private final IdentityStore identities;
|
|
||||||
private final Consumer<Bookmark> onConnect;
|
private final Consumer<Bookmark> onConnect;
|
||||||
private final Runnable onChanged;
|
private final Runnable onChanged;
|
||||||
private final DefaultListModel<Bookmark> listModel = new DefaultListModel<>();
|
private final DefaultListModel<Bookmark> listModel = new DefaultListModel<>();
|
||||||
private final JList<Bookmark> list = new JList<>(listModel);
|
private final JList<Bookmark> list = new JList<>(listModel);
|
||||||
|
|
||||||
|
private final JTextField label = new JTextField();
|
||||||
|
private final JTextField address = new JTextField();
|
||||||
|
private final JTextField port = new JTextField();
|
||||||
|
private final JTextField nick = new JTextField();
|
||||||
|
private final JPasswordField password = new JPasswordField();
|
||||||
|
private final JTextField channel = new JTextField();
|
||||||
|
private final JPasswordField channelPassword = new JPasswordField();
|
||||||
|
private final JCheckBox connectOnStartup = new JCheckBox("Connect on startup");
|
||||||
|
private final IdentityChooser identity;
|
||||||
|
|
||||||
|
private final JPanel details;
|
||||||
|
|
||||||
|
/** Bookmark the form currently shows, so edits can be flushed back into it. */
|
||||||
|
private Bookmark editing;
|
||||||
|
|
||||||
public BookmarksDialog(Frame owner, Bookmarks bookmarks, IdentityStore identities,
|
public BookmarksDialog(Frame owner, Bookmarks bookmarks, IdentityStore identities,
|
||||||
Consumer<Bookmark> onConnect, Runnable onChanged) {
|
Consumer<Bookmark> onConnect, Runnable onChanged) {
|
||||||
super(owner, "Manage Bookmarks", true);
|
super(owner, "Manage Bookmarks", true);
|
||||||
this.bookmarks = bookmarks;
|
this.bookmarks = bookmarks;
|
||||||
this.identities = identities;
|
|
||||||
this.onConnect = onConnect;
|
this.onConnect = onConnect;
|
||||||
this.onChanged = onChanged;
|
this.onChanged = onChanged;
|
||||||
|
this.identity = new IdentityChooser(identities, true, "");
|
||||||
|
|
||||||
reload();
|
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
list.setVisibleRowCount(10);
|
list.setVisibleRowCount(12);
|
||||||
|
list.addListSelectionListener(e -> {
|
||||||
|
if (!e.getValueIsAdjusting()) onSelectionChanged();
|
||||||
|
});
|
||||||
|
|
||||||
JScrollPane scroll = new JScrollPane(list);
|
details = buildDetails();
|
||||||
scroll.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
|
||||||
|
|
||||||
JPanel buttons = new JPanel();
|
JScrollPane listScroll = new JScrollPane(list);
|
||||||
buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));
|
listScroll.setPreferredSize(new Dimension(180, 320));
|
||||||
buttons.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 8));
|
|
||||||
addButton(buttons, "Connect", this::connectSelected);
|
JSplitPane split = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, listScroll, details);
|
||||||
addButton(buttons, "Add…", this::addBookmark);
|
split.setBorder(BorderFactory.createEmptyBorder(8, 8, 4, 8));
|
||||||
addButton(buttons, "Edit…", this::editSelected);
|
split.setResizeWeight(0.3);
|
||||||
addButton(buttons, "Remove", this::removeSelected);
|
split.setContinuousLayout(true);
|
||||||
buttons.add(Box.createVerticalGlue());
|
|
||||||
addButton(buttons, "Close", this::dispose);
|
|
||||||
|
|
||||||
getContentPane().setLayout(new BorderLayout());
|
getContentPane().setLayout(new BorderLayout());
|
||||||
getContentPane().add(scroll, BorderLayout.CENTER);
|
getContentPane().add(split, BorderLayout.CENTER);
|
||||||
getContentPane().add(buttons, BorderLayout.EAST);
|
getContentPane().add(buildButtons(), BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
reload();
|
||||||
|
if (!listModel.isEmpty()) list.setSelectedIndex(0);
|
||||||
|
else showBookmark(null);
|
||||||
|
|
||||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
setSize(new Dimension(420, 300));
|
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosing(java.awt.event.WindowEvent e) {
|
||||||
|
commit();
|
||||||
|
persist();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
setSize(new Dimension(640, 400));
|
||||||
setLocationRelativeTo(owner);
|
setLocationRelativeTo(owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- UI construction ----
|
||||||
|
|
||||||
|
private JPanel buildDetails() {
|
||||||
|
JPanel panel = new JPanel(new GridBagLayout());
|
||||||
|
panel.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 0));
|
||||||
|
|
||||||
|
channel.setToolTipText("Channel path, e.g. \"Lobby/Games\", or a channel id. "
|
||||||
|
+ "Leave empty for the server's default channel.");
|
||||||
|
connectOnStartup.setToolTipText("Connect to this server automatically when the client starts.");
|
||||||
|
|
||||||
|
int row = 0;
|
||||||
|
addRow(panel, row++, "Label:", label);
|
||||||
|
addRow(panel, row++, "Address:", address);
|
||||||
|
addRow(panel, row++, "Port:", port);
|
||||||
|
addRow(panel, row++, "Nickname:", nick);
|
||||||
|
addRow(panel, row++, "Password:", password);
|
||||||
|
addRow(panel, row++, "Default channel:", channel);
|
||||||
|
addRow(panel, row++, "Channel password:", channelPassword);
|
||||||
|
addRow(panel, row++, "Identity:", identity);
|
||||||
|
addRow(panel, row++, "", connectOnStartup);
|
||||||
|
|
||||||
|
GridBagConstraints filler = new GridBagConstraints();
|
||||||
|
filler.gridx = 0;
|
||||||
|
filler.gridy = row;
|
||||||
|
filler.weighty = 1;
|
||||||
|
filler.fill = GridBagConstraints.VERTICAL;
|
||||||
|
panel.add(Box.createVerticalGlue(), filler);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addRow(JPanel panel, int row, String text, java.awt.Component field) {
|
||||||
|
GridBagConstraints c = new GridBagConstraints();
|
||||||
|
c.gridy = row;
|
||||||
|
c.insets = new Insets(2, 0, 2, 4);
|
||||||
|
c.anchor = GridBagConstraints.WEST;
|
||||||
|
c.gridx = 0;
|
||||||
|
panel.add(new JLabel(text), c);
|
||||||
|
c.gridx = 1;
|
||||||
|
c.weightx = 1;
|
||||||
|
c.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
panel.add(field, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel buildButtons() {
|
||||||
|
JPanel buttons = new JPanel();
|
||||||
|
buttons.setLayout(new BoxLayout(buttons, BoxLayout.X_AXIS));
|
||||||
|
buttons.setBorder(BorderFactory.createEmptyBorder(0, 8, 8, 8));
|
||||||
|
addButton(buttons, "Add", this::addBookmark);
|
||||||
|
addButton(buttons, "Remove", this::removeSelected);
|
||||||
|
buttons.add(Box.createHorizontalGlue());
|
||||||
|
addButton(buttons, "Connect", this::connectSelected);
|
||||||
|
addButton(buttons, "Close", this::closeDialog);
|
||||||
|
return buttons;
|
||||||
|
}
|
||||||
|
|
||||||
private void addButton(JPanel panel, String text, Runnable action) {
|
private void addButton(JPanel panel, String text, Runnable action) {
|
||||||
JButton b = new JButton(text);
|
JButton b = new JButton(text);
|
||||||
b.setAlignmentX(LEFT_ALIGNMENT);
|
|
||||||
b.setMaximumSize(new Dimension(Integer.MAX_VALUE, b.getPreferredSize().height));
|
|
||||||
b.addActionListener(e -> action.run());
|
b.addActionListener(e -> action.run());
|
||||||
panel.add(b);
|
panel.add(b);
|
||||||
panel.add(Box.createVerticalStrut(4));
|
panel.add(Box.createHorizontalStrut(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Form <-> model ----
|
||||||
|
|
||||||
|
private void onSelectionChanged() {
|
||||||
|
commit();
|
||||||
|
showBookmark(list.getSelectedValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showBookmark(Bookmark b) {
|
||||||
|
editing = b;
|
||||||
|
setEnabledRecursively(details, b != null);
|
||||||
|
label.setText(b == null ? "" : nullToEmpty(b.label));
|
||||||
|
address.setText(b == null ? "" : nullToEmpty(b.address));
|
||||||
|
port.setText(b == null ? "" : Integer.toString(b.port));
|
||||||
|
nick.setText(b == null ? "" : nullToEmpty(b.nickname));
|
||||||
|
password.setText(b == null ? "" : nullToEmpty(b.password));
|
||||||
|
channel.setText(b == null ? "" : nullToEmpty(b.channel));
|
||||||
|
channelPassword.setText(b == null ? "" : nullToEmpty(b.channelPassword));
|
||||||
|
connectOnStartup.setSelected(b != null && b.connectOnStartup);
|
||||||
|
identity.setSelectedIdentityId(b == null ? "" : nullToEmpty(b.identityId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Writes the form back into the bookmark it was loaded from. */
|
||||||
|
private void commit() {
|
||||||
|
if (editing == null) return;
|
||||||
|
editing.label = label.getText().trim();
|
||||||
|
editing.address = address.getText().trim();
|
||||||
|
try {
|
||||||
|
editing.port = Integer.parseInt(port.getText().trim());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
editing.port = 9987;
|
||||||
|
}
|
||||||
|
editing.nickname = nick.getText().trim();
|
||||||
|
editing.password = new String(password.getPassword());
|
||||||
|
editing.channel = channel.getText().trim();
|
||||||
|
editing.channelPassword = new String(channelPassword.getPassword());
|
||||||
|
editing.identityId = identity.getSelectedIdentityId();
|
||||||
|
editing.connectOnStartup = connectOnStartup.isSelected();
|
||||||
|
// Only one bookmark can be connected to at startup.
|
||||||
|
if (editing.connectOnStartup) {
|
||||||
|
for (Bookmark other : bookmarks.all()) {
|
||||||
|
if (other != editing) other.connectOnStartup = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Actions ----
|
||||||
|
|
||||||
|
private void addBookmark() {
|
||||||
|
commit();
|
||||||
|
Bookmark b = new Bookmark("New bookmark", "", 9987,
|
||||||
|
System.getProperty("user.name", "TS3J User"), "");
|
||||||
|
bookmarks.add(b);
|
||||||
|
reload();
|
||||||
|
list.setSelectedValue(b, true);
|
||||||
|
persist();
|
||||||
|
label.requestFocusInWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeSelected() {
|
||||||
|
int idx = list.getSelectedIndex();
|
||||||
|
if (idx < 0) return;
|
||||||
|
editing = null; // dropped, nothing to flush back
|
||||||
|
bookmarks.remove(idx);
|
||||||
|
reload();
|
||||||
|
if (!listModel.isEmpty()) list.setSelectedIndex(Math.min(idx, listModel.size() - 1));
|
||||||
|
else showBookmark(null);
|
||||||
|
persist();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connectSelected() {
|
||||||
|
commit();
|
||||||
|
Bookmark b = list.getSelectedValue();
|
||||||
|
if (b == null) return;
|
||||||
|
if (b.address == null || b.address.isBlank()) {
|
||||||
|
JOptionPane.showMessageDialog(this, "Address is required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
persist();
|
||||||
|
onConnect.accept(b);
|
||||||
|
dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeDialog() {
|
||||||
|
commit();
|
||||||
|
persist();
|
||||||
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reload() {
|
private void reload() {
|
||||||
@@ -80,94 +256,19 @@ public final class BookmarksDialog extends JDialog {
|
|||||||
for (Bookmark b : bookmarks.all()) listModel.addElement(b);
|
for (Bookmark b : bookmarks.all()) listModel.addElement(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connectSelected() {
|
private void persist() {
|
||||||
Bookmark b = list.getSelectedValue();
|
|
||||||
if (b != null) {
|
|
||||||
onConnect.accept(b);
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addBookmark() {
|
|
||||||
Bookmark b = new Bookmark("", "", 9987, System.getProperty("user.name", "TS3J User"), "");
|
|
||||||
if (promptBookmark(b)) {
|
|
||||||
bookmarks.add(b);
|
|
||||||
persistAndRefresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void editSelected() {
|
|
||||||
Bookmark b = list.getSelectedValue();
|
|
||||||
if (b != null && promptBookmark(b)) {
|
|
||||||
persistAndRefresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeSelected() {
|
|
||||||
int idx = list.getSelectedIndex();
|
|
||||||
if (idx >= 0) {
|
|
||||||
bookmarks.remove(idx);
|
|
||||||
persistAndRefresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void persistAndRefresh() {
|
|
||||||
bookmarks.save();
|
bookmarks.save();
|
||||||
reload();
|
|
||||||
if (onChanged != null) onChanged.run();
|
if (onChanged != null) onChanged.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Modal add/edit form. Mutates {@code b} and returns whether the user confirmed. */
|
private static void setEnabledRecursively(java.awt.Container container, boolean enabled) {
|
||||||
private boolean promptBookmark(Bookmark b) {
|
for (java.awt.Component c : container.getComponents()) {
|
||||||
JTextField label = new JTextField(b.label == null ? "" : b.label);
|
c.setEnabled(enabled);
|
||||||
JTextField address = new JTextField(b.address == null ? "" : b.address);
|
if (c instanceof java.awt.Container child) setEnabledRecursively(child, enabled);
|
||||||
JTextField port = new JTextField(Integer.toString(b.port));
|
|
||||||
JTextField nick = new JTextField(b.nickname == null ? "" : b.nickname);
|
|
||||||
JPasswordField password = new JPasswordField(b.password == null ? "" : b.password);
|
|
||||||
JTextField channel = new JTextField(b.channel == null ? "" : b.channel);
|
|
||||||
channel.setToolTipText("Channel path, e.g. \"Lobby/Games\", or a channel id. "
|
|
||||||
+ "Leave empty for the server's default channel.");
|
|
||||||
JPasswordField channelPassword = new JPasswordField(b.channelPassword == null ? "" : b.channelPassword);
|
|
||||||
IdentityChooser identity = new IdentityChooser(identities, true, b.identityId);
|
|
||||||
|
|
||||||
JPanel form = new JPanel(new GridLayout(0, 1, 0, 2));
|
|
||||||
form.add(new JLabel("Label:"));
|
|
||||||
form.add(label);
|
|
||||||
form.add(new JLabel("Address:"));
|
|
||||||
form.add(address);
|
|
||||||
form.add(new JLabel("Port:"));
|
|
||||||
form.add(port);
|
|
||||||
form.add(new JLabel("Nickname:"));
|
|
||||||
form.add(nick);
|
|
||||||
form.add(new JLabel("Password (optional):"));
|
|
||||||
form.add(password);
|
|
||||||
form.add(new JLabel("Default channel (path or id, optional):"));
|
|
||||||
form.add(channel);
|
|
||||||
form.add(new JLabel("Channel password (optional):"));
|
|
||||||
form.add(channelPassword);
|
|
||||||
form.add(new JLabel("Identity:"));
|
|
||||||
form.add(identity);
|
|
||||||
|
|
||||||
int result = JOptionPane.showConfirmDialog(this, form,
|
|
||||||
"Bookmark", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
|
|
||||||
if (result != JOptionPane.OK_OPTION) return false;
|
|
||||||
|
|
||||||
if (address.getText().trim().isEmpty()) {
|
|
||||||
JOptionPane.showMessageDialog(this, "Address is required.");
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
b.label = label.getText().trim();
|
|
||||||
b.address = address.getText().trim();
|
|
||||||
try {
|
|
||||||
b.port = Integer.parseInt(port.getText().trim());
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
b.port = 9987;
|
|
||||||
}
|
}
|
||||||
b.nickname = nick.getText().trim();
|
|
||||||
b.password = new String(password.getPassword());
|
private static String nullToEmpty(String s) {
|
||||||
b.channel = channel.getText().trim();
|
return s == null ? "" : s;
|
||||||
b.channelPassword = new String(channelPassword.getPassword());
|
|
||||||
b.identityId = identity.getSelectedIdentityId();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,16 +17,16 @@ final class IdentityChooser extends JComboBox<Object> {
|
|||||||
|
|
||||||
private static final String USE_DEFAULT = "‹Default identity›";
|
private static final String USE_DEFAULT = "‹Default identity›";
|
||||||
|
|
||||||
|
private final IdentityStore identities;
|
||||||
|
private final boolean allowDefault;
|
||||||
|
|
||||||
IdentityChooser(IdentityStore identities, boolean allowDefault, String selectedId) {
|
IdentityChooser(IdentityStore identities, boolean allowDefault, String selectedId) {
|
||||||
|
this.identities = identities;
|
||||||
|
this.allowDefault = allowDefault;
|
||||||
if (allowDefault) addItem(USE_DEFAULT);
|
if (allowDefault) addItem(USE_DEFAULT);
|
||||||
for (IdentityEntry e : identities.all()) addItem(e);
|
for (IdentityEntry e : identities.all()) addItem(e);
|
||||||
|
|
||||||
IdentityEntry selected = identities.byId(selectedId);
|
setSelectedIdentityId(selectedId);
|
||||||
if (selected != null) {
|
|
||||||
setSelectedItem(selected);
|
|
||||||
} else if (allowDefault) {
|
|
||||||
setSelectedItem(USE_DEFAULT);
|
|
||||||
}
|
|
||||||
|
|
||||||
setRenderer(new DefaultListCellRenderer() {
|
setRenderer(new DefaultListCellRenderer() {
|
||||||
@Override
|
@Override
|
||||||
@@ -45,4 +45,14 @@ final class IdentityChooser extends JComboBox<Object> {
|
|||||||
Object v = getSelectedItem();
|
Object v = getSelectedItem();
|
||||||
return v instanceof IdentityEntry ? ((IdentityEntry) v).getId() : "";
|
return v instanceof IdentityEntry ? ((IdentityEntry) v).getId() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Selects the identity with the given id, falling back to the default entry. */
|
||||||
|
void setSelectedIdentityId(String id) {
|
||||||
|
IdentityEntry selected = identities.byId(id);
|
||||||
|
if (selected != null) {
|
||||||
|
setSelectedItem(selected);
|
||||||
|
} else if (allowDefault) {
|
||||||
|
setSelectedItem(USE_DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -141,6 +141,12 @@ public final class MainFrame extends JFrame implements ConnectionListener, Serve
|
|||||||
statusTimer.start();
|
statusTimer.start();
|
||||||
setSize(880, 560);
|
setSize(880, 560);
|
||||||
setLocationRelativeTo(null);
|
setLocationRelativeTo(null);
|
||||||
|
|
||||||
|
Bookmark startup = bookmarks.startupBookmark();
|
||||||
|
if (startup != null) {
|
||||||
|
// Deferred so the window is on screen before we start connecting.
|
||||||
|
SwingUtilities.invokeLater(() -> connectToBookmark(startup));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- UI construction ----
|
// ---- UI construction ----
|
||||||
|
|||||||
Reference in New Issue
Block a user