Compare commits
72 commits
Author | SHA1 | Date | |
---|---|---|---|
|
712144a65d | ||
|
c4985886aa | ||
|
cbe56800f3 | ||
|
5b5c866a69 | ||
|
bbf7f5caa9 | ||
|
e23156f65c | ||
|
26c712aaf2 | ||
![]() |
c4d68f6b9d | ||
![]() |
0fa050d80e | ||
![]() |
4c87a025d6 | ||
![]() |
bcc1003d1d | ||
|
d7b3e24411 | ||
|
309ff7924a | ||
|
99821d6fd1 | ||
|
24a82f8b9e | ||
|
a825d31a8e | ||
|
25a2349715 | ||
|
1b1d8b2330 | ||
|
51edfed385 | ||
|
5e774934a4 | ||
|
8679a556ca | ||
|
55788f58b5 | ||
|
3ff5f782de | ||
|
3f2ce3b810 | ||
|
6229197fba | ||
|
edacc0a359 | ||
|
b26e0ecf5e | ||
|
7a59e40b38 | ||
|
208f56eae9 | ||
|
70bb8d3af6 | ||
|
9dee564c4f | ||
|
ae54130eaa | ||
|
bd185d1462 | ||
|
ce89b7a14a | ||
|
b02d9d7132 | ||
|
b28eb18c75 | ||
|
e1972daa11 | ||
|
0293bce73e | ||
|
6dece5006d | ||
|
4dbf2ece0a | ||
|
fbc1a28068 | ||
|
bb68eb53f7 | ||
|
2f56b00881 | ||
|
d718323976 | ||
|
7870670ce0 | ||
|
91b91777de | ||
|
770f227637 | ||
|
2609364f7e | ||
|
684c924cec | ||
|
b0a2945ddc | ||
|
53e5e3a338 | ||
|
24df5f0f1a | ||
|
a30d5ea135 | ||
|
a477926c99 | ||
|
5242d629a5 | ||
|
41ae37c789 | ||
|
53781e7f95 | ||
|
df5d167935 | ||
|
b46dd4c184 | ||
|
1a935e7313 | ||
|
984f42bd49 | ||
|
f1eb274ed8 | ||
|
464f4a400e | ||
|
0fd299cf44 | ||
|
fd84ef242f | ||
|
016ecd8acd | ||
|
05983ec78f | ||
|
1105a52a0a | ||
|
e3b0e1be80 | ||
|
5081c300cc | ||
|
234f899020 | ||
|
1bd79efe32 |
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -5,9 +5,12 @@
|
|||
target/
|
||||
out/
|
||||
build/
|
||||
classes/
|
||||
.project
|
||||
.classpath
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
/llanfair.xml
|
||||
/llanfair.xml
|
||||
/*.jar
|
||||
/*.app
|
73
README.md
73
README.md
|
@ -1,58 +1,77 @@
|
|||
# Llanfair
|
||||
|
||||
**This project is not maintained anymore. I have not speedrun anything in over a year, and while I once thought I might come back to it again, that is highly unlikely to be the case anymore.**
|
||||
|
||||
---
|
||||
|
||||
[From the homepage](http://jenmaarai.com/llanfair/en/):
|
||||
|
||||
> Llanfair is a free software that helps speedrunners keep track of their run. Released in August 2012, its capacity for customization and its portability allowed it to garner some recognition in the scene. Developed in Java, Llanfair can run on Windows, MacOS, or Unix.
|
||||
|
||||
The original author Xavier "Xunkar" Sencert was kind enough to release the sources
|
||||
(see [here](https://twitter.com/Xunkar/status/671042537134624768) and [here](https://twitter.com/Xunkar/status/671099823563632641))
|
||||
when I asked. I'm not completely certain if Xunkar ever intends to continue development of Llanfair himself as it
|
||||
seems he uses LiveSplit now (?).
|
||||
|
||||
Regardless, here I will be extending the original application as best I can by adding some missing features here and
|
||||
when I asked. Here I will be extending the original application as best I can by adding some missing features here and
|
||||
there and fixing bugs as needed.
|
||||
|
||||
Note that Xunkar has started working on Llanfair v2.0 which is a complete rewrite. You can
|
||||
[check it's progress here](https://github.com/xunkar/llanfair).
|
||||
|
||||
## Download
|
||||
|
||||
Check the [releases page](https://github.com/gered/Llanfair/releases) for downloadable JARs.
|
||||
Check the [releases page](https://github.com/gered/Llanfair/releases) for downloadable JARs.
|
||||
|
||||
JARs can be run from the command line via something similar to:
|
||||
Llanfair requires Java 7 or later (you are encouraged to use the most recent version of Java).
|
||||
|
||||
Downloaded JARs can be run from the command line via something similar to:
|
||||
|
||||
```
|
||||
$ java -jar Llanfair.jar
|
||||
$ java -jar /path/to/Llanfair.jar
|
||||
```
|
||||
|
||||
## Changes
|
||||
## Major Changes / Fixes
|
||||
|
||||
### 1.5
|
||||
Changes from v1.4.3 (the last official release). Note that a couple of these changes were actually by
|
||||
Xunkar himself as he did some work post-1.4.3 that he just never released.
|
||||
The main changes from v1.4.3 (the last official release from Xunkar) are as follows:
|
||||
|
||||
* If the JNativeHook event hook registration fails the app will display an error prompt instead of failing
|
||||
silently. Additionally on some OS's you may see your OS prompt you with some kind of accessibility permissions
|
||||
request (note that in either case, you will have to restart Llanfair for the permissions change to take effect).
|
||||
* XML formats for both app config and saved runs. Some backwards compatibility for opening the old binary versions.
|
||||
* Config and runs are now saved by default under `$user_home/.llanfair/`.
|
||||
* Runs are now saved with a default `.lfs` file extension.
|
||||
* Optional world record display, via run data from speedrun.com. Contributed by [4ilo](https://github.com/4ilo).
|
||||
* Enhancements to JNativeHook support for global key events. Llanfair will prompt with an error
|
||||
if the hook could not be registered instead of failing silently. Additionally on some OS's you
|
||||
may see your OS prompt you with some kind of accessibility permissions request.
|
||||
* Choice between global or non-global hotkeys.
|
||||
* Human-readable config and splits file formats (XML). This change is almost entirely based on work
|
||||
Xunkar had started after release of v1.4.3.
|
||||
* Support for a delayed/negative run start time. Useful if you want to start the run at a time more convenient for you
|
||||
but before any of the segments should start (e.g. to skip initial loading, fadeouts, etc)
|
||||
* Attempt counter (both number of total attempts and number of completed attempts).
|
||||
* Additional font customization settings.
|
||||
but before any of the segments should start (e.g. to skip initial loading, fadeouts, etc).
|
||||
* "Sum of best" time display option.
|
||||
* Attempt counter showing: the number of total attempts, number of completed runs and a per-session attempt counter.
|
||||
* Additional font and colour customization settings.
|
||||
* Coloring of split time deltas using slightly different color shades based on if you're gaining/losing time while
|
||||
already ahead/behind.
|
||||
* Other very minor bug fixes.
|
||||
* Run goal text setting has been changed to a more generic run sub-title setting.
|
||||
* By default the config file is saved under `$user_home/.llanfair/` and the default location
|
||||
to save/load splits is `$user_home/.llanfair/splits/` (though you can of course also choose
|
||||
whatever other location you like).
|
||||
* Ensure application settings are saved when a Mac user quits via Cmd+Q.
|
||||
* Saved splits are now saved with a default `.lfs` file extension.
|
||||
* Fix that prevents existing splits files from being accidentally overwritten when choosing "New" option from menu (after you already had a splits file open), and then choose "Save."
|
||||
* User setting to control amount of files shown in the "Open Recent" menu list.
|
||||
* Option to set a different default splits file directory (this is merely an additional convenience, most people probably won't use this).
|
||||
* Other minor bug fixes.
|
||||
|
||||
**NOTE**: I've temporarily disabled localization support. Some of the strings used were out of sync between English
|
||||
and the other languages and I ended up adding new English strings too. I only speak English and so have no way to
|
||||
update the other language translations. It seemed wrong to include an option in the app to switch languages when the
|
||||
other language text was "bad", so it will remain disabled until these translations are brought up to date again.
|
||||
### Important Note About Localization
|
||||
|
||||
I've temporarily disabled localization support. Some of the strings used in Llanfair were out of sync between English
|
||||
and the other languages and I ended up adding new English strings too as I've been working on feature enhancements and
|
||||
bug fixing. I only speak English and so have no way to update the other language translations. It seemed wrong to me to
|
||||
include an option in the app to switch languages when the other language text was incomplete, so it will remain
|
||||
disabled until these translations are brought up to date again (which will have to be done by someone else -- pull
|
||||
requests are more then welcome!).
|
||||
|
||||
## Building and Running
|
||||
|
||||
You will need Gradle. Obviously any IDE with Gradle support will simply mean you can just open this project
|
||||
right away in your IDE and get developing immediately. Easy.
|
||||
|
||||
Llanfair currently requires Java 7.
|
||||
Llanfair currently requires Java 7 or later.
|
||||
|
||||
#### Command Line Building / Running / Distribution
|
||||
|
||||
|
@ -75,7 +94,7 @@ $ gradle shadowJar
|
|||
```
|
||||
|
||||
Which will spit out an "uber JAR" under `build/libs` under the naming convention `llanfair-[VERSION]-all.jar`. This
|
||||
JAR will of course work on any OS.
|
||||
JAR will of course work on any OS and includes all required dependencies inside it.
|
||||
|
||||
To build a redistributable Mac app bundle (.app):
|
||||
|
||||
|
|
10
build.gradle
10
build.gradle
|
@ -1,21 +1,18 @@
|
|||
buildscript {
|
||||
repositories { jcenter() }
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.2'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id 'com.github.johnrengelman.shadow' version '1.2.2'
|
||||
id 'edu.sc.seis.macAppBundle' version '2.1.3'
|
||||
id 'com.github.johnrengelman.shadow' version '2.0.4'
|
||||
id 'edu.sc.seis.macAppBundle' version '2.2.1'
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.7
|
||||
targetCompatibility = 1.7
|
||||
|
||||
version = '1.5'
|
||||
version = '1.6-SNAPHSOT'
|
||||
mainClassName = 'org.fenix.llanfair.Llanfair'
|
||||
|
||||
repositories {
|
||||
|
@ -26,6 +23,7 @@ dependencies {
|
|||
compile fileTree(dir: 'lib', include: ['*.jar'])
|
||||
compile 'com.1stleg:jnativehook:2.0.2'
|
||||
compile 'com.thoughtworks.xstream:xstream:1.4.4'
|
||||
compile group: 'org.json', name: 'json', version: '20180130'
|
||||
}
|
||||
|
||||
macAppBundle {
|
||||
|
|
32
src/main/java/org/fenix/WorldRecord/Category.java
Normal file
32
src/main/java/org/fenix/WorldRecord/Category.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package org.fenix.WorldRecord;
|
||||
|
||||
/**
|
||||
* Category class to represent a speedrun.com category
|
||||
* @author 4ilo 2018
|
||||
*/
|
||||
public class Category
|
||||
{
|
||||
private String name = "";
|
||||
private String id = "";
|
||||
|
||||
public Category(String name, String id)
|
||||
{
|
||||
this.name = name;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
}
|
32
src/main/java/org/fenix/WorldRecord/Game.java
Normal file
32
src/main/java/org/fenix/WorldRecord/Game.java
Normal file
|
@ -0,0 +1,32 @@
|
|||
package org.fenix.WorldRecord;
|
||||
|
||||
/**
|
||||
* Game class to represent a speedrun.com game
|
||||
* @author 4ilo 2018
|
||||
*/
|
||||
public class Game
|
||||
{
|
||||
private String title;
|
||||
private String id;
|
||||
|
||||
public Game(String title, String id)
|
||||
{
|
||||
this.title = title;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle()
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return this.title;
|
||||
}
|
||||
}
|
58
src/main/java/org/fenix/WorldRecord/JSONReader.java
Normal file
58
src/main/java/org/fenix/WorldRecord/JSONReader.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
package org.fenix.WorldRecord;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* JSONReader class to fetch JSON from api
|
||||
* @author 4ilo 2018
|
||||
*/
|
||||
public class JSONReader
|
||||
{
|
||||
/**
|
||||
* Get the content of the given reader as a string
|
||||
* @param reader a reader object
|
||||
* @return String with the data of the reader
|
||||
* @throws IOException
|
||||
*/
|
||||
private static String readAll(Reader reader) throws IOException
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int cp;
|
||||
|
||||
while((cp = reader.read()) != -1)
|
||||
{
|
||||
builder.append((char) cp);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the json from the given json-api url
|
||||
* @param url the api url
|
||||
* @return JSONObject with the data from the url
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static JSONObject readJsonFromUrl(String url) throws IOException, JSONException
|
||||
{
|
||||
InputStream stream = new URL(url).openStream();
|
||||
|
||||
try
|
||||
{
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, Charset.forName("UTF-8")));
|
||||
String jsonText = readAll(reader);
|
||||
|
||||
return new JSONObject(jsonText);
|
||||
|
||||
} finally
|
||||
{
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
}
|
289
src/main/java/org/fenix/WorldRecord/RecordDialog.java
Normal file
289
src/main/java/org/fenix/WorldRecord/RecordDialog.java
Normal file
|
@ -0,0 +1,289 @@
|
|||
package org.fenix.WorldRecord;
|
||||
|
||||
import org.fenix.llanfair.Llanfair;
|
||||
import org.fenix.llanfair.dialog.EditRun;
|
||||
import org.fenix.llanfair.dialog.LlanfairDialog;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Dialog window to select a world record on speedrun.com
|
||||
* @author 4ilo 2018
|
||||
*/
|
||||
public class RecordDialog extends LlanfairDialog
|
||||
{
|
||||
final private Llanfair master;
|
||||
|
||||
private JLabel searchLabel = new JLabel("Search game:");
|
||||
private JButton searchButton = new JButton("Search");
|
||||
private JTextField searchField = new JTextField();
|
||||
|
||||
private DefaultComboBoxModel<Game> gameListModel = new DefaultComboBoxModel<>();
|
||||
private JComboBox<Game> games = new JComboBox<>(gameListModel);
|
||||
private JLabel gamesLabel = new JLabel("Games:");
|
||||
|
||||
private DefaultComboBoxModel<Category> categoryListModel = new DefaultComboBoxModel<>();
|
||||
private JComboBox<Category> categories = new JComboBox<>(categoryListModel);
|
||||
private JLabel categoriesLabel = new JLabel("Categories:");
|
||||
|
||||
private JButton close = new JButton("Close");
|
||||
private JButton ok = new JButton("Ok");
|
||||
private JLabel worldRecord = new JLabel("Unknown World Record");
|
||||
|
||||
private ActionListener categoryListener;
|
||||
private ActionListener gameListener;
|
||||
|
||||
private Category category;
|
||||
private EditRun editRun;
|
||||
|
||||
|
||||
public RecordDialog(EditRun editRun, Llanfair master)
|
||||
{
|
||||
super(editRun);
|
||||
this.master = master;
|
||||
this.editRun = editRun;
|
||||
|
||||
JPanel searchPanel = new JPanel(new FlowLayout());
|
||||
{
|
||||
searchField.setPreferredSize(new Dimension(200,30));
|
||||
|
||||
searchPanel.add(searchLabel);
|
||||
searchPanel.add(searchField);
|
||||
searchPanel.add(searchButton);
|
||||
|
||||
searchButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
searchGame(searchField.getText());
|
||||
}
|
||||
});
|
||||
}
|
||||
JPanel gamesPanel = new JPanel(new GridLayout(1,2));
|
||||
{
|
||||
gamesPanel.add(gamesLabel);
|
||||
gamesPanel.add(games);
|
||||
|
||||
games.setEnabled(false);
|
||||
}
|
||||
JPanel categoriesPanel = new JPanel(new GridLayout(1,2));
|
||||
{
|
||||
categoriesPanel.add(categoriesLabel);
|
||||
categoriesPanel.add(categories);
|
||||
|
||||
categories.setEnabled(false);
|
||||
}
|
||||
JPanel buttonPanel = new JPanel(new FlowLayout());
|
||||
{
|
||||
buttonPanel.add(ok);
|
||||
buttonPanel.add(close);
|
||||
|
||||
ok.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
actionOk();
|
||||
}
|
||||
});
|
||||
|
||||
close.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
|
||||
this.add(searchPanel);
|
||||
this.add(gamesPanel);
|
||||
this.add(categoriesPanel);
|
||||
|
||||
this.add(worldRecord);
|
||||
worldRecord.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
|
||||
this.add(buttonPanel);
|
||||
|
||||
this.setTitle("World Record selection");
|
||||
pack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Search the game title on speedrun.com
|
||||
* @param name The name of the game
|
||||
*/
|
||||
private void searchGame(String name)
|
||||
{
|
||||
this.resetFields();
|
||||
|
||||
ArrayList<Game> games = new ArrayList<>();
|
||||
|
||||
try {
|
||||
games = WorldRecordParser.searchGames(name);
|
||||
} catch (IOException e)
|
||||
{
|
||||
master.showError("Error searching for matching games from speedrun.com.", e);
|
||||
}
|
||||
|
||||
this.setGames(games);
|
||||
this.addGameListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the categories for a game on speedrun.com
|
||||
* @param game A game object received from the game search
|
||||
*/
|
||||
private void getCategories(Game game)
|
||||
{
|
||||
ArrayList<Category> categories = new ArrayList<>();
|
||||
|
||||
try {
|
||||
categories = WorldRecordParser.getCategories(game);
|
||||
} catch (IOException e)
|
||||
{
|
||||
master.showError("Error fetching game categories from speedrun.com.", e);
|
||||
}
|
||||
|
||||
this.setCategories(categories);
|
||||
this.addCategoryListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world record time and owner from speedrun.com
|
||||
* @param category A category object received from the category search
|
||||
*/
|
||||
private void getWorldRecord(Category category)
|
||||
{
|
||||
String worldRecord = "";
|
||||
|
||||
try {
|
||||
worldRecord = WorldRecordParser.getRecord(category);
|
||||
} catch (IOException e)
|
||||
{
|
||||
master.showError("Error fetching game category world record time/owner from speedrun.com.", e);
|
||||
}
|
||||
|
||||
this.worldRecord.setText(worldRecord);
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the games in the array to the games combobox
|
||||
* @param games A ArrayList of game objects
|
||||
*/
|
||||
private void setGames(ArrayList<Game> games)
|
||||
{
|
||||
this.resetFields();
|
||||
|
||||
for(Game game: games)
|
||||
{
|
||||
gameListModel.addElement(game);
|
||||
}
|
||||
|
||||
this.games.setEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the categories in the array to the categories checkbox
|
||||
* @param categories A ArrayList of Category objects
|
||||
*/
|
||||
private void setCategories(ArrayList<Category> categories)
|
||||
{
|
||||
categoryListModel.removeAllElements();
|
||||
|
||||
for(Category category: categories)
|
||||
{
|
||||
categoryListModel.addElement(category);
|
||||
}
|
||||
|
||||
this.categories.setEnabled(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the dialog without saving
|
||||
*/
|
||||
private void close()
|
||||
{
|
||||
this.setVisible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the dialog and send a signal to the parent window
|
||||
*/
|
||||
private void actionOk()
|
||||
{
|
||||
this.category = ((Category) categories.getSelectedItem());
|
||||
this.setVisible(false);
|
||||
|
||||
this.editRun.recordSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the changeListener to the categories combobox
|
||||
*/
|
||||
private void addCategoryListener()
|
||||
{
|
||||
categoryListener = new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
getWorldRecord((Category) categories.getSelectedItem());
|
||||
}
|
||||
};
|
||||
|
||||
categories.addActionListener(categoryListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the changeListener to the games combobox
|
||||
*/
|
||||
private void addGameListener()
|
||||
{
|
||||
|
||||
gameListener = new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
getCategories((Game) games.getSelectedItem());
|
||||
}
|
||||
};
|
||||
|
||||
games.addActionListener(gameListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the comboboxes
|
||||
*/
|
||||
private void resetFields()
|
||||
{
|
||||
try {
|
||||
categories.removeActionListener(categoryListener);
|
||||
games.removeActionListener(gameListener);
|
||||
} catch (Exception e){}
|
||||
|
||||
categories.setEnabled(false);
|
||||
games.setEnabled(false);
|
||||
|
||||
gameListModel.removeAllElements();
|
||||
categoryListModel.removeAllElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected category object
|
||||
* @return Category
|
||||
*/
|
||||
public Category getCategory()
|
||||
{
|
||||
return category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the selected record string
|
||||
* @return String
|
||||
*/
|
||||
public String getRecordString()
|
||||
{
|
||||
return worldRecord.getText();
|
||||
}
|
||||
}
|
132
src/main/java/org/fenix/WorldRecord/WorldRecordParser.java
Normal file
132
src/main/java/org/fenix/WorldRecord/WorldRecordParser.java
Normal file
|
@ -0,0 +1,132 @@
|
|||
package org.fenix.WorldRecord;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author 4ilo 2018
|
||||
*/
|
||||
public class WorldRecordParser
|
||||
{
|
||||
/**
|
||||
* Search the speedrun.com database for the game with the given name
|
||||
* @param name The name of the game you want to search
|
||||
* @return List of games
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ArrayList<Game> searchGames(String name) throws IOException
|
||||
{
|
||||
String url = "https://www.speedrun.com/api/v1/games?name=" + name;
|
||||
JSONArray json_games;
|
||||
ArrayList<Game> games = new ArrayList<>();
|
||||
|
||||
|
||||
JSONObject json = JSONReader.readJsonFromUrl(url);
|
||||
json_games = json.getJSONArray("data");
|
||||
|
||||
|
||||
for(Object game: json_games)
|
||||
{
|
||||
JSONObject obj = (JSONObject) game;
|
||||
games.add(new Game(
|
||||
obj.getJSONObject("names").get("international").toString(),
|
||||
obj.get("id").toString()
|
||||
));
|
||||
}
|
||||
|
||||
return games;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the speedrun.com categories for the given game
|
||||
* @param game WorldRecord.Game object received from a game search
|
||||
* @return List of categories
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ArrayList<Category> getCategories(Game game) throws IOException
|
||||
{
|
||||
String url = "https://www.speedrun.com/api/v1/games/" + game.getId() + "/categories";
|
||||
|
||||
ArrayList<Category> categories = new ArrayList<>();
|
||||
|
||||
|
||||
JSONObject json = JSONReader.readJsonFromUrl(url);
|
||||
JSONArray json_categories = json.getJSONArray("data");
|
||||
|
||||
for(Object category: json_categories)
|
||||
{
|
||||
JSONObject obj = (JSONObject) category;
|
||||
categories.add(new Category(
|
||||
obj.get("name").toString(),
|
||||
obj.get("id").toString()
|
||||
));
|
||||
}
|
||||
|
||||
return categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the world record string for the given speedrun.com category
|
||||
* @param category WorldRecord.Category object received from a category search
|
||||
* @return The world record string
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String getRecord(Category category) throws IOException
|
||||
{
|
||||
String url = "https://www.speedrun.com/api/v1/categories/" + category.getId() + "/records";
|
||||
|
||||
JSONObject json = JSONReader.readJsonFromUrl(url);
|
||||
JSONArray json_runs = json.getJSONArray("data").getJSONObject(0).getJSONArray("runs");
|
||||
|
||||
JSONObject wr_run = json_runs.getJSONObject(0).getJSONObject("run");
|
||||
|
||||
String player_name = getPlayerName(wr_run.getJSONArray("players").getJSONObject(0));
|
||||
|
||||
return "World record: " + parseTime(wr_run.getJSONObject("times").getFloat("primary_t")) + " by " + player_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the speedrun.com player name for the given player JSONObject
|
||||
* @param player The player JSONObject extracted from a run object
|
||||
* @return The players name
|
||||
* @throws IOException
|
||||
*/
|
||||
private static String getPlayerName(JSONObject player) throws IOException
|
||||
{
|
||||
String uri = player.get("uri").toString();
|
||||
JSONObject json = JSONReader.readJsonFromUrl(uri);
|
||||
return json.getJSONObject("data").getJSONObject("names").get("international").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a time in seconds and miliseconds to a HH:MM:SS.sss format
|
||||
* @param time_seconds the time in seconds
|
||||
* @return Time in HH:MM:SS.sss format
|
||||
*/
|
||||
private static String parseTime(float time_seconds)
|
||||
{
|
||||
int hours = (int) time_seconds / 3600;
|
||||
int secLeft = (int) time_seconds - hours*3600;
|
||||
|
||||
int minutes = secLeft / 60;
|
||||
int seconds = secLeft - minutes * 60;
|
||||
|
||||
String time = "";
|
||||
|
||||
if(hours != 0)
|
||||
time += String.format("%02d:", hours);
|
||||
|
||||
time += String.format("%02d:%02d", minutes, seconds);
|
||||
|
||||
if(time_seconds % 1 != 0)
|
||||
{
|
||||
int miliseconds = Math.round((time_seconds%1) * 1000);
|
||||
time += "." + miliseconds;
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
}
|
|
@ -56,7 +56,7 @@ final class Actions {
|
|||
master = owner;
|
||||
|
||||
file = null;
|
||||
fileChooser = new JFileChooser(UserSettings.getSplitsPath());
|
||||
fileChooser = new JFileChooser(UserSettings.getSplitsPath(this.file));
|
||||
fileChooser.setFileFilter(new FileNameExtensionFilter("" + Language.RUN_FILE_FILTER, "lfs"));
|
||||
|
||||
lastUnsplit = 0L;
|
||||
|
@ -117,11 +117,12 @@ final class Actions {
|
|||
MenuItem source = ( MenuItem ) event.getSource();
|
||||
|
||||
if ( source == MenuItem.EDIT ) {
|
||||
EditRun dialog = new EditRun( run );
|
||||
EditRun dialog = new EditRun( run, master );
|
||||
dialog.display( true, master );
|
||||
} else if ( source == MenuItem.NEW ) {
|
||||
if ( confirmOverwrite() ) {
|
||||
master.setRun( new Run() );
|
||||
this.file = null;
|
||||
}
|
||||
} else if ( source == MenuItem.OPEN ) {
|
||||
open( null );
|
||||
|
@ -132,23 +133,29 @@ final class Actions {
|
|||
} else if ( source == MenuItem.SAVE ) {
|
||||
run.saveLiveTimes( !run.isPersonalBest() );
|
||||
run.reset();
|
||||
save();
|
||||
save(this.file);
|
||||
} else if ( source == MenuItem.SAVE_AS ) {
|
||||
file = null;
|
||||
save();
|
||||
save(null);
|
||||
} else if ( source == MenuItem.RESET ) {
|
||||
reset();
|
||||
} else if ( source == MenuItem.LOCK ) {
|
||||
master.setIgnoreNativeInputs( true );
|
||||
master.setLockedHotkeys(true);
|
||||
} else if ( source == MenuItem.UNLOCK ) {
|
||||
master.setIgnoreNativeInputs( false );
|
||||
master.setLockedHotkeys(false);
|
||||
} else if ( source == MenuItem.SETTINGS ) {
|
||||
EditSettings dialog = new EditSettings();
|
||||
EditSettings dialog = new EditSettings(master);
|
||||
dialog.display( true, master );
|
||||
} else if ( source == MenuItem.ABOUT ) {
|
||||
about();
|
||||
} else if ( source == MenuItem.EXIT ) {
|
||||
if ( confirmOverwrite() ) {
|
||||
// these might not really be necessary, but at this point just trying to be sure a running timer isn't
|
||||
// sometimes keeping the app open somehow ??
|
||||
if (run.getState() == Run.State.ONGOING)
|
||||
run.stop();
|
||||
run.reset();
|
||||
|
||||
// exit the app
|
||||
master.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -247,6 +254,8 @@ final class Actions {
|
|||
private File selectFile(FILE_CHOOSER_TYPE dialogType) {
|
||||
int action = -1;
|
||||
|
||||
fileChooser.setCurrentDirectory(new File(UserSettings.getSplitsPath(this.file)));
|
||||
|
||||
if (dialogType == FILE_CHOOSER_TYPE.OPEN)
|
||||
action = fileChooser.showOpenDialog(master);
|
||||
else if (dialogType == FILE_CHOOSER_TYPE.SAVE)
|
||||
|
@ -295,7 +304,7 @@ final class Actions {
|
|||
else if ( option == JOptionPane.YES_OPTION ) {
|
||||
run.saveLiveTimes( !betterRun );
|
||||
run.reset();
|
||||
save();
|
||||
save(this.file);
|
||||
confirmed = true;
|
||||
}
|
||||
}
|
||||
|
@ -391,13 +400,16 @@ final class Actions {
|
|||
/**
|
||||
* Saves the currently opened run to the currently selected file. If no
|
||||
* file has been selected, the user is asked for one.
|
||||
*
|
||||
* @param file the file to save to, or if null, the user is prompted to select one
|
||||
*/
|
||||
private void save() {
|
||||
private void save(File file) {
|
||||
if ( file == null ) {
|
||||
if ( ( file = selectFile(FILE_CHOOSER_TYPE.SAVE) ) == null ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.file = file;
|
||||
Settings.coordinates.set( master.getLocationOnScreen(), true );
|
||||
Settings.dimension.set( master.getSize(), true );
|
||||
|
||||
|
@ -406,6 +418,7 @@ final class Actions {
|
|||
try {
|
||||
XStream xml = new XStream( new DomDriver() );
|
||||
SerializationUtils.customize(xml);
|
||||
xml.autodetectAnnotations(true);
|
||||
out = new BufferedOutputStream( new FileOutputStream( file ) );
|
||||
xml.toXML( master.getRun(), out );
|
||||
} catch ( Exception ex ) {
|
||||
|
|
|
@ -19,6 +19,8 @@ public enum Language {
|
|||
|
||||
// Settings > Generic
|
||||
setting_alwaysOnTop,
|
||||
setting_useDefaultSplitsPath,
|
||||
setting_customSplitsPath,
|
||||
setting_language,
|
||||
setting_viewerLanguage,
|
||||
setting_recentFiles,
|
||||
|
@ -28,16 +30,23 @@ public enum Language {
|
|||
setting_accuracy,
|
||||
setting_locked,
|
||||
setting_warnOnReset,
|
||||
setting_windowUserResizable,
|
||||
setting_windowWidth,
|
||||
setting_maxRecentFiles,
|
||||
|
||||
// Settings > Color
|
||||
setting_color_background,
|
||||
setting_color_foreground,
|
||||
setting_color_time,
|
||||
setting_color_timer,
|
||||
setting_color_timeGained,
|
||||
setting_color_timeLost,
|
||||
setting_color_negativeTime,
|
||||
setting_color_timeGainedWhileAhead,
|
||||
setting_color_timeLostWhileAhead,
|
||||
setting_color_timeGainedWhileBehind,
|
||||
setting_color_timeLostWhileBehind,
|
||||
setting_color_newRecord,
|
||||
setting_color_title,
|
||||
setting_color_subTitle,
|
||||
setting_color_highlight,
|
||||
setting_color_separators,
|
||||
|
||||
|
@ -50,14 +59,14 @@ public enum Language {
|
|||
setting_hotkey_stop,
|
||||
setting_hotkey_pause,
|
||||
setting_hotkey_lock,
|
||||
GLOBAL_HOTKEYS_WARNING,
|
||||
GLOBAL_HOTKEYS_STARTUP_WARNING,
|
||||
GLOBAL_HOTKEYS_STARTUP_ERROR,
|
||||
|
||||
// Settings > Header
|
||||
setting_header_goal,
|
||||
setting_header_subTitle,
|
||||
setting_header_title,
|
||||
setting_header_showAttempts,
|
||||
setting_header_titleFont,
|
||||
setting_header_subTitleFont,
|
||||
|
||||
// Settings > History
|
||||
setting_history_rowCount,
|
||||
|
@ -99,6 +108,8 @@ public enum Language {
|
|||
setting_footer_bestTime,
|
||||
setting_footer_multiline,
|
||||
setting_footer_deltaLabels,
|
||||
setting_footer_sumOfBest,
|
||||
setting_footer_worldRecord,
|
||||
|
||||
// Accuracy
|
||||
accuracy_seconds,
|
||||
|
@ -135,6 +146,9 @@ public enum Language {
|
|||
error_read_file,
|
||||
error_write_file,
|
||||
error_import_run,
|
||||
error_window_width,
|
||||
error_max_recent_files,
|
||||
error_splits_path,
|
||||
|
||||
// Actions
|
||||
action_accept,
|
||||
|
@ -147,7 +161,7 @@ public enum Language {
|
|||
FOOTER,
|
||||
MISC,
|
||||
USE_MAIN_FONT,
|
||||
LB_GOAL,
|
||||
LB_SUBTITLE,
|
||||
ICON,
|
||||
COLORS,
|
||||
|
||||
|
@ -181,6 +195,7 @@ public enum Language {
|
|||
LB_FT_LIVE,
|
||||
LB_FT_SEGMENT,
|
||||
LB_FT_SPLIT,
|
||||
LB_FT_SUM_OF_BEST,
|
||||
|
||||
/*
|
||||
* Messages.
|
||||
|
@ -226,13 +241,13 @@ public enum Language {
|
|||
ACCEPT,
|
||||
APPLICATION,
|
||||
BEST,
|
||||
BOLD,
|
||||
CANCEL,
|
||||
COMPARE_METHOD,
|
||||
COMPONENTS,
|
||||
DISABLED,
|
||||
EDITING,
|
||||
ERROR,
|
||||
GOAL,
|
||||
IMAGE,
|
||||
INPUTS,
|
||||
MAX_ORDINATE,
|
||||
|
@ -241,10 +256,12 @@ public enum Language {
|
|||
RUN_FILE_FILTER,
|
||||
SEGMENT,
|
||||
SEGMENTS,
|
||||
SELECT_SPLITS_DIR,
|
||||
SPLIT,
|
||||
TIME,
|
||||
UNTITLED,
|
||||
WARNING,
|
||||
WINDOW_SIZE,
|
||||
|
||||
/*
|
||||
* 1.4
|
||||
|
|
|
@ -35,7 +35,7 @@ import java.util.logging.Logger;
|
|||
*/
|
||||
public class Llanfair extends BorderlessFrame implements TableModelListener,
|
||||
LocaleListener, MouseWheelListener, ActionListener, NativeKeyListener,
|
||||
PropertyChangeListener, WindowListener {
|
||||
PropertyChangeListener, WindowListener, ComponentListener {
|
||||
|
||||
private static Resources RESOURCES = null;
|
||||
|
||||
|
@ -46,6 +46,29 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
ToolTipManager.sharedInstance().setReshowDelay( 0 );
|
||||
}
|
||||
|
||||
// this class only exists so that the "on app quit" logic is somewhere that can be
|
||||
// added to the JVM's shutdown hook for Mac OS for when the user uses Cmd+Q to quit
|
||||
public class AppShutdown implements Runnable {
|
||||
private boolean hasRun = false;
|
||||
|
||||
public void run()
|
||||
{
|
||||
if (hasRun)
|
||||
return;
|
||||
|
||||
Settings.save();
|
||||
try {
|
||||
GlobalScreen.unregisterNativeHook();
|
||||
} catch (NativeHookException e) {
|
||||
|
||||
}
|
||||
|
||||
hasRun = true;
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable onAppShutdown = new AppShutdown();
|
||||
|
||||
private Run run;
|
||||
private RunPane runPane;
|
||||
|
||||
|
@ -53,6 +76,7 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
|
||||
private JPopupMenu popupMenu;
|
||||
|
||||
private volatile boolean lockedHotkeys;
|
||||
private volatile boolean ignoreNativeInputs;
|
||||
|
||||
private Dimension preferredSize;
|
||||
|
@ -73,16 +97,32 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
|
||||
RESOURCES = new Resources();
|
||||
registerFonts();
|
||||
setResizable(Settings.windowUserResizable.get());
|
||||
setLookAndFeel();
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
addComponentListener(this);
|
||||
|
||||
// if a mac user uses Cmd+Q to exit the application that quits the program in a way that doesn't fire
|
||||
// the window closed event for some fucked up reason. wow
|
||||
// the only "fancy" ways to deal with this problem that i could find are only relevant on Apple's Java 1.6
|
||||
// runtime which everyone should avoid nowadays. so we'll do this instead...
|
||||
if (System.getProperty("os.name").startsWith("Mac OS")) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(onAppShutdown));
|
||||
}
|
||||
|
||||
run = new Run();
|
||||
runPane = null;
|
||||
lockedHotkeys = false;
|
||||
ignoreNativeInputs = false;
|
||||
preferredSize = null;
|
||||
actions = new Actions( this );
|
||||
|
||||
setMenu();
|
||||
setBehavior();
|
||||
|
||||
boolean behaviourSucceed = setBehavior();
|
||||
if (!behaviourSucceed)
|
||||
return;
|
||||
|
||||
setRun( run );
|
||||
|
||||
setVisible( true );
|
||||
|
@ -176,6 +216,14 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
}
|
||||
}
|
||||
|
||||
public synchronized boolean areHotkeysLocked() {
|
||||
return lockedHotkeys;
|
||||
}
|
||||
|
||||
public synchronized void setLockedHotkeys(boolean lockedHotkeys) {
|
||||
this.lockedHotkeys = lockedHotkeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not Llanfair currently ignores all native inputs.
|
||||
* Since native inputs can be caught even when the application does not have
|
||||
|
@ -208,10 +256,22 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
*
|
||||
* @param message the localized error message
|
||||
*/
|
||||
void showError( String message ) {
|
||||
JOptionPane.showMessageDialog(
|
||||
this, message, Language.ERROR.get(), JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
public void showError( String message ) {
|
||||
showError(message, null);
|
||||
}
|
||||
|
||||
public void showError(String message, Throwable ex) {
|
||||
String errorMessage;
|
||||
if (ex != null)
|
||||
errorMessage = message + "\n\n" + ex.toString();
|
||||
else
|
||||
errorMessage = message;
|
||||
|
||||
JOptionPane pane = new JOptionPane(errorMessage, JOptionPane.ERROR_MESSAGE);
|
||||
JDialog dialog = pane.createDialog(Language.ERROR.get());
|
||||
dialog.setAlwaysOnTop(true);
|
||||
dialog.setVisible(true);
|
||||
dialog.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,11 +345,11 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
* main thread.
|
||||
*/
|
||||
@Override public void nativeKeyPressed( final NativeKeyEvent event ) {
|
||||
if (Settings.useGlobalHotkeys.get() || this.hasFocus()) {
|
||||
if (Settings.useGlobalHotkeys.get() || this.isFocused()) {
|
||||
int keyCode = event.getKeyCode();
|
||||
boolean hotkeysEnabler = ( keyCode == Settings.hotkeyLock.get() );
|
||||
|
||||
if ( !ignoresNativeInputs() || hotkeysEnabler ) {
|
||||
if ( (!areHotkeysLocked() && !ignoresNativeInputs()) || hotkeysEnabler ) {
|
||||
SwingUtilities.invokeLater( new Runnable() {
|
||||
@Override public void run() {
|
||||
actions.process( event );
|
||||
|
@ -313,7 +373,14 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
String property = event.getPropertyName();
|
||||
|
||||
if ( Run.STATE_PROPERTY.equals( property ) ) {
|
||||
MenuItem.setActiveState( run.getState() );
|
||||
MenuItem.setActiveState(run.getState());
|
||||
//forceInternalComponentsResize();
|
||||
} else if (Run.NAME_PROPERTY.equals(property)) {
|
||||
forceInternalComponentsResize();
|
||||
} else if (Run.SUBTITLE_PROPERTY.equals(property)) {
|
||||
forceInternalComponentsResize();
|
||||
} else if (Settings.headerShowAttempts.equals(property)) {
|
||||
forceInternalComponentsResize();
|
||||
} else if ( Settings.alwaysOnTop.equals( property ) ) {
|
||||
setAlwaysOnTop( Settings.alwaysOnTop.get() );
|
||||
} else if (Settings.historyRowCount.equals(property)
|
||||
|
@ -322,7 +389,7 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
|| Settings.footerUseSplitData.equals(property)
|
||||
|| Settings.coreIconSize.equals(property)
|
||||
|| Settings.accuracy.equals(property)
|
||||
|| Settings.headerShowGoal.equals(property)
|
||||
|| Settings.headerShowSubtitle.equals(property)
|
||||
|| Settings.headerShowTitle.equals(property)
|
||||
|| Settings.historyDeltas.equals(property)
|
||||
|| Settings.historySegmentFont.equals(property)
|
||||
|
@ -345,9 +412,13 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
|| Settings.footerShowDeltaLabels.equals(property)
|
||||
|| Settings.footerVerbose.equals(property)
|
||||
|| Settings.footerMultiline.equals(property)
|
||||
|| Settings.footerShowSumOfBest.equals(property)
|
||||
|| Settings.windowUserResizable.equals(property)
|
||||
|| Settings.windowWidth.equals(property)
|
||||
|| Run.NAME_PROPERTY.equals(property)) {
|
||||
setPreferredSize(null);
|
||||
pack();
|
||||
setResizable(Settings.windowUserResizable.get());
|
||||
MenuItem.enableResizeOptions(Settings.windowUserResizable.get());
|
||||
forceResize();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -401,12 +472,7 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
* unregister the native hook of {@code JNativeHook}.
|
||||
*/
|
||||
@Override public void windowClosed( WindowEvent event ) {
|
||||
Settings.save();
|
||||
try {
|
||||
GlobalScreen.unregisterNativeHook();
|
||||
} catch (NativeHookException e) {
|
||||
|
||||
}
|
||||
onAppShutdown.run();
|
||||
}
|
||||
|
||||
@Override public void windowClosing(WindowEvent event) {}
|
||||
|
@ -421,6 +487,20 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
|
||||
@Override public void windowDeiconified(WindowEvent event) {}
|
||||
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
// we may have to override the width, but allow the height to grow as needed
|
||||
// don't have to change anything if autosizing
|
||||
if (!Settings.windowUserResizable.get())
|
||||
setSize(new Dimension(Settings.windowWidth.get(), getHeight()));
|
||||
}
|
||||
|
||||
@Override public void componentMoved(ComponentEvent e) {}
|
||||
|
||||
@Override public void componentShown(ComponentEvent e) {}
|
||||
|
||||
@Override public void componentHidden(ComponentEvent e) {}
|
||||
|
||||
/**
|
||||
* Action events are fired by clicking on the entries of the context menu.
|
||||
*/
|
||||
|
@ -434,7 +514,7 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
} );
|
||||
|
||||
if (source.equals(MenuItem.EDIT)) {
|
||||
|
||||
/*
|
||||
} else if (source.equals(MenuItem.RESIZE_DEFAULT)) {
|
||||
setPreferredSize(null);
|
||||
pack();
|
||||
|
@ -442,6 +522,7 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
} else if (source.equals(MenuItem.RESIZE_PREFERRED)) {
|
||||
setPreferredSize(preferredSize);
|
||||
pack();
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -450,13 +531,27 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
*
|
||||
* @throws IllegalStateException if JNativeHook cannot be registered.
|
||||
*/
|
||||
private void setBehavior() {
|
||||
registerNativeKeyHook();
|
||||
private boolean setBehavior() {
|
||||
boolean registered = registerNativeKeyHook();
|
||||
|
||||
if (!registered) {
|
||||
// NOTE: in the event of a failure, JNativeHook now has some ability (on some OS's at least)
|
||||
// to pop up an OS-specific dialog or other action that allows the user to rectify the
|
||||
// problem. e.g. on OS X, if an exception is thrown a dialog telling the user that the
|
||||
// application has requested some accessibility-related access shows up.
|
||||
|
||||
showError(Language.GLOBAL_HOTKEYS_STARTUP_ERROR.get());
|
||||
this.dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
setAlwaysOnTop(Settings.alwaysOnTop.get());
|
||||
addWindowListener(this);
|
||||
addMouseWheelListener(this);
|
||||
Settings.addPropertyChangeListener(this);
|
||||
GlobalScreen.addNativeKeyListener(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -477,14 +572,24 @@ public class Llanfair extends BorderlessFrame implements TableModelListener,
|
|||
GlobalScreen.registerNativeHook();
|
||||
return true;
|
||||
} catch (NativeHookException e) {
|
||||
// NOTE: in the event of a failure, JNativeHook now has some ability (on some OS's at least)
|
||||
// to pop up an OS-specific dialog or other action that allows the user to rectify the
|
||||
// problem. e.g. on OS X, if an exception is thrown a dialog telling the user that the
|
||||
// application has requested some accessibility-related access shows up.
|
||||
|
||||
JOptionPane.showMessageDialog(this, Language.GLOBAL_HOTKEYS_WARNING, Language.ERROR.get(), JOptionPane.ERROR_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void forceResize() {
|
||||
Dimension newSize = new Dimension();
|
||||
newSize.height = getHeight();
|
||||
if (Settings.windowUserResizable.get())
|
||||
newSize.width = getWidth();
|
||||
else
|
||||
newSize.width = Settings.windowWidth.get();
|
||||
setSize(newSize);
|
||||
pack();
|
||||
}
|
||||
|
||||
private void forceInternalComponentsResize()
|
||||
{
|
||||
setPreferredSize( preferredSize );
|
||||
pack();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ enum MenuItem implements ActionListener {
|
|||
*/
|
||||
private static EventListenerList listeners = new EventListenerList();
|
||||
|
||||
private static final int MAX_FILES = 5;
|
||||
private static final int TRUNCATE = 30;
|
||||
|
||||
private boolean isEndOfGroup;
|
||||
|
@ -121,6 +120,14 @@ enum MenuItem implements ActionListener {
|
|||
listeners.add( ActionListener.class, listener );
|
||||
}
|
||||
|
||||
static void trimRecentFilesList(List<String> recentFiles) {
|
||||
int numToTrim = recentFiles.size() - Settings.maxRecentFiles.get();
|
||||
if (numToTrim > 0) {
|
||||
for (int i = 0; i < numToTrim; ++i)
|
||||
recentFiles.remove(recentFiles.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to invoke whenever a file is opened. This method will sort the
|
||||
* recent files menu to put the recently opened file at the top.
|
||||
|
@ -136,23 +143,21 @@ enum MenuItem implements ActionListener {
|
|||
}
|
||||
recentFiles.add( 0, path );
|
||||
|
||||
if ( recentFiles.size() > MAX_FILES ) {
|
||||
recentFiles.remove( MAX_FILES );
|
||||
}
|
||||
trimRecentFilesList(recentFiles);
|
||||
|
||||
Settings.recentFiles.set( recentFiles );
|
||||
populateRecentlyOpened();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the {@code OPEN_RECENT} item with the list of recently opened
|
||||
* files. If {@code MAX_FILES} is somehow lower than the recent files list
|
||||
* length, the overflowing files are removed.
|
||||
* files.
|
||||
*/
|
||||
static void populateRecentlyOpened() {
|
||||
List<String> recentFiles = Settings.recentFiles.get();
|
||||
for ( int i = MAX_FILES; i < recentFiles.size(); i++ ) {
|
||||
recentFiles.remove( i - 1 );
|
||||
}
|
||||
trimRecentFilesList(recentFiles);
|
||||
Settings.recentFiles.set(recentFiles);
|
||||
|
||||
OPEN_RECENT.menuItem.removeAll();
|
||||
for ( String fileName : Settings.recentFiles.get() ) {
|
||||
String text = fileName;
|
||||
|
@ -225,4 +230,11 @@ enum MenuItem implements ActionListener {
|
|||
}
|
||||
}
|
||||
|
||||
public static void enableResizeOptions(boolean enable) {
|
||||
for (MenuItem item : values()) {
|
||||
if (item == RESIZE_DEFAULT || item == RESIZE_PREFERRED)
|
||||
item.menuItem.setEnabled(enable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package org.fenix.llanfair;
|
||||
|
||||
import com.thoughtworks.xstream.annotations.XStreamOmitField;
|
||||
import org.fenix.WorldRecord.Category;
|
||||
import org.fenix.WorldRecord.WorldRecordParser;
|
||||
import org.fenix.llanfair.config.Settings;
|
||||
import org.fenix.utils.TableModelSupport;
|
||||
import org.fenix.utils.config.Configuration;
|
||||
|
@ -79,6 +82,8 @@ public class Run implements TableModel, Serializable {
|
|||
*/
|
||||
public static final String NAME_PROPERTY = "run.name";
|
||||
|
||||
public static final String SUBTITLE_PROPERTY = "run.subTitle";
|
||||
|
||||
/**
|
||||
* Identifier for the bean property state of the run.
|
||||
*/
|
||||
|
@ -89,8 +94,6 @@ public class Run implements TableModel, Serializable {
|
|||
*/
|
||||
public static final String CURRENT_SEGMENT_PROPERTY = "run.currentSegment";
|
||||
|
||||
public static final String GOAL_PROPERTY = "run.goal";
|
||||
|
||||
public static final String COUNTER_VALUE_PROPERTY = "run.counters.value";
|
||||
|
||||
public static final String COUNTER_ADD_PROPERTY = "run.counters.add";
|
||||
|
@ -103,6 +106,10 @@ public class Run implements TableModel, Serializable {
|
|||
|
||||
public static final String COMPLETED_ATTEMPT_COUNTER_PROPERTY = "run.completedAttemptCounter";
|
||||
|
||||
public static final String RECORD_CATEGORY_PROPERTY = "run.record.category";
|
||||
|
||||
public static final String DELAYED_START_PROPERTY = "run.delayedStart";
|
||||
|
||||
// ------------------------------------------------------------- ATTRIBUTES
|
||||
|
||||
/**
|
||||
|
@ -110,6 +117,11 @@ public class Run implements TableModel, Serializable {
|
|||
*/
|
||||
private String name;
|
||||
|
||||
/***
|
||||
* The subtitle of the run.
|
||||
*/
|
||||
private String subTitle;
|
||||
|
||||
/**
|
||||
* Current state of the run. Transient as a deserialized run will always
|
||||
* start in {@link State#READY}.
|
||||
|
@ -162,8 +174,6 @@ public class Run implements TableModel, Serializable {
|
|||
*/
|
||||
private long delayedStart;
|
||||
|
||||
private String goal;
|
||||
|
||||
private boolean segmented;
|
||||
|
||||
private List<Counters> counters;
|
||||
|
@ -174,6 +184,11 @@ public class Run implements TableModel, Serializable {
|
|||
|
||||
private int numberOfCompletedAttempts;
|
||||
|
||||
@XStreamOmitField
|
||||
private int sessionAttempts;
|
||||
|
||||
private Category recordCategory;
|
||||
|
||||
// ----------------------------------------------------------- CONSTRUCTORS
|
||||
|
||||
/**
|
||||
|
@ -187,8 +202,8 @@ public class Run implements TableModel, Serializable {
|
|||
throw new NullPointerException("null run name");
|
||||
}
|
||||
this.name = name;
|
||||
this.subTitle = "";
|
||||
segments = new ArrayList<Segment>();
|
||||
goal = "";
|
||||
segmented = false;
|
||||
counters = new ArrayList<Counters>();
|
||||
initializeTransients();
|
||||
|
@ -213,8 +228,8 @@ public class Run implements TableModel, Serializable {
|
|||
return name;
|
||||
}
|
||||
|
||||
public String getGoal() {
|
||||
return goal;
|
||||
public String getSubTitle() {
|
||||
return subTitle;
|
||||
}
|
||||
|
||||
public long getDelayedStart() {
|
||||
|
@ -492,6 +507,23 @@ public class Run implements TableModel, Serializable {
|
|||
|
||||
// ------------------------------------------------------ INHERITED GETTERS
|
||||
|
||||
public Time getSumOfBest() {
|
||||
long sum = 0;
|
||||
for (int i = 0; i < segments.size(); ++i) {
|
||||
Segment segment = segments.get(i);
|
||||
Time best = segment.getTime(Segment.BEST);
|
||||
Time live = segment.getTime(Segment.LIVE);
|
||||
|
||||
if (best != null || live != null) {
|
||||
long bestMs = (best == null ? Long.MAX_VALUE : best.getMilliseconds());
|
||||
long liveMs = (live == null ? Long.MAX_VALUE : live.getMilliseconds());
|
||||
|
||||
sum += Math.min(bestMs, liveMs);
|
||||
}
|
||||
}
|
||||
return new Time(sum);
|
||||
}
|
||||
|
||||
/**
|
||||
* As specified by {@code TableModel}.
|
||||
*/
|
||||
|
@ -568,6 +600,25 @@ public class Run implements TableModel, Serializable {
|
|||
return numberOfCompletedAttempts;
|
||||
}
|
||||
|
||||
public int getSessionAttempts() { return sessionAttempts; }
|
||||
|
||||
public String getRecordString() {
|
||||
String recordString;
|
||||
|
||||
try {
|
||||
recordString = WorldRecordParser.getRecord(this.recordCategory);
|
||||
} catch (Exception e) {
|
||||
recordString = "Unknown World Record";
|
||||
}
|
||||
|
||||
return recordString;
|
||||
}
|
||||
|
||||
public Category getRecordCategory()
|
||||
{
|
||||
return recordCategory;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- SETTERS
|
||||
|
||||
public<T> T getSetting( String key ) {
|
||||
|
@ -600,23 +651,31 @@ public class Run implements TableModel, Serializable {
|
|||
pcSupport.firePropertyChange(NAME_PROPERTY, old, name);
|
||||
}
|
||||
|
||||
public void setSegmented(boolean segmented) {
|
||||
this.segmented = segmented;
|
||||
public void setSubTitle(String subTitle) {
|
||||
String old = this.subTitle;
|
||||
this.subTitle = subTitle;
|
||||
pcSupport.firePropertyChange(SUBTITLE_PROPERTY, old, subTitle);
|
||||
}
|
||||
|
||||
public void setGoal(String goal) {
|
||||
if (goal == null) {
|
||||
throw new NullPointerException("null goal string");
|
||||
}
|
||||
String old = this.goal;
|
||||
this.goal = goal;
|
||||
pcSupport.firePropertyChange(GOAL_PROPERTY, old, goal);
|
||||
public void setSegmented(boolean segmented) {
|
||||
this.segmented = segmented;
|
||||
}
|
||||
|
||||
public void setDelayedStart(long delayedStart) {
|
||||
if (delayedStart < 0)
|
||||
throw new InvalidParameterException("negative delayed start");
|
||||
long old = this.delayedStart;
|
||||
this.delayedStart = delayedStart;
|
||||
pcSupport.firePropertyChange(DELAYED_START_PROPERTY, old, delayedStart);
|
||||
}
|
||||
|
||||
public void setRecordCategory(Category category)
|
||||
{
|
||||
Category old = this.recordCategory;
|
||||
|
||||
this.recordCategory = category;
|
||||
|
||||
pcSupport.firePropertyChange(RECORD_CATEGORY_PROPERTY, old, name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -703,6 +762,7 @@ public class Run implements TableModel, Serializable {
|
|||
segments.get(current).setStartTime(startTime);
|
||||
|
||||
numberOfAttempts += 1;
|
||||
sessionAttempts += 1;
|
||||
|
||||
pcSupport.firePropertyChange(ATTEMPT_COUNTER_PROPERTY, numberOfAttempts - 1, numberOfAttempts);
|
||||
pcSupport.firePropertyChange(STATE_PROPERTY, State.READY, state);
|
||||
|
@ -1107,16 +1167,17 @@ public class Run implements TableModel, Serializable {
|
|||
* Initialize all transient fields.
|
||||
*/
|
||||
private void initializeTransients() {
|
||||
pcSupport = new PropertyChangeSupport(this);
|
||||
tmSupport = new TableModelSupport(this);
|
||||
segmentsBackup = null;
|
||||
stateBackup = null;
|
||||
state = getRowCount() > 0 ? State.READY : State.NULL;
|
||||
current = -1;
|
||||
startTime = 0L;
|
||||
pcSupport = new PropertyChangeSupport(this);
|
||||
tmSupport = new TableModelSupport(this);
|
||||
segmentsBackup = null;
|
||||
stateBackup = null;
|
||||
state = getRowCount() > 0 ? State.READY : State.NULL;
|
||||
current = -1;
|
||||
startTime = 0L;
|
||||
sessionAttempts = 0;
|
||||
|
||||
if (goal == null) {
|
||||
goal = "";
|
||||
if (subTitle == null) {
|
||||
subTitle = "";
|
||||
}
|
||||
if (counters == null) {
|
||||
counters = new ArrayList<Counters>();
|
||||
|
@ -1124,6 +1185,9 @@ public class Run implements TableModel, Serializable {
|
|||
if ( configuration == null ) {
|
||||
configuration = new Configuration();
|
||||
}
|
||||
if(recordCategory == null) {
|
||||
recordCategory = new Category("", "");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -95,6 +95,9 @@ public class SerializationUtils {
|
|||
writer.startNode("size");
|
||||
writer.setValue("" + font.getSize());
|
||||
writer.endNode();
|
||||
writer.startNode("style");
|
||||
writer.setValue("" + font.getStyle());
|
||||
writer.endNode();
|
||||
writer.endNode();
|
||||
}
|
||||
|
||||
|
@ -102,6 +105,7 @@ public class SerializationUtils {
|
|||
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
|
||||
String family = null;
|
||||
int size = 0;
|
||||
int style = 0;
|
||||
reader.moveDown();
|
||||
while (reader.hasMoreChildren()) {
|
||||
reader.moveDown();
|
||||
|
@ -109,11 +113,13 @@ public class SerializationUtils {
|
|||
family = reader.getValue();
|
||||
else if (reader.getNodeName().equals("size"))
|
||||
size = Integer.parseInt(reader.getValue());
|
||||
else if (reader.getNodeName().equals("style"))
|
||||
style = Integer.parseInt(reader.getValue());
|
||||
reader.moveUp();
|
||||
}
|
||||
reader.moveUp();
|
||||
|
||||
return new Font(family, Font.PLAIN, size);
|
||||
return new Font(family, style, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -32,11 +32,16 @@ public class Settings {
|
|||
public static final Property<Locale> language = new Property<>( "language" );
|
||||
public static final Property<Locale> viewerLanguage = new Property<>( "viewerLanguage" );
|
||||
public static final Property<List<String>> recentFiles = new Property<>( "recentFiles" );
|
||||
public static final Property<Integer> maxRecentFiles = new Property<>( "maxRecentFiles" );
|
||||
public static final Property<Point> coordinates = new Property<>( "coordinates" );
|
||||
public static final Property<Dimension> dimension = new Property<>( "dimension" );
|
||||
public static final Property<Compare> compareMethod = new Property<>( "compareMethod" );
|
||||
public static final Property<Accuracy> accuracy = new Property<>( "accuracy" );
|
||||
public static final Property<Boolean> warnOnReset = new Property<>( "warnOnReset" );
|
||||
public static final Property<Boolean> windowUserResizable = new Property<>( "windowUserResizable" );
|
||||
public static final Property<Integer> windowWidth = new Property<>( "windowWidth" );
|
||||
public static final Property<Boolean> useDefaultSplitsPath = new Property<>( "useDefaultSplitsPath" );
|
||||
public static final Property<String> customSplitsPath = new Property<>( "customSplitsPath" );
|
||||
|
||||
/* COLOR properties */
|
||||
|
||||
|
@ -44,10 +49,14 @@ public class Settings {
|
|||
public static final Property<Color> colorForeground = new Property<>( "color.foreground" );
|
||||
public static final Property<Color> colorTime = new Property<>( "color.time" );
|
||||
public static final Property<Color> colorTimer = new Property<>( "color.timer" );
|
||||
public static final Property<Color> colorTimeGained = new Property<>( "color.timeGained" );
|
||||
public static final Property<Color> colorTimeLost = new Property<>( "color.timeLost" );
|
||||
public static final Property<Color> colorNegativeTime = new Property<>( "color.negativeTime" );
|
||||
public static final Property<Color> colorTimeGainedWhileAhead = new Property<>( "color.timeGainedWhileAhead" );
|
||||
public static final Property<Color> colorTimeLostWhileAhead = new Property<>( "color.timeLostWhileAhead" );
|
||||
public static final Property<Color> colorTimeGainedWhileBehind = new Property<>( "color.timeGainedWhileBehind" );
|
||||
public static final Property<Color> colorTimeLostWhileBehind = new Property<>( "color.timeLostWhileBehind" );
|
||||
public static final Property<Color> colorNewRecord = new Property<>( "color.newRecord" );
|
||||
public static final Property<Color> colorTitle = new Property<>( "color.title" );
|
||||
public static final Property<Color> colorSubTitle = new Property<>( "color.subTitle" );
|
||||
public static final Property<Color> colorHighlight = new Property<>( "color.highlight" );
|
||||
public static final Property<Color> colorSeparators = new Property<>( "color.separators" );
|
||||
|
||||
|
@ -64,10 +73,11 @@ public class Settings {
|
|||
|
||||
/* HEADER properties */
|
||||
|
||||
public static final Property<Boolean> headerShowGoal = new Property<>( "header.goal" );
|
||||
public static final Property<Boolean> headerShowSubtitle = new Property<>( "header.subTitle" );
|
||||
public static final Property<Boolean> headerShowTitle = new Property<>( "header.title" );
|
||||
public static final Property<Boolean> headerShowAttempts = new Property<>( "header.showAttempts" );
|
||||
public static final Property<Font> headerTitleFont = new Property<>(" header.titleFont" );
|
||||
public static final Property<Font> headerTitleFont = new Property<>( "header.titleFont" );
|
||||
public static final Property<Font> headerSubTitleFont = new Property<>( "header.subTitleFont" );
|
||||
|
||||
/* HISTORY properties */
|
||||
|
||||
|
@ -113,6 +123,8 @@ public class Settings {
|
|||
public static final Property<Boolean> footerShowBestTime = new Property<>( "footer.bestTime" );
|
||||
public static final Property<Boolean> footerMultiline = new Property<>( "footer.multiline" );
|
||||
public static final Property<Boolean> footerShowDeltaLabels = new Property<>( "footer.deltaLabels" );
|
||||
public static final Property<Boolean> footerShowSumOfBest = new Property<>( "footer.sumOfBest" );
|
||||
public static final Property<Boolean> footerShowWorldRecord = new Property<>("footer.worldRecord");
|
||||
|
||||
private static Configuration global = null;
|
||||
private static Run run = null;
|
||||
|
@ -178,96 +190,112 @@ public class Settings {
|
|||
|
||||
/**
|
||||
* Retrieves the configuration of Llanfair. The configuration is read from
|
||||
* {@code llanfair.xml} placed in the working directory. If such a file
|
||||
* cannot be found, a default configuration is loaded. No local
|
||||
* configuration is loaded here, a call to {@code setRun} is required to
|
||||
* {@code llanfair.xml} placed in the working directory. Default configuration
|
||||
* values are applied for any values not present in the loaded configuration file.
|
||||
* No local configuration is loaded here, a call to {@code setRun} is required to
|
||||
* do just that. This method is lenient and called by the first property
|
||||
* whose value is requested.
|
||||
*/
|
||||
private static void retrieve() {
|
||||
global = Configuration.newInstance( new File(UserSettings.getSettingsPath() + File.separator + "llanfair.xml" ) );
|
||||
if ( global.isEmpty() ) {
|
||||
setDefaultValues();
|
||||
}
|
||||
setDefaultValues();
|
||||
}
|
||||
|
||||
private static void setDefault(String key, Object value, boolean force) {
|
||||
if (force || !global.contains(key))
|
||||
global.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the global configuration with every property, assigning them their
|
||||
* default value. This method can be called even when the global
|
||||
* configuration is not empty, and will thus function as a reset.
|
||||
* Sets default values in the global configuration for each property which is
|
||||
* missing (key is not present). Existing values are preserved (even nulls).
|
||||
*/
|
||||
private static void setDefaultValues() {
|
||||
global.put( alwaysOnTop.key, true );
|
||||
global.put( language.key, Locale.ENGLISH );
|
||||
global.put( viewerLanguage.key, Locale.ENGLISH );
|
||||
global.put( recentFiles.key, new ArrayList<String>() );
|
||||
global.put( coordinates.key, null );
|
||||
global.put( dimension.key, null );
|
||||
global.put( compareMethod.key, Compare.BEST_OVERALL_RUN );
|
||||
global.put( accuracy.key, Accuracy.TENTH );
|
||||
global.put( warnOnReset.key, true );
|
||||
boolean force = false;
|
||||
|
||||
global.put( colorBackground.key, Color.decode( "0x000000" ) );
|
||||
global.put( colorForeground.key, Color.decode( "0xc0c0c0" ) );
|
||||
global.put( colorTime.key, Color.decode( "0xffffff" ) );
|
||||
global.put( colorTimer.key, Color.decode( "0x22cc22" ) );
|
||||
global.put( colorTimeGained.key, Color.decode( "0x6295fc" ) );
|
||||
global.put( colorTimeLost.key, Color.decode( "0xe82323" ) );
|
||||
global.put( colorNewRecord.key, Color.decode( "0xf0b012" ) );
|
||||
global.put( colorTitle.key, Color.decode( "0xf0b012" ) );
|
||||
global.put( colorHighlight.key, Color.decode( "0xffffff" ) );
|
||||
global.put( colorSeparators.key, Color.decode( "0x666666" ) );
|
||||
setDefault( alwaysOnTop.key, true, force );
|
||||
setDefault( language.key, Locale.ENGLISH, force );
|
||||
setDefault( viewerLanguage.key, Locale.ENGLISH, force );
|
||||
setDefault( recentFiles.key, new ArrayList<String>(), force );
|
||||
setDefault( maxRecentFiles.key, 10, force );
|
||||
setDefault( coordinates.key, null, force );
|
||||
setDefault( dimension.key, null, force );
|
||||
setDefault( compareMethod.key, Compare.BEST_OVERALL_RUN, force );
|
||||
setDefault( accuracy.key, Accuracy.TENTH, force );
|
||||
setDefault( warnOnReset.key, true, force );
|
||||
setDefault( windowUserResizable.key, true, force );
|
||||
setDefault( windowWidth.key, null, force );
|
||||
setDefault( useDefaultSplitsPath.key, true, force );
|
||||
setDefault( customSplitsPath.key, null, force );
|
||||
|
||||
global.put( useGlobalHotkeys.key, false );
|
||||
global.put( hotkeySplit.key, -1 );
|
||||
global.put( hotkeyUnsplit.key, -1 );
|
||||
global.put( hotkeySkip.key, -1 );
|
||||
global.put( hotkeyReset.key, -1 );
|
||||
global.put( hotkeyStop.key, -1 );
|
||||
global.put( hotkeyPause.key, -1 );
|
||||
global.put( hotkeyLock.key, -1 );
|
||||
setDefault( colorBackground.key, Color.decode("0x000000"), force );
|
||||
setDefault( colorForeground.key, Color.decode( "0xc0c0c0" ), force );
|
||||
setDefault( colorTime.key, Color.decode( "0xffffff" ), force );
|
||||
setDefault( colorTimer.key, Color.decode( "0x22cc22" ), force );
|
||||
setDefault( colorNegativeTime.key, Color.decode ("0x808080" ), force );
|
||||
setDefault( colorTimeGainedWhileAhead.key, Color.decode( "0x6295fc" ), force );
|
||||
setDefault( colorTimeLostWhileAhead.key, Color.decode( "0x99ccff" ), force );
|
||||
setDefault( colorTimeGainedWhileBehind.key, Color.decode( "0xff8e8e" ), force );
|
||||
setDefault( colorTimeLostWhileBehind.key, Color.decode( "0xe82323" ), force );
|
||||
setDefault( colorNewRecord.key, Color.decode( "0xf0b012" ), force );
|
||||
setDefault( colorTitle.key, Color.decode( "0xf0b012" ), force );
|
||||
setDefault( colorSubTitle.key, Color.decode( "0xffffff" ), force );
|
||||
setDefault( colorHighlight.key, Color.decode( "0xffffff" ), force );
|
||||
setDefault( colorSeparators.key, Color.decode( "0x666666" ), force );
|
||||
|
||||
global.put( headerShowGoal.key, true );
|
||||
global.put( headerShowTitle.key, true );
|
||||
global.put( headerShowAttempts.key, true );
|
||||
global.put( headerTitleFont.key, Font.decode( "Arial-14" ) );
|
||||
setDefault( useGlobalHotkeys.key, false, force );
|
||||
setDefault( hotkeySplit.key, -1, force );
|
||||
setDefault( hotkeyUnsplit.key, -1, force );
|
||||
setDefault( hotkeySkip.key, -1, force );
|
||||
setDefault( hotkeyReset.key, -1, force );
|
||||
setDefault( hotkeyStop.key, -1, force );
|
||||
setDefault( hotkeyPause.key, -1, force );
|
||||
setDefault( hotkeyLock.key, -1, force );
|
||||
|
||||
global.put( historyRowCount.key, 8 );
|
||||
global.put( historyTabular.key, true );
|
||||
global.put( historyBlankRows.key, false );
|
||||
global.put( historyMultiline.key, false );
|
||||
global.put( historyMerge.key, Merge.LIVE );
|
||||
global.put( historyLiveTimes.key, true );
|
||||
global.put( historyDeltas.key, true );
|
||||
global.put( historyIcons.key, true );
|
||||
global.put( historyIconSize.key, 16 );
|
||||
global.put( historyOffset.key, 0 );
|
||||
global.put( historyAlwaysShowLast.key, true );
|
||||
global.put( historySegmentFont.key, Font.decode( "Arial-12" ) );
|
||||
global.put( historyTimeFont.key, Font.decode( "Arial-11" ) );
|
||||
setDefault( headerShowSubtitle.key, true, force );
|
||||
setDefault( headerShowTitle.key, true, force );
|
||||
setDefault( headerShowAttempts.key, true, force );
|
||||
setDefault( headerTitleFont.key, Font.decode( "Arial-14" ), force );
|
||||
setDefault( headerSubTitleFont.key, Font.decode( "Arial-12" ), force );
|
||||
|
||||
global.put( coreAccuracy.key, Accuracy.HUNDREDTH );
|
||||
global.put( coreShowIcons.key, true );
|
||||
global.put( coreIconSize.key, 40 );
|
||||
global.put( coreShowSegmentName.key, true );
|
||||
global.put( coreShowSplitTime.key, false );
|
||||
global.put( coreShowSegmentTime.key, true );
|
||||
global.put( coreShowBestTime.key, true );
|
||||
global.put( coreShowSegmentTimer.key, true );
|
||||
global.put( coreTimerFont.key, Font.decode( "Digitalism-32" ) );
|
||||
global.put( coreSegmentTimerFont.key, Font.decode( "Digitalism-18" ) );
|
||||
global.put( coreFont.key, Font.decode( "Arial-12" ) );
|
||||
global.put( coreOtherTimeFont.key, Font.decode( "Arial-11" ) );
|
||||
setDefault( historyRowCount.key, 8, force );
|
||||
setDefault( historyTabular.key, true, force );
|
||||
setDefault( historyBlankRows.key, false, force );
|
||||
setDefault( historyMultiline.key, false, force );
|
||||
setDefault( historyMerge.key, Merge.LIVE, force );
|
||||
setDefault( historyLiveTimes.key, true, force );
|
||||
setDefault( historyDeltas.key, true, force );
|
||||
setDefault( historyIcons.key, true, force );
|
||||
setDefault( historyIconSize.key, 16, force );
|
||||
setDefault( historyOffset.key, 0, force );
|
||||
setDefault( historyAlwaysShowLast.key, true, force );
|
||||
setDefault( historySegmentFont.key, Font.decode( "Arial-12" ), force );
|
||||
setDefault( historyTimeFont.key, Font.decode( "Arial-11" ), force );
|
||||
|
||||
global.put( graphDisplay.key, true );
|
||||
global.put( graphScale.key, 3.0F );
|
||||
setDefault( coreAccuracy.key, Accuracy.HUNDREDTH, force );
|
||||
setDefault( coreShowIcons.key, true, force );
|
||||
setDefault( coreIconSize.key, 40, force );
|
||||
setDefault( coreShowSegmentName.key, true, force );
|
||||
setDefault( coreShowSplitTime.key, false, force );
|
||||
setDefault( coreShowSegmentTime.key, true, force );
|
||||
setDefault( coreShowBestTime.key, true, force );
|
||||
setDefault( coreShowSegmentTimer.key, true, force );
|
||||
setDefault( coreTimerFont.key, Font.decode( "Digitalism-32" ), force );
|
||||
setDefault( coreSegmentTimerFont.key, Font.decode( "Digitalism-18" ), force );
|
||||
setDefault( coreFont.key, Font.decode( "Arial-12" ), force );
|
||||
setDefault( coreOtherTimeFont.key, Font.decode( "Arial-11" ), force );
|
||||
|
||||
global.put( footerDisplay.key, true );
|
||||
global.put( footerVerbose.key, true );
|
||||
global.put( footerUseSplitData.key, false );
|
||||
global.put( footerShowBestTime.key, true );
|
||||
global.put( footerMultiline.key, true );
|
||||
global.put( footerShowDeltaLabels.key, true );
|
||||
setDefault( graphDisplay.key, true, force );
|
||||
setDefault( graphScale.key, 3.0F, force );
|
||||
|
||||
setDefault( footerDisplay.key, true, force );
|
||||
setDefault( footerVerbose.key, true, force );
|
||||
setDefault( footerUseSplitData.key, false, force );
|
||||
setDefault( footerShowBestTime.key, true, force );
|
||||
setDefault( footerMultiline.key, true, force );
|
||||
setDefault( footerShowDeltaLabels.key, true, force );
|
||||
setDefault( footerShowSumOfBest.key, false, force );
|
||||
setDefault( footerShowWorldRecord.key, true, force);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,8 @@ import org.fenix.llanfair.*;
|
|||
import org.fenix.llanfair.config.Accuracy;
|
||||
import org.fenix.utils.gui.GBC;
|
||||
|
||||
import org.fenix.WorldRecord.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
|
@ -41,6 +43,8 @@ implements ActionListener, ListSelectionListener {
|
|||
|
||||
// -------------------------------------------------------------- ATTRIBUTS
|
||||
|
||||
final private Llanfair master;
|
||||
|
||||
/**
|
||||
* Course éditée par cette boîte de dialogue.
|
||||
*/
|
||||
|
@ -49,13 +53,15 @@ implements ActionListener, ListSelectionListener {
|
|||
/**
|
||||
* Zone d’édition du titre de la course.
|
||||
*/
|
||||
private JTextField runTitle;
|
||||
private JTextArea runTitle;
|
||||
|
||||
/**
|
||||
* Étiquette du {@link runTitle}.
|
||||
*/
|
||||
private JLabel runTitleLabel;
|
||||
|
||||
private JTextField runSubTitle;
|
||||
|
||||
/**
|
||||
* Table d’édition des segments de la course.
|
||||
*/
|
||||
|
@ -103,10 +109,18 @@ implements ActionListener, ListSelectionListener {
|
|||
*/
|
||||
private JButton moveDown;
|
||||
|
||||
private JTextField runGoal;
|
||||
|
||||
private JTextField runDelayedStart;
|
||||
|
||||
/**
|
||||
* World record data from spedrun.com
|
||||
*/
|
||||
private JLabel recordLabel;
|
||||
private JButton selectRecord;
|
||||
private RecordDialog recordSelector;
|
||||
private Category recordCategory;
|
||||
private JLabel recordString;
|
||||
|
||||
|
||||
// ----------------------------------------------------------- CONSTRUCTEURS
|
||||
|
||||
/**
|
||||
|
@ -114,11 +128,12 @@ implements ActionListener, ListSelectionListener {
|
|||
*
|
||||
* @param run - la course a éditer.
|
||||
*/
|
||||
public EditRun(Run run) {
|
||||
super();
|
||||
public EditRun(Run run, Llanfair master) {
|
||||
super(master);
|
||||
if (run == null) {
|
||||
throw new NullPointerException("EditDialog.EditDialog(): null run");
|
||||
}
|
||||
this.master = master;
|
||||
this.run = run;
|
||||
run.saveBackup();
|
||||
|
||||
|
@ -126,9 +141,9 @@ implements ActionListener, ListSelectionListener {
|
|||
|
||||
String delayedStartString = new Time(run.getDelayedStart()).toString(Accuracy.HUNDREDTH);
|
||||
|
||||
runTitle = new JTextField(run.getName(), 61);
|
||||
runTitle = new JTextArea(run.getName(), 2, 61);
|
||||
runTitleLabel = new JLabel(Language.RUN_TITLE.get());
|
||||
runGoal = new JTextField(run.getGoal(), 48);
|
||||
runSubTitle = new JTextField(run.getSubTitle(), 61);
|
||||
runDelayedStart = new JTextField(delayedStartString, 5);
|
||||
segments = new JTable(run) {
|
||||
@Override protected JTableHeader createDefaultTableHeader() {
|
||||
|
@ -159,6 +174,20 @@ implements ActionListener, ListSelectionListener {
|
|||
moveUp = new JButton(Llanfair.getResources().getIcon("ARROW_UP.png"));
|
||||
moveDown = new JButton(Llanfair.getResources().getIcon("ARROW_DOWN.png"));
|
||||
segmented = new JCheckBox("" + Language.ED_SEGMENTED, run.isSegmented());
|
||||
recordLabel = new JLabel("World record");
|
||||
selectRecord = new JButton("Select record");
|
||||
recordCategory = run.getRecordCategory();
|
||||
|
||||
if(!recordCategory.getId().equals("")) {
|
||||
try {
|
||||
recordString = new JLabel(WorldRecordParser.getRecord(recordCategory));
|
||||
} catch(Exception e) {
|
||||
this.master.showError("Error displaying selected world record information.", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
recordString = new JLabel();
|
||||
}
|
||||
|
||||
placeComponents();
|
||||
setBehavior();
|
||||
|
@ -171,25 +200,29 @@ implements ActionListener, ListSelectionListener {
|
|||
*/
|
||||
private void placeComponents() {
|
||||
setLayout(new GridBagLayout());
|
||||
add(runTitleLabel, GBC.grid(0, 0).insets(4, 4, 0, 4).anchor(GBC.LE));
|
||||
add(runTitle, GBC.grid(1, 0, 3, 1).insets(4, 0, 0, 4).anchor(GBC.LS));
|
||||
add(new JLabel("" + Language.LB_GOAL), GBC.grid(0, 1).insets(4, 4, 0, 4).anchor(GBC.LE));
|
||||
add(runGoal, GBC.grid(1, 1).insets(4, 0, 0, 4).anchor(GBC.LS));
|
||||
add(new JLabel("" + Language.ED_DELAYED_START), GBC.grid(0, 2).insets(4, 4, 0, 4).anchor(GBC.LE));
|
||||
add(runDelayedStart, GBC.grid(1, 2).insets(4, 0, 0, 4).anchor(GBC.LS));
|
||||
add(segmented, GBC.grid(2, 2, 2, 1).insets(4, 0, 0, 4).anchor(GBC.LS));
|
||||
add(runTitleLabel, GBC.grid(0, 0).insets(4, 4, 0, 4).anchor(GBC.LINE_END));
|
||||
add(runTitle, GBC.grid(1, 0, 3, 1).insets(4, 0, 0, 4).anchor(GBC.LINE_START));
|
||||
add(new JLabel("" + Language.LB_SUBTITLE), GBC.grid(0, 1).insets(4, 4, 0, 4).anchor(GBC.LINE_END));
|
||||
add(runSubTitle, GBC.grid(1, 1).insets(4, 0, 0, 4).anchor(GBC.LINE_START));
|
||||
add(new JLabel("" + Language.ED_DELAYED_START), GBC.grid(0, 2).insets(4, 4, 0, 4).anchor(GBC.LINE_END));
|
||||
add(runDelayedStart, GBC.grid(1, 2).insets(4, 0, 0, 4).anchor(GBC.LINE_START));
|
||||
add(segmented, GBC.grid(2, 2, 2, 1).insets(4, 0, 0, 4).anchor(GBC.LINE_START));
|
||||
add(segmentsLabel, GBC.grid(0, 3).insets(5, 4, 4, 0)
|
||||
.anchor(GBC.LE));
|
||||
add(scrollPane, GBC.grid(1, 3, 3, 4).insets(4, 4, 0, 0).anchor(GBC.LS));
|
||||
add(addSegment, GBC.grid(3, 3).insets(0, 4).anchor(GBC.FLS));
|
||||
add(remSegment, GBC.grid(3, 4).insets(4, 4).anchor(GBC.FLS));
|
||||
add(moveUp, GBC.grid(3, 5).insets(0, 4).anchor(GBC.FLS));
|
||||
add(moveDown, GBC.grid(3, 6).insets(4, 4).anchor(GBC.FLS));
|
||||
.anchor(GBC.LINE_END));
|
||||
add(scrollPane, GBC.grid(1, 3, 3, 4).insets(4, 4, 0, 0).anchor(GBC.LINE_START));
|
||||
add(addSegment, GBC.grid(3, 3).insets(0, 4).anchor(GBC.FIRST_LINE_START));
|
||||
add(remSegment, GBC.grid(3, 4).insets(4, 4).anchor(GBC.FIRST_LINE_START));
|
||||
add(moveUp, GBC.grid(3, 5).insets(0, 4).anchor(GBC.FIRST_LINE_START));
|
||||
add(moveDown, GBC.grid(3, 6).insets(4, 4).anchor(GBC.FIRST_LINE_START));
|
||||
|
||||
add(recordLabel, GBC.grid(0,7).insets(5,4,4,0).anchor(GBC.LINE_END));
|
||||
add(selectRecord, GBC.grid(1,7).insets(4,0,0,4).anchor(GBC.LINE_START));
|
||||
add(recordString, GBC.grid(1,8).insets(4,0,0,4).anchor(GBC.LINE_START));
|
||||
|
||||
JPanel controls = new JPanel();
|
||||
controls.add(save);
|
||||
controls.add(cancel);
|
||||
add(controls, GBC.grid(0, 7, 4, 1));
|
||||
add(controls, GBC.grid(0, 8, 4, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -227,6 +260,7 @@ implements ActionListener, ListSelectionListener {
|
|||
moveDown.addActionListener(this);
|
||||
cancel.addActionListener(this);
|
||||
save.addActionListener(this);
|
||||
selectRecord.addActionListener(this);
|
||||
|
||||
// Insertion des délégués de rendus et d’édition.
|
||||
segments.setDefaultRenderer(Icon.class, new IconRenderer());
|
||||
|
@ -266,7 +300,7 @@ implements ActionListener, ListSelectionListener {
|
|||
Time time = new Time(text);
|
||||
result = time.getMilliseconds();
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(this, e.getMessage(), Language.ERROR.get(), JOptionPane.ERROR_MESSAGE);
|
||||
master.showError("Invalid delayed start time.", e);
|
||||
result = -1;
|
||||
}
|
||||
|
||||
|
@ -295,12 +329,14 @@ implements ActionListener, ListSelectionListener {
|
|||
|
||||
} else if (source.equals(save)) {
|
||||
run.setName(runTitle.getText());
|
||||
run.setGoal(runGoal.getText());
|
||||
run.setSubTitle(runSubTitle.getText());
|
||||
run.setSegmented(segmented.isSelected());
|
||||
|
||||
long delayedStart = parseDelayedStartTime(runDelayedStart.getText());
|
||||
run.setDelayedStart(delayedStart == -1 ? 0 : delayedStart);
|
||||
|
||||
run.setRecordCategory(recordCategory);
|
||||
|
||||
dispose();
|
||||
|
||||
} else if (source.equals(cancel)) {
|
||||
|
@ -317,6 +353,9 @@ implements ActionListener, ListSelectionListener {
|
|||
int selected = segments.getSelectedRow();
|
||||
run.moveSegmentDown(selected);
|
||||
segments.setRowSelectionInterval(selected + 1, selected + 1);
|
||||
} else if (source.equals(selectRecord)) {
|
||||
recordSelector = new RecordDialog(this, master);
|
||||
recordSelector.display(true, master);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -339,6 +378,13 @@ implements ActionListener, ListSelectionListener {
|
|||
moveDown.setEnabled(enabled && selected < run.getRowCount() - 1);
|
||||
}
|
||||
|
||||
public void recordSet() {
|
||||
this.recordCategory = recordSelector.getCategory();
|
||||
this.recordString.setText(recordSelector.getRecordString());
|
||||
|
||||
this.recordSelector.dispose();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- CLASSE INTERNE
|
||||
|
||||
/**
|
||||
|
@ -440,8 +486,7 @@ implements ActionListener, ListSelectionListener {
|
|||
try {
|
||||
return super.stopCellEditing();
|
||||
} catch (Exception e) {
|
||||
JOptionPane.showMessageDialog(editor, e.getMessage(),
|
||||
Language.ERROR.get(), JOptionPane.ERROR_MESSAGE);
|
||||
master.showError(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
package org.fenix.llanfair.dialog;
|
||||
|
||||
import java.awt.GridBagLayout;
|
||||
import org.fenix.llanfair.Language;
|
||||
import org.fenix.llanfair.Llanfair;
|
||||
import org.fenix.utils.gui.GBC;
|
||||
import org.fenix.utils.locale.LocaleDelegate;
|
||||
import org.fenix.utils.locale.LocaleEvent;
|
||||
import org.fenix.utils.locale.LocaleListener;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
@ -8,18 +16,6 @@ import java.awt.event.WindowListener;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTabbedPane;
|
||||
|
||||
import org.fenix.llanfair.Language;
|
||||
import org.fenix.llanfair.config.Settings;
|
||||
import org.fenix.utils.gui.GBC;
|
||||
import org.fenix.utils.locale.LocaleDelegate;
|
||||
import org.fenix.utils.locale.LocaleEvent;
|
||||
import org.fenix.utils.locale.LocaleListener;
|
||||
|
||||
/**
|
||||
* ConfigDialog
|
||||
*
|
||||
|
@ -31,6 +27,8 @@ public class EditSettings extends LlanfairDialog
|
|||
|
||||
// ATTRIBUTS
|
||||
|
||||
final private Llanfair master;
|
||||
|
||||
/**
|
||||
* Bouton permettant de valider et de fermer la boîte de dialogue.
|
||||
*/
|
||||
|
@ -45,7 +43,10 @@ public class EditSettings extends LlanfairDialog
|
|||
/**
|
||||
* Construction d’une boîte de dialogue d’édition de paramètres.
|
||||
*/
|
||||
public EditSettings() {
|
||||
public EditSettings(Llanfair master) {
|
||||
super(master);
|
||||
this.master = master;
|
||||
|
||||
settingsTabs = new ArrayList<SettingsTab>();
|
||||
settingsTabs.add(new TabGeneral());
|
||||
settingsTabs.add(new TabLook());
|
||||
|
@ -53,6 +54,7 @@ public class EditSettings extends LlanfairDialog
|
|||
settingsTabs.add(new TabHistory());
|
||||
settingsTabs.add(new TabComponents());
|
||||
|
||||
this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
createResources();
|
||||
placeComponents();
|
||||
setPersistentBehavior();
|
||||
|
@ -121,10 +123,16 @@ public class EditSettings extends LlanfairDialog
|
|||
public void actionPerformed(ActionEvent e) {
|
||||
Object source = e.getSource();
|
||||
if (source.equals(actionOK)) {
|
||||
for (SettingsTab tab : settingsTabs) {
|
||||
tab.doDelayedSettingChange();
|
||||
try {
|
||||
for (SettingsTab tab : settingsTabs) {
|
||||
tab.doDelayedSettingChange();
|
||||
}
|
||||
dispose();
|
||||
} catch (InvalidSettingException ex) {
|
||||
ex.tab.requestFocusInWindow();
|
||||
ex.field.requestFocusInWindow();
|
||||
master.showError(ex.getMessage());
|
||||
}
|
||||
dispose();
|
||||
} else if (source.equals(reset)) {
|
||||
int option = JOptionPane.showConfirmDialog(this,
|
||||
"" + Language.WARN_RESET_SETTINGS);
|
||||
|
@ -144,10 +152,15 @@ public class EditSettings extends LlanfairDialog
|
|||
@Override public void windowOpened(WindowEvent e) {}
|
||||
|
||||
@Override public void windowClosing(WindowEvent e) {
|
||||
for (SettingsTab tab : settingsTabs) {
|
||||
tab.doDelayedSettingChange();
|
||||
try {
|
||||
for (SettingsTab tab : settingsTabs) {
|
||||
tab.doDelayedSettingChange();
|
||||
}
|
||||
dispose();
|
||||
} catch (InvalidSettingException ex) {
|
||||
ex.tab.grabFocus();
|
||||
master.showError(ex.getMessage());
|
||||
}
|
||||
dispose();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package org.fenix.llanfair.dialog;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public class InvalidSettingException extends Exception {
|
||||
public final SettingsTab tab;
|
||||
public final Component field;
|
||||
|
||||
public InvalidSettingException(SettingsTab tab, Component field, String message) {
|
||||
super(message);
|
||||
this.tab = tab;
|
||||
this.field = field;
|
||||
}
|
||||
|
||||
public InvalidSettingException(SettingsTab tab, Component field, String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
this.tab = tab;
|
||||
this.field = field;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package org.fenix.llanfair.dialog;
|
|||
import org.fenix.llanfair.Llanfair;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
|
@ -16,6 +17,18 @@ public class LlanfairDialog extends JDialog {
|
|||
|
||||
// ATTRIBUTS
|
||||
|
||||
public LlanfairDialog() {
|
||||
super();
|
||||
}
|
||||
|
||||
public LlanfairDialog(Frame owner) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
public LlanfairDialog(JDialog owner) {
|
||||
super(owner);
|
||||
}
|
||||
|
||||
public void display(boolean lockNativeInputs, final Llanfair llanfair) {
|
||||
if (lockNativeInputs) {
|
||||
llanfair.setIgnoreNativeInputs(true);
|
||||
|
@ -27,6 +40,7 @@ public class LlanfairDialog extends JDialog {
|
|||
}
|
||||
setAlwaysOnTop(true);
|
||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
||||
setAutoRequestFocus(true);
|
||||
pack();
|
||||
setLocationRelativeTo(getOwner());
|
||||
setVisible(true);
|
||||
|
|
|
@ -20,7 +20,7 @@ abstract class SettingsTab extends JPanel {
|
|||
checkBoxes = new HashMap<String, SCheckBox>();
|
||||
}
|
||||
|
||||
abstract void doDelayedSettingChange();
|
||||
abstract void doDelayedSettingChange() throws InvalidSettingException;
|
||||
|
||||
protected class SCheckBox extends LinkedCheckBox {
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ public class TabComponents extends SettingsTab
|
|||
SCB_SETTINGS.add(Settings.footerShowBestTime);
|
||||
SCB_SETTINGS.add(Settings.footerMultiline);
|
||||
SCB_SETTINGS.add(Settings.footerVerbose);
|
||||
SCB_SETTINGS.add(Settings.footerShowWorldRecord);
|
||||
SCB_SETTINGS.add(Settings.footerShowSumOfBest);
|
||||
SCB_SETTINGS.add(Settings.coreShowSegmentName);
|
||||
SCB_SETTINGS.add(Settings.coreShowSplitTime);
|
||||
SCB_SETTINGS.add(Settings.coreShowSegmentTime);
|
||||
|
@ -38,7 +40,7 @@ public class TabComponents extends SettingsTab
|
|||
SCB_SETTINGS.add(Settings.graphDisplay);
|
||||
SCB_SETTINGS.add(Settings.coreShowSegmentTimer);
|
||||
SCB_SETTINGS.add(Settings.coreShowIcons);
|
||||
SCB_SETTINGS.add(Settings.headerShowGoal);
|
||||
SCB_SETTINGS.add(Settings.headerShowSubtitle);
|
||||
SCB_SETTINGS.add(Settings.headerShowAttempts);
|
||||
}
|
||||
|
||||
|
@ -46,24 +48,40 @@ public class TabComponents extends SettingsTab
|
|||
|
||||
private JComboBox timerFont;
|
||||
|
||||
private JCheckBox timerFontBold;
|
||||
|
||||
private JSpinner timerSize;
|
||||
|
||||
private JComboBox timerSegFont;
|
||||
|
||||
private JCheckBox timerSegFontBold;
|
||||
|
||||
private JSpinner timerSegSize;
|
||||
|
||||
private JCheckBox timerSameFont;
|
||||
|
||||
private JComboBox headerTitleFont;
|
||||
|
||||
private JCheckBox headerTitleFontBold;
|
||||
|
||||
private JSpinner headerTitleSize;
|
||||
|
||||
private JComboBox headerSubTitleFont;
|
||||
|
||||
private JCheckBox headerSubTitleFontBold;
|
||||
|
||||
private JSpinner headerSubTitleSize;
|
||||
|
||||
private JComboBox coreFont;
|
||||
|
||||
private JCheckBox coreFontBold;
|
||||
|
||||
private JSpinner coreFontSize;
|
||||
|
||||
private JComboBox otherTimeFont;
|
||||
|
||||
private JCheckBox otherTimeFontBold;
|
||||
|
||||
private JSpinner otherTimeSize;
|
||||
|
||||
TabComponents() {
|
||||
|
@ -98,6 +116,10 @@ public class TabComponents extends SettingsTab
|
|||
timerFont.setPreferredSize(new Dimension(130, 22));
|
||||
timerFont.addActionListener(this);
|
||||
|
||||
timerFontBold = new JCheckBox("" + Language.BOLD);
|
||||
timerFontBold.setSelected(Settings.coreTimerFont.get().isBold());
|
||||
timerFontBold.addChangeListener(this);
|
||||
|
||||
timerSize = new JSpinner(new SpinnerNumberModel(
|
||||
Settings.coreTimerFont.get().getSize(), 8, 240, 1)
|
||||
);
|
||||
|
@ -110,6 +132,10 @@ public class TabComponents extends SettingsTab
|
|||
timerSegFont.setPreferredSize(new Dimension(130, 22));
|
||||
timerSegFont.addActionListener(this);
|
||||
|
||||
timerSegFontBold = new JCheckBox("" + Language.BOLD);
|
||||
timerSegFontBold.setSelected(Settings.coreSegmentTimerFont.get().isBold());
|
||||
timerSegFontBold.addChangeListener(this);
|
||||
|
||||
timerSegSize = new JSpinner(new SpinnerNumberModel(
|
||||
Settings.coreSegmentTimerFont.get().getSize(), 8, 240, 1)
|
||||
);
|
||||
|
@ -122,11 +148,31 @@ public class TabComponents extends SettingsTab
|
|||
headerTitleFont.setPreferredSize(new Dimension(130, 22));
|
||||
headerTitleFont.addActionListener(this);
|
||||
|
||||
headerTitleFontBold = new JCheckBox("" + Language.BOLD);
|
||||
headerTitleFontBold.setSelected(Settings.headerTitleFont.get().isBold());
|
||||
headerTitleFontBold.addChangeListener(this);
|
||||
|
||||
headerTitleSize = new JSpinner(new SpinnerNumberModel(
|
||||
Settings.headerTitleFont.get().getSize(), 8, 240, 1)
|
||||
);
|
||||
headerTitleSize.addChangeListener(this);
|
||||
|
||||
// header sub-title font family and size selector
|
||||
String headerSubTitleFontName = Settings.headerSubTitleFont.get().getName();
|
||||
headerSubTitleFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
|
||||
headerSubTitleFont.setSelectedItem(headerSubTitleFontName);
|
||||
headerSubTitleFont.setPreferredSize(new Dimension(130, 22));
|
||||
headerSubTitleFont.addActionListener(this);
|
||||
|
||||
headerSubTitleFontBold = new JCheckBox("" + Language.BOLD);
|
||||
headerSubTitleFontBold.setSelected(Settings.headerSubTitleFont.get().isBold());
|
||||
headerSubTitleFontBold.addChangeListener(this);
|
||||
|
||||
headerSubTitleSize = new JSpinner(new SpinnerNumberModel(
|
||||
Settings.headerSubTitleFont.get().getSize(), 8, 240, 1)
|
||||
);
|
||||
headerSubTitleSize.addChangeListener(this);
|
||||
|
||||
// other/general font family and size selector
|
||||
String coreFontName = Settings.coreFont.get().getName();
|
||||
coreFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
|
||||
|
@ -134,6 +180,10 @@ public class TabComponents extends SettingsTab
|
|||
coreFont.setPreferredSize(new Dimension(130, 22));
|
||||
coreFont.addActionListener(this);
|
||||
|
||||
coreFontBold = new JCheckBox("" + Language.BOLD);
|
||||
coreFontBold.setSelected(Settings.coreFont.get().isBold());
|
||||
coreFontBold.addChangeListener(this);
|
||||
|
||||
coreFontSize = new JSpinner(new SpinnerNumberModel(
|
||||
Settings.coreFont.get().getSize(), 8, 240, 1)
|
||||
);
|
||||
|
@ -146,6 +196,10 @@ public class TabComponents extends SettingsTab
|
|||
otherTimeFont.setPreferredSize(new Dimension(130, 22));
|
||||
otherTimeFont.addActionListener(this);
|
||||
|
||||
otherTimeFontBold = new JCheckBox("" + Language.BOLD);
|
||||
otherTimeFontBold.setSelected(Settings.coreOtherTimeFont.get().isBold());
|
||||
otherTimeFontBold.addChangeListener(this);
|
||||
|
||||
otherTimeSize = new JSpinner(new SpinnerNumberModel(
|
||||
Settings.coreOtherTimeFont.get().getSize(), 8, 240, 1)
|
||||
);
|
||||
|
@ -167,6 +221,7 @@ public class TabComponents extends SettingsTab
|
|||
Object source = event.getSource();
|
||||
if (source.equals(iconSizes)) {
|
||||
Settings.coreIconSize.set((Integer) iconSizes.getSelectedItem());
|
||||
|
||||
} else if (source.equals(timerFont)) {
|
||||
String fontName = timerFont.getSelectedItem().toString();
|
||||
Font font = Font.decode(fontName).deriveFont(
|
||||
|
@ -176,12 +231,14 @@ public class TabComponents extends SettingsTab
|
|||
if (timerSameFont.isSelected()) {
|
||||
timerSegFont.setSelectedItem(fontName);
|
||||
}
|
||||
|
||||
} else if (source.equals(timerSegFont)) {
|
||||
String fontName = timerSegFont.getSelectedItem().toString();
|
||||
Font font = Font.decode(fontName).deriveFont(
|
||||
(float) Settings.coreSegmentTimerFont.get().getSize()
|
||||
);
|
||||
Settings.coreSegmentTimerFont.set(font);
|
||||
|
||||
} else if (source.equals(headerTitleFont)) {
|
||||
String fontName = headerTitleFont.getSelectedItem().toString();
|
||||
Font font = Font.decode(fontName).deriveFont(
|
||||
|
@ -189,6 +246,13 @@ public class TabComponents extends SettingsTab
|
|||
);
|
||||
Settings.headerTitleFont.set(font);
|
||||
|
||||
} else if (source.equals(headerSubTitleFont)) {
|
||||
String fontName = headerSubTitleFont.getSelectedItem().toString();
|
||||
Font font = Font.decode(fontName).deriveFont(
|
||||
(float) Settings.headerSubTitleFont.get().getSize()
|
||||
);
|
||||
Settings.headerSubTitleFont.set(font);
|
||||
|
||||
} else if (source.equals(coreFont)) {
|
||||
String fontName = coreFont.getSelectedItem().toString();
|
||||
Font font = Font.decode(fontName).deriveFont(
|
||||
|
@ -207,8 +271,9 @@ public class TabComponents extends SettingsTab
|
|||
timerSegFont.setEnabled(!timerSameFont.isSelected());
|
||||
if (timerSameFont.isSelected()) {
|
||||
int size = (Integer) timerSegSize.getValue();
|
||||
int style = timerSegFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.coreSegmentTimerFont.set(
|
||||
Settings.coreTimerFont.get().deriveFont((float) size)
|
||||
Settings.coreTimerFont.get().deriveFont((float) size).deriveFont(style)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -216,33 +281,60 @@ public class TabComponents extends SettingsTab
|
|||
|
||||
@Override public void stateChanged(ChangeEvent event) {
|
||||
Object source = event.getSource();
|
||||
|
||||
if (source.equals(timerSize)) {
|
||||
int size = (Integer) timerSize.getValue();
|
||||
Settings.coreTimerFont.set(
|
||||
Settings.coreTimerFont.get().deriveFont((float) size)
|
||||
);
|
||||
} else if (source.equals(timerFontBold)) {
|
||||
int style = timerFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.coreTimerFont.set(Settings.coreTimerFont.get().deriveFont(style));
|
||||
|
||||
} else if (source.equals(timerSegSize)) {
|
||||
int size = (Integer) timerSegSize.getValue();
|
||||
Settings.coreSegmentTimerFont.set(
|
||||
Settings.coreSegmentTimerFont.get().deriveFont((float) size)
|
||||
);
|
||||
} else if (source.equals(timerSegFontBold)) {
|
||||
int style = timerSegFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.coreSegmentTimerFont.set(Settings.coreSegmentTimerFont.get().deriveFont(style));
|
||||
|
||||
} else if (source.equals(headerTitleSize)) {
|
||||
int size = (Integer) headerTitleSize.getValue();
|
||||
Settings.headerTitleFont.set(
|
||||
Settings.headerTitleFont.get().deriveFont((float) size)
|
||||
);
|
||||
} else if (source.equals(headerTitleFontBold)) {
|
||||
int style = headerTitleFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.headerTitleFont.set(Settings.headerTitleFont.get().deriveFont(style));
|
||||
|
||||
} else if (source.equals(headerSubTitleSize)) {
|
||||
int size = (Integer) headerSubTitleSize.getValue();
|
||||
Settings.headerSubTitleFont.set(
|
||||
Settings.headerSubTitleFont.get().deriveFont((float) size)
|
||||
);
|
||||
} else if (source.equals(headerSubTitleFontBold)) {
|
||||
int style = headerSubTitleFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.headerSubTitleFont.set(Settings.headerSubTitleFont.get().deriveFont(style));
|
||||
|
||||
} else if (source.equals(coreFontSize)) {
|
||||
int size = (Integer) coreFontSize.getValue();
|
||||
Settings.coreFont.set(
|
||||
Settings.coreFont.get().deriveFont((float) size)
|
||||
);
|
||||
} else if (source.equals(coreFontBold)) {
|
||||
int style = coreFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.coreFont.set(Settings.coreFont.get().deriveFont(style));
|
||||
|
||||
} else if (source.equals(otherTimeSize)) {
|
||||
int size = (Integer) otherTimeSize.getValue();
|
||||
Settings.coreOtherTimeFont.set(
|
||||
Settings.coreOtherTimeFont.get().deriveFont((float) size)
|
||||
);
|
||||
} else if (source.equals(otherTimeFontBold)) {
|
||||
int style = otherTimeFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.coreOtherTimeFont.set(Settings.coreOtherTimeFont.get().deriveFont(style));
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -267,83 +359,98 @@ public class TabComponents extends SettingsTab
|
|||
|
||||
fontPanel.add(
|
||||
new JLabel("" + Language.setting_core_timerFont),
|
||||
GBC.grid(0, 0).anchor(GBC.LS).insets(0, 5)
|
||||
GBC.grid(0, 0).anchor(GBC.LINE_START).insets(0, 5)
|
||||
);
|
||||
fontPanel.add(timerFont, GBC.grid(1, 0));
|
||||
fontPanel.add(timerSize, GBC.grid(2, 0).insets(0, 5));
|
||||
fontPanel.add(timerFontBold, GBC.grid(3, 0));
|
||||
|
||||
fontPanel.add(
|
||||
new JLabel("" + Language.setting_core_segmentTimerFont),
|
||||
GBC.grid(0, 1).anchor(GBC.LS).insets(3, 5)
|
||||
GBC.grid(0, 1).anchor(GBC.LINE_START).insets(3, 5)
|
||||
);
|
||||
fontPanel.add(
|
||||
timerSameFont,
|
||||
GBC.grid(1, 1, 2, 1).anchor(GBC.LS).insets(3, 0)
|
||||
GBC.grid(1, 1, 2, 1).anchor(GBC.LINE_START).insets(3, 0)
|
||||
);
|
||||
fontPanel.add(timerSegFont, GBC.grid(1, 2));
|
||||
fontPanel.add(timerSegSize, GBC.grid(2, 2).insets(0, 5));
|
||||
fontPanel.add(timerSegFontBold, GBC.grid(3, 2));
|
||||
|
||||
fontPanel.add(
|
||||
new JLabel("" + Language.setting_header_titleFont),
|
||||
GBC.grid(0, 3).anchor(GBC.LS).insets(0, 5)
|
||||
GBC.grid(0, 3).anchor(GBC.LINE_START).insets(0, 5)
|
||||
);
|
||||
fontPanel.add(headerTitleFont, GBC.grid(1, 3));
|
||||
fontPanel.add(headerTitleSize, GBC.grid(2, 3).insets(0, 5));
|
||||
fontPanel.add(headerTitleFontBold, GBC.grid(3, 3));
|
||||
|
||||
fontPanel.add(
|
||||
new JLabel("" + Language.setting_header_subTitleFont),
|
||||
GBC.grid(0, 4).anchor(GBC.LINE_START).insets(0, 5)
|
||||
);
|
||||
fontPanel.add(headerSubTitleFont, GBC.grid(1, 4));
|
||||
fontPanel.add(headerSubTitleSize, GBC.grid(2, 4).insets(0, 5));
|
||||
fontPanel.add(headerSubTitleFontBold, GBC.grid(3, 4));
|
||||
|
||||
fontPanel.add(
|
||||
new JLabel("" + Language.setting_core_font),
|
||||
GBC.grid(0, 4).anchor(GBC.LS).insets(0, 5)
|
||||
GBC.grid(0, 5).anchor(GBC.LINE_START).insets(0, 5)
|
||||
);
|
||||
fontPanel.add(coreFont, GBC.grid(1, 4));
|
||||
fontPanel.add(coreFontSize, GBC.grid(2, 4).insets(0, 5));
|
||||
fontPanel.add(coreFont, GBC.grid(1, 5));
|
||||
fontPanel.add(coreFontSize, GBC.grid(2, 5).insets(0, 5));
|
||||
fontPanel.add(coreFontBold, GBC.grid(3, 5));
|
||||
|
||||
fontPanel.add(
|
||||
new JLabel("" + Language.setting_core_otherTimeFont),
|
||||
GBC.grid(0, 5).anchor(GBC.LS).insets(0, 5)
|
||||
GBC.grid(0, 6).anchor(GBC.LINE_START).insets(0, 5)
|
||||
);
|
||||
fontPanel.add(otherTimeFont, GBC.grid(1, 5));
|
||||
fontPanel.add(otherTimeSize, GBC.grid(2, 5).insets(0, 5));
|
||||
fontPanel.add(otherTimeFont, GBC.grid(1, 6));
|
||||
fontPanel.add(otherTimeSize, GBC.grid(2, 6).insets(0, 5));
|
||||
fontPanel.add(otherTimeFontBold, GBC.grid(3, 6));
|
||||
}
|
||||
JPanel timerPanel = new JPanel(new GridBagLayout()); {
|
||||
timerPanel.add(
|
||||
new JLabel("" + Language.setting_core_iconSize),
|
||||
GBC.grid(0, 1).anchor(GBC.LE).insets(5, 5)
|
||||
GBC.grid(0, 1).anchor(GBC.LINE_END).insets(5, 5)
|
||||
);
|
||||
timerPanel.add(iconSizes, GBC.grid(1, 1).insets(5, 5));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowIcons.getKey()), GBC.grid(0, 2, 2, 1).anchor(GBC.LS));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowSegmentName.getKey()), GBC.grid(0, 3, 2, 1).anchor(GBC.LS));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowSplitTime.getKey()), GBC.grid(0, 4, 2, 1).anchor(GBC.LS));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowSegmentTime.getKey()), GBC.grid(0, 5, 2, 1).anchor(GBC.LS));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowBestTime.getKey()), GBC.grid(0, 6, 2, 1).anchor(GBC.LS));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowSegmentTimer.getKey()), GBC.grid(0, 7, 2, 1).anchor(GBC.LS));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowIcons.getKey()), GBC.grid(0, 2, 2, 1).anchor(GBC.LINE_START));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowSegmentName.getKey()), GBC.grid(0, 3, 2, 1).anchor(GBC.LINE_START));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowSplitTime.getKey()), GBC.grid(0, 4, 2, 1).anchor(GBC.LINE_START));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowSegmentTime.getKey()), GBC.grid(0, 5, 2, 1).anchor(GBC.LINE_START));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowBestTime.getKey()), GBC.grid(0, 6, 2, 1).anchor(GBC.LINE_START));
|
||||
timerPanel.add(checkBoxes.get(Settings.coreShowSegmentTimer.getKey()), GBC.grid(0, 7, 2, 1).anchor(GBC.LINE_START));
|
||||
timerPanel.setBorder(
|
||||
BorderFactory.createTitledBorder("" + Language.TIMER)
|
||||
);
|
||||
}
|
||||
JPanel footerPanel = new JPanel(new GridBagLayout()); {
|
||||
footerPanel.add(checkBoxes.get(Settings.footerDisplay.getKey()), GBC.grid(0, 0).anchor(GBC.LS));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerUseSplitData.getKey()), GBC.grid(0, 1).anchor(GBC.LS));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerVerbose.getKey()), GBC.grid(0, 2).anchor(GBC.LS));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerShowDeltaLabels.getKey()), GBC.grid(1, 0).anchor(GBC.LS));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerShowBestTime.getKey()), GBC.grid(1, 1).anchor(GBC.LS));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerMultiline.getKey()), GBC.grid(1, 2).anchor(GBC.LS));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerDisplay.getKey()), GBC.grid(0, 0).anchor(GBC.LINE_START));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerUseSplitData.getKey()), GBC.grid(0, 1).anchor(GBC.LINE_START));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerVerbose.getKey()), GBC.grid(0, 2).anchor(GBC.LINE_START));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerShowDeltaLabels.getKey()), GBC.grid(1, 0).anchor(GBC.LINE_START));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerShowBestTime.getKey()), GBC.grid(1, 1).anchor(GBC.LINE_START));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerMultiline.getKey()), GBC.grid(1, 2).anchor(GBC.LINE_START));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerShowSumOfBest.getKey()), GBC.grid(0, 3).anchor(GBC.LINE_START));
|
||||
footerPanel.add(checkBoxes.get(Settings.footerShowWorldRecord.getKey()), GBC.grid(1,3).anchor(GBC.LINE_START));
|
||||
footerPanel.setBorder(
|
||||
BorderFactory.createTitledBorder("" + Language.FOOTER)
|
||||
);
|
||||
}
|
||||
JPanel miscPanel = new JPanel(new GridBagLayout()); {
|
||||
miscPanel.add(checkBoxes.get(Settings.headerShowGoal.getKey()), GBC.grid(0, 0).anchor(GBC.LS));
|
||||
miscPanel.add(checkBoxes.get(Settings.headerShowTitle.getKey()), GBC.grid(0, 1).anchor(GBC.LS));
|
||||
miscPanel.add(checkBoxes.get(Settings.graphDisplay.getKey()), GBC.grid(0, 2).anchor(GBC.LS));
|
||||
miscPanel.add(checkBoxes.get(Settings.headerShowAttempts.getKey()), GBC.grid(0, 3).anchor(GBC.LS));
|
||||
miscPanel.add(checkBoxes.get(Settings.headerShowSubtitle.getKey()), GBC.grid(0, 0).anchor(GBC.LINE_START));
|
||||
miscPanel.add(checkBoxes.get(Settings.headerShowTitle.getKey()), GBC.grid(0, 1).anchor(GBC.LINE_START));
|
||||
miscPanel.add(checkBoxes.get(Settings.graphDisplay.getKey()), GBC.grid(0, 2).anchor(GBC.LINE_START));
|
||||
miscPanel.add(checkBoxes.get(Settings.headerShowAttempts.getKey()), GBC.grid(0, 3).anchor(GBC.LINE_START));
|
||||
miscPanel.setBorder(
|
||||
BorderFactory.createTitledBorder("" + Language.MISC)
|
||||
);
|
||||
}
|
||||
add(fontPanel, GBC.grid(0, 0, 2, 1).fill(GBC.B).anchor(GBC.FLS));
|
||||
add(timerPanel, GBC.grid(2, 0, 1, 2).fill(GBC.B).anchor(GBC.FLS));
|
||||
add(footerPanel, GBC.grid(0, 1).fill(GBC.B).anchor(GBC.FLS));
|
||||
add(miscPanel, GBC.grid(1, 1).fill(GBC.B).anchor(GBC.FLS));
|
||||
add(fontPanel, GBC.grid(0, 0, 2, 1).fill(GBC.BOTH).anchor(GBC.FIRST_LINE_START));
|
||||
add(timerPanel, GBC.grid(2, 0, 1, 2).fill(GBC.BOTH).anchor(GBC.FIRST_LINE_START));
|
||||
add(footerPanel, GBC.grid(0, 1).fill(GBC.BOTH).anchor(GBC.FIRST_LINE_START));
|
||||
add(miscPanel, GBC.grid(1, 1).fill(GBC.BOTH).anchor(GBC.FIRST_LINE_START));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,12 +5,14 @@ import org.fenix.llanfair.Llanfair;
|
|||
import org.fenix.llanfair.config.Accuracy;
|
||||
import org.fenix.llanfair.config.Compare;
|
||||
import org.fenix.llanfair.config.Settings;
|
||||
import org.fenix.utils.UserSettings;
|
||||
import org.fenix.utils.gui.GBC;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.File;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
|
||||
|
@ -30,6 +32,12 @@ public class TabGeneral extends SettingsTab implements ActionListener {
|
|||
|
||||
private JLabel alwaysOnTopText;
|
||||
|
||||
private JCheckBox useDefaultSplitsPath;
|
||||
|
||||
private JTextField customSplitsPath;
|
||||
|
||||
private JButton selectCustomSplitsPath;
|
||||
|
||||
private ButtonGroup compare;
|
||||
|
||||
private JLabel compareText;
|
||||
|
@ -40,6 +48,18 @@ public class TabGeneral extends SettingsTab implements ActionListener {
|
|||
|
||||
private JCheckBox warnOnReset;
|
||||
|
||||
private JLabel windowSizeLabel;
|
||||
|
||||
private JCheckBox windowUserResizable;
|
||||
|
||||
private JTextField windowSize;
|
||||
|
||||
private JLabel windowSizeUnitsText;
|
||||
|
||||
private JTextField maxRecentFiles;
|
||||
|
||||
private JLabel maxRecentFilesLabel;
|
||||
|
||||
// ----------------------------------------------------------- CONSTRUCTORS
|
||||
|
||||
TabGeneral() {
|
||||
|
@ -51,6 +71,22 @@ public class TabGeneral extends SettingsTab implements ActionListener {
|
|||
alwaysOnTop = new JCheckBox("" + Language.setting_alwaysOnTop);
|
||||
alwaysOnTop.setSelected(Settings.alwaysOnTop.get());
|
||||
|
||||
useDefaultSplitsPath = new JCheckBox("" + Language.setting_useDefaultSplitsPath);
|
||||
useDefaultSplitsPath.setSelected(Settings.useDefaultSplitsPath.get());
|
||||
useDefaultSplitsPath.addActionListener(this);
|
||||
|
||||
String path = UserSettings.getSplitsPath(null);
|
||||
if (path == null)
|
||||
path = "";
|
||||
|
||||
customSplitsPath = new JTextField(path);
|
||||
customSplitsPath.setEnabled(!Settings.useDefaultSplitsPath.get());
|
||||
customSplitsPath.setColumns(30);
|
||||
|
||||
selectCustomSplitsPath = new JButton("" + Language.SELECT_SPLITS_DIR);
|
||||
selectCustomSplitsPath.addActionListener(this);
|
||||
selectCustomSplitsPath.setEnabled(!Settings.useDefaultSplitsPath.get());
|
||||
|
||||
compare = new ButtonGroup();
|
||||
Compare setCmp = Settings.compareMethod.get();
|
||||
for (Compare method : Compare.values()) {
|
||||
|
@ -75,6 +111,25 @@ public class TabGeneral extends SettingsTab implements ActionListener {
|
|||
warnOnReset.setSelected(Settings.warnOnReset.get());
|
||||
warnOnReset.addActionListener(this);
|
||||
|
||||
windowSizeLabel = new JLabel("" + Language.WINDOW_SIZE);
|
||||
|
||||
windowUserResizable = new JCheckBox("" + Language.setting_windowUserResizable);
|
||||
windowUserResizable.setSelected(Settings.windowUserResizable.get());
|
||||
windowUserResizable.addActionListener(this);
|
||||
|
||||
String windowWidthText = "";
|
||||
if (Settings.windowWidth.get() != null)
|
||||
windowWidthText = "" + Settings.windowWidth.get();
|
||||
windowSize = new JTextField(windowWidthText, 4);
|
||||
windowSize.setEnabled(!windowUserResizable.isSelected());
|
||||
windowSize.addActionListener(this);
|
||||
|
||||
maxRecentFiles = new JTextField("" + Settings.maxRecentFiles.get(), 4);
|
||||
maxRecentFiles.addActionListener(this);
|
||||
|
||||
windowSizeUnitsText = new JLabel("" + Language.setting_windowWidth);
|
||||
maxRecentFilesLabel = new JLabel("" + Language.setting_maxRecentFiles);
|
||||
|
||||
languageText = new JLabel("" + Language.setting_language);
|
||||
alwaysOnTopText = new JLabel("" + Language.APPLICATION);
|
||||
compareText = new JLabel("" + Language.COMPARE_METHOD);
|
||||
|
@ -100,13 +155,65 @@ public class TabGeneral extends SettingsTab implements ActionListener {
|
|||
}
|
||||
} else if (source.equals(warnOnReset)) {
|
||||
Settings.warnOnReset.set(warnOnReset.isSelected());
|
||||
} else if (source.equals(windowUserResizable)) {
|
||||
windowSize.setEnabled(!windowUserResizable.isSelected());
|
||||
} else if (source.equals(useDefaultSplitsPath)) {
|
||||
Settings.useDefaultSplitsPath.set(useDefaultSplitsPath.isSelected());
|
||||
if (useDefaultSplitsPath.isEnabled())
|
||||
Settings.customSplitsPath.set(null);
|
||||
String path = UserSettings.getSplitsPath(null);
|
||||
if (path == null)
|
||||
path = "";
|
||||
customSplitsPath.setText(path);
|
||||
boolean enabled = !Settings.useDefaultSplitsPath.get();
|
||||
customSplitsPath.setEnabled(enabled);
|
||||
selectCustomSplitsPath.setEnabled(enabled);
|
||||
} else if (source.equals(selectCustomSplitsPath)) {
|
||||
JFileChooser chooser = new JFileChooser();
|
||||
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
|
||||
int action = chooser.showOpenDialog(this);
|
||||
if (action == JFileChooser.APPROVE_OPTION) {
|
||||
customSplitsPath.setText(chooser.getSelectedFile().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------- INHERITED
|
||||
|
||||
void doDelayedSettingChange() {
|
||||
void doDelayedSettingChange() throws InvalidSettingException {
|
||||
Settings.alwaysOnTop.set(alwaysOnTop.isSelected());
|
||||
|
||||
Settings.windowUserResizable.set(windowUserResizable.isSelected());
|
||||
|
||||
if (!windowUserResizable.isSelected()) {
|
||||
int windowWidth;
|
||||
try {
|
||||
windowWidth = Integer.parseInt(windowSize.getText().trim());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new InvalidSettingException(this, windowSize, "" + Language.error_window_width);
|
||||
}
|
||||
|
||||
Settings.windowWidth.set(windowWidth);
|
||||
}
|
||||
|
||||
int numRecentFiles;
|
||||
try {
|
||||
numRecentFiles = Integer.parseInt(maxRecentFiles.getText().trim());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new InvalidSettingException(this, maxRecentFiles, "" + Language.error_max_recent_files);
|
||||
}
|
||||
|
||||
Settings.maxRecentFiles.set(numRecentFiles);
|
||||
|
||||
if (!Settings.useDefaultSplitsPath.get()) {
|
||||
String path = customSplitsPath.getText().trim();
|
||||
if (!new File(path).exists()) {
|
||||
throw new InvalidSettingException(this, customSplitsPath, "" + Language.error_splits_path);
|
||||
}
|
||||
Settings.customSplitsPath.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,13 +232,29 @@ public class TabGeneral extends SettingsTab implements ActionListener {
|
|||
setLayout(new GridBagLayout());
|
||||
|
||||
add(
|
||||
alwaysOnTopText, GBC.grid(0, 0).anchor(GBC.LE).insets(5, 10)
|
||||
alwaysOnTopText, GBC.grid(0, 0).anchor(GBC.LINE_END).insets(5, 10)
|
||||
);
|
||||
add(alwaysOnTop, GBC.grid(1, 0).anchor(GBC.LS));
|
||||
add(warnOnReset, GBC.grid(1, 1).anchor(GBC.LS));
|
||||
add(alwaysOnTop, GBC.grid(1, 0).anchor(GBC.LINE_START));
|
||||
add(warnOnReset, GBC.grid(1, 1).anchor(GBC.LINE_START));
|
||||
|
||||
//add(languageText, GBC.grid(0, 2).anchor(GBC.LE).insets(10, 10));
|
||||
//add(language, GBC.grid(1, 2).fill(GBC.H));
|
||||
JPanel panelSplitsPath = new JPanel(new GridBagLayout()); {
|
||||
panelSplitsPath.add(
|
||||
useDefaultSplitsPath,
|
||||
GBC.grid(0, 0, 2, 1).anchor(GBC.LINE_START)
|
||||
);
|
||||
panelSplitsPath.add(
|
||||
customSplitsPath,
|
||||
GBC.grid(0, 1).anchor(GBC.LINE_START).insets(0, 5)
|
||||
);
|
||||
panelSplitsPath.add(
|
||||
selectCustomSplitsPath,
|
||||
GBC.grid(1, 1).anchor(GBC.LINE_START)
|
||||
);
|
||||
};
|
||||
add(panelSplitsPath, GBC.grid(1, 2).anchor(GBC.LINE_START));
|
||||
|
||||
//add(languageText, GBC.grid(0, 2).anchor(GBC.LINE_END).insets(10, 10));
|
||||
//add(language, GBC.grid(1, 2).fill(GBC.HORIZONTAL));
|
||||
|
||||
JPanel comparePanel = new JPanel(new GridLayout(0, 1)); {
|
||||
Enumeration<AbstractButton> buttons = compare.getElements();
|
||||
|
@ -139,8 +262,8 @@ public class TabGeneral extends SettingsTab implements ActionListener {
|
|||
comparePanel.add(buttons.nextElement());
|
||||
}
|
||||
}
|
||||
add(compareText, GBC.grid(0, 3).anchor(GBC.FLE).insets(14, 10));
|
||||
add(comparePanel, GBC.grid(1, 3).fill(GBC.H).insets(10, 0, 0, 0));
|
||||
add(compareText, GBC.grid(0, 3).anchor(GBC.FIRST_LINE_END).insets(14, 10));
|
||||
add(comparePanel, GBC.grid(1, 3).fill(GBC.HORIZONTAL).insets(10, 0, 0, 0));
|
||||
|
||||
JPanel accuracyPanel = new JPanel(new GridLayout(0, 1)); {
|
||||
Enumeration<AbstractButton> buttons = accuracy.getElements();
|
||||
|
@ -148,8 +271,19 @@ public class TabGeneral extends SettingsTab implements ActionListener {
|
|||
accuracyPanel.add(buttons.nextElement());
|
||||
}
|
||||
}
|
||||
add(accuracyText, GBC.grid(0, 4).anchor(GBC.FLE).insets(14, 10));
|
||||
add(accuracyPanel, GBC.grid(1, 4).fill(GBC.H).insets(10, 0));
|
||||
add(accuracyText, GBC.grid(0, 4).anchor(GBC.FIRST_LINE_END).insets(14, 10));
|
||||
add(accuracyPanel, GBC.grid(1, 4).fill(GBC.HORIZONTAL).insets(10, 0));
|
||||
|
||||
add(windowSizeLabel, GBC.grid(0, 5).anchor(GBC.LINE_END).insets(5, 10));
|
||||
add(windowUserResizable, GBC.grid(1, 5).anchor(GBC.LINE_START));
|
||||
JPanel windowSizeContainer = new JPanel();
|
||||
windowSizeContainer.add(windowSize);
|
||||
windowSizeContainer.add(windowSizeUnitsText);
|
||||
add(windowSizeContainer, GBC.grid(1, 6).anchor(GBC.LINE_START));
|
||||
|
||||
add(maxRecentFilesLabel, GBC.grid(0, 7).anchor(GBC.LINE_END).insets(5, 10));
|
||||
add(maxRecentFiles, GBC.grid(1, 7).anchor(GBC.LINE_START).insets(0, 5));
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- INTERNAL TYPES
|
||||
|
|
|
@ -69,10 +69,14 @@ class TabHistory extends SettingsTab implements ActionListener, ChangeListener {
|
|||
|
||||
private JComboBox nameFont;
|
||||
|
||||
private JCheckBox nameFontBold;
|
||||
|
||||
private JSpinner nameSize;
|
||||
|
||||
private JComboBox timeFont;
|
||||
|
||||
private JCheckBox timeFontBold;
|
||||
|
||||
private JSpinner timeSize;
|
||||
|
||||
// ----------------------------------------------------------- CONSTRUCTORS
|
||||
|
@ -145,6 +149,14 @@ class TabHistory extends SettingsTab implements ActionListener, ChangeListener {
|
|||
timeFont.setPreferredSize(new Dimension(130, 22));
|
||||
timeFont.addActionListener(this);
|
||||
|
||||
nameFontBold = new JCheckBox("" + Language.BOLD);
|
||||
nameFontBold.setSelected(Settings.historySegmentFont.get().isBold());
|
||||
nameFontBold.addChangeListener(this);
|
||||
|
||||
timeFontBold = new JCheckBox("" + Language.BOLD);
|
||||
timeFontBold.setSelected(Settings.historyTimeFont.get().isBold());
|
||||
timeFontBold.addChangeListener(this);
|
||||
|
||||
nameSize = new JSpinner(new SpinnerNumberModel(
|
||||
Settings.historySegmentFont.get().getSize(), 8, 240, 1)
|
||||
);
|
||||
|
@ -223,6 +235,12 @@ class TabHistory extends SettingsTab implements ActionListener, ChangeListener {
|
|||
Settings.historyTimeFont.set(
|
||||
Settings.historyTimeFont.get().deriveFont((float) size)
|
||||
);
|
||||
} else if (source.equals(nameFontBold)) {
|
||||
int style = nameFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.historySegmentFont.set(Settings.historySegmentFont.get().deriveFont(style));
|
||||
} else if (source.equals(timeFontBold)) {
|
||||
int style = timeFontBold.isSelected() ? Font.BOLD : Font.PLAIN;
|
||||
Settings.historyTimeFont.set(Settings.historyTimeFont.get().deriveFont(style));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -264,11 +282,11 @@ class TabHistory extends SettingsTab implements ActionListener, ChangeListener {
|
|||
showLast.setHorizontalTextPosition(JLabel.LEADING);
|
||||
// Display
|
||||
JPanel display = new JPanel(new GridBagLayout()); {
|
||||
display.add(icons , GBC.grid(0, 0).anchor(GBC.LS));
|
||||
display.add(lives , GBC.grid(0, 1).anchor(GBC.LS));
|
||||
display.add(deltas, GBC.grid(0, 2).anchor(GBC.LS));
|
||||
// display.add(tabular, GBC.grid(0, 3).anchor(GBC.LS));
|
||||
display.add(merge , GBC.grid(0, 4).anchor(GBC.LS));
|
||||
display.add(icons , GBC.grid(0, 0).anchor(GBC.LINE_START));
|
||||
display.add(lives , GBC.grid(0, 1).anchor(GBC.LINE_START));
|
||||
display.add(deltas, GBC.grid(0, 2).anchor(GBC.LINE_START));
|
||||
// display.add(tabular, GBC.grid(0, 3).anchor(GBC.LINE_START));
|
||||
display.add(merge , GBC.grid(0, 4).anchor(GBC.LINE_START));
|
||||
display.setBorder(
|
||||
BorderFactory.createTitledBorder("" + Language.PN_DISPLAY)
|
||||
);
|
||||
|
@ -277,19 +295,19 @@ class TabHistory extends SettingsTab implements ActionListener, ChangeListener {
|
|||
JPanel dimension = new JPanel(new GridBagLayout()); {
|
||||
dimension.add(
|
||||
new JLabel("" + Language.setting_history_iconSize),
|
||||
GBC.grid(0, 0).anchor(GBC.LE)
|
||||
GBC.grid(0, 0).anchor(GBC.LINE_END)
|
||||
);
|
||||
dimension.add(iconSize, GBC.grid(1, 0).anchor(GBC.LE).insets(2, 3));
|
||||
dimension.add(iconSize, GBC.grid(1, 0).anchor(GBC.LINE_END).insets(2, 3));
|
||||
dimension.add(
|
||||
new JLabel("" + Language.setting_history_rowCount),
|
||||
GBC.grid(0, 1).anchor(GBC.LE)
|
||||
GBC.grid(0, 1).anchor(GBC.LINE_END)
|
||||
);
|
||||
dimension.add(
|
||||
rows, GBC.grid(1, 1).anchor(GBC.LE).fill(GBC.H).insets(2, 3)
|
||||
rows, GBC.grid(1, 1).anchor(GBC.LINE_END).fill(GBC.HORIZONTAL).insets(2, 3)
|
||||
);
|
||||
dimension.add(blankRows, GBC.grid(0, 2, 2, 1).anchor(GBC.LE));
|
||||
dimension.add(blankRows, GBC.grid(0, 2, 2, 1).anchor(GBC.LINE_END));
|
||||
dimension.add(
|
||||
twoLines , GBC.grid(0, 3, 2, 1).anchor(GBC.LE).insets(0, 1)
|
||||
twoLines , GBC.grid(0, 3, 2, 1).anchor(GBC.LINE_END).insets(0, 1)
|
||||
);
|
||||
dimension.setBorder(
|
||||
BorderFactory.createTitledBorder("" + Language.PN_DIMENSION)
|
||||
|
@ -299,16 +317,18 @@ class TabHistory extends SettingsTab implements ActionListener, ChangeListener {
|
|||
JPanel fonts = new JPanel(new GridBagLayout()); {
|
||||
fonts.add(
|
||||
new JLabel("" + Language.setting_history_segmentFont),
|
||||
GBC.grid(0, 0).anchor(GBC.LE)
|
||||
GBC.grid(0, 0).anchor(GBC.LINE_END)
|
||||
);
|
||||
fonts.add(nameFont, GBC.grid(1, 0).anchor(GBC.LS).insets(5, 5));
|
||||
fonts.add(nameSize, GBC.grid(2, 0).anchor(GBC.LS));
|
||||
fonts.add(nameFont, GBC.grid(1, 0).anchor(GBC.LINE_START).insets(5, 5));
|
||||
fonts.add(nameSize, GBC.grid(2, 0).anchor(GBC.LINE_START));
|
||||
fonts.add(nameFontBold, GBC.grid(3, 0).anchor(GBC.LINE_START));
|
||||
fonts.add(
|
||||
new JLabel("" + Language.setting_history_timeFont),
|
||||
GBC.grid(0, 1).anchor(GBC.LE)
|
||||
GBC.grid(0, 1).anchor(GBC.LINE_END)
|
||||
);
|
||||
fonts.add(timeFont, GBC.grid(1, 1).anchor(GBC.LS).insets(5, 5));
|
||||
fonts.add(timeSize, GBC.grid(2, 1).anchor(GBC.LS));
|
||||
fonts.add(timeFont, GBC.grid(1, 1).anchor(GBC.LINE_START).insets(5, 5));
|
||||
fonts.add(timeSize, GBC.grid(2, 1).anchor(GBC.LINE_START));
|
||||
fonts.add(timeFontBold, GBC.grid(3, 1).anchor(GBC.LINE_START));
|
||||
fonts.setBorder(
|
||||
BorderFactory.createTitledBorder("" + Language.PN_FONTS)
|
||||
);
|
||||
|
@ -316,22 +336,22 @@ class TabHistory extends SettingsTab implements ActionListener, ChangeListener {
|
|||
// Scrolling
|
||||
JPanel scrolling = new JPanel(new GridBagLayout()); {
|
||||
scrolling.add(
|
||||
offsetHelper, GBC.grid(0, 0).anchor(GBC.LE).insets(0, 8)
|
||||
offsetHelper, GBC.grid(0, 0).anchor(GBC.LINE_END).insets(0, 8)
|
||||
);
|
||||
scrolling.add(
|
||||
new JLabel("" + Language.setting_history_offset),
|
||||
GBC.grid(1, 0).anchor(GBC.LE)
|
||||
GBC.grid(1, 0).anchor(GBC.LINE_END)
|
||||
);
|
||||
scrolling.add(offset , GBC.grid(2, 0).anchor(GBC.LE));
|
||||
scrolling.add(showLast, GBC.grid(0, 1, 3, 1).anchor(GBC.LE));
|
||||
scrolling.add(offset , GBC.grid(2, 0).anchor(GBC.LINE_END));
|
||||
scrolling.add(showLast, GBC.grid(0, 1, 3, 1).anchor(GBC.LINE_END));
|
||||
scrolling.setBorder(
|
||||
BorderFactory.createTitledBorder("" + Language.PN_SCROLLING)
|
||||
);
|
||||
}
|
||||
add(fonts , GBC.grid(0, 0).fill(GBC.B).padding(10, 10));
|
||||
add(dimension, GBC.grid(1, 0).fill(GBC.B).padding(10, 10));
|
||||
add(display , GBC.grid(0, 1).fill(GBC.B).padding(10, 10));
|
||||
add(scrolling, GBC.grid(1, 1).fill(GBC.B).padding(10, 10));
|
||||
add(fonts , GBC.grid(0, 0).fill(GBC.BOTH).padding(10, 10));
|
||||
add(dimension, GBC.grid(1, 0).fill(GBC.BOTH).padding(10, 10));
|
||||
add(display , GBC.grid(0, 1).fill(GBC.BOTH).padding(10, 10));
|
||||
add(scrolling, GBC.grid(1, 1).fill(GBC.BOTH).padding(10, 10));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@ class TabHotkeys extends SettingsTab {
|
|||
|
||||
private JCheckBox globalHotKeys;
|
||||
|
||||
private JLabel globalHotKeysHookWarning;
|
||||
|
||||
/**
|
||||
* List of all key fields customizable by the user.
|
||||
*/
|
||||
|
@ -50,9 +48,6 @@ class TabHotkeys extends SettingsTab {
|
|||
public void actionPerformed(ActionEvent e) { Settings.useGlobalHotkeys.set(globalHotKeys.isSelected()); }
|
||||
});
|
||||
|
||||
globalHotKeysHookWarning = new JLabel("" + Language.GLOBAL_HOTKEYS_WARNING);
|
||||
globalHotKeysHookWarning.setForeground(Color.RED);
|
||||
|
||||
keyFields = new ArrayList<KeyField>();
|
||||
keyLabels = new ArrayList<JLabel>();
|
||||
|
||||
|
@ -91,15 +86,12 @@ class TabHotkeys extends SettingsTab {
|
|||
for (row = 0; row < keyFields.size(); row++) {
|
||||
add(
|
||||
keyLabels.get(row),
|
||||
GBC.grid(0, row).insets(10, 0, 10, 10).anchor(GBC.LE)
|
||||
GBC.grid(0, row).insets(10, 0, 10, 10).anchor(GBC.LINE_END)
|
||||
);
|
||||
add(keyFields.get(row), GBC.grid(1, row));
|
||||
}
|
||||
|
||||
add(globalHotKeys, GBC.grid(2, 3).insets(0, 50, 0, 0).anchor(GBC.LS));
|
||||
|
||||
if (GlobalScreen.isNativeHookRegistered())
|
||||
add(globalHotKeysHookWarning, GBC.grid(0, row + 1, 3, 1).insets(10, 0, 10, 0));
|
||||
add(globalHotKeys, GBC.grid(2, 3).insets(0, 50, 0, 0).anchor(GBC.LINE_START));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- INTERNAL TYPES
|
||||
|
|
|
@ -180,7 +180,7 @@ class TabLook extends SettingsTab implements ActionListener, ChangeListener {
|
|||
for (int row = 0; row < colorButtons.size(); row++) {
|
||||
colorPanel.add(
|
||||
colorTexts.get(row),
|
||||
GBC.grid(0, row).anchor(GBC.LE).insets(1, 4)
|
||||
GBC.grid(0, row).anchor(GBC.LINE_END).insets(1, 4)
|
||||
);
|
||||
colorPanel.add(
|
||||
colorButtons.get(row), GBC.grid(1, row).insets(2, 0)
|
||||
|
@ -192,8 +192,8 @@ class TabLook extends SettingsTab implements ActionListener, ChangeListener {
|
|||
swatchPanel.add(colorChooser, GBC.grid(0, 0));
|
||||
swatchPanel.add(helperText, GBC.grid(0, 1));
|
||||
}
|
||||
add(colorPanel, GBC.grid(0, 0).fill(GBC.B));
|
||||
add(swatchPanel, GBC.grid(1, 0).fill(GBC.B));
|
||||
add(colorPanel, GBC.grid(0, 0).fill(GBC.BOTH));
|
||||
add(swatchPanel, GBC.grid(1, 0).fill(GBC.BOTH));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- INTERNAL TYPES
|
||||
|
|
|
@ -197,8 +197,8 @@ class Core extends JPanel implements ActionListener {
|
|||
this.run = run;
|
||||
updateValues(ALL);
|
||||
updateVisibility(ALL);
|
||||
resize = true;
|
||||
revalidate();
|
||||
updateColors(ALL);
|
||||
forceResize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -220,70 +220,65 @@ class Core extends JPanel implements ActionListener {
|
|||
if (graphics != null) {
|
||||
Time tmFake = new Time(600000L);
|
||||
Time tmRun = run.getTime(Segment.SET);
|
||||
|
||||
FontMetrics coreFontMetric = graphics.getFontMetrics(Settings.coreFont.get());
|
||||
FontMetrics coreOtherTimeFontMetric = graphics.getFontMetrics(Settings.coreOtherTimeFont.get());
|
||||
FontMetrics coreTimerFontMetric = graphics.getFontMetrics(Settings.coreTimerFont.get());
|
||||
FontMetrics coreSegmentTimerFontMetric = graphics.getFontMetrics(Settings.coreSegmentTimerFont.get());
|
||||
|
||||
// Segment Name
|
||||
FontMetrics metric = graphics.getFontMetrics();
|
||||
int wName = 0;
|
||||
int hName = 0;
|
||||
int segmentNameWidth = 0;
|
||||
int segmentNameHeight = 0;
|
||||
if (Settings.coreShowSegmentName.get()) {
|
||||
for (int i = 0; i < run.getRowCount(); i++) {
|
||||
String sName = run.getSegment(i).getName();
|
||||
wName = Math.max(wName, metric.stringWidth(sName));
|
||||
segmentNameWidth = Math.max(segmentNameWidth, coreFontMetric.stringWidth(sName));
|
||||
}
|
||||
hName = metric.getHeight();
|
||||
segmentNameHeight = coreFontMetric.getHeight();
|
||||
}
|
||||
// Segment Times
|
||||
int wTime = 0;
|
||||
int hTime = 0;
|
||||
int hBuff = metric.getHeight();
|
||||
int wBuff = metric.stringWidth(
|
||||
"" + (tmRun == null ? tmFake : tmRun)
|
||||
);
|
||||
wBuff += metric.stringWidth("XX:");
|
||||
int timeWidth = 0;
|
||||
int timeHeight = 0;
|
||||
int otherTimerFontHeight = coreOtherTimeFontMetric.getHeight();
|
||||
int otherTimerWidth = coreOtherTimeFontMetric.stringWidth("" + (tmRun == null ? tmFake : tmRun));
|
||||
otherTimerWidth += coreOtherTimeFontMetric.stringWidth("XX:");
|
||||
|
||||
if (Settings.coreShowBestTime.get()) {
|
||||
hTime = hTime + hBuff;
|
||||
wTime = wBuff;
|
||||
timeHeight += otherTimerFontHeight;
|
||||
timeWidth = otherTimerWidth;
|
||||
}
|
||||
if (Settings.coreShowSegmentTime.get()) {
|
||||
hTime = hTime + hBuff;
|
||||
wTime = wBuff;
|
||||
timeHeight += otherTimerFontHeight;
|
||||
timeWidth = otherTimerWidth;
|
||||
}
|
||||
if (Settings.coreShowSplitTime.get()) {
|
||||
hTime = hTime + hBuff;
|
||||
wTime = wBuff;
|
||||
timeHeight += otherTimerFontHeight;
|
||||
timeWidth = otherTimerWidth;
|
||||
}
|
||||
// Segment Icon
|
||||
int hIcon = 0;
|
||||
int wIcon = 0;
|
||||
int iconHeight = 0;
|
||||
int iconWidth = 0;
|
||||
// TODO hasIcon ?
|
||||
if (Settings.coreShowIcons.get() || run.getMaxIconHeight() != 0) {
|
||||
hIcon = Settings.coreIconSize.get();
|
||||
wIcon = hIcon;
|
||||
iconHeight = Settings.coreIconSize.get();
|
||||
iconWidth = iconHeight; // always assume square icon size (will be scaled as such)
|
||||
}
|
||||
// Run Timer
|
||||
metric = graphics.getFontMetrics(Settings.coreTimerFont.get());
|
||||
int wSpTimer = metric.stringWidth(
|
||||
"" + (tmRun == null ? tmFake : tmRun)
|
||||
);
|
||||
int hSpTimer = metric.getHeight();
|
||||
int splitTimerWidth = coreTimerFontMetric.stringWidth("" + (tmRun == null ? tmFake : tmRun));
|
||||
int splitTimerHeight = coreTimerFontMetric.getHeight();
|
||||
|
||||
// Segment Timer
|
||||
int wSeTimer = 0;
|
||||
int hSeTimer = 0;
|
||||
int segmentTimerWidth = 0;
|
||||
int segmentTimerHeight = 0;
|
||||
if (Settings.coreShowSegmentTimer.get()) {
|
||||
metric = graphics.getFontMetrics(
|
||||
Settings.coreSegmentTimerFont.get()
|
||||
);
|
||||
wSeTimer = metric.stringWidth(
|
||||
"" + (tmRun == null ? tmFake : tmRun)
|
||||
);
|
||||
hSeTimer = metric.getHeight();
|
||||
segmentTimerWidth = coreSegmentTimerFontMetric.stringWidth("" + (tmRun == null ? tmFake : tmRun));
|
||||
segmentTimerHeight = coreSegmentTimerFontMetric.getHeight();
|
||||
}
|
||||
|
||||
int maxHeight = Math.max(hIcon, hSpTimer + hSeTimer);
|
||||
maxHeight = Math.max(maxHeight, hTime + hName);
|
||||
int maxHeight = Math.max(iconHeight, splitTimerHeight + segmentTimerHeight);
|
||||
maxHeight = Math.max(maxHeight, timeHeight + segmentNameHeight);
|
||||
|
||||
int maxWidth = wIcon + Math.max(wName, wTime)
|
||||
+ Math.max(wSpTimer, wSeTimer) + 5;
|
||||
int maxWidth = iconWidth + Math.max(segmentNameWidth, timeWidth) + Math.max(splitTimerWidth, segmentTimerWidth) + 5;
|
||||
|
||||
preferredSize = new Dimension(maxWidth, maxHeight);
|
||||
setMinimumSize(new Dimension(MIN_WIDTH, maxHeight));
|
||||
|
@ -307,7 +302,7 @@ class Core extends JPanel implements ActionListener {
|
|||
Time segmentElapsed = new Time(now - current.getStartTime());
|
||||
|
||||
if (splitElapsed.getMilliseconds() < 0)
|
||||
splitTimer.setForeground(Color.GRAY);
|
||||
splitTimer.setForeground(Settings.colorNegativeTime.get());
|
||||
else
|
||||
splitTimer.setForeground(Settings.colorTimer.get());
|
||||
|
||||
|
@ -317,7 +312,7 @@ class Core extends JPanel implements ActionListener {
|
|||
Color bg = Settings.colorBackground.get();
|
||||
if (splitTimer.getForeground().equals(bg)) {
|
||||
if (pauseTime.compareTo(splitTime) > 0) {
|
||||
splitTimer.setForeground(Settings.colorTimeLost.get());
|
||||
splitTimer.setForeground(Settings.colorTimeLostWhileBehind.get());
|
||||
} else {
|
||||
splitTimer.setForeground(Settings.colorTimer.get());
|
||||
}
|
||||
|
@ -337,11 +332,11 @@ class Core extends JPanel implements ActionListener {
|
|||
|
||||
if (!splitLoss && splitElapsed.compareTo(splitTime) > 0) {
|
||||
splitLoss = true;
|
||||
splitTimer.setForeground(Settings.colorTimeLost.get());
|
||||
splitTimer.setForeground(Settings.colorTimeLostWhileBehind.get());
|
||||
}
|
||||
if (!segmentLoss && segmentElapsed.compareTo(segmentTime) > 0) {
|
||||
segmentLoss = true;
|
||||
segmentTimer.setForeground(Settings.colorTimeLost.get());
|
||||
segmentTimer.setForeground(Settings.colorTimeLostWhileBehind.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,6 +361,9 @@ class Core extends JPanel implements ActionListener {
|
|||
} else if (Run.CURRENT_SEGMENT_PROPERTY.equals(property)) {
|
||||
updateValues(ALL & ~TIMER);
|
||||
updateColors(TIMER);
|
||||
} else if (Run.DELAYED_START_PROPERTY.equals(property)) {
|
||||
updateValues(TIMER);
|
||||
updateColors(TIMER);
|
||||
} else if (Settings.colorForeground.equals(property)) {
|
||||
updateColors(NAME);
|
||||
} else if (Settings.colorTime.equals(property)) {
|
||||
|
@ -375,49 +373,46 @@ class Core extends JPanel implements ActionListener {
|
|||
} else if (Settings.compareMethod.equals(property)) {
|
||||
updateValues(TIME);
|
||||
updateColors(TIMER);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.accuracy.equals(property)) {
|
||||
updateValues(TIME | TIMER);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.coreShowBestTime.equals(property)
|
||||
|| Settings.coreShowSegmentTime.equals(property)
|
||||
|| Settings.coreShowSplitTime.equals(property)) {
|
||||
updateVisibility(TIME);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.coreShowSegmentName.equals(property)) {
|
||||
updateVisibility(NAME);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.coreIconSize.equals(property)) {
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.coreTimerFont.equals(property)
|
||||
|| Settings.coreSegmentTimerFont.equals(property)) {
|
||||
updateFonts(TIMER);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.coreShowSegmentTimer.equals(property)) {
|
||||
updateVisibility(TIMER);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.coreShowIcons.equals(property)) {
|
||||
updateVisibility(ICON);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.coreFont.equals(property)) {
|
||||
updateFonts(NAME);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.coreOtherTimeFont.equals(property)) {
|
||||
updateFonts(TIME);
|
||||
resize = true;
|
||||
revalidate();
|
||||
forceResize();
|
||||
} else if (Settings.windowUserResizable.equals(property) || Settings.windowWidth.equals(property)) {
|
||||
updateSize();
|
||||
forceResize();
|
||||
}
|
||||
}
|
||||
|
||||
private void forceResize() {
|
||||
resize = true;
|
||||
revalidate();
|
||||
}
|
||||
/**
|
||||
* Callback invoked by the parent when the run table of segments is
|
||||
* updated.
|
||||
|
@ -447,35 +442,35 @@ class Core extends JPanel implements ActionListener {
|
|||
setLayout(new GridBagLayout());
|
||||
JPanel infoPanel = new JPanel(new GridBagLayout()); {
|
||||
infoPanel.add(
|
||||
name, GBC.grid(0, 0, 2, 1).anchor(GBC.LS).weight(1.0, 0.0)
|
||||
name, GBC.grid(0, 0, 2, 1).anchor(GBC.LINE_START).weight(1.0, 0.0)
|
||||
);
|
||||
infoPanel.add(
|
||||
labelSplit, GBC.grid(0, 1).anchor(GBC.LS).insets(0, 0, 0, 3)
|
||||
labelSplit, GBC.grid(0, 1).anchor(GBC.LINE_START).insets(0, 0, 0, 3)
|
||||
);
|
||||
infoPanel.add(
|
||||
split, GBC.grid(1, 1).anchor(GBC.LS).weight(1.0, 0.0)
|
||||
split, GBC.grid(1, 1).anchor(GBC.LINE_START).weight(1.0, 0.0)
|
||||
);
|
||||
infoPanel.add(
|
||||
labelSegment,
|
||||
GBC.grid(0, 2).anchor(GBC.LS).insets(0, 0, 0, 3)
|
||||
GBC.grid(0, 2).anchor(GBC.LINE_START).insets(0, 0, 0, 3)
|
||||
);
|
||||
infoPanel.add(
|
||||
segment, GBC.grid(1, 2).anchor(GBC.LS).weight(1.0, 0.0)
|
||||
segment, GBC.grid(1, 2).anchor(GBC.LINE_START).weight(1.0, 0.0)
|
||||
);
|
||||
infoPanel.add(
|
||||
labelBest, GBC.grid(0, 3).anchor(GBC.LS).insets(0, 0, 0, 3)
|
||||
labelBest, GBC.grid(0, 3).anchor(GBC.LINE_START).insets(0, 0, 0, 3)
|
||||
);
|
||||
infoPanel.add(best, GBC.grid(1, 3).anchor(GBC.LS).weight(1.0, 0.0));
|
||||
infoPanel.add(best, GBC.grid(1, 3).anchor(GBC.LINE_START).weight(1.0, 0.0));
|
||||
infoPanel.setOpaque(false);
|
||||
}
|
||||
JPanel timePanel = new JPanel(new GridBagLayout()); {
|
||||
timePanel.add(splitTimer, GBC.grid(0, 0).anchor(GBC.LE));
|
||||
timePanel.add(segmentTimer, GBC.grid(0, 1).anchor(GBC.LE));
|
||||
timePanel.add(splitTimer, GBC.grid(0, 0).anchor(GBC.LINE_END));
|
||||
timePanel.add(segmentTimer, GBC.grid(0, 1).anchor(GBC.LINE_END));
|
||||
timePanel.setOpaque(false);
|
||||
}
|
||||
add(icon, GBC.grid(0, 0).insets(0, 0, 0, 8));
|
||||
add(infoPanel, GBC.grid(1, 0).fill(GBC.B).weight(1.0, 1.0));
|
||||
add(timePanel, GBC.grid(2, 0).fill(GBC.H));
|
||||
add(infoPanel, GBC.grid(1, 0).fill(GBC.BOTH).weight(1.0, 1.0));
|
||||
add(timePanel, GBC.grid(2, 0).fill(GBC.HORIZONTAL));
|
||||
}
|
||||
|
||||
private void updateVisibility(int identifier) {
|
||||
|
@ -559,8 +554,7 @@ class Core extends JPanel implements ActionListener {
|
|||
segmentLoss = false;
|
||||
segmentTimer.setText("");
|
||||
Time time = run.getTime(Segment.LIVE);
|
||||
splitTimer.setText(
|
||||
"" + (time == null ? Language.RUN_STOPPED : time));
|
||||
splitTimer.setText("" + (time == null ? Language.RUN_STOPPED : time));
|
||||
} else if (state == State.NULL) {
|
||||
splitTimer.setText("" + Language.RUN_NULL);
|
||||
segmentTimer.setText("");
|
||||
|
@ -568,7 +562,8 @@ class Core extends JPanel implements ActionListener {
|
|||
timer.stop();
|
||||
splitLoss = false;
|
||||
segmentLoss = false;
|
||||
splitTimer.setText("" + Language.RUN_READY);
|
||||
String timeString = getLiveTimeString();
|
||||
splitTimer.setText("" + (timeString == null ? Language.RUN_READY : timeString));
|
||||
segmentTimer.setText("");
|
||||
} else if (state == State.ONGOING) {
|
||||
timer.restart();
|
||||
|
@ -600,7 +595,10 @@ class Core extends JPanel implements ActionListener {
|
|||
if ((identifier & TIMER) == TIMER) {
|
||||
synchronized (this) {
|
||||
Color color = Settings.colorTimer.get();
|
||||
splitTimer.setForeground(color);
|
||||
if (isShowingNegativeTime() && run.getState() == State.READY)
|
||||
splitTimer.setForeground(Settings.colorNegativeTime.get());
|
||||
else
|
||||
splitTimer.setForeground(color);
|
||||
segmentTimer.setForeground(color);
|
||||
}
|
||||
}
|
||||
|
@ -628,4 +626,34 @@ class Core extends JPanel implements ActionListener {
|
|||
segmentTimer.setFont(Settings.coreSegmentTimerFont.get());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSize() {
|
||||
}
|
||||
|
||||
private boolean isShowingNegativeTime() {
|
||||
if (run != null) {
|
||||
Time time = run.getTime(Segment.LIVE);
|
||||
if (time == null)
|
||||
time = new Time(0 - run.getDelayedStart());
|
||||
|
||||
return time.getMilliseconds() < 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String getLiveTimeString() {
|
||||
if (run != null) {
|
||||
Time time = run.getTime(Segment.LIVE);
|
||||
if (time == null)
|
||||
time = new Time(0 - run.getDelayedStart());
|
||||
|
||||
if (time.getMilliseconds() < 0)
|
||||
return "-" + time.toString();
|
||||
else
|
||||
return time.toString();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ class Footer extends JPanel {
|
|||
private static final int TEXT = 0x04;
|
||||
private static final int BEST = 0x08;
|
||||
private static final int VERBOSE = 0x10;
|
||||
private static final int WORLD_RECORD = 0x20;
|
||||
|
||||
private static final int INSET = 3;
|
||||
|
||||
|
@ -56,9 +57,14 @@ class Footer extends JPanel {
|
|||
private JPanel panelBest; // labelBest + best
|
||||
private JPanel panelDeltaBest; // labelDeltaBest + deltaBest
|
||||
|
||||
private JLabel labelSumOfBest;
|
||||
private JLabel sumOfBest;
|
||||
|
||||
private boolean resize;
|
||||
private Dimension preferredSize;
|
||||
|
||||
private JLabel worldRecord;
|
||||
|
||||
/**
|
||||
* Creates a default panel displaying informations for the given run.
|
||||
*
|
||||
|
@ -73,16 +79,20 @@ class Footer extends JPanel {
|
|||
deltaBest = new JLabel();
|
||||
inlineBest = new JLabel();
|
||||
inlineDeltaBest = new JLabel();
|
||||
sumOfBest = new JLabel();
|
||||
|
||||
labelLive = new JLabel();
|
||||
labelPrev = new JLabel();
|
||||
labelBest = new JLabel();
|
||||
labelDelta = new JLabel();
|
||||
labelDeltaBest = new JLabel();
|
||||
labelSumOfBest = new JLabel();
|
||||
|
||||
preferredSize = null;
|
||||
resize = false;
|
||||
|
||||
worldRecord = new JLabel();
|
||||
|
||||
setRun(run);
|
||||
setOpaque(false);
|
||||
|
||||
|
@ -97,40 +107,44 @@ class Footer extends JPanel {
|
|||
@Override public Dimension getPreferredSize() {
|
||||
Graphics graphics = getGraphics();
|
||||
if (resize && (graphics != null)) {
|
||||
FontMetrics metrics = graphics.getFontMetrics();
|
||||
FontMetrics coreFontMetrics = graphics.getFontMetrics(Settings.coreFont.get());
|
||||
FontMetrics coreOtherTimeFontMetrics = graphics.getFontMetrics(Settings.coreOtherTimeFont.get());
|
||||
|
||||
int timeW;
|
||||
int timeH = metrics.getHeight();
|
||||
int timeH = coreOtherTimeFontMetrics.getHeight();
|
||||
int smtmW;
|
||||
if (run.getRowCount() > 0) {
|
||||
Time segmentTime = run.getSegment(0).getTime(Segment.RUN);
|
||||
Time tenthTime = new Time(segmentTime.getMilliseconds() / 10L);
|
||||
timeW = metrics.stringWidth("" + segmentTime);
|
||||
smtmW = metrics.stringWidth("" + tenthTime);
|
||||
timeW = coreOtherTimeFontMetrics.stringWidth("" + segmentTime);
|
||||
smtmW = coreOtherTimeFontMetrics.stringWidth("" + tenthTime);
|
||||
} else {
|
||||
timeW = metrics.stringWidth("" + Time.ZERO);
|
||||
timeW = coreOtherTimeFontMetrics.stringWidth("" + Time.ZERO);
|
||||
smtmW = timeW;
|
||||
}
|
||||
|
||||
int liveW = metrics.stringWidth("" + Language.LB_FT_LIVE);
|
||||
int prevW = metrics.stringWidth("" + Language.LB_FT_SEGMENT);
|
||||
int bestW = metrics.stringWidth("" + Language.LB_FT_BEST);
|
||||
int dltaW = metrics.stringWidth("" + Language.LB_FT_DELTA);
|
||||
int dltbW = metrics.stringWidth("" + Language.LB_FT_DELTA_BEST);
|
||||
|
||||
int labelH = coreFontMetrics.getHeight();
|
||||
int liveW = coreFontMetrics.stringWidth("" + Language.LB_FT_LIVE);
|
||||
int prevW = coreFontMetrics.stringWidth("" + Language.LB_FT_SEGMENT);
|
||||
int bestW = coreFontMetrics.stringWidth("" + Language.LB_FT_BEST);
|
||||
int dltaW = coreFontMetrics.stringWidth("" + Language.LB_FT_DELTA);
|
||||
int dltbW = coreFontMetrics.stringWidth("" + Language.LB_FT_DELTA_BEST);
|
||||
|
||||
boolean ftBest = Settings.footerShowBestTime.get();
|
||||
boolean ftLabels = Settings.footerShowDeltaLabels.get();
|
||||
boolean ftVerbose = Settings.footerVerbose.get();
|
||||
boolean ftTwoLines = Settings.footerMultiline.get();
|
||||
boolean ftSumOfBest = Settings.footerShowSumOfBest.get();
|
||||
boolean ftWorldRecord = Settings.footerShowWorldRecord.get();
|
||||
|
||||
int height = timeH;
|
||||
int height = Math.max(timeH, labelH);
|
||||
int width = prevW + timeW + smtmW + INSET * 2;
|
||||
if (ftLabels) {
|
||||
width += dltaW;
|
||||
}
|
||||
if (ftVerbose) {
|
||||
width += timeW + liveW - (ftLabels ? 0 : dltaW)
|
||||
+ metrics.stringWidth(" []");
|
||||
+ coreOtherTimeFontMetrics.stringWidth(" []");
|
||||
}
|
||||
if (ftBest) {
|
||||
if (ftTwoLines) {
|
||||
|
@ -138,12 +152,19 @@ class Footer extends JPanel {
|
|||
int breakW = bestW + timeW + smtmW + (ftLabels ? dltbW : 0);
|
||||
width = Math.max(width, breakW);
|
||||
} else {
|
||||
width += timeW + smtmW + metrics.stringWidth("| ");
|
||||
width += timeW + smtmW + coreOtherTimeFontMetrics.stringWidth("| ");
|
||||
}
|
||||
if (ftVerbose) {
|
||||
width += 5;
|
||||
}
|
||||
}
|
||||
if (ftSumOfBest) {
|
||||
height += labelH;
|
||||
}
|
||||
if (ftWorldRecord) {
|
||||
height += labelH;
|
||||
}
|
||||
|
||||
preferredSize = new Dimension(width, height);
|
||||
setMinimumSize(new Dimension(50, height));
|
||||
resize = false;
|
||||
|
@ -179,8 +200,10 @@ class Footer extends JPanel {
|
|||
updateValues(ALL & ~TEXT);
|
||||
updateColors(TIME | DELTA);
|
||||
updateVisibility(ALL);
|
||||
} else if (Settings.colorTimeLost.equals(property)
|
||||
|| Settings.colorTimeGained.equals(property)) {
|
||||
} else if (Settings.colorTimeGainedWhileBehind.equals(property)
|
||||
|| Settings.colorTimeLostWhileBehind.equals(property)
|
||||
|| Settings.colorTimeLostWhileAhead.equals(property)
|
||||
|| Settings.colorTimeGainedWhileAhead.equals(property)) {
|
||||
updateColors(DELTA);
|
||||
|
||||
} else if (Settings.colorTime.equals(property)
|
||||
|
@ -207,7 +230,8 @@ class Footer extends JPanel {
|
|||
|
||||
updateVisibility(BEST);
|
||||
forceResize();
|
||||
} else if (Settings.footerShowDeltaLabels.equals(property)) {
|
||||
} else if (Settings.footerShowDeltaLabels.equals(property)
|
||||
|| Settings.footerShowWorldRecord.equals(property)) {
|
||||
updateVisibility(TEXT);
|
||||
forceResize();
|
||||
} else if (Settings.footerVerbose.equals(property)) {
|
||||
|
@ -220,6 +244,17 @@ class Footer extends JPanel {
|
|||
} else if (Settings.coreOtherTimeFont.equals(property)) {
|
||||
updateFonts(TIME | DELTA);
|
||||
forceResize();
|
||||
} else if (Settings.windowUserResizable.equals(property)
|
||||
|| Settings.windowWidth.equals(property)) {
|
||||
updateSize();
|
||||
forceResize();
|
||||
} else if (Settings.footerShowSumOfBest.equals(property)) {
|
||||
updateVisibility(BEST | TEXT);
|
||||
updateValues(TIME | TEXT);
|
||||
updateSize();
|
||||
forceResize();
|
||||
} else if (Run.RECORD_CATEGORY_PROPERTY.equals(property)) {
|
||||
updateValues(WORLD_RECORD);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,55 +284,77 @@ class Footer extends JPanel {
|
|||
JPanel timePanel = new JPanel(new GridBagLayout()); {
|
||||
timePanel.add(
|
||||
labelPrev,
|
||||
GBC.grid(0, 0).anchor(GBC.LS).insets(0, 0, 0, INSET)
|
||||
GBC.grid(0, 0).anchor(GBC.LINE_START).insets(0, 0, 0, INSET)
|
||||
);
|
||||
timePanel.add(liveL, GBC.grid(1, 0).anchor(GBC.LS));
|
||||
timePanel.add(time, GBC.grid(2, 0).anchor(GBC.LS));
|
||||
timePanel.add(liveL, GBC.grid(1, 0).anchor(GBC.LINE_START));
|
||||
timePanel.add(time, GBC.grid(2, 0).anchor(GBC.LINE_START));
|
||||
timePanel.add(
|
||||
inlineBest,
|
||||
GBC.grid(3, 0).anchor(GBC.LS).insets(0, INSET, 0, 0)
|
||||
GBC.grid(3, 0).anchor(GBC.LINE_START).insets(0, INSET, 0, 0)
|
||||
);
|
||||
timePanel.setOpaque(false);
|
||||
}
|
||||
JPanel deltaPanel = new JPanel(new GridBagLayout()); {
|
||||
deltaPanel.add(
|
||||
labelDelta,
|
||||
GBC.grid(0, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
|
||||
GBC.grid(0, 0).anchor(GBC.LINE_END).insets(0, 0, 0, INSET)
|
||||
);
|
||||
deltaPanel.add(
|
||||
labelLive,
|
||||
GBC.grid(1, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
|
||||
GBC.grid(1, 0).anchor(GBC.LINE_END).insets(0, 0, 0, INSET)
|
||||
);
|
||||
deltaPanel.add(
|
||||
liveR, GBC.grid(2, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
|
||||
liveR, GBC.grid(2, 0).anchor(GBC.LINE_END).insets(0, 0, 0, INSET)
|
||||
);
|
||||
deltaPanel.add(delta, GBC.grid(3, 0).anchor(GBC.LE));
|
||||
deltaPanel.add(delta, GBC.grid(3, 0).anchor(GBC.LINE_END));
|
||||
deltaPanel.add(
|
||||
inlineDeltaBest,
|
||||
GBC.grid(4, 0).anchor(GBC.LE).insets(0, INSET, 0, 0)
|
||||
GBC.grid(4, 0).anchor(GBC.LINE_END).insets(0, INSET, 0, 0)
|
||||
);
|
||||
deltaPanel.setOpaque(false);
|
||||
}
|
||||
panelBest = new JPanel(new GridBagLayout()); {
|
||||
panelBest.add(
|
||||
labelBest,
|
||||
GBC.grid(0, 0).anchor(GBC.LS).insets(0, 0, 0, INSET)
|
||||
GBC.grid(0, 0).anchor(GBC.LINE_START).insets(0, 0, 0, INSET)
|
||||
);
|
||||
panelBest.add(best, GBC.grid(1, 0).anchor(GBC.LS));
|
||||
panelBest.add(best, GBC.grid(1, 0).anchor(GBC.LINE_START));
|
||||
panelBest.setOpaque(false);
|
||||
}
|
||||
panelDeltaBest = new JPanel(new GridBagLayout()); {
|
||||
panelDeltaBest.add(
|
||||
labelDeltaBest,
|
||||
GBC.grid(0, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
|
||||
GBC.grid(0, 0).anchor(GBC.LINE_END).insets(0, 0, 0, INSET)
|
||||
);
|
||||
panelDeltaBest.add(deltaBest, GBC.grid(1, 0).anchor(GBC.LE));
|
||||
panelDeltaBest.add(deltaBest, GBC.grid(1, 0).anchor(GBC.LINE_END));
|
||||
panelDeltaBest.setOpaque(false);
|
||||
}
|
||||
add(timePanel, GBC.grid(0, 0).anchor(GBC.LS).weight(0.5, 0.0));
|
||||
add(deltaPanel, GBC.grid(1, 0).anchor(GBC.LE).weight(0.5, 0.0));
|
||||
add(panelBest, GBC.grid(0, 1).anchor(GBC.LS).weight(0.5, 0.0));
|
||||
add(panelDeltaBest, GBC.grid(1, 1).anchor(GBC.LE).weight(0.5, 0.0));
|
||||
JPanel panelSumOfBest = new JPanel(new GridBagLayout());
|
||||
{
|
||||
panelSumOfBest.add(
|
||||
labelSumOfBest,
|
||||
GBC.grid(0, 0).anchor(GBC.LINE_START).insets(0, 0, 0, INSET)
|
||||
);
|
||||
panelSumOfBest.add(
|
||||
sumOfBest,
|
||||
GBC.grid(1, 0).anchor(GBC.LINE_END).insets(0, 0, 0, INSET)
|
||||
);
|
||||
panelSumOfBest.setOpaque(false);
|
||||
}
|
||||
JPanel panelWorldRecord= new JPanel(new GridBagLayout());
|
||||
{
|
||||
panelWorldRecord.add(
|
||||
worldRecord,
|
||||
GBC.grid(0, 0).insets(0, 0, 0, INSET)
|
||||
);
|
||||
panelWorldRecord.setOpaque(false);
|
||||
}
|
||||
add(timePanel, GBC.grid(0, 0).anchor(GBC.LINE_START).weight(0.5, 0.0));
|
||||
add(deltaPanel, GBC.grid(1, 0).anchor(GBC.LINE_END).weight(0.5, 0.0));
|
||||
add(panelBest, GBC.grid(0, 1).anchor(GBC.LINE_START).weight(0.5, 0.0));
|
||||
add(panelDeltaBest, GBC.grid(1, 1).anchor(GBC.LINE_END).weight(0.5, 0.0));
|
||||
add(panelSumOfBest, GBC.grid(0, 2).anchor(GBC.LINE_START).weight(0.5, 0.0));
|
||||
add(panelWorldRecord, GBC.grid(0,3, 2, 1).weight(0.5,0.0));
|
||||
}
|
||||
|
||||
private void updateVisibility(int identifier) {
|
||||
|
@ -308,6 +365,7 @@ class Footer extends JPanel {
|
|||
panelDeltaBest.setVisible(ftTwoLines);
|
||||
inlineBest.setVisible(!ftTwoLines && ftBest);
|
||||
inlineDeltaBest.setVisible(!ftTwoLines && ftBest);
|
||||
sumOfBest.setVisible(Settings.footerShowSumOfBest.get());
|
||||
}
|
||||
if ((identifier & TEXT) == TEXT) {
|
||||
boolean ftLabels = Settings.footerShowDeltaLabels.get();
|
||||
|
@ -315,6 +373,8 @@ class Footer extends JPanel {
|
|||
labelLive.setVisible(ftLabels && ftVerbose);
|
||||
labelDelta.setVisible(ftLabels && !ftVerbose);
|
||||
labelDeltaBest.setVisible(ftLabels);
|
||||
labelSumOfBest.setVisible(Settings.footerShowSumOfBest.get());
|
||||
worldRecord.setVisible(Settings.footerShowWorldRecord.get());
|
||||
}
|
||||
if ((identifier & VERBOSE) == VERBOSE) {
|
||||
boolean ftVerbose = Settings.footerVerbose.get();
|
||||
|
@ -347,6 +407,7 @@ class Footer extends JPanel {
|
|||
time.setForeground(colorTM);
|
||||
best.setForeground(colorTM);
|
||||
inlineBest.setForeground(colorTM);
|
||||
sumOfBest.setForeground(colorTM);
|
||||
}
|
||||
if ((identifier & DELTA) == DELTA) {
|
||||
if (run.hasPreviousSegment()) {
|
||||
|
@ -363,9 +424,9 @@ class Footer extends JPanel {
|
|||
} else {
|
||||
int compare = tmDlta.compareTo(Time.ZERO);
|
||||
if (compare > 0) {
|
||||
delta.setForeground(Settings.colorTimeLost.get());
|
||||
delta.setForeground(Settings.colorTimeLostWhileBehind.get());
|
||||
} else {
|
||||
delta.setForeground(Settings.colorTimeGained.get());
|
||||
delta.setForeground(Settings.colorTimeGainedWhileAhead.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -377,6 +438,8 @@ class Footer extends JPanel {
|
|||
labelLive.setForeground(color);
|
||||
labelBest.setForeground(color);
|
||||
labelDeltaBest.setForeground(color);
|
||||
labelSumOfBest.setForeground(color);
|
||||
worldRecord.setForeground(color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,6 +455,7 @@ class Footer extends JPanel {
|
|||
time.setFont(Settings.coreOtherTimeFont.get());
|
||||
best.setFont(Settings.coreOtherTimeFont.get());
|
||||
inlineBest.setFont(Settings.coreOtherTimeFont.get());
|
||||
sumOfBest.setFont(Settings.coreOtherTimeFont.get());
|
||||
}
|
||||
if ((identifier & DELTA) == DELTA) {
|
||||
delta.setFont(Settings.coreOtherTimeFont.get());
|
||||
|
@ -404,6 +468,8 @@ class Footer extends JPanel {
|
|||
labelLive.setFont(Settings.coreFont.get());
|
||||
labelBest.setFont(Settings.coreFont.get());
|
||||
labelDeltaBest.setFont(Settings.coreFont.get());
|
||||
labelSumOfBest.setFont(Settings.coreFont.get());
|
||||
worldRecord.setFont(Settings.coreFont.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -446,6 +512,11 @@ class Footer extends JPanel {
|
|||
best.setText("");
|
||||
inlineBest.setText("");
|
||||
}
|
||||
Time sumOfBestTime = run.getSumOfBest();
|
||||
if (sumOfBestTime.getMilliseconds() > 0)
|
||||
sumOfBest.setText(sumOfBestTime.toString());
|
||||
else
|
||||
sumOfBest.setText("");
|
||||
}
|
||||
if ((identifier & DELTA) == DELTA) {
|
||||
if (hasPrevious) {
|
||||
|
@ -515,6 +586,13 @@ class Footer extends JPanel {
|
|||
labelBest.setText("" + Language.LB_FT_BEST);
|
||||
labelDelta.setText("" + Language.LB_FT_DELTA);
|
||||
labelDeltaBest.setText("" + Language.LB_FT_DELTA_BEST);
|
||||
labelSumOfBest.setText("" + Language.LB_FT_SUM_OF_BEST);
|
||||
}
|
||||
if((identifier & WORLD_RECORD) == WORLD_RECORD) {
|
||||
worldRecord.setText(run.getRecordString());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSize() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,8 +148,10 @@ class Graph extends JPanel {
|
|||
// Settings.COLOR_BACKGROUND, COLOR_TIME_LOST, COLOR_TIME_GAINED
|
||||
// or Run.CURRENT_SEGMENT_PROPERTY
|
||||
} else if (Settings.colorBackground.equals(property)
|
||||
|| Settings.colorTimeLost.equals(property)
|
||||
|| Settings.colorTimeGained.equals(property)
|
||||
|| Settings.colorTimeGainedWhileBehind.equals(property)
|
||||
|| Settings.colorTimeLostWhileBehind.equals(property)
|
||||
|| Settings.colorTimeGainedWhileAhead.equals(property)
|
||||
|| Settings.colorTimeLostWhileAhead.equals(property)
|
||||
|| Settings.colorNewRecord.equals(property)
|
||||
|| Run.CURRENT_SEGMENT_PROPERTY.equals(property)) {
|
||||
canvas.repaint();
|
||||
|
@ -170,9 +172,16 @@ class Graph extends JPanel {
|
|||
}
|
||||
} else if (Settings.coreFont.equals(property)) {
|
||||
updateFonts(ALL);
|
||||
} else if (Settings.windowUserResizable.equals(property) || Settings.windowWidth.equals(property)) {
|
||||
updateSize();
|
||||
forceResize();
|
||||
}
|
||||
}
|
||||
|
||||
private void forceResize() {
|
||||
revalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked by the parent when the run table of segments is
|
||||
* updated.
|
||||
|
@ -230,8 +239,8 @@ class Graph extends JPanel {
|
|||
scalePanel.add(scale);
|
||||
scalePanel.setOpaque(false);
|
||||
}
|
||||
add(scalePanel, GBC.grid(0, 0).anchor(GBC.LS));
|
||||
add(canvas, GBC.grid(0, 1).fill(GBC.B).weight(1.0, 1.0));
|
||||
add(scalePanel, GBC.grid(0, 0).anchor(GBC.LINE_START));
|
||||
add(canvas, GBC.grid(0, 1).fill(GBC.BOTH).weight(1.0, 1.0));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -283,6 +292,9 @@ class Graph extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
private void updateSize() {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- INTERNAL TYPE
|
||||
|
||||
/**
|
||||
|
@ -310,8 +322,8 @@ class Graph extends JPanel {
|
|||
g2.fillRect(0, 0, clipW, clipH);
|
||||
|
||||
Color colorFG = Settings.colorForeground.get();
|
||||
Color colorTG = Settings.colorTimeGained.get();
|
||||
Color colorTL = Settings.colorTimeLost.get();
|
||||
Color colorTG = Settings.colorTimeGainedWhileAhead.get();
|
||||
Color colorTL = Settings.colorTimeLostWhileBehind.get();
|
||||
Color colorRC = Settings.colorNewRecord.get();
|
||||
|
||||
// Draw the axis.
|
||||
|
|
|
@ -255,9 +255,11 @@ public class History extends JPanel {
|
|||
} else if (Settings.historyRowCount.equals(property)
|
||||
|| Settings.historyBlankRows.equals(property)) {
|
||||
populateRows();
|
||||
} else if (Settings.colorTimeGained.equals(property)
|
||||
|| Settings.colorTimeLost.equals(property)
|
||||
|| Settings.colorNewRecord.equals(property)) {
|
||||
} else if (Settings.colorTimeGainedWhileAhead.equals(property)
|
||||
|| Settings.colorTimeLostWhileAhead.equals(property)
|
||||
|| Settings.colorTimeGainedWhileBehind.equals(property)
|
||||
|| Settings.colorTimeLostWhileBehind.equals(property)
|
||||
|| Settings.colorNewRecord.equals(property)) {
|
||||
updateColors(LIVE);
|
||||
} else if (Settings.colorHighlight.equals(property)) {
|
||||
updateColors(MARKER);
|
||||
|
@ -279,6 +281,9 @@ public class History extends JPanel {
|
|||
updateValues(TIME | LIVE);
|
||||
updateColors(TIME);
|
||||
forceResize();
|
||||
} else if (Settings.windowUserResizable.equals(property) || Settings.windowWidth.equals(property)) {
|
||||
updateSize();
|
||||
forceResize();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,14 +328,25 @@ public class History extends JPanel {
|
|||
segmentRows.clear();
|
||||
// At most, we need as much segments as the run has or as much as
|
||||
// is demanded by the user.
|
||||
int count = Settings.historyRowCount.get();
|
||||
count = Math.max(count, run.getRowCount());
|
||||
int count = Math.max(Settings.historyRowCount.get(), run.getRowCount());
|
||||
// Create and place the rows.
|
||||
for (int i = 0; i < count; i++) {
|
||||
SegmentRow row = new SegmentRow();
|
||||
add(row, GBC.grid(0, i).fill(GBC.H).weight(1.0, 0.0));
|
||||
add(row, GBC.grid(0, i).fill(GBC.HORIZONTAL).anchor(GBC.NORTH).weight(1.0, 0.0));
|
||||
segmentRows.add(i, row);
|
||||
}
|
||||
|
||||
// HACK: if we're to display a set number of rows (even if blank, if the run has less then this number)
|
||||
// we place an additional filler row that has vertical weight which pushes up the actual run rows so
|
||||
// that all the rows appear top-aligned.
|
||||
// jesus christ, why is layouting so shit that this is even required!?
|
||||
if (Settings.historyBlankRows.get()) {
|
||||
if (run.getRowCount() < count) {
|
||||
JPanel filler = new JPanel();
|
||||
filler.setOpaque(false);
|
||||
add(filler, GBC.grid(0, count).fill(GBC.BOTH).anchor(GBC.NORTH).weight(1.0, 1.0));
|
||||
}
|
||||
}
|
||||
// Fill the rows with the segment data and colorize.
|
||||
updateValues(ALL);
|
||||
updateColors(ALL);
|
||||
|
@ -375,9 +391,8 @@ public class History extends JPanel {
|
|||
}
|
||||
// Set the visibility of every segments accordingly.
|
||||
for (int i = 0; i < segmentRows.size(); i++) {
|
||||
segmentRows.get(i).setVisible(
|
||||
(i > lastSeg - realCount) && (i <= lastSeg)
|
||||
);
|
||||
boolean visible = (i > lastSeg - realCount) && (i <= lastSeg);
|
||||
segmentRows.get(i).setVisible(visible);
|
||||
}
|
||||
// Display the last segment if the setting is enabled.
|
||||
if (Settings.historyAlwaysShowLast.get() && run.getRowCount() > 0) {
|
||||
|
@ -444,6 +459,9 @@ public class History extends JPanel {
|
|||
updateFonts(identifier, 0, run.getRowCount() - 1);
|
||||
}
|
||||
|
||||
private void updateSize() {
|
||||
}
|
||||
|
||||
// --------------------------------------------------------- INTERNAL TYPES
|
||||
|
||||
private class SegmentRow extends JPanel {
|
||||
|
@ -611,18 +629,10 @@ public class History extends JPanel {
|
|||
}
|
||||
}
|
||||
if ((identifier & LIVE) == LIVE && (index > -1)) {
|
||||
Color lost = Settings.colorTimeLost.get();
|
||||
Color gain = Settings.colorTimeGained.get();
|
||||
Color neut = Settings.colorTime.get();
|
||||
Color recd = Settings.colorNewRecord.get();
|
||||
int prev = run.getPrevious();
|
||||
|
||||
// TODO: replace with color settings the user can set themselves
|
||||
Color lostWhileAhead = gain.brighter();
|
||||
Color gainWhileAhead = gain; //.darker();
|
||||
Color lostWhileBehind = lost.brighter();
|
||||
Color gainWhileBehind = lost; //.darker();
|
||||
|
||||
Merge merge = Settings.historyMerge.get();
|
||||
JLabel realDelta = (merge == Merge.DELTA) ? time : delta;
|
||||
JLabel realLive = (merge == Merge.LIVE ) ? time : live;
|
||||
|
@ -642,19 +652,23 @@ public class History extends JPanel {
|
|||
} else {
|
||||
boolean isGainingTime = run.isBetterSegment(run.getPrevious());
|
||||
if (compare > 0) {
|
||||
Color color;
|
||||
if (isGainingTime)
|
||||
realDelta.setForeground(gainWhileBehind);
|
||||
color = Settings.colorTimeGainedWhileBehind.get();
|
||||
else
|
||||
realDelta.setForeground(lostWhileBehind);
|
||||
color = Settings.colorTimeLostWhileBehind.get();
|
||||
|
||||
realLive.setForeground(lost);
|
||||
realDelta.setForeground(color);
|
||||
realLive.setForeground(color);
|
||||
} else {
|
||||
Color color;
|
||||
if (isGainingTime)
|
||||
realDelta.setForeground(gainWhileAhead);
|
||||
color = Settings.colorTimeGainedWhileAhead.get();
|
||||
else
|
||||
realDelta.setForeground(lostWhileAhead);
|
||||
color = Settings.colorTimeLostWhileAhead.get();
|
||||
|
||||
realLive.setForeground(gain);
|
||||
realDelta.setForeground(color);
|
||||
realLive.setForeground(color);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -682,23 +696,17 @@ public class History extends JPanel {
|
|||
|
||||
private void placeComponents(boolean multiline) {
|
||||
if (!multiline) {
|
||||
add(icon , GBC.grid(0, 0).anchor(GBC.C));
|
||||
add(
|
||||
name, GBC.grid(1, 0).insets(0, INSET, 0, 0).anchor(GBC.LS)
|
||||
.fill(GBC.H).weight(1.0, 0.0)
|
||||
);
|
||||
add(time , GBC.grid(2, 0).insets(0, INSET, 0, 0).anchor(GBC.LE));
|
||||
add(live , GBC.grid(3, 0).insets(0, INSET, 0, 0).anchor(GBC.LE));
|
||||
add(delta, GBC.grid(4, 0).insets(0, INSET, 0, 0).anchor(GBC.LE));
|
||||
add(icon , GBC.grid(0, 0).anchor(GBC.CENTER));
|
||||
add(name, GBC.grid(1, 0).insets(0, INSET, 0, 0).anchor(GBC.LINE_START).fill(GBC.HORIZONTAL).weight(1.0, 0.0));
|
||||
add(time , GBC.grid(2, 0).insets(0, INSET, 0, 0).anchor(GBC.LINE_END));
|
||||
add(live , GBC.grid(3, 0).insets(0, INSET, 0, 0).anchor(GBC.LINE_END));
|
||||
add(delta, GBC.grid(4, 0).insets(0, INSET, 0, 0).anchor(GBC.LINE_END));
|
||||
} else {
|
||||
add(icon, GBC.grid(0, 0, 1, 2).anchor(GBC.C));
|
||||
add(
|
||||
name, GBC.grid(1, 0, 3, 1).anchor(GBC.LS).fill(GBC.H)
|
||||
.weight(1.0, 0.0).insets(0, INSET, 0, 0)
|
||||
);
|
||||
add(time, GBC.grid(1, 1).anchor(GBC.LE).insets(0, INSET, 0, 0));
|
||||
add(live, GBC.grid(2, 1).anchor(GBC.LE).insets(0, INSET, 0, 0));
|
||||
add(delta, GBC.grid(3, 1).anchor(GBC.LE));
|
||||
add(icon, GBC.grid(0, 0, 1, 2).anchor(GBC.CENTER));
|
||||
add(name, GBC.grid(1, 0, 3, 1).anchor(GBC.LINE_START).fill(GBC.HORIZONTAL).weight(1.0, 0.0).insets(0, INSET, 0, 0));
|
||||
add(time, GBC.grid(1, 1).anchor(GBC.LINE_END).insets(0, INSET, 0, 0));
|
||||
add(live, GBC.grid(2, 1).anchor(GBC.LINE_END).insets(0, INSET, 0, 0));
|
||||
add(delta, GBC.grid(3, 1).anchor(GBC.LINE_END));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,18 @@
|
|||
package org.fenix.llanfair.gui;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridBagLayout;
|
||||
import org.fenix.llanfair.Run;
|
||||
import org.fenix.llanfair.Run.State;
|
||||
import org.fenix.llanfair.config.Settings;
|
||||
import org.fenix.utils.gui.GBC;
|
||||
import org.fenix.utils.locale.LocaleEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
import java.awt.*;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.event.TableModelEvent;
|
||||
|
||||
import org.fenix.llanfair.Language;
|
||||
import org.fenix.llanfair.Run;
|
||||
import org.fenix.llanfair.Segment;
|
||||
import org.fenix.llanfair.config.Settings;
|
||||
import org.fenix.llanfair.Run.State;
|
||||
import org.fenix.llanfair.Time;
|
||||
import org.fenix.utils.gui.GBC;
|
||||
import org.fenix.utils.locale.LocaleEvent;
|
||||
|
||||
/**
|
||||
* A panel representing informations on a given run. This panel uses numerous
|
||||
* sub-component to given different kind of informations like a graph or a
|
||||
|
@ -45,7 +37,7 @@ public class RunPane extends JPanel {
|
|||
/**
|
||||
* Update identifier for time variables.
|
||||
*/
|
||||
private static final int GOAL = 0x01;
|
||||
private static final int SUBTITLE = 0x01;
|
||||
|
||||
/**
|
||||
* Update identifier for separator variables.
|
||||
|
@ -96,26 +88,20 @@ public class RunPane extends JPanel {
|
|||
private JLabel title;
|
||||
|
||||
/**
|
||||
* Label displaying the current goal of run. By default it’s the time of
|
||||
* the run we’re comparing against, but it can be a customized string.
|
||||
* Label displaying the sub title of the current run.
|
||||
*/
|
||||
private JLabel goal;
|
||||
|
||||
/**
|
||||
* Label describing the goal value being displayed.
|
||||
*/
|
||||
private JLabel goalText;
|
||||
|
||||
/**
|
||||
* Panel containing both goal value and text.
|
||||
*/
|
||||
private JPanel goalPane;
|
||||
private JLabel subTitle;
|
||||
|
||||
/**
|
||||
* Label displaying the number of attempts the user has made of this run.
|
||||
*/
|
||||
private JLabel attemptCounter;
|
||||
|
||||
/**
|
||||
* Label displaying the number of attempts the user has made of this run in this session only.
|
||||
*/
|
||||
private JLabel sessionAttemptCounter;
|
||||
|
||||
/**
|
||||
* A list containing empty labels serving as separators.
|
||||
*/
|
||||
|
@ -156,15 +142,18 @@ public class RunPane extends JPanel {
|
|||
if (run == null) {
|
||||
throw new NullPointerException("null run");
|
||||
}
|
||||
title = new JLabel();
|
||||
goal = new JLabel();
|
||||
goalText = new JLabel();
|
||||
attemptCounter = new JLabel();
|
||||
core = new Core(run);
|
||||
graph = new Graph(run);
|
||||
history = new History(run);
|
||||
footer = new Footer(run);
|
||||
separators = new ArrayList<JLabel>();
|
||||
title = new JLabel();
|
||||
subTitle = new JLabel();
|
||||
attemptCounter = new JLabel();
|
||||
sessionAttemptCounter = new JLabel();
|
||||
core = new Core(run);
|
||||
graph = new Graph(run);
|
||||
history = new History(run);
|
||||
footer = new Footer(run);
|
||||
separators = new ArrayList<JLabel>();
|
||||
|
||||
title.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
subTitle.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
|
||||
placeComponents();
|
||||
setRun(run);
|
||||
|
@ -225,9 +214,8 @@ public class RunPane extends JPanel {
|
|||
|
||||
String property = event.getPropertyName();
|
||||
if (Run.STATE_PROPERTY.equals(property)) {
|
||||
if (run.getState() == State.READY
|
||||
|| run.getState() == State.NULL) {
|
||||
updateValues(GOAL | SEPARATOR);
|
||||
if (run.getState() == State.READY || run.getState() == State.NULL) {
|
||||
updateValues(ATTEMPTS | SEPARATOR);
|
||||
}
|
||||
} else if (Run.NAME_PROPERTY.equals(property)) {
|
||||
updateValues(TITLE);
|
||||
|
@ -239,35 +227,35 @@ public class RunPane extends JPanel {
|
|||
updateColors(SEPARATOR);
|
||||
} else if (Settings.historyRowCount.equals(property)) {
|
||||
updateValues(SEPARATOR);
|
||||
} else if (Settings.colorTime.equals(property)) {
|
||||
updateColors(GOAL);
|
||||
} else if (Settings.colorTitle.equals(property)) {
|
||||
} else if (Settings.colorTitle.equals(property)
|
||||
|| Settings.colorSubTitle.equals(property)) {
|
||||
updateColors(TITLE);
|
||||
} else if (Settings.compareMethod.equals(property)) {
|
||||
updateValues(GOAL);
|
||||
} else if (Settings.graphDisplay.equals(property)) {
|
||||
updateVisibility(GRAPH);
|
||||
} else if (Settings.footerDisplay.equals(property)) {
|
||||
updateVisibility(FOOTER);
|
||||
} else if (Settings.headerShowTitle.equals(property)) {
|
||||
updateVisibility(TITLE | GOAL);
|
||||
updateVisibility(TITLE);
|
||||
updateValues(SEPARATOR);
|
||||
} else if (Settings.headerShowGoal.equals(property)) {
|
||||
updateVisibility(TITLE | GOAL);
|
||||
} else if (Settings.headerShowSubtitle.equals(property)) {
|
||||
updateVisibility(SUBTITLE);
|
||||
updateValues(SEPARATOR);
|
||||
} else if (Settings.headerShowAttempts.equals(property)) {
|
||||
updateVisibility(ATTEMPTS);
|
||||
updateValues(SEPARATOR);
|
||||
} else if (Settings.accuracy.equals(property)
|
||||
|| Run.GOAL_PROPERTY.equals(property)) {
|
||||
updateValues(GOAL);
|
||||
|| Run.SUBTITLE_PROPERTY.equals(property)) {
|
||||
updateValues(TITLE);
|
||||
} else if (Run.ATTEMPT_COUNTER_PROPERTY.equals(property) ||
|
||||
Run.COMPLETED_ATTEMPT_COUNTER_PROPERTY.equals(property)) {
|
||||
updateValues(ATTEMPTS);
|
||||
} else if (Settings.headerTitleFont.equals(property)) {
|
||||
} else if (Settings.headerTitleFont.equals(property)
|
||||
|| Settings.headerSubTitleFont.equals(property)) {
|
||||
updateFonts(TITLE);
|
||||
} else if (Settings.coreFont.equals(property)) {
|
||||
updateFonts(ALL & ~TITLE);
|
||||
} else if (Settings.windowUserResizable.equals(property) || Settings.windowWidth.equals(property)) {
|
||||
updateSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,7 +272,7 @@ public class RunPane extends JPanel {
|
|||
if (event.getType() == TableModelEvent.INSERT
|
||||
|| event.getType() == TableModelEvent.DELETE
|
||||
|| event.getType() == TableModelEvent.UPDATE) {
|
||||
updateValues(GOAL);
|
||||
updateValues(TITLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,17 +294,14 @@ public class RunPane extends JPanel {
|
|||
* Places the sub-components within this component.
|
||||
*/
|
||||
private void placeComponents() {
|
||||
add(title,GBC.grid(0, 0).insets(3, 0, 1, 0));
|
||||
goalPane = new JPanel(new GridBagLayout()); {
|
||||
goalPane.add(goalText, GBC.grid(0, 1));
|
||||
goalPane.add(goal, GBC.grid(1, 1).insets(0, 3, 0, 0));
|
||||
goalPane.setOpaque(false);
|
||||
}
|
||||
add(attemptCounter, GBC.grid(0, 2).insets(1, 0, 1, 3).anchor(GBC.LE));
|
||||
add(createSeparator(), GBC.grid(0, 3).insets(3, 0).fill(GBC.H));
|
||||
add(history, GBC.grid(0, 4).fill(GBC.H).insets(0, 5));
|
||||
add(createSeparator(), GBC.grid(0, 5).insets(3, 0).fill(GBC.H));
|
||||
add(createSeparator(), GBC.grid(0, 7).insets(3, 0, 0, 0).fill(GBC.H));
|
||||
add(title, GBC.grid(0, 0, 2, 1).insets(3, 0, 1, 0).fill(GBC.BOTH));
|
||||
add(subTitle, GBC.grid(0, 1, 2, 1).insets(3, 0, 0, 0).fill(GBC.BOTH));
|
||||
add(sessionAttemptCounter, GBC.grid(0, 2, 1, 1).insets(1, 3, 1, 0).anchor(GBC.LINE_START));
|
||||
add(attemptCounter, GBC.grid(1, 2, 1, 1).insets(1, 0, 1, 3).anchor(GBC.LINE_END));
|
||||
add(createSeparator(), GBC.grid(0, 3, 2, 1).insets(3, 0).fill(GBC.HORIZONTAL));
|
||||
add(history, GBC.grid(0, 4, 2, 1).fill(GBC.HORIZONTAL).anchor(GBC.NORTH).insets(0, 5));
|
||||
add(createSeparator(), GBC.grid(0, 5, 2, 1).insets(3, 0).fill(GBC.HORIZONTAL));
|
||||
add(createSeparator(), GBC.grid(0, 7, 2, 1).insets(3, 0, 0, 0).fill(GBC.HORIZONTAL));
|
||||
|
||||
updateVisibility(ALL);
|
||||
}
|
||||
|
@ -328,19 +313,9 @@ public class RunPane extends JPanel {
|
|||
* @param identifier - one of the constant update identifier.
|
||||
*/
|
||||
private void updateValues(int identifier) {
|
||||
if ((identifier & GOAL) == GOAL) {
|
||||
if (run.getGoal() == null || run.getGoal().equals("")) {
|
||||
Time time = run.getTime(Segment.SET);
|
||||
goal.setText("" + (time == null ? "???" : time));
|
||||
} else {
|
||||
goal.setText(run.getGoal());
|
||||
}
|
||||
}
|
||||
if ((identifier & TEXT) == TEXT) {
|
||||
goalText.setText("" + Language.GOAL);
|
||||
}
|
||||
if ((identifier & TITLE) == TITLE) {
|
||||
title.setText("" + run.getName());
|
||||
title.setText("<html><div style='text-align: center;'>" + sanitizeTitleString(run.getName()) + "</div></html>");
|
||||
subTitle.setText(run.getSubTitle());
|
||||
}
|
||||
if ((identifier & ATTEMPTS) == ATTEMPTS) {
|
||||
int attempts = run.getNumberOfAttempts();
|
||||
|
@ -351,14 +326,17 @@ public class RunPane extends JPanel {
|
|||
attemptCounter.setText(String.format("%d", attempts));
|
||||
else
|
||||
attemptCounter.setText(String.format("%d / %d", completedAttempts, attempts));
|
||||
|
||||
int sessionAttempts = run.getSessionAttempts();
|
||||
sessionAttemptCounter.setText(String.format("%d", sessionAttempts));
|
||||
}
|
||||
if ((identifier & SEPARATOR) == SEPARATOR) {
|
||||
boolean hdTitle = Settings.headerShowGoal.get();
|
||||
boolean hdGoal = Settings.headerShowTitle.get();
|
||||
boolean hdTitle = Settings.headerShowSubtitle.get();
|
||||
boolean hdSubtitle = Settings.headerShowTitle.get();
|
||||
boolean hdAttempts = Settings.headerShowAttempts.get();
|
||||
boolean hsRows = history.getRowCount() > 0;
|
||||
|
||||
separators.get(0).setVisible(hdTitle || hdGoal || hdAttempts);
|
||||
separators.get(0).setVisible(hdTitle || hdSubtitle || hdAttempts);
|
||||
separators.get(1).setVisible(hsRows);
|
||||
}
|
||||
}
|
||||
|
@ -370,14 +348,9 @@ public class RunPane extends JPanel {
|
|||
* @param identifier - one of the constant update identifier.
|
||||
*/
|
||||
private void updateColors(int identifier) {
|
||||
if ((identifier & GOAL) == GOAL) {
|
||||
goal.setForeground(Settings.colorTime.get());
|
||||
}
|
||||
if ((identifier & TEXT) == TEXT) {
|
||||
goalText.setForeground(Settings.colorForeground.get());
|
||||
}
|
||||
if ((identifier & TITLE) == TITLE) {
|
||||
title.setForeground(Settings.colorTitle.get());
|
||||
subTitle.setForeground(Settings.colorSubTitle.get());
|
||||
}
|
||||
if ((identifier & BACKGROUND) == BACKGROUND) {
|
||||
setBackground(Settings.colorBackground.get());
|
||||
|
@ -390,7 +363,8 @@ public class RunPane extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
attemptCounter.setForeground(Color.WHITE);
|
||||
attemptCounter.setForeground(Settings.colorForeground.get());
|
||||
sessionAttemptCounter.setForeground(Settings.colorForeground.get());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -402,15 +376,11 @@ public class RunPane extends JPanel {
|
|||
private void updateFonts(int identifier) {
|
||||
if ((identifier & TITLE) == TITLE) {
|
||||
title.setFont(Settings.headerTitleFont.get());
|
||||
}
|
||||
if ((identifier & GOAL) == GOAL) {
|
||||
goal.setFont(Settings.coreFont.get());
|
||||
}
|
||||
if ((identifier & TEXT) == TEXT) {
|
||||
goalText.setFont(Settings.coreFont.get());
|
||||
subTitle.setFont(Settings.headerSubTitleFont.get());
|
||||
}
|
||||
if ((identifier & ATTEMPTS) == ATTEMPTS) {
|
||||
attemptCounter.setFont(Settings.coreFont.get());
|
||||
sessionAttemptCounter.setFont(Settings.coreFont.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -424,32 +394,32 @@ public class RunPane extends JPanel {
|
|||
if ((identifier & GRAPH) == GRAPH) {
|
||||
if (Settings.graphDisplay.get()) {
|
||||
remove(core);
|
||||
add(core, GBC.grid(0, 6).insets(0, 5).fill(GBC.H));
|
||||
add(graph, GBC.grid(0, 8).fill(GBC.B).insets(0, 0, 3, 0)
|
||||
add(core, GBC.grid(0, 6, 2, 1).insets(0, 5).fill(GBC.HORIZONTAL));
|
||||
add(graph, GBC.grid(0, 8, 2, 1).fill(GBC.BOTH).insets(0, 0, 3, 0)
|
||||
.weight(1.0, 1.0));
|
||||
} else {
|
||||
remove(graph);
|
||||
remove(core);
|
||||
add(core, GBC.grid(0, 6).insets(0, 5).fill(GBC.H)
|
||||
add(core, GBC.grid(0, 6, 2, 1).insets(0, 5).fill(GBC.HORIZONTAL)
|
||||
.weight(1.0, 1.0));
|
||||
}
|
||||
}
|
||||
if ((identifier & FOOTER) == FOOTER) {
|
||||
if (Settings.footerDisplay.get()) {
|
||||
add(footer, GBC.grid(0, 9).insets(0, 3).fill(GBC.H));
|
||||
add(footer, GBC.grid(0, 9, 2, 1).insets(0, 3).fill(GBC.HORIZONTAL));
|
||||
} else {
|
||||
remove(footer);
|
||||
}
|
||||
}
|
||||
if ((identifier & GOAL) == GOAL) {
|
||||
if (Settings.headerShowGoal.get()) {
|
||||
if ((identifier & SUBTITLE) == SUBTITLE) {
|
||||
if (Settings.headerShowSubtitle.get()) {
|
||||
if (Settings.headerShowTitle.get()) {
|
||||
add(goalPane, GBC.grid(0, 1));
|
||||
add(subTitle, GBC.grid(0, 1, 2, 1));
|
||||
} else {
|
||||
add(goalPane, GBC.grid(0, 1).insets(3, 0, 0, 0));
|
||||
add(subTitle, GBC.grid(0, 1, 2, 1).insets(3, 0, 0, 0));
|
||||
}
|
||||
} else {
|
||||
remove(goalPane);
|
||||
remove(subTitle);
|
||||
}
|
||||
}
|
||||
if ((identifier & TITLE) == TITLE) {
|
||||
|
@ -457,9 +427,20 @@ public class RunPane extends JPanel {
|
|||
}
|
||||
if ((identifier & ATTEMPTS) == ATTEMPTS) {
|
||||
attemptCounter.setVisible(Settings.headerShowAttempts.get());
|
||||
sessionAttemptCounter.setVisible(Settings.headerShowAttempts.get());
|
||||
}
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void updateSize() {
|
||||
}
|
||||
|
||||
private String sanitizeTitleString(String title) {
|
||||
return title
|
||||
.replace("<", "<")
|
||||
.replace(">", ">")
|
||||
.replace("\n", "<br/>");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.fenix.utils;
|
||||
|
||||
import org.fenix.llanfair.config.Settings;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class UserSettings {
|
||||
|
@ -31,7 +33,19 @@ public class UserSettings {
|
|||
* Returns the path to the location where splits can be saved in. This is
|
||||
* located as a subdirectory within the user settings directory.
|
||||
*/
|
||||
public static String getSplitsPath() {
|
||||
public static String getDefaultSplitsPath() {
|
||||
return splitsPath.getPath();
|
||||
}
|
||||
|
||||
public static String getSplitsPath(File selectedFile) {
|
||||
if (selectedFile != null)
|
||||
return selectedFile.toString();
|
||||
else {
|
||||
if (Settings.useDefaultSplitsPath.get())
|
||||
return UserSettings.getDefaultSplitsPath();
|
||||
else
|
||||
return Settings.customSplitsPath.get();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -99,7 +99,12 @@ public class Configuration implements Serializable {
|
|||
|
||||
Object old = subMap.get(key);
|
||||
subMap.put(key, value);
|
||||
this.pcSupport.firePropertyChange(section + "." + key, old, value);
|
||||
String propertyName;
|
||||
if (section.length() == 0)
|
||||
propertyName = key;
|
||||
else
|
||||
propertyName = section + "." + key;
|
||||
this.pcSupport.firePropertyChange(propertyName, old, value);
|
||||
} else {
|
||||
throw new NullPointerException("Null key");
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class BorderlessFrame extends JFrame implements MouseListener, MouseMotio
|
|||
}
|
||||
|
||||
public void mousePressed(MouseEvent event) {
|
||||
if(this.getCursor().getType() != 0) {
|
||||
if(this.getCursor().getType() != Cursor.DEFAULT_CURSOR && this.isResizable()) {
|
||||
this.doResize = true;
|
||||
this.startFrameX = this.getX();
|
||||
this.startFrameY = this.getY();
|
||||
|
@ -79,31 +79,31 @@ public class BorderlessFrame extends JFrame implements MouseListener, MouseMotio
|
|||
int height = this.getHeight();
|
||||
int frameX = this.startFrameX;
|
||||
int frameY = this.startFrameY;
|
||||
if(type == 5) {
|
||||
if(type == Cursor.SE_RESIZE_CURSOR) {
|
||||
width = event.getXOnScreen() - this.startFrameX;
|
||||
height = event.getYOnScreen() - this.startFrameY;
|
||||
} else if(type == 11) {
|
||||
} else if(type == Cursor.E_RESIZE_CURSOR) {
|
||||
width = event.getXOnScreen() - this.startFrameX;
|
||||
} else if(type == 7) {
|
||||
} else if(type == Cursor.NE_RESIZE_CURSOR) {
|
||||
width = event.getXOnScreen() - this.startFrameX;
|
||||
height = this.startFrameY + this.startHeight - event.getYOnScreen();
|
||||
frameY = event.getYOnScreen();
|
||||
} else if(type == 8) {
|
||||
} else if(type == Cursor.N_RESIZE_CURSOR) {
|
||||
height = this.startFrameY + this.startHeight - event.getYOnScreen();
|
||||
frameY = event.getYOnScreen();
|
||||
} else if(type == 6) {
|
||||
} else if(type == Cursor.NW_RESIZE_CURSOR) {
|
||||
width = this.startFrameX + this.startWidth - event.getXOnScreen();
|
||||
height = this.startFrameY + this.startHeight - event.getYOnScreen();
|
||||
frameY = event.getYOnScreen();
|
||||
frameX = event.getXOnScreen();
|
||||
} else if(type == 10) {
|
||||
} else if(type == Cursor.W_RESIZE_CURSOR) {
|
||||
width = this.startFrameX + this.startWidth - event.getXOnScreen();
|
||||
frameX = event.getXOnScreen();
|
||||
} else if(type == 4) {
|
||||
} else if(type == Cursor.SW_RESIZE_CURSOR) {
|
||||
width = this.startFrameX + this.startWidth - event.getXOnScreen();
|
||||
height = event.getYOnScreen() - this.startFrameY;
|
||||
frameX = event.getXOnScreen();
|
||||
} else if(type == 9) {
|
||||
} else if(type == Cursor.S_RESIZE_CURSOR) {
|
||||
height = event.getYOnScreen() - this.startFrameY;
|
||||
}
|
||||
|
||||
|
@ -130,28 +130,32 @@ public class BorderlessFrame extends JFrame implements MouseListener, MouseMotio
|
|||
int lowSnapY = this.getY() + this.snapSize;
|
||||
int mouseX = event.getXOnScreen();
|
||||
int mouseY = event.getYOnScreen();
|
||||
if(mouseX > farSnapX) {
|
||||
if(mouseY > farSnapY) {
|
||||
this.changeCursor(5);
|
||||
} else if(mouseY < lowSnapY) {
|
||||
this.changeCursor(7);
|
||||
if (this.isResizable()) {
|
||||
if (mouseX > farSnapX) {
|
||||
if (mouseY > farSnapY) {
|
||||
this.changeCursor(Cursor.SE_RESIZE_CURSOR);
|
||||
} else if (mouseY < lowSnapY) {
|
||||
this.changeCursor(Cursor.NE_RESIZE_CURSOR);
|
||||
} else {
|
||||
this.changeCursor(Cursor.E_RESIZE_CURSOR);
|
||||
}
|
||||
} else if (mouseX < lowSnapX) {
|
||||
if (mouseY < lowSnapY) {
|
||||
this.changeCursor(Cursor.NW_RESIZE_CURSOR);
|
||||
} else if (mouseY > farSnapY) {
|
||||
this.changeCursor(Cursor.SW_RESIZE_CURSOR);
|
||||
} else {
|
||||
this.changeCursor(Cursor.W_RESIZE_CURSOR);
|
||||
}
|
||||
} else if (mouseY > farSnapY) {
|
||||
this.changeCursor(Cursor.S_RESIZE_CURSOR);
|
||||
} else if (mouseY < lowSnapY) {
|
||||
this.changeCursor(Cursor.N_RESIZE_CURSOR);
|
||||
} else {
|
||||
this.changeCursor(11);
|
||||
this.changeCursor(Cursor.DEFAULT_CURSOR);
|
||||
}
|
||||
} else if(mouseX < lowSnapX) {
|
||||
if(mouseY < lowSnapY) {
|
||||
this.changeCursor(6);
|
||||
} else if(mouseY > farSnapY) {
|
||||
this.changeCursor(4);
|
||||
} else {
|
||||
this.changeCursor(10);
|
||||
}
|
||||
} else if(mouseY > farSnapY) {
|
||||
this.changeCursor(9);
|
||||
} else if(mouseY < lowSnapY) {
|
||||
this.changeCursor(8);
|
||||
} else {
|
||||
this.changeCursor(0);
|
||||
this.changeCursor(Cursor.DEFAULT_CURSOR);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,21 +9,6 @@ import java.awt.GridBagConstraints;
|
|||
import java.awt.Insets;
|
||||
|
||||
public class GBC extends GridBagConstraints {
|
||||
public static final int LS = 21;
|
||||
public static final int LE = 22;
|
||||
public static final int PS = 19;
|
||||
public static final int PE = 20;
|
||||
public static final int BL = 512;
|
||||
public static final int BT = 768;
|
||||
public static final int FLS = 23;
|
||||
public static final int FLE = 24;
|
||||
public static final int LLS = 25;
|
||||
public static final int LLE = 26;
|
||||
public static final int H = 2;
|
||||
public static final int V = 3;
|
||||
public static final int C = 10;
|
||||
public static final int B = 1;
|
||||
|
||||
public GBC() {
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Settings > Generic
|
||||
setting_alwaysOnTop = Always on Top
|
||||
setting_useDefaultSplitsPath = Use Default Splits Directory
|
||||
setting_customSplitsPath = Splits Directory
|
||||
setting_language = Language
|
||||
setting_viewerLanguage = Viewer's Language
|
||||
setting_recentFiles =
|
||||
|
@ -9,16 +11,23 @@ setting_compareMethod = Compare Method
|
|||
setting_accuracy = Accuracy
|
||||
setting_locked =
|
||||
setting_warnOnReset = Warn on Reset if better times
|
||||
setting_windowUserResizable = User Resizable
|
||||
setting_windowWidth = Fixed Width (Pixels)
|
||||
setting_maxRecentFiles = Recent Files Limit
|
||||
|
||||
# Settings > Color
|
||||
setting_color_background = Background
|
||||
setting_color_foreground = Foreground
|
||||
setting_color_time = Time
|
||||
setting_color_timer = Timer
|
||||
setting_color_timeGained = Time Gained
|
||||
setting_color_timeLost = Time Lost
|
||||
setting_color_negativeTime = Negative Time
|
||||
setting_color_timeGainedWhileAhead = Time Gained (While Ahead)
|
||||
setting_color_timeLostWhileAhead = Time Lost (While Ahead)
|
||||
setting_color_timeGainedWhileBehind = Time Gained (While Behind)
|
||||
setting_color_timeLostWhileBehind = Time Lost (While Behind)
|
||||
setting_color_newRecord = New Record
|
||||
setting_color_title = Title
|
||||
setting_color_subTitle = Sub Title
|
||||
setting_color_highlight = Highlight
|
||||
setting_color_separators = Separators
|
||||
|
||||
|
@ -31,14 +40,14 @@ setting_hotkey_reset = Reset
|
|||
setting_hotkey_stop = Stop
|
||||
setting_hotkey_pause = Pause
|
||||
setting_hotkey_lock = Lock / Unlock
|
||||
GLOBAL_HOTKEYS_WARNING = <html><div style="width: 300px;">Key event hook registration failed. <strong>You will not be able to set or use any of your hotkeys until this is fixed!</strong> To fix this, you will probably need to grant Llanfair some extended accessibility permissions from your operating system and then restart Llanfair.</div></html>
|
||||
GLOBAL_HOTKEYS_STARTUP_WARNING = Key event hook registration failed. You will not be able to set or use any of your hotkeys until this is fixed! To fix this, you will probably need to grant Llanfair some extended accessibility permissions from your operating system and then restart Llanfair.
|
||||
GLOBAL_HOTKEYS_STARTUP_ERROR = <html><div style="width: 300px;">Key event hook registration failed.<br /><br />Llanfair requires global access to key events which (depending on your OS) might require some extra security or accessibility permissions. Click the "OK" button to close Llanfair. You will need to grant the required permissions before you will be able to use Llanfair.<br /><br />Your OS might have just now popped up some sort of notification which will allow you to quickly grant Llanfair the required permissions.</div></html>
|
||||
|
||||
# Settings > Header
|
||||
setting_header_goal = Display Goal
|
||||
setting_header_subTitle = Display Sub Title
|
||||
setting_header_title = Display Run Title
|
||||
setting_header_showAttempts = Display Attempt Counter
|
||||
setting_header_titleFont = Main Title
|
||||
setting_header_subTitleFont = Sub Title
|
||||
|
||||
# Settings > History
|
||||
setting_history_rowCount = Number of Rows
|
||||
|
@ -80,6 +89,8 @@ setting_footer_verbose = Show More Info
|
|||
setting_footer_bestTime = Best Time
|
||||
setting_footer_multiline = Display on Two Lines
|
||||
setting_footer_deltaLabels = Delta Labels
|
||||
setting_footer_sumOfBest = Sum of Best
|
||||
setting_footer_worldRecord = World Record
|
||||
|
||||
# Accuracy
|
||||
accuracy_seconds = Seconds
|
||||
|
@ -116,6 +127,9 @@ menuItem_exit = Exit
|
|||
error_read_file = "{0}" isn't a valid Llanfair run or you do not have permission to read it.
|
||||
error_write_file = You do not have permission to write in that folder.
|
||||
error_import_run = "{0}" isn't a recognized run file.
|
||||
error_window_width = Window Width must be a positive integer.
|
||||
error_max_recent_files = Recent files limit must be a positive integer.
|
||||
error_splits_path = Splits Directory cannot be read or does not exist.
|
||||
|
||||
# Actions
|
||||
action_accept =
|
||||
|
@ -128,7 +142,7 @@ TIMER = Timer
|
|||
FOOTER = Footer
|
||||
MISC = Miscellaneous
|
||||
USE_MAIN_FONT = Use Main Timer's font
|
||||
LB_GOAL = Goal
|
||||
LB_SUBTITLE = Sub Title
|
||||
ICON = Icon
|
||||
COLORS = Colors
|
||||
|
||||
|
@ -151,17 +165,18 @@ MERGE_NONE = Don't Merge
|
|||
TT_HS_OFFSET = Segment, relative to the current one, to follow while scrolling e.g. with +1 the history will scroll while always displaying the next segment (+1 from the current.)
|
||||
|
||||
# Core
|
||||
LB_CR_BEST = Be:
|
||||
LB_CR_SEGMENT = Se:
|
||||
LB_CR_SPLIT = Sp:
|
||||
LB_CR_BEST = Best:
|
||||
LB_CR_SEGMENT = PB:
|
||||
LB_CR_SPLIT = Split:
|
||||
|
||||
# Footer
|
||||
LB_FT_BEST = P.Be:
|
||||
LB_FT_DELTA = Delta:
|
||||
LB_FT_DELTA_BEST = B.Delta:
|
||||
LB_FT_BEST = Prev. Best:
|
||||
LB_FT_DELTA = vs PB:
|
||||
LB_FT_DELTA_BEST = vs Best:
|
||||
LB_FT_LIVE = Live:
|
||||
LB_FT_SEGMENT = P.Se:
|
||||
LB_FT_SPLIT = P.Sp:
|
||||
LB_FT_SEGMENT = Prev. Segment:
|
||||
LB_FT_SPLIT = Prev. Split:
|
||||
LB_FT_SUM_OF_BEST = Sum of Best:
|
||||
|
||||
# Messages
|
||||
ICON_TOO_BIG =
|
||||
|
@ -200,13 +215,13 @@ HUNDREDTH = 100th of a second
|
|||
ACCEPT = Accept
|
||||
APPLICATION = Application
|
||||
BEST = Best Segment
|
||||
BOLD = Bold
|
||||
CANCEL = Cancel
|
||||
COMPARE_METHOD = Compare Method
|
||||
COMPONENTS = Components
|
||||
DISABLED = <Disabled>
|
||||
EDITING = Editing Run
|
||||
ERROR = Error
|
||||
GOAL = Goal:
|
||||
IMAGE = Image
|
||||
INPUTS = Hotkeys
|
||||
MAX_ORDINATE = Max Ord.:
|
||||
|
@ -215,10 +230,12 @@ RUN_TITLE = Run Title
|
|||
RUN_FILE_FILTER = Llanfair Run Splits
|
||||
SEGMENT = Time (Segment)
|
||||
SEGMENTS = Segments
|
||||
SELECT_SPLITS_DIR = Choose ...
|
||||
SPLIT = Split:
|
||||
TIME = Time (Split)
|
||||
UNTITLED = <untitled>
|
||||
WARNING = Warning
|
||||
WINDOW_SIZE = Window Size
|
||||
|
||||
# 1.4
|
||||
INCREMENT =
|
||||
|
|
|
@ -2,4 +2,4 @@ website = https://github.com/gered/Llanfair
|
|||
|
||||
# donate = https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=EPTSR32CQTHBY
|
||||
|
||||
about = <html><div width=280 align=justify>Llanfair 1.5<br><br>This application is distributed under the creative commons licence <b>BY-NC-SA</b> which stipulates that you must abide by the following rules: the application must be redistributed under the same licence, the name(s) of the author(s) must always be cited and you cannot make a commercial use of it.<br><br>NOTE: This version of Llanfair is a fork of the original by Xunkar. Any issues with this version should not be brought to him.<br><br><b>Author</b><br> Xunkar<br> Gered King (minor enhancements only)<br><br><b>Localization provided by</b><br> Kokarn (Swedish)<br> MickeyG (Dutch)<br> Vulpone (German)<br> Xunkar (French)<br><br><b>Special Thanks to</b><br> ChristosOwen & Ketran</html>
|
||||
about = <html><div width=280 align=justify>Llanfair 1.6 (Development Snapshot)<br><br>This application is distributed under the creative commons licence <b>BY-NC-SA</b> which stipulates that you must abide by the following rules: the application must be redistributed under the same licence, the name(s) of the author(s) must always be cited and you cannot make a commercial use of it.<br><br>NOTE: This version of Llanfair is a fork of the original by Xunkar. Any issues with this version should not be brought to him.<br><br><b>Author</b><br> Xunkar<br> Gered King (minor enhancements only)<br><br><b>Localization provided by</b><br> Kokarn (Swedish)<br> MickeyG (Dutch)<br> Vulpone (German)<br> Xunkar (French)<br><br><b>Special Thanks to</b><br> ChristosOwen & Ketran</html>
|
Reference in a new issue