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;
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.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
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
public void start(Stage primaryStage) throws Exception {
setupLogger();
Parent root = FXMLLoader.load(getClass().getResource("MainView.fxml"));
primaryStage.setTitle("Numbers Station");
primaryStage.setScene(new Scene(root));
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) {
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);
}
}

View File

@ -10,6 +10,8 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Random;
import java.util.ResourceBundle;
import javafx.beans.property.SimpleStringProperty;
@ -29,6 +31,8 @@ import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController implements Initializable {
private static final Logger logger = Logger.getLogger(Main.class.getName());
private Stage settingsStage;
private Stage selectStationStage;
private MainSettings settings;
@ -75,7 +79,7 @@ public class MainController implements Initializable {
updateStationSettings(selectedStation);
});
} catch (Exception e) {
e.printStackTrace();
logger.log(Level.SEVERE, "Exception while opening settings", e);
}
} else {
settingsStage.toFront();
@ -83,7 +87,7 @@ public class MainController implements Initializable {
}
@FXML
private void handleSelectStationButtonPress() {
private void handleSelectStationButtonPress() throws Exception {
if (selectStationStage == null || !selectStationStage.isShowing()) {
try {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("StationSelectionView.fxml"));
@ -107,7 +111,7 @@ public class MainController implements Initializable {
updateStationSettings(newSelectedStation);
});
} catch (Exception e) {
e.printStackTrace();
logger.log(Level.SEVERE, "Exception while selecting station", e);
}
} else {
selectStationStage.toFront();
@ -118,12 +122,10 @@ public class MainController implements Initializable {
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.registerModule(new JavaTimeModule());
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)) {
Files.createDirectories(directoryPath);
}
@ -141,9 +143,8 @@ public class MainController implements Initializable {
}
}
} 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));
e.printStackTrace();
}
}
@ -218,6 +219,8 @@ public class MainController implements Initializable {
}
private void updateStationSettings(StationSettings newStationSettings) {
logger.info("Updating station settings: " + newStationSettings.getName());
if (newStationSettings == null) {
return;
}

View File

@ -11,8 +11,11 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.logging.Logger;
public class MainSettings {
private static final Logger logger = Logger.getLogger(Main.class.getName());
private int digitsPerGroup;
private String username;
private int refreshInterval;
@ -25,6 +28,21 @@ public class MainSettings {
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() {
return stations;
}
@ -58,16 +76,15 @@ public class MainSettings {
xmlMapper.registerModule(new JavaTimeModule());
xmlMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
try {
String userHome = System.getProperty("user.home");
Path directoryPath = Paths.get(userHome, ".numbers-station");
Path filePath = directoryPath.resolve("settings.xml");
Path filePath = getSettingsFilePath();
Path directoryPath = filePath.getParent();
// Create the directory if it doesn't exist
if (!Files.exists(directoryPath)) {
Files.createDirectories(directoryPath);
}
xmlMapper.writeValue(new File(filePath.toString()), this);
logger.info("Settings saved to " + filePath.toString());
} catch (IOException e) {
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
private void handleRemoveButtonPress(ActionEvent event) {
int selectedIndex = stationListView.getSelectionModel().getSelectedIndex();

View File

@ -13,10 +13,10 @@
<Font size="20.0" />
</font>
</Label>
<Button layoutX="358.0" layoutY="17.0" mnemonicParsing="false" text="+" AnchorPane.rightAnchor="54.5" AnchorPane.topAnchor="17.0" onAction="#handleAddButtonPress"/>
<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="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" 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" />
<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>
</AnchorPane>