Compare commits

..

3 Commits

Author SHA1 Message Date
5a3df459f9 Add logging 2025-01-20 22:38:01 -08:00
1ae75d4891 Implement station selection cancel button 2025-01-20 20:41:29 -08:00
0704a4a931 Use XDG_CONFIG_HOME 2025-01-20 20:13:44 -08:00
6 changed files with 131 additions and 17 deletions

View File

@ -0,0 +1,34 @@
package name.nathanmcrae.numbersstation;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
public class CustomFormatter extends Formatter {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
@Override
public String format(LogRecord record) {
String timestamp = dateFormat.format(new Date(record.getMillis()));
String logLevel = record.getLevel().getName();
String className = record.getSourceClassName();
String methodName = record.getSourceMethodName();
String message = formatMessage(record);
StringBuilder logMessage = new StringBuilder();
logMessage.append(String.format("%s\t%s\t%s\t%s\t%s", timestamp, logLevel, className, methodName, message));
if (record.getThrown() != null) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
record.getThrown().printStackTrace(pw);
logMessage.append("\n\t").append(sw.toString());
}
logMessage.append(System.lineSeparator());
return logMessage.toString();
}
}

View File

@ -1,21 +1,74 @@
package name.nathanmcrae.numbersstation; package name.nathanmcrae.numbersstation;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.FileHandler;
import java.util.logging.Logger;
import java.util.logging.Level;
import javafx.application.Application; import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
public class Main extends Application { public class Main extends Application {
private static final Logger logger = Logger.getLogger(Main.class.getName());
private static Path statePath = null;
public Path getStatePath() {
if (statePath == null) {
String stateHome = System.getenv("XDG_STATE_HOME");
if (stateHome == null || stateHome.isEmpty() || !Paths.get(stateHome).isAbsolute()) {
String userHome = System.getProperty("user.home");
statePath = Paths.get(userHome, ".local", "state", "numbers-station");
} else {
statePath = Paths.get(stateHome, "numbers-station");
}
}
return statePath;
}
@Override @Override
public void start(Stage primaryStage) throws Exception { public void start(Stage primaryStage) throws Exception {
setupLogger();
Parent root = FXMLLoader.load(getClass().getResource("MainView.fxml")); Parent root = FXMLLoader.load(getClass().getResource("MainView.fxml"));
primaryStage.setTitle("Numbers Station"); primaryStage.setTitle("Numbers Station");
primaryStage.setScene(new Scene(root)); primaryStage.setScene(new Scene(root));
primaryStage.show(); primaryStage.show();
logger.info("Application started");
}
private void setupLogger() {
try {
Path logFile = getStatePath().resolve("main.log");
if (!Files.exists(logFile.getParent())) {
Files.createDirectories(logFile.getParent());
}
FileHandler fileHandler = new FileHandler(logFile.toString(), true);
fileHandler.setFormatter(new CustomFormatter());
logger.addHandler(fileHandler);
} catch (IOException e) {
System.out.println("Failed to set up logger: " + e.getMessage());
}
} }
public static void main(String[] args) { public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
logger.log(Level.SEVERE, "Unhandled exception caught", throwable);
});
Platform.setImplicitExit(false);
Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
logger.log(Level.SEVERE, "Unhandled exception in JavaFX application thread", throwable);
});
launch(args); launch(args);
} }
} }

View File

@ -10,6 +10,8 @@ import java.net.URL;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Random; import java.util.Random;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
@ -29,6 +31,8 @@ import javafx.stage.Modality;
import javafx.stage.Stage; import javafx.stage.Stage;
public class MainController implements Initializable { public class MainController implements Initializable {
private static final Logger logger = Logger.getLogger(Main.class.getName());
private Stage settingsStage; private Stage settingsStage;
private Stage selectStationStage; private Stage selectStationStage;
private MainSettings settings; private MainSettings settings;
@ -75,7 +79,7 @@ public class MainController implements Initializable {
updateStationSettings(selectedStation); updateStationSettings(selectedStation);
}); });
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.log(Level.SEVERE, "Exception while opening settings", e);
} }
} else { } else {
settingsStage.toFront(); settingsStage.toFront();
@ -83,7 +87,7 @@ public class MainController implements Initializable {
} }
@FXML @FXML
private void handleSelectStationButtonPress() { private void handleSelectStationButtonPress() throws Exception {
if (selectStationStage == null || !selectStationStage.isShowing()) { if (selectStationStage == null || !selectStationStage.isShowing()) {
try { try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StationSelectionView.fxml")); FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StationSelectionView.fxml"));
@ -107,7 +111,7 @@ public class MainController implements Initializable {
updateStationSettings(newSelectedStation); updateStationSettings(newSelectedStation);
}); });
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); logger.log(Level.SEVERE, "Exception while selecting station", e);
} }
} else { } else {
selectStationStage.toFront(); selectStationStage.toFront();
@ -118,12 +122,10 @@ public class MainController implements Initializable {
XmlMapper xmlMapper = new XmlMapper(); XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new JavaTimeModule()); xmlMapper.registerModule(new JavaTimeModule());
String userHome = System.getProperty("user.home"); String userHome = System.getProperty("user.home");
// TODO: XDG
Path directoryPath = Paths.get(userHome, ".numbers-station");
Path filePath = directoryPath.resolve("settings.xml");
try {
// Create the directory if it doesn't exist Path filePath = MainSettings.getSettingsFilePath();
Path directoryPath = filePath.getParent();
try {
if (!Files.exists(directoryPath)) { if (!Files.exists(directoryPath)) {
Files.createDirectories(directoryPath); Files.createDirectories(directoryPath);
} }
@ -141,9 +143,8 @@ public class MainController implements Initializable {
} }
} }
} catch (IOException e) { } catch (IOException e) {
// Print the contents of filePath logger.log(Level.SEVERE, "Failed to load settings from " + filePath.toString(), e);
System.out.println("File contents: " + Files.readString(filePath)); System.out.println("File contents: " + Files.readString(filePath));
e.printStackTrace();
} }
} }
@ -218,6 +219,8 @@ public class MainController implements Initializable {
} }
private void updateStationSettings(StationSettings newStationSettings) { private void updateStationSettings(StationSettings newStationSettings) {
logger.info("Updating station settings: " + newStationSettings.getName());
if (newStationSettings == null) { if (newStationSettings == null) {
return; return;
} }

View File

@ -11,8 +11,11 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.logging.Logger;
public class MainSettings { public class MainSettings {
private static final Logger logger = Logger.getLogger(Main.class.getName());
private int digitsPerGroup; private int digitsPerGroup;
private String username; private String username;
private int refreshInterval; private int refreshInterval;
@ -25,6 +28,21 @@ public class MainSettings {
stations.add(new StationSettings("Station 1")); stations.add(new StationSettings("Station 1"));
} }
public static Path getSettingsFilePath() {
String configHome = System.getenv("XDG_CONFIG_HOME");
Path directoryPath;
Path filePath;
if (configHome != null && !configHome.isEmpty() && Paths.get(configHome).isAbsolute()) {
directoryPath = Paths.get(configHome, "numbers-station");
} else {
String userHome = System.getProperty("user.home");
directoryPath = Paths.get(userHome, ".config", "numbers-station");
}
return directoryPath.resolve("main-settings.xml");
}
public ArrayList<StationSettings> getStations() { public ArrayList<StationSettings> getStations() {
return stations; return stations;
} }
@ -58,16 +76,15 @@ public class MainSettings {
xmlMapper.registerModule(new JavaTimeModule()); xmlMapper.registerModule(new JavaTimeModule());
xmlMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); xmlMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
try { try {
String userHome = System.getProperty("user.home"); Path filePath = getSettingsFilePath();
Path directoryPath = Paths.get(userHome, ".numbers-station"); Path directoryPath = filePath.getParent();
Path filePath = directoryPath.resolve("settings.xml");
// Create the directory if it doesn't exist
if (!Files.exists(directoryPath)) { if (!Files.exists(directoryPath)) {
Files.createDirectories(directoryPath); Files.createDirectories(directoryPath);
} }
xmlMapper.writeValue(new File(filePath.toString()), this); xmlMapper.writeValue(new File(filePath.toString()), this);
logger.info("Settings saved to " + filePath.toString());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -63,6 +63,13 @@ public class StationSelectionController {
}); });
} }
@FXML
private void handleCancelButtonPress(Event event) {
Node node = (Node) event.getSource();
Stage stage = (Stage) node.getScene().getWindow();
stage.close();
}
@FXML @FXML
private void handleRemoveButtonPress(ActionEvent event) { private void handleRemoveButtonPress(ActionEvent event) {
int selectedIndex = stationListView.getSelectionModel().getSelectedIndex(); int selectedIndex = stationListView.getSelectionModel().getSelectedIndex();

View File

@ -13,10 +13,10 @@
<Font size="20.0" /> <Font size="20.0" />
</font> </font>
</Label> </Label>
<Button layoutX="358.0" layoutY="17.0" mnemonicParsing="false" text="+" AnchorPane.rightAnchor="54.5" AnchorPane.topAnchor="17.0" onAction="#handleAddButtonPress"/> <Button layoutX="358.0" layoutY="17.0" mnemonicParsing="false" onAction="#handleAddButtonPress" text="+" AnchorPane.rightAnchor="54.5" AnchorPane.topAnchor="17.0" />
<Button layoutX="391.0" layoutY="17.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="25.0" text="-" AnchorPane.rightAnchor="21.0" AnchorPane.topAnchor="17.0" onAction="#handleRemoveButtonPress"/> <Button layoutX="391.0" layoutY="17.0" mnemonicParsing="false" onAction="#handleRemoveButtonPress" prefHeight="25.0" prefWidth="25.0" text="-" AnchorPane.rightAnchor="21.0" AnchorPane.topAnchor="17.0" />
<ListView fx:id="stationListView" layoutX="14.0" layoutY="57.0" prefHeight="301.0" prefWidth="409.0" AnchorPane.bottomAnchor="42.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="57.0" /> <ListView fx:id="stationListView" layoutX="14.0" layoutY="57.0" prefHeight="301.0" prefWidth="409.0" AnchorPane.bottomAnchor="42.0" AnchorPane.leftAnchor="14.0" AnchorPane.rightAnchor="14.0" AnchorPane.topAnchor="57.0" />
<Button layoutX="313.0" layoutY="365.0" mnemonicParsing="false" onMousePressed="#handleSelectButtonPress" text="Select" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="76.5" /> <Button layoutX="313.0" layoutY="365.0" mnemonicParsing="false" onMousePressed="#handleSelectButtonPress" text="Select" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="76.5" />
<Button layoutX="370.0" layoutY="365.0" mnemonicParsing="false" text="Cancel" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="15.5" /> <Button layoutX="370.0" layoutY="365.0" mnemonicParsing="false" onMousePressed="#handleCancelButtonPress" text="Cancel" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="15.5" />
</children> </children>
</AnchorPane> </AnchorPane>