package com.ts3client.ui; import com.ts3client.config.Bookmark; import com.ts3client.config.Bookmarks; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swing.JTextField; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridLayout; import java.util.function.Consumer; /** Manage saved servers: add, edit, remove and quick-connect. */ public final class BookmarksDialog extends JDialog { private final Bookmarks bookmarks; private final Consumer onConnect; private final Runnable onChanged; private final DefaultListModel listModel = new DefaultListModel<>(); private final JList list = new JList<>(listModel); public BookmarksDialog(Frame owner, Bookmarks bookmarks, Consumer onConnect, Runnable onChanged) { super(owner, "Manage Bookmarks", true); this.bookmarks = bookmarks; this.onConnect = onConnect; this.onChanged = onChanged; reload(); list.setVisibleRowCount(10); JScrollPane scroll = new JScrollPane(list); scroll.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); JPanel buttons = new JPanel(); buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS)); buttons.setBorder(BorderFactory.createEmptyBorder(8, 0, 8, 8)); addButton(buttons, "Connect", this::connectSelected); addButton(buttons, "Add…", this::addBookmark); addButton(buttons, "Edit…", this::editSelected); addButton(buttons, "Remove", this::removeSelected); buttons.add(Box.createVerticalGlue()); addButton(buttons, "Close", this::dispose); getContentPane().setLayout(new BorderLayout()); getContentPane().add(scroll, BorderLayout.CENTER); getContentPane().add(buttons, BorderLayout.EAST); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setSize(new Dimension(420, 300)); setLocationRelativeTo(owner); } private void addButton(JPanel panel, String text, Runnable action) { JButton b = new JButton(text); b.setAlignmentX(LEFT_ALIGNMENT); b.setMaximumSize(new Dimension(Integer.MAX_VALUE, b.getPreferredSize().height)); b.addActionListener(e -> action.run()); panel.add(b); panel.add(Box.createVerticalStrut(4)); } private void reload() { listModel.clear(); for (Bookmark b : bookmarks.all()) listModel.addElement(b); } private void connectSelected() { 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(); reload(); if (onChanged != null) onChanged.run(); } /** Modal add/edit form. Mutates {@code b} and returns whether the user confirmed. */ private boolean promptBookmark(Bookmark b) { JTextField label = new JTextField(b.label == null ? "" : b.label); JTextField address = new JTextField(b.address == null ? "" : b.address); 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); 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); 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()); return true; } }