Makes it possible to load a controller without generating a GUI, for better testing Makes sure not to try and parse empty profile lines Saves controller settings in a more readable and appendable format Adds code for using the correct java version for the occasion Adds a new function for writing to files
448 lines
16 KiB
Java
448 lines
16 KiB
Java
package net.knarcraft.minecraftserverlauncher.userinterface;
|
|
|
|
import net.knarcraft.minecraftserverlauncher.Main;
|
|
import net.knarcraft.minecraftserverlauncher.profile.Collection;
|
|
import net.knarcraft.minecraftserverlauncher.profile.ServerLauncherController;
|
|
import net.knarcraft.minecraftserverlauncher.server.Server;
|
|
import net.knarcraft.minecraftserverlauncher.utility.CommonFunctions;
|
|
|
|
import javax.imageio.ImageIO;
|
|
import javax.naming.ConfigurationException;
|
|
import javax.swing.*;
|
|
import java.awt.*;
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.io.File;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileWriter;
|
|
import java.io.IOException;
|
|
import java.io.PrintWriter;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Scanner;
|
|
import java.util.concurrent.Executors;
|
|
|
|
import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE;
|
|
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.getResourceAsScanner;
|
|
import static net.knarcraft.minecraftserverlauncher.utility.CommonFunctions.getResourceAsStream;
|
|
|
|
/**
|
|
* Generates a ServerLauncherGUI.
|
|
*
|
|
* @author Kristian Knarvik <kristian.knarvik@knett.no>
|
|
* @version 1.0.0
|
|
* @since 1.0.0
|
|
*/
|
|
public class ServerLauncherGUI extends MessageHandler implements ActionListener, GUI {
|
|
|
|
private final JLabel lblStatuslabel = new JLabel("Servers are stopped");
|
|
private final ServerLauncherController controller;
|
|
private Map<String, String> textStrings;
|
|
private Tray applicationTray;
|
|
|
|
private JFrame frame;
|
|
private JTabbedPane tabbedPane;
|
|
private JTabbedPane serversPane;
|
|
private ServerControlTab serverControlTab;
|
|
private ServerLauncherMenu serverLauncherMenu;
|
|
|
|
//Basic controls
|
|
private JButton startServerButton;
|
|
private JButton stopServerButton;
|
|
private JButton addServerButton;
|
|
private JButton backupButton;
|
|
private JButton addProfileButton;
|
|
private JButton deleteProfileButton;
|
|
private JComboBox<String> profiles;
|
|
|
|
/**
|
|
* Creates the application window
|
|
*/
|
|
public ServerLauncherGUI() throws IOException {
|
|
super(false);
|
|
initialize(440, 170);
|
|
loadMessages();
|
|
this.controller = Main.getController();
|
|
}
|
|
|
|
/**
|
|
* Creates the application window
|
|
*
|
|
* @param silent <p>Whether to make the GUI silent (hidden, for testing)</p>
|
|
*/
|
|
public ServerLauncherGUI(boolean silent) throws IOException {
|
|
super(silent);
|
|
if (!silent) {
|
|
initialize(440, 170);
|
|
}
|
|
loadMessages();
|
|
this.controller = Main.getController();
|
|
}
|
|
|
|
/**
|
|
* Creates the application window with a preferred width and height
|
|
*
|
|
* @param width <p>The preferred width</p>
|
|
* @param height <p>The preferred height</p>
|
|
*/
|
|
public ServerLauncherGUI(int width, int height) throws IOException {
|
|
super(false);
|
|
initialize(width, height);
|
|
loadMessages();
|
|
this.controller = Main.getController();
|
|
}
|
|
|
|
/**
|
|
* Gets the pane used for server configurations
|
|
*
|
|
* @return <p>The pane used for server configurations</p>
|
|
*/
|
|
public JTabbedPane getPane() {
|
|
return this.serversPane;
|
|
}
|
|
|
|
@Override
|
|
public void setStatus(String text) {
|
|
this.lblStatuslabel.setText(text);
|
|
try (PrintWriter file = new PrintWriter(new FileWriter(Main.getApplicationWorkDirectory() +
|
|
File.separator + "latestrun.log", true))) {
|
|
file.println(text);
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public File askForDirectory(String prompt) {
|
|
JFileChooser chooser = new JFileChooser();
|
|
chooser.setCurrentDirectory(new java.io.File("."));
|
|
chooser.setDialogTitle(prompt);
|
|
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
|
chooser.setAcceptAllFileFilterUsed(false);
|
|
|
|
if (chooser.showOpenDialog(null) != JFileChooser.APPROVE_OPTION) {
|
|
setStatus("Choice aborted by user");
|
|
return null;
|
|
}
|
|
return chooser.getSelectedFile();
|
|
}
|
|
|
|
/**
|
|
* Updates the profiles combo
|
|
*/
|
|
public void updateProfiles() {
|
|
String selectedProfile = Main.getController().getCurrentProfile().getName();
|
|
this.profiles.removeAllItems();
|
|
for (String profile : Main.getController().getProfileNames()) {
|
|
this.profiles.addItem(profile);
|
|
}
|
|
this.profiles.setSelectedItem(selectedProfile);
|
|
}
|
|
|
|
/**
|
|
* Gets the server control tab used by this GUI
|
|
*
|
|
* @return <p>The server control tab used by this GUI</p>
|
|
*/
|
|
public ServerControlTab getServerControlTab() {
|
|
return this.serverControlTab;
|
|
}
|
|
|
|
/**
|
|
* Gets the size of the main JFrame
|
|
*
|
|
* @return <p>The Dimension of the main JFrame</p>
|
|
*/
|
|
public Dimension getSize() {
|
|
return frame.getContentPane().getSize();
|
|
}
|
|
|
|
/**
|
|
* Updates ServerLauncherGUI according to current profile settings
|
|
*/
|
|
public void updateWithSavedProfileData() {
|
|
ServerLauncherController controller = Main.getController();
|
|
serversPane.removeAll();
|
|
serverLauncherMenu.update();
|
|
serverControlTab.update();
|
|
for (Collection collection : controller.getCurrentProfile().getCollections()) {
|
|
serversPane.addTab(collection.getName(), collection.getServerTab().getPanel());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initializes the server launcher GUI
|
|
*
|
|
* @param width <p>The width of the GUI</p>
|
|
* @param height <p>The height of the GUI</p>
|
|
* @throws IOException <p>If unable to load the GUI icon</p>
|
|
*/
|
|
private void initialize(int width, int height) throws IOException {
|
|
try {
|
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
|
} catch (ClassNotFoundException |
|
|
UnsupportedLookAndFeelException |
|
|
InstantiationException |
|
|
IllegalAccessException e
|
|
) {
|
|
e.printStackTrace();
|
|
}
|
|
|
|
frame = new JFrame("Minecraft server launcher");
|
|
frame.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
|
|
frame.getContentPane().setPreferredSize(new Dimension(width, height));
|
|
|
|
ImageIcon img = new ImageIcon(ImageIO.read(getResourceAsStream("GUIIcon.png")));
|
|
|
|
frame.setIconImage(img.getImage());
|
|
|
|
JMenuBar menuBar = new JMenuBar();
|
|
frame.setJMenuBar(menuBar);
|
|
|
|
this.serverLauncherMenu = new ServerLauncherMenu(menuBar, this);
|
|
|
|
tabbedPane = new JTabbedPane(JTabbedPane.TOP);
|
|
frame.getContentPane().add(tabbedPane);
|
|
|
|
JPanel panelBasic = new JPanel();
|
|
tabbedPane.addTab("Control panel", null, panelBasic, null);
|
|
SpringLayout sl_panel = new SpringLayout();
|
|
panelBasic.setLayout(sl_panel);
|
|
|
|
JLabel lblBasicControls = new JLabel("Basic controls");
|
|
sl_panel.putConstraint(SpringLayout.NORTH, lblBasicControls, 10, SpringLayout.NORTH, panelBasic);
|
|
panelBasic.add(lblBasicControls);
|
|
|
|
startServerButton = new JButton("Start servers");
|
|
sl_panel.putConstraint(SpringLayout.WEST, lblBasicControls, 0, SpringLayout.WEST, startServerButton);
|
|
sl_panel.putConstraint(SpringLayout.NORTH, startServerButton, 6, SpringLayout.SOUTH, lblBasicControls);
|
|
sl_panel.putConstraint(SpringLayout.WEST, startServerButton, 10, SpringLayout.WEST, panelBasic);
|
|
panelBasic.add(startServerButton);
|
|
startServerButton.addActionListener(this);
|
|
|
|
stopServerButton = new JButton("Stop servers");
|
|
sl_panel.putConstraint(SpringLayout.NORTH, stopServerButton, 0, SpringLayout.NORTH, startServerButton);
|
|
sl_panel.putConstraint(SpringLayout.WEST, stopServerButton, 6, SpringLayout.EAST, startServerButton);
|
|
panelBasic.add(stopServerButton);
|
|
stopServerButton.addActionListener(this);
|
|
|
|
JLabel lblProfile = new JLabel("Profile");
|
|
sl_panel.putConstraint(SpringLayout.NORTH, lblProfile, 6, SpringLayout.SOUTH, startServerButton);
|
|
sl_panel.putConstraint(SpringLayout.WEST, lblProfile, 10, SpringLayout.WEST, panelBasic);
|
|
panelBasic.add(lblProfile);
|
|
|
|
addProfileButton = new JButton("+");
|
|
sl_panel.putConstraint(SpringLayout.NORTH, addProfileButton, 6, SpringLayout.SOUTH, lblProfile);
|
|
sl_panel.putConstraint(SpringLayout.WEST, addProfileButton, 10, SpringLayout.WEST, panelBasic);
|
|
panelBasic.add(addProfileButton);
|
|
addProfileButton.addActionListener(this);
|
|
|
|
deleteProfileButton = new JButton("-");
|
|
sl_panel.putConstraint(SpringLayout.NORTH, deleteProfileButton, 0, SpringLayout.NORTH, addProfileButton);
|
|
sl_panel.putConstraint(SpringLayout.WEST, deleteProfileButton, 6, SpringLayout.EAST, addProfileButton);
|
|
panelBasic.add(deleteProfileButton);
|
|
deleteProfileButton.addActionListener(this);
|
|
|
|
profiles = new JComboBox<>();
|
|
sl_panel.putConstraint(SpringLayout.NORTH, profiles, 0, SpringLayout.NORTH, addProfileButton);
|
|
sl_panel.putConstraint(SpringLayout.WEST, profiles, 6, SpringLayout.EAST, deleteProfileButton);
|
|
sl_panel.putConstraint(SpringLayout.EAST, profiles, 124, SpringLayout.EAST, deleteProfileButton);
|
|
panelBasic.add(profiles);
|
|
profiles.addActionListener(this);
|
|
|
|
sl_panel.putConstraint(SpringLayout.NORTH, lblStatuslabel, 6, SpringLayout.SOUTH, addProfileButton);
|
|
sl_panel.putConstraint(SpringLayout.SOUTH, lblStatuslabel, -10, SpringLayout.SOUTH, panelBasic);
|
|
sl_panel.putConstraint(SpringLayout.WEST, lblStatuslabel, 10, SpringLayout.WEST, panelBasic);
|
|
sl_panel.putConstraint(SpringLayout.EAST, lblStatuslabel, -10, SpringLayout.EAST, panelBasic);
|
|
panelBasic.add(lblStatuslabel);
|
|
|
|
addServerButton = new JButton("Add server");
|
|
sl_panel.putConstraint(SpringLayout.NORTH, addServerButton, 0, SpringLayout.NORTH, startServerButton);
|
|
sl_panel.putConstraint(SpringLayout.WEST, addServerButton, 6, SpringLayout.EAST, stopServerButton);
|
|
panelBasic.add(addServerButton);
|
|
addServerButton.addActionListener(this);
|
|
|
|
backupButton = new JButton("Backup");
|
|
sl_panel.putConstraint(SpringLayout.NORTH, backupButton, 0, SpringLayout.NORTH, startServerButton);
|
|
sl_panel.putConstraint(SpringLayout.WEST, backupButton, 6, SpringLayout.EAST, addServerButton);
|
|
panelBasic.add(backupButton);
|
|
backupButton.addActionListener(this);
|
|
|
|
JPanel controlServers = new JPanel();
|
|
tabbedPane.addTab("Control servers", null, controlServers, null);
|
|
|
|
serverControlTab = new ServerControlTab(frame, controlServers);
|
|
|
|
|
|
JPanel panel_2 = new JPanel();
|
|
tabbedPane.addTab("Servers", null, panel_2, null);
|
|
SpringLayout sl_panel_2 = new SpringLayout();
|
|
panel_2.setLayout(sl_panel_2);
|
|
|
|
JTabbedPane tabbedPane_1 = new JTabbedPane(JTabbedPane.TOP);
|
|
sl_panel_2.putConstraint(SpringLayout.NORTH, tabbedPane_1, 0, SpringLayout.NORTH, panel_2);
|
|
sl_panel_2.putConstraint(SpringLayout.WEST, tabbedPane_1, 0, SpringLayout.WEST, panel_2);
|
|
sl_panel_2.putConstraint(SpringLayout.SOUTH, tabbedPane_1, 0, SpringLayout.SOUTH, panel_2);
|
|
sl_panel_2.putConstraint(SpringLayout.EAST, tabbedPane_1, 0, SpringLayout.EAST, panel_2);
|
|
panel_2.add(tabbedPane_1);
|
|
|
|
this.serversPane = tabbedPane_1;
|
|
tabbedPane_1.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
|
|
|
|
frame.validate();
|
|
frame.pack();
|
|
frame.setVisible(true);
|
|
applicationTray = new Tray(frame, this);
|
|
updateGUIElementsWhenServersStartOrStop(false);
|
|
}
|
|
|
|
/**
|
|
* Hides the GUI to the system tray
|
|
*/
|
|
public void hideToTray() {
|
|
applicationTray.hideToTray();
|
|
}
|
|
|
|
/**
|
|
* Gets the currently selected profile
|
|
*
|
|
* @return <p>The currently selected profile or null</p>
|
|
*/
|
|
public String getSelectedProfile() {
|
|
Object selectedProfile = profiles.getSelectedItem();
|
|
if (selectedProfile != null) {
|
|
return selectedProfile.toString();
|
|
} else {
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
public void actionPerformed(ActionEvent e) {
|
|
Object actionSource = e.getSource();
|
|
//Registers actions on the main tab
|
|
handleMainTabButtons(actionSource);
|
|
}
|
|
|
|
|
|
/**
|
|
* Handles buttons and the combo on the main tab
|
|
*
|
|
* @param actionSource <p>The object being interacted with</p>
|
|
*/
|
|
private void handleMainTabButtons(Object actionSource) {
|
|
if (actionSource == startServerButton) {
|
|
controller.saveState();
|
|
Executors.newSingleThreadExecutor().execute(Server::startServers);
|
|
} else if (actionSource == stopServerButton) {
|
|
stopServers();
|
|
} else if (actionSource == addServerButton) {
|
|
addServer();
|
|
} else if (actionSource == backupButton) {
|
|
CommonFunctions.backup(this);
|
|
} else if (actionSource == addProfileButton) {
|
|
controller.addProfile(JOptionPane.showInputDialog("Profile name: "));
|
|
updateProfiles();
|
|
} else if (actionSource == deleteProfileButton) {
|
|
deleteProfile();
|
|
} else if (actionSource == profiles) {
|
|
changeProfile();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes the selected profile
|
|
*/
|
|
private void deleteProfile() {
|
|
Object selected = profiles.getSelectedItem();
|
|
if (selected != null) {
|
|
controller.removeProfile(selected.toString());
|
|
updateProfiles();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a new server with a server tab
|
|
*/
|
|
private void addServer() {
|
|
String serverName = JOptionPane.showInputDialog("Name of server: ");
|
|
try {
|
|
controller.getCurrentProfile().addCollection(serverName);
|
|
} catch (ConfigurationException e1) {
|
|
e1.printStackTrace();
|
|
}
|
|
this.updateWithSavedProfileData();
|
|
controller.getCurrentProfile().updateConsoles();
|
|
}
|
|
|
|
/**
|
|
* Updates the ServerLauncherGUI components to block a user from doing illegal actions
|
|
*
|
|
* @param running <p>Whether the servers are currently running</p>
|
|
*/
|
|
public void updateGUIElementsWhenServersStartOrStop(boolean running) {
|
|
boolean stopped = !running; //Most gui is only enabled when the server is stopped rather than running.
|
|
profiles.setEnabled(stopped);
|
|
addProfileButton.setEnabled(stopped);
|
|
deleteProfileButton.setEnabled(stopped);
|
|
startServerButton.setEnabled(stopped);
|
|
addServerButton.setEnabled(stopped);
|
|
tabbedPane.setEnabledAt(2, stopped);
|
|
stopServerButton.setEnabled(running);
|
|
}
|
|
|
|
/**
|
|
* Saves the previous profile and loads data from the new profile
|
|
*/
|
|
private void changeProfile() {
|
|
controller.saveState();
|
|
Object current = this.profiles.getSelectedItem();
|
|
if (current != null) {
|
|
controller.setCurrentProfile(current.toString());
|
|
}
|
|
this.updateWithSavedProfileData();
|
|
controller.getCurrentProfile().updateConsoles();
|
|
}
|
|
|
|
/**
|
|
* Stops all servers
|
|
*/
|
|
public void stopServers() {
|
|
try {
|
|
setStatus("Servers are stopping...");
|
|
Server.stop();
|
|
} catch (IOException e1) {
|
|
showError("Could not stop server.");
|
|
e1.printStackTrace();
|
|
} catch (InterruptedException e) {
|
|
showError("Could not kill server.");
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets a specific message from its key
|
|
*
|
|
* @param stringKey <p>The key for the string to get</p>
|
|
* @return <p>The corresponding string</p>
|
|
*/
|
|
public String getMessage(String stringKey) {
|
|
return textStrings.get(stringKey);
|
|
}
|
|
|
|
/**
|
|
* Loads popup messages from a text file
|
|
*/
|
|
private void loadMessages() throws FileNotFoundException {
|
|
textStrings = new HashMap<>();
|
|
Scanner file = getResourceAsScanner("menumsg.csv");
|
|
while (file.hasNextLine()) {
|
|
String nextLine = file.nextLine();
|
|
String[] line = nextLine.split("=");
|
|
String content = line[1].replaceAll("_BREAK_", System.getProperty("line.separator"));
|
|
textStrings.put(line[0], content);
|
|
}
|
|
}
|
|
}
|