little style cleanups and use tabs for indentation (read below...)

the only reason i'm bothering with doing something semi-drastic like
changing the entire indentation style from spaces to tabs is because
i prefer tabs, but more importantly, since all my editors are configured
to use tabs, i will likely accidentally introduce tab indenting
somewhere without realizing it and then everything would end up being
inconsistent. would rather just solve this problem right now. :)
This commit is contained in:
Gered 2015-11-29 23:54:54 -05:00
parent 378024c3b0
commit 5aebf5eefd
32 changed files with 7641 additions and 7699 deletions

View file

@ -2,23 +2,6 @@ package org.fenix.llanfair;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ResourceBundle;
import javax.swing.Action;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import org.fenix.llanfair.config.Settings;
import org.fenix.llanfair.dialog.EditRun;
import org.fenix.llanfair.dialog.EditSettings;
@ -26,6 +9,14 @@ import org.fenix.llanfair.extern.WSplit;
import org.fenix.utils.about.AboutDialog;
import org.jnativehook.keyboard.NativeKeyEvent;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ResourceBundle;
/**
* Regroups all actions, in the meaning of {@link Action}, used by Llanfair.
* All inputs and menu items callbacks are processed by this delegate to
@ -35,410 +26,410 @@ import org.jnativehook.keyboard.NativeKeyEvent;
* @version 1.0
*/
final class Actions {
private static final long GHOST_DELAY = 300L;
private static ResourceBundle BUNDLE = null;
private Llanfair master;
private File file;
private JFileChooser fileChooser;
private volatile long lastUnsplit;
private volatile long lastSkip;
/**
* Creates a new delegate. This constructor is package private since it
* only need be called by the main class.
*
* @param owner the Llanfair instance owning this delegate
*/
Actions( Llanfair owner ) {
assert ( owner != null );
master = owner;
file = null;
fileChooser = new JFileChooser( "." );
lastUnsplit = 0L;
lastSkip = 0L;
if ( BUNDLE == null ) {
BUNDLE = Llanfair.getResources().getBundle( "llanfair" );
}
}
private static final long GHOST_DELAY = 300L;
private static ResourceBundle BUNDLE = null;
/**
* Processes the given native key event. This method must be called from
* a thread to prevent possible deadlock.
*
* @param event the native key event to process
*/
void process( NativeKeyEvent event ) {
assert ( event != null );
int keyCode = event.getKeyCode();
Run run = master.getRun();
Run.State state = run.getState();
if ( keyCode == Settings.KEY_SPLT.get() ) {
split();
} else if ( keyCode == Settings.KEY_RSET.get() ) {
reset();
} else if ( keyCode == Settings.KEY_USPL.get() ) {
unsplit();
} else if ( keyCode == Settings.KEY_SKIP.get() ) {
skip();
} else if ( keyCode == Settings.KEY_STOP.get() ) {
if ( state == Run.State.ONGOING ) {
run.stop();
}
} else if ( keyCode == Settings.KEY_PAUS.get() ) {
if ( state == Run.State.ONGOING ) {
run.pause();
} else if ( state == Run.State.PAUSED ) {
run.resume();
}
} else if ( keyCode == Settings.KEY_LOCK.get() ) {
master.setIgnoreNativeInputs( !master.ignoresNativeInputs() );
}
}
/**
* Processes the given action event. It is assumed here that the action
* event is one triggered by a menu item. This method must be called from
* a thread to prevent possible deadlock.
*
* @param event the event to process
*/
void process( ActionEvent event ) {
assert ( event != null );
Run run = master.getRun();
MenuItem source = ( MenuItem ) event.getSource();
if ( source == MenuItem.EDIT ) {
EditRun dialog = new EditRun( run );
dialog.display( true, master );
} else if ( source == MenuItem.NEW ) {
if ( confirmOverwrite() ) {
master.setRun( new Run() );
}
} else if ( source == MenuItem.OPEN ) {
open( null );
} else if ( source == MenuItem.OPEN_RECENT ) {
open( new File( event.getActionCommand() ) );
} else if ( source == MenuItem.IMPORT ) {
imprt();
} else if ( source == MenuItem.SAVE ) {
run.saveLiveTimes( !run.isPersonalBest() );
run.reset();
save();
} else if ( source == MenuItem.SAVE_AS ) {
file = null;
save();
} else if ( source == MenuItem.RESET ) {
reset();
} else if ( source == MenuItem.LOCK ) {
master.setIgnoreNativeInputs( true );
} else if ( source == MenuItem.UNLOCK ) {
master.setIgnoreNativeInputs( false );
} else if ( source == MenuItem.SETTINGS ) {
EditSettings dialog = new EditSettings();
dialog.display( true, master );
} else if ( source == MenuItem.ABOUT ) {
about();
} else if ( source == MenuItem.EXIT ) {
if ( confirmOverwrite() ) {
master.dispose();
}
}
}
/**
* Performs a split or starts the run if it is ready. Can also resume a
* paused run in case the run is segmented.
*/
private void split() {
Run run = master.getRun();
Run.State state = run.getState();
if ( state == Run.State.ONGOING ) {
long milli = System.nanoTime() / 1000000L;
long start = run.getSegment( run.getCurrent() ).getStartTime();
if ( milli - start > GHOST_DELAY ) {
run.split();
}
} else if ( state == Run.State.READY ) {
run.start();
} else if ( state == Run.State.PAUSED && run.isSegmented() ) {
run.resume();
}
}
/**
* Resets the current run to a ready state. If the user asked to be warned
* a pop-up will ask confirmation in case some live times are better.
*/
private void reset() {
Run run = master.getRun();
if ( run.getState() != Run.State.NULL ) {
if ( !Settings.GNR_WARN.get() || confirmOverwrite() ) {
run.reset();
}
}
}
/**
* Performs an "unsplit" on the current run. If a split has been made, it
* is canceled and the time that passed after said split is added back to
* the timer, as if the split had not taken place.
*/
private void unsplit() {
Run run = master.getRun();
Run.State state = run.getState();
if ( state == Run.State.ONGOING || state == Run.State.STOPPED ) {
long milli = System.nanoTime() / 1000000L;
if ( milli - lastUnsplit > GHOST_DELAY ) {
lastUnsplit = milli;
run.unsplit();
}
}
}
/**
* Skips the current split in the run. Skipping a split sets an undefined
* time for the current segment and merges the live time of the current
* segment with the following one.
*/
private void skip() {
Run run = master.getRun();
if ( run.getState() == Run.State.ONGOING ) {
long milli = System.nanoTime() / 1000000L;
if ( milli - lastSkip > GHOST_DELAY ) {
lastSkip = milli;
run.skip();
}
}
}
/**
* Displays a dialog to let the user select a file. The user is able to
* cancel this action, which results in a {@code null} being returned.
*
* @return a file selected by the user or {@code null} if he canceled
*/
private File selectFile() {
int option = fileChooser.showDialog( master,
Language.action_accept.get() );
if ( option == JFileChooser.APPROVE_OPTION ) {
return fileChooser.getSelectedFile();
} else {
return null;
}
}
/**
* Asks the user to confirm the discard of the current run. The popup
* window will only trigger if the current run has not been saved after
* some editing or if the run presents better times.
*
* @return {@code true} if the user wants to discard the run
*/
private boolean confirmOverwrite() {
boolean before = master.ignoresNativeInputs();
master.setIgnoreNativeInputs( true );
Run run = master.getRun();
boolean betterRun = run.isPersonalBest();
boolean betterSgt = run.hasSegmentsBest();
private Llanfair master;
if ( betterRun || betterSgt ) {
String message = betterRun
? Language.WARN_BETTER_RUN.get()
: Language.WARN_BETTER_TIMES.get();
int option = JOptionPane.showConfirmDialog( master, message,
Language.WARNING.get(), JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE );
if ( option == JOptionPane.CANCEL_OPTION ) {
master.setIgnoreNativeInputs( false );
return false;
} else if ( option == JOptionPane.YES_OPTION ) {
run.saveLiveTimes( !betterRun );
run.reset();
save();
}
}
master.setIgnoreNativeInputs( before );
return true;
}
/**
* Opens the given file. If the file is {@code null}, the user is asked
* to select one. Before anything is done, the user is also asked for
* a confirmation if the current run has not been saved.
*
* @param file the file to open
*/
void open( File file ) {
if ( !confirmOverwrite() ) {
return;
}
if ( file == null ) {
if ( ( file = selectFile() ) == null ) {
return;
}
}
this.file = file;
String name = file.getName();
try {
open();
} catch ( Exception ex ) {
master.showError( Language.error_read_file.get( name ) );
this.file = null;
}
}
/**
* Opens the currently selected file. This method will first try to open
* the file using the new method (XStream XML) and if it fails will try to
* use the legacy method (Java ObjectStream.)
*
* @throws Exception if the reading operation fails
*/
private void open() throws Exception {
BufferedInputStream in = null;
try {
in = new BufferedInputStream( new FileInputStream( file ) );
try {
in.mark( Integer.MAX_VALUE );
xmlRead( in );
} catch ( Exception ex ) {
in.reset();
legacyRead( new ObjectInputStream( in ) );
}
MenuItem.recentlyOpened( "" + file );
} catch ( Exception ex ) {
throw ex;
} finally {
try {
in.close();
} catch ( Exception ex ) {
//$FALL-THROUGH$
}
}
}
/**
* Reads a stream on a run file using the new (since 1.5) XML method. This
* method will not be able to read a legacy run file and will throw an
* exception if confronted with such run file.
*
* @param in the input stream on the run file
*/
private void xmlRead( InputStream in ) {
XStream xml = new XStream( new DomDriver() );
master.setRun( ( Run ) xml.fromXML( in ) );
}
/**
* Reads a stream on a run file using the legacy Java method. This method
* might fail if the given file is not a Llanfair run.
*
* @param in an input stream on the run file
* @throws Exception if the stream cannot be read
*/
private void legacyRead( ObjectInputStream in ) throws Exception {
master.setRun( ( Run ) in.readObject() );
try {
Settings.GNR_SIZE.set( ( Dimension ) in.readObject(), true );
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
/**
* Saves the currently opened run to the currently selected file. If no
* file has been selected, the user is asked for one.
*/
private void save() {
if ( file == null ) {
if ( ( file = selectFile() ) == null ) {
return;
}
}
Settings.GNR_COOR.set( master.getLocationOnScreen(), true );
Settings.GNR_SIZE.set( master.getSize(), true );
String name = file.getName();
BufferedOutputStream out = null;
try {
XStream xml = new XStream( new DomDriver() );
out = new BufferedOutputStream( new FileOutputStream( file ) );
xml.toXML( master.getRun(), out );
} catch ( Exception ex ) {
master.showError( Language.error_write_file.get( name ) );
} finally {
try {
out.close();
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
}
/**
* Imports a run from another timer application. If no file has been
* selected, the user is asked for one. As of now, only WSplit run files
* are supported.
*/
private void imprt() {
if ( !confirmOverwrite() ) {
return;
}
if ( file == null ) {
if ( ( file = selectFile() ) == null ) {
return;
}
}
String name = file.getName();
BufferedReader in = null;
try {
in = new BufferedReader( new FileReader( file ) );
WSplit.parse( master, in );
} catch ( Exception ex ) {
master.showError( Language.error_import_run.get( name ) );
} finally {
try {
in.close();
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
}
/**
* Displays the "about" dialog. The dialog displays the version of Llanfair,
* the creative commons licence, the credits of development, a link to
* Llanfair's website and a link to donate.
*/
private void about() {
AboutDialog dialog = new AboutDialog(
master, Language.title_about.get() );
dialog.setMessage( BUNDLE.getString( "about" ));
try {
dialog.setWebsite( new URL( BUNDLE.getString( "website" ) ) );
} catch ( MalformedURLException ex ) {
// $FALL-THROUGH$
}
try {
dialog.setDonateLink( new URL( BUNDLE.getString( "donate" ) ),
Llanfair.getResources().getIcon( "donate" ) );
} catch ( MalformedURLException ex ) {
// $FALL-THROUGH$
}
dialog.display();
}
private File file;
private JFileChooser fileChooser;
private volatile long lastUnsplit;
private volatile long lastSkip;
/**
* Creates a new delegate. This constructor is package private since it
* only need be called by the main class.
*
* @param owner the Llanfair instance owning this delegate
*/
Actions( Llanfair owner ) {
assert ( owner != null );
master = owner;
file = null;
fileChooser = new JFileChooser( "." );
lastUnsplit = 0L;
lastSkip = 0L;
if ( BUNDLE == null ) {
BUNDLE = Llanfair.getResources().getBundle( "llanfair" );
}
}
/**
* Processes the given native key event. This method must be called from
* a thread to prevent possible deadlock.
*
* @param event the native key event to process
*/
void process( NativeKeyEvent event ) {
assert ( event != null );
int keyCode = event.getKeyCode();
Run run = master.getRun();
Run.State state = run.getState();
if ( keyCode == Settings.KEY_SPLT.get() ) {
split();
} else if ( keyCode == Settings.KEY_RSET.get() ) {
reset();
} else if ( keyCode == Settings.KEY_USPL.get() ) {
unsplit();
} else if ( keyCode == Settings.KEY_SKIP.get() ) {
skip();
} else if ( keyCode == Settings.KEY_STOP.get() ) {
if ( state == Run.State.ONGOING ) {
run.stop();
}
} else if ( keyCode == Settings.KEY_PAUS.get() ) {
if ( state == Run.State.ONGOING ) {
run.pause();
} else if ( state == Run.State.PAUSED ) {
run.resume();
}
} else if ( keyCode == Settings.KEY_LOCK.get() ) {
master.setIgnoreNativeInputs( !master.ignoresNativeInputs() );
}
}
/**
* Processes the given action event. It is assumed here that the action
* event is one triggered by a menu item. This method must be called from
* a thread to prevent possible deadlock.
*
* @param event the event to process
*/
void process( ActionEvent event ) {
assert ( event != null );
Run run = master.getRun();
MenuItem source = ( MenuItem ) event.getSource();
if ( source == MenuItem.EDIT ) {
EditRun dialog = new EditRun( run );
dialog.display( true, master );
} else if ( source == MenuItem.NEW ) {
if ( confirmOverwrite() ) {
master.setRun( new Run() );
}
} else if ( source == MenuItem.OPEN ) {
open( null );
} else if ( source == MenuItem.OPEN_RECENT ) {
open( new File( event.getActionCommand() ) );
} else if ( source == MenuItem.IMPORT ) {
imprt();
} else if ( source == MenuItem.SAVE ) {
run.saveLiveTimes( !run.isPersonalBest() );
run.reset();
save();
} else if ( source == MenuItem.SAVE_AS ) {
file = null;
save();
} else if ( source == MenuItem.RESET ) {
reset();
} else if ( source == MenuItem.LOCK ) {
master.setIgnoreNativeInputs( true );
} else if ( source == MenuItem.UNLOCK ) {
master.setIgnoreNativeInputs( false );
} else if ( source == MenuItem.SETTINGS ) {
EditSettings dialog = new EditSettings();
dialog.display( true, master );
} else if ( source == MenuItem.ABOUT ) {
about();
} else if ( source == MenuItem.EXIT ) {
if ( confirmOverwrite() ) {
master.dispose();
}
}
}
/**
* Performs a split or starts the run if it is ready. Can also resume a
* paused run in case the run is segmented.
*/
private void split() {
Run run = master.getRun();
Run.State state = run.getState();
if ( state == Run.State.ONGOING ) {
long milli = System.nanoTime() / 1000000L;
long start = run.getSegment( run.getCurrent() ).getStartTime();
if ( milli - start > GHOST_DELAY ) {
run.split();
}
} else if ( state == Run.State.READY ) {
run.start();
} else if ( state == Run.State.PAUSED && run.isSegmented() ) {
run.resume();
}
}
/**
* Resets the current run to a ready state. If the user asked to be warned
* a pop-up will ask confirmation in case some live times are better.
*/
private void reset() {
Run run = master.getRun();
if ( run.getState() != Run.State.NULL ) {
if ( !Settings.GNR_WARN.get() || confirmOverwrite() ) {
run.reset();
}
}
}
/**
* Performs an "unsplit" on the current run. If a split has been made, it
* is canceled and the time that passed after said split is added back to
* the timer, as if the split had not taken place.
*/
private void unsplit() {
Run run = master.getRun();
Run.State state = run.getState();
if ( state == Run.State.ONGOING || state == Run.State.STOPPED ) {
long milli = System.nanoTime() / 1000000L;
if ( milli - lastUnsplit > GHOST_DELAY ) {
lastUnsplit = milli;
run.unsplit();
}
}
}
/**
* Skips the current split in the run. Skipping a split sets an undefined
* time for the current segment and merges the live time of the current
* segment with the following one.
*/
private void skip() {
Run run = master.getRun();
if ( run.getState() == Run.State.ONGOING ) {
long milli = System.nanoTime() / 1000000L;
if ( milli - lastSkip > GHOST_DELAY ) {
lastSkip = milli;
run.skip();
}
}
}
/**
* Displays a dialog to let the user select a file. The user is able to
* cancel this action, which results in a {@code null} being returned.
*
* @return a file selected by the user or {@code null} if he canceled
*/
private File selectFile() {
int option = fileChooser.showDialog( master,
Language.action_accept.get() );
if ( option == JFileChooser.APPROVE_OPTION ) {
return fileChooser.getSelectedFile();
} else {
return null;
}
}
/**
* Asks the user to confirm the discard of the current run. The popup
* window will only trigger if the current run has not been saved after
* some editing or if the run presents better times.
*
* @return {@code true} if the user wants to discard the run
*/
private boolean confirmOverwrite() {
boolean before = master.ignoresNativeInputs();
master.setIgnoreNativeInputs( true );
Run run = master.getRun();
boolean betterRun = run.isPersonalBest();
boolean betterSgt = run.hasSegmentsBest();
if ( betterRun || betterSgt ) {
String message = betterRun
? Language.WARN_BETTER_RUN.get()
: Language.WARN_BETTER_TIMES.get();
int option = JOptionPane.showConfirmDialog( master, message,
Language.WARNING.get(), JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE );
if ( option == JOptionPane.CANCEL_OPTION ) {
master.setIgnoreNativeInputs( false );
return false;
} else if ( option == JOptionPane.YES_OPTION ) {
run.saveLiveTimes( !betterRun );
run.reset();
save();
}
}
master.setIgnoreNativeInputs( before );
return true;
}
/**
* Opens the given file. If the file is {@code null}, the user is asked
* to select one. Before anything is done, the user is also asked for
* a confirmation if the current run has not been saved.
*
* @param file the file to open
*/
void open( File file ) {
if ( !confirmOverwrite() ) {
return;
}
if ( file == null ) {
if ( ( file = selectFile() ) == null ) {
return;
}
}
this.file = file;
String name = file.getName();
try {
open();
} catch ( Exception ex ) {
master.showError( Language.error_read_file.get( name ) );
this.file = null;
}
}
/**
* Opens the currently selected file. This method will first try to open
* the file using the new method (XStream XML) and if it fails will try to
* use the legacy method (Java ObjectStream.)
*
* @throws Exception if the reading operation fails
*/
private void open() throws Exception {
BufferedInputStream in = null;
try {
in = new BufferedInputStream( new FileInputStream( file ) );
try {
in.mark( Integer.MAX_VALUE );
xmlRead( in );
} catch ( Exception ex ) {
in.reset();
legacyRead( new ObjectInputStream( in ) );
}
MenuItem.recentlyOpened( "" + file );
} catch ( Exception ex ) {
throw ex;
} finally {
try {
in.close();
} catch ( Exception ex ) {
//$FALL-THROUGH$
}
}
}
/**
* Reads a stream on a run file using the new (since 1.5) XML method. This
* method will not be able to read a legacy run file and will throw an
* exception if confronted with such run file.
*
* @param in the input stream on the run file
*/
private void xmlRead( InputStream in ) {
XStream xml = new XStream( new DomDriver() );
master.setRun( ( Run ) xml.fromXML( in ) );
}
/**
* Reads a stream on a run file using the legacy Java method. This method
* might fail if the given file is not a Llanfair run.
*
* @param in an input stream on the run file
* @throws Exception if the stream cannot be read
*/
private void legacyRead( ObjectInputStream in ) throws Exception {
master.setRun( ( Run ) in.readObject() );
try {
Settings.GNR_SIZE.set( ( Dimension ) in.readObject(), true );
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
/**
* Saves the currently opened run to the currently selected file. If no
* file has been selected, the user is asked for one.
*/
private void save() {
if ( file == null ) {
if ( ( file = selectFile() ) == null ) {
return;
}
}
Settings.GNR_COOR.set( master.getLocationOnScreen(), true );
Settings.GNR_SIZE.set( master.getSize(), true );
String name = file.getName();
BufferedOutputStream out = null;
try {
XStream xml = new XStream( new DomDriver() );
out = new BufferedOutputStream( new FileOutputStream( file ) );
xml.toXML( master.getRun(), out );
} catch ( Exception ex ) {
master.showError( Language.error_write_file.get( name ) );
} finally {
try {
out.close();
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
}
/**
* Imports a run from another timer application. If no file has been
* selected, the user is asked for one. As of now, only WSplit run files
* are supported.
*/
private void imprt() {
if ( !confirmOverwrite() ) {
return;
}
if ( file == null ) {
if ( ( file = selectFile() ) == null ) {
return;
}
}
String name = file.getName();
BufferedReader in = null;
try {
in = new BufferedReader( new FileReader( file ) );
WSplit.parse( master, in );
} catch ( Exception ex ) {
master.showError( Language.error_import_run.get( name ) );
} finally {
try {
in.close();
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
}
/**
* Displays the "about" dialog. The dialog displays the version of Llanfair,
* the creative commons licence, the credits of development, a link to
* Llanfair's website and a link to donate.
*/
private void about() {
AboutDialog dialog = new AboutDialog(
master, Language.title_about.get() );
dialog.setMessage( BUNDLE.getString( "about" ));
try {
dialog.setWebsite( new URL( BUNDLE.getString( "website" ) ) );
} catch ( MalformedURLException ex ) {
// $FALL-THROUGH$
}
try {
dialog.setDonateLink( new URL( BUNDLE.getString( "donate" ) ),
Llanfair.getResources().getIcon( "donate" ) );
} catch ( MalformedURLException ex ) {
// $FALL-THROUGH$
}
dialog.display();
}
}

View file

@ -1,171 +1,169 @@
package org.fenix.llanfair;
import org.fenix.utils.TableModelSupport;
import javax.swing.*;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Icon;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import org.fenix.llanfair.Language;
import org.fenix.utils.TableModelSupport;
/**
*
* @author Xavier "Xunkar" Sencert
*/
public class Counters implements TableModel, Serializable {
// -------------------------------------------------------------- CONSTANTES
public static final long serialVersionUID = 1000L;
public static final int COLUMN_ICON = 0;
public static final int COLUMN_NAME = 1;
public static final int COLUMN_START = 2;
public static final int COLUMN_INCREMENT = 3;
public static final int COLUMN_COUNT = 4;
// -------------------------------------------------------------- ATTRIBUTS
private List<Counter> data;
private TableModelSupport tmSupport;
// ---------------------------------------------------------- CONSTRUCTEURS
public Counters() {
data = new ArrayList<Counter>();
tmSupport = new TableModelSupport(this);
}
public int getColumnCount() {
return COLUMN_COUNT;
}
public int getRowCount() {
return data.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= data.size()) {
throw new IllegalArgumentException("illegal counter id " + rowIndex);
}
return data.get(rowIndex).get(columnIndex);
}
public String getColumnName(int columnIndex) {
switch (columnIndex) {
case COLUMN_ICON: return "" + Language.ICON;
case COLUMN_INCREMENT: return "" + Language.INCREMENT;
case COLUMN_NAME: return "" + Language.NAME;
case COLUMN_START: return "" + Language.START_VALUE;
}
return null;
}
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case COLUMN_ICON: return Icon.class;
case COLUMN_INCREMENT: return Integer.class;
case COLUMN_NAME: return String.class;
case COLUMN_START: return Integer.class;
}
return null;
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= data.size()) {
throw new IllegalArgumentException("illegal counter id " + rowIndex);
}
data.get(rowIndex).set(columnIndex, aValue);
}
public void addTableModelListener(TableModelListener l) {
tmSupport.addTableModelListener(l);
}
public void removeTableModelListener(TableModelListener l) {
tmSupport.removeTableModelListener(l);
}
// ----------------------------------------------------------- CLASSES
public static class Counter implements Serializable {
public static final long serialVersionUID = 1000L;
private String name;
private Icon icon;
private int increment;
private int start;
private int saved;
private int live;
public Counter() {
name = "" + Language.UNTITLED;
icon = null;
start = 0;
live = 0;
increment = 1;
}
public Object get(int columnIndex) {
switch (columnIndex) {
case COLUMN_ICON: return icon;
case COLUMN_INCREMENT: return increment;
case COLUMN_NAME: return name;
case COLUMN_START: return start;
default: return name;
}
}
public void set(int columnIndex, Object value) {
switch (columnIndex) {
case COLUMN_ICON: icon = (Icon) value; break;
case COLUMN_INCREMENT: increment = (Integer) value; break;
case COLUMN_NAME: name = (String) value; break;
case COLUMN_START: start = (Integer) value; break;
}
}
public Icon getIcon() {
return icon;
}
public String getName() {
return name;
}
public int getLive() {
return live;
}
public int getStart() {
return start;
}
public int getSaved() {
return saved;
}
public void nextStep() {
live += increment;
}
}
// -------------------------------------------------------------- CONSTANTES
public static final long serialVersionUID = 1000L;
public static final int COLUMN_ICON = 0;
public static final int COLUMN_NAME = 1;
public static final int COLUMN_START = 2;
public static final int COLUMN_INCREMENT = 3;
public static final int COLUMN_COUNT = 4;
// -------------------------------------------------------------- ATTRIBUTS
private List<Counter> data;
private TableModelSupport tmSupport;
// ---------------------------------------------------------- CONSTRUCTEURS
public Counters() {
data = new ArrayList<Counter>();
tmSupport = new TableModelSupport(this);
}
public int getColumnCount() {
return COLUMN_COUNT;
}
public int getRowCount() {
return data.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= data.size()) {
throw new IllegalArgumentException("illegal counter id " + rowIndex);
}
return data.get(rowIndex).get(columnIndex);
}
public String getColumnName(int columnIndex) {
switch (columnIndex) {
case COLUMN_ICON: return "" + Language.ICON;
case COLUMN_INCREMENT: return "" + Language.INCREMENT;
case COLUMN_NAME: return "" + Language.NAME;
case COLUMN_START: return "" + Language.START_VALUE;
}
return null;
}
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case COLUMN_ICON: return Icon.class;
case COLUMN_INCREMENT: return Integer.class;
case COLUMN_NAME: return String.class;
case COLUMN_START: return Integer.class;
}
return null;
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= data.size()) {
throw new IllegalArgumentException("illegal counter id " + rowIndex);
}
data.get(rowIndex).set(columnIndex, aValue);
}
public void addTableModelListener(TableModelListener l) {
tmSupport.addTableModelListener(l);
}
public void removeTableModelListener(TableModelListener l) {
tmSupport.removeTableModelListener(l);
}
// ----------------------------------------------------------- CLASSES
public static class Counter implements Serializable {
public static final long serialVersionUID = 1000L;
private String name;
private Icon icon;
private int increment;
private int start;
private int saved;
private int live;
public Counter() {
name = "" + Language.UNTITLED;
icon = null;
start = 0;
live = 0;
increment = 1;
}
public Object get(int columnIndex) {
switch (columnIndex) {
case COLUMN_ICON: return icon;
case COLUMN_INCREMENT: return increment;
case COLUMN_NAME: return name;
case COLUMN_START: return start;
default: return name;
}
}
public void set(int columnIndex, Object value) {
switch (columnIndex) {
case COLUMN_ICON: icon = (Icon) value; break;
case COLUMN_INCREMENT: increment = (Integer) value; break;
case COLUMN_NAME: name = (String) value; break;
case COLUMN_START: start = (Integer) value; break;
}
}
public Icon getIcon() {
return icon;
}
public String getName() {
return name;
}
public int getLive() {
return live;
}
public int getStart() {
return start;
}
public int getSaved() {
return saved;
}
public void nextStep() {
live += increment;
}
}
}

View file

@ -1,9 +1,10 @@
package org.fenix.llanfair;
import org.fenix.utils.Resources;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.fenix.utils.Resources;
/**
* Enumeration of all externalized strings used by {@code Llanfair}. While it is
@ -15,278 +16,278 @@ import org.fenix.utils.Resources;
* @see Resources
*/
public enum Language {
// Settings > Generic
setting_alwaysOnTop,
setting_language,
setting_viewerLanguage,
setting_recentFiles,
setting_coordinates,
setting_dimension,
setting_compareMethod,
setting_accuracy,
setting_locked,
setting_warnOnReset,
// Settings > Color
setting_color_background,
setting_color_foreground,
setting_color_time,
setting_color_timer,
setting_color_timeGained,
setting_color_timeLost,
setting_color_newRecord,
setting_color_title,
setting_color_highlight,
setting_color_separators,
// Settings > Hotkey
setting_hotkey_split,
setting_hotkey_unsplit,
setting_hotkey_skip,
setting_hotkey_reset,
setting_hotkey_stop,
setting_hotkey_pause,
setting_hotkey_lock,
// Settings > Header
setting_header_goal,
setting_header_title,
// Settings > History
setting_history_rowCount,
setting_history_tabular,
setting_history_blankRows,
setting_history_multiline,
setting_history_merge,
setting_history_liveTimes,
setting_history_deltas,
setting_history_icons,
setting_history_iconSize,
setting_history_offset,
setting_history_alwaysShowLast,
setting_history_segmentFont,
setting_history_timeFont,
// Settings > Core
setting_core_accuracy,
setting_core_icons,
setting_core_iconSize,
setting_core_segmentName,
setting_core_splitTime,
setting_core_segmentTime,
setting_core_bestTime,
setting_core_segmentTimer,
setting_core_timerFont,
setting_core_segmentTimerFont,
// Settings > Graph
setting_graph_display,
setting_graph_scale,
// Settings > Footer
setting_footer_display,
setting_footer_useSplitData,
setting_footer_verbose,
setting_footer_bestTime,
setting_footer_multiline,
setting_footer_deltaLabels,
// Accuracy
accuracy_seconds,
accuracy_tenth,
accuracy_hundredth,
// Compare
compare_best_overall_run,
compare_sum_of_best_segments,
// Merge
merge_none,
merge_live,
merge_delta,
// MenuItem
menuItem_edit,
menuItem_new,
menuItem_open,
menuItem_open_recent,
menuItem_import,
menuItem_save,
menuItem_save_as,
menuItem_reset,
menuItem_lock,
menuItem_unlock,
menuItem_resize_default,
menuItem_resize_preferred,
menuItem_settings,
menuItem_about,
menuItem_exit,
// Errors
error_read_file,
error_write_file,
error_import_run,
// Actions
action_accept,
// Titles
title_about,
GENERAL,
TIMER,
FOOTER,
MISC,
USE_MAIN_FONT,
LB_GOAL,
ICON,
COLORS,
// Edit Dialog
ED_SEGMENTED,
TT_ED_SEGMENTED,
// Panels Title
PN_DIMENSION,
PN_DISPLAY,
PN_FONTS,
PN_SCROLLING,
// History
HISTORY,
MERGE_DELTA,
MERGE_LIVE,
MERGE_NONE,
TT_HS_OFFSET,
// Core
LB_CR_BEST,
LB_CR_SEGMENT,
LB_CR_SPLIT,
// Footer
LB_FT_BEST,
LB_FT_DELTA,
LB_FT_DELTA_BEST,
LB_FT_LIVE,
LB_FT_SEGMENT,
LB_FT_SPLIT,
/*
* Messages.
*/
ICON_TOO_BIG,
ILLEGAL_TIME,
ILLEGAL_SEGMENT_TIME,
INPUT_NAN,
INPUT_NEGATIVE,
INVALID_TIME_STAMP,
WARN_BETTER_RUN,
WARN_BETTER_TIMES,
WARN_RESET_SETTINGS,
/*
* Tooltips.
*/
TT_ADD_SEGMENT,
TT_COLOR_PICK,
TT_COLUMN_BEST,
TT_COLUMN_SEGMENT,
TT_COLUMN_TIME,
TT_REMOVE_SEGMENT,
TT_MOVE_SEGMENT_UP,
TT_MOVE_SEGMENT_DOWN,
/*
* Run.State enumeration.
*/
RUN_NULL,
RUN_OVER,
RUN_READY,
RUN_STOPPED,
// Settings > Generic
setting_alwaysOnTop,
setting_language,
setting_viewerLanguage,
setting_recentFiles,
setting_coordinates,
setting_dimension,
setting_compareMethod,
setting_accuracy,
setting_locked,
setting_warnOnReset,
/*
* Time.Accuracy enumeration.
*/
ACCURACY,
// Settings > Color
setting_color_background,
setting_color_foreground,
setting_color_time,
setting_color_timer,
setting_color_timeGained,
setting_color_timeLost,
setting_color_newRecord,
setting_color_title,
setting_color_highlight,
setting_color_separators,
// Settings > Hotkey
setting_hotkey_split,
setting_hotkey_unsplit,
setting_hotkey_skip,
setting_hotkey_reset,
setting_hotkey_stop,
setting_hotkey_pause,
setting_hotkey_lock,
// Settings > Header
setting_header_goal,
setting_header_title,
// Settings > History
setting_history_rowCount,
setting_history_tabular,
setting_history_blankRows,
setting_history_multiline,
setting_history_merge,
setting_history_liveTimes,
setting_history_deltas,
setting_history_icons,
setting_history_iconSize,
setting_history_offset,
setting_history_alwaysShowLast,
setting_history_segmentFont,
setting_history_timeFont,
// Settings > Core
setting_core_accuracy,
setting_core_icons,
setting_core_iconSize,
setting_core_segmentName,
setting_core_splitTime,
setting_core_segmentTime,
setting_core_bestTime,
setting_core_segmentTimer,
setting_core_timerFont,
setting_core_segmentTimerFont,
// Settings > Graph
setting_graph_display,
setting_graph_scale,
// Settings > Footer
setting_footer_display,
setting_footer_useSplitData,
setting_footer_verbose,
setting_footer_bestTime,
setting_footer_multiline,
setting_footer_deltaLabels,
// Accuracy
accuracy_seconds,
accuracy_tenth,
accuracy_hundredth,
// Compare
compare_best_overall_run,
compare_sum_of_best_segments,
// Merge
merge_none,
merge_live,
merge_delta,
// MenuItem
menuItem_edit,
menuItem_new,
menuItem_open,
menuItem_open_recent,
menuItem_import,
menuItem_save,
menuItem_save_as,
menuItem_reset,
menuItem_lock,
menuItem_unlock,
menuItem_resize_default,
menuItem_resize_preferred,
menuItem_settings,
menuItem_about,
menuItem_exit,
// Errors
error_read_file,
error_write_file,
error_import_run,
// Actions
action_accept,
// Titles
title_about,
GENERAL,
TIMER,
FOOTER,
MISC,
USE_MAIN_FONT,
LB_GOAL,
ICON,
COLORS,
// Edit Dialog
ED_SEGMENTED,
TT_ED_SEGMENTED,
// Panels Title
PN_DIMENSION,
PN_DISPLAY,
PN_FONTS,
PN_SCROLLING,
// History
HISTORY,
MERGE_DELTA,
MERGE_LIVE,
MERGE_NONE,
TT_HS_OFFSET,
// Core
LB_CR_BEST,
LB_CR_SEGMENT,
LB_CR_SPLIT,
// Footer
LB_FT_BEST,
LB_FT_DELTA,
LB_FT_DELTA_BEST,
LB_FT_LIVE,
LB_FT_SEGMENT,
LB_FT_SPLIT,
/*
* Messages.
*/
ICON_TOO_BIG,
ILLEGAL_TIME,
ILLEGAL_SEGMENT_TIME,
INPUT_NAN,
INPUT_NEGATIVE,
INVALID_TIME_STAMP,
WARN_BETTER_RUN,
WARN_BETTER_TIMES,
WARN_RESET_SETTINGS,
/*
* Tooltips.
*/
TT_ADD_SEGMENT,
TT_COLOR_PICK,
TT_COLUMN_BEST,
TT_COLUMN_SEGMENT,
TT_COLUMN_TIME,
TT_REMOVE_SEGMENT,
TT_MOVE_SEGMENT_UP,
TT_MOVE_SEGMENT_DOWN,
/*
* Run.State enumeration.
*/
RUN_NULL,
RUN_OVER,
RUN_READY,
RUN_STOPPED,
/*
* Time.Accuracy enumeration.
*/
ACCURACY,
/*
* Miscellaneous tokens.
*/
ACCEPT,
APPLICATION,
BEST,
CANCEL,
COMPARE_METHOD,
COMPONENTS,
DISABLED,
EDITING,
ERROR,
GOAL,
IMAGE,
INPUTS,
MAX_ORDINATE,
NAME,
RUN_TITLE,
SEGMENT,
SEGMENTS,
SPLIT,
TIME,
UNTITLED,
WARNING,
/*
* 1.4
*/
INCREMENT,
START_VALUE;
public static final Locale[] LANGUAGES = new Locale[] {
Locale.ENGLISH,
Locale.FRENCH,
Locale.GERMAN,
new Locale("nl"),
new Locale("sv")
};
public static final Map<String, String> LOCALE_NAMES =
new HashMap<String, String>();
static {
LOCALE_NAMES.put("de", "Deutsch");
LOCALE_NAMES.put("en", "English");
LOCALE_NAMES.put("fr", "Français");
LOCALE_NAMES.put("nl", "Nederlands");
LOCALE_NAMES.put("sv", "Svenska");
}
/*
* Miscellaneous tokens.
*/
ACCEPT,
APPLICATION,
BEST,
CANCEL,
COMPARE_METHOD,
COMPONENTS,
DISABLED,
EDITING,
ERROR,
GOAL,
IMAGE,
INPUTS,
MAX_ORDINATE,
NAME,
RUN_TITLE,
SEGMENT,
SEGMENTS,
SPLIT,
TIME,
UNTITLED,
WARNING,
// -------------------------------------------------------------- INTERFACE
/*
* 1.4
*/
INCREMENT,
START_VALUE;
/**
* Returns the localized string get of this language element.
*
* @return the localized string for this element.
*/
public String get() {
return Llanfair.getResources().getString(name());
}
/**
* Returns the localized string get of this language element. This method
* also passes down an array of parameters to replace the tokens with.
*
* @param parameters - the array of values for each token of the string.
* @return the localized string filled with the given parameters.
*/
public String get(Object... parameters) {
return Llanfair.getResources().getString(name(), parameters);
}
public static final Locale[] LANGUAGES = new Locale[] {
Locale.ENGLISH,
Locale.FRENCH,
Locale.GERMAN,
new Locale("nl"),
new Locale("sv")
};
/**
* The string representation of an enumerate is the localized string
* corresponding to its name.
*/
@Override public String toString() {
return get();
}
public static final Map<String, String> LOCALE_NAMES =
new HashMap<String, String>();
static {
LOCALE_NAMES.put("de", "Deutsch");
LOCALE_NAMES.put("en", "English");
LOCALE_NAMES.put("fr", "Français");
LOCALE_NAMES.put("nl", "Nederlands");
LOCALE_NAMES.put("sv", "Svenska");
}
// -------------------------------------------------------------- INTERFACE
/**
* Returns the localized string get of this language element.
*
* @return the localized string for this element.
*/
public String get() {
return Llanfair.getResources().getString(name());
}
/**
* Returns the localized string get of this language element. This method
* also passes down an array of parameters to replace the tokens with.
*
* @param parameters - the array of values for each token of the string.
* @return the localized string filled with the given parameters.
*/
public String get(Object... parameters) {
return Llanfair.getResources().getString(name(), parameters);
}
/**
* The string representation of an enumerate is the localized string
* corresponding to its name.
*/
@Override public String toString() {
return get();
}
}

View file

@ -31,427 +31,427 @@ import java.util.Locale;
* @version 1.5
*/
public class Llanfair extends BorderlessFrame implements TableModelListener,
LocaleListener, MouseWheelListener, ActionListener, NativeKeyListener,
PropertyChangeListener, WindowListener {
LocaleListener, MouseWheelListener, ActionListener, NativeKeyListener,
PropertyChangeListener, WindowListener {
private static Resources RESOURCES = null;
static {
ToolTipManager.sharedInstance().setInitialDelay( 1000 );
ToolTipManager.sharedInstance().setDismissDelay( 7000 );
ToolTipManager.sharedInstance().setReshowDelay( 0 );
}
private static Resources RESOURCES = null;
private Run run;
private RunPane runPane;
private Actions actions;
private JPopupMenu popupMenu;
static {
ToolTipManager.sharedInstance().setInitialDelay( 1000 );
ToolTipManager.sharedInstance().setDismissDelay( 7000 );
ToolTipManager.sharedInstance().setReshowDelay( 0 );
}
private volatile boolean ignoreNativeInputs;
private Dimension preferredSize;
private Run run;
private RunPane runPane;
/**
* Creates and initializes the application. As with any Swing application
* this constructor should be called from within a thread to avoid
* dead-lock.
*/
private Llanfair() {
super( "Llanfair" );
LocaleDelegate.setDefault( Settings.GNR_LANG.get() );
LocaleDelegate.addLocaleListener( this );
private Actions actions;
//ResourceBundle b = ResourceBundle.getBundle("language");
RESOURCES = new Resources();
registerFonts();
setLookAndFeel();
private JPopupMenu popupMenu;
run = new Run();
runPane = null;
ignoreNativeInputs = false;
preferredSize = null;
actions = new Actions( this );
setMenu();
setBehavior();
setRun( run );
setVisible( true );
}
/**
* Main entry point of the application. This is the method called by Java
* when a user executes the JAR. Simply instantiantes a new Llanfair object.
* If an argument is passed, the program will not launch but will instead
* enter localization mode, dumping all language variables for the specified
* locale.
*
* @param args array of command line parameters supplied at launch
*/
public static void main( String[] args ) {
if ( args.length > 0 ) {
String locale = args[0];
LocaleDelegate.setDefault( new Locale( locale ) );
RESOURCES = new Resources();
dumpLocalization();
System.exit( 0 );
}
SwingUtilities.invokeLater( new Runnable() {
@Override public void run() {
new Llanfair();
}
} );
}
/**
* Grabs the resources of Llanfair. The Resources object is a front-end
* for every classpath resources associated with the application, including
* localization strings, icons, and properties.
*
* @return the resources object
*/
public static Resources getResources() {
return RESOURCES;
}
private volatile boolean ignoreNativeInputs;
/**
* Returns the run currently associated with this instance of Llanfair.
* While the run can be empty, it cannot be {@code null}.
*
* @return the current run
*/
Run getRun() {
return run;
}
/**
* Sets the run to represent in this application to the given run. If the
* GUI does not exist (in other words, we are registering the first run) it
* is created on the fly.
*
* @param run the run to represent, cannot be {@code null}
*/
public final void setRun( Run run ) {
if ( run == null ) {
throw new NullPointerException( "Null run" );
}
this.run = run;
// If we have a GUI, set the new model; else, create the GUI
if ( runPane != null ) {
runPane.setRun( run );
} else {
runPane = new RunPane( run );
add( runPane );
}
Settings.setRun( run );
run.addTableModelListener( this );
run.addPropertyChangeListener( this );
MenuItem.setActiveState( run.getState() );
setPreferredSize( preferredSize );
pack();
// Replace the window to the run preferred location; center if none
Point location = Settings.GNR_COOR.get();
if ( location == null ) {
setLocationRelativeTo( null );
} else {
setLocation( location );
}
}
/**
* Indicates whether or not Llanfair currently ignores all native inputs.
* Since native inputs can be caught even when the application does not have
* the focus, it is necessary to be able to lock the application when the
* user needs to do something else whilst not interfering with the behavior
* of Llanfair.
*
* @return {@code true} if the current instance ignores native inputs
*/
public synchronized boolean ignoresNativeInputs() {
return ignoreNativeInputs;
}
/**
* Tells Llanfair whether to ignore native input events or not. Since native
* inputs can be caught even when the application does not have the focus,
* it is necessary to be able to lock the application when the user needs
* to do something else whilst not interfering with the behavior of
* Llanfair.
*
* @param ignore if Llanfair must ignore the native inputs or not
*/
public synchronized void setIgnoreNativeInputs( boolean ignore ) {
ignoreNativeInputs = ignore;
}
/**
* Outputs the given error in a dialog box. Only errors that are made for
* and useful to the user need to be displayed that way.
*
* @param message the localized error message
*/
void showError( String message ) {
JOptionPane.showMessageDialog(
this, message, Language.ERROR.get(), JOptionPane.ERROR_MESSAGE
);
}
/**
* Sets the look and feel of the application. Provides a task bar icon and
* a general system dependent theme.
*/
private void setLookAndFeel() {
setIconImage( RESOURCES.getImage( "Llanfair" ) );
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName()
);
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
/**
* Register the fonts provided with Llanfair with its environment.
*/
private void registerFonts() {
InputStream fontFile = RESOURCES.getStream( "digitalism.ttf" );
try {
Font digitalism = Font.createFont( Font.TRUETYPE_FONT, fontFile );
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(
digitalism
);
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
/**
* Writes all values from the {@code Language} enum in a property file.
* This method will append all the newly defined entries to the list of
* already existing values.
*/
private static void dumpLocalization() {
try {
String iso = Locale.getDefault().getLanguage();
FileWriter fw = new FileWriter( "language_" + iso + ".properties" );
for ( Language lang : Language.values() ) {
String old = RESOURCES.getString( lang.name() );
fw.write( lang.name() + " = " );
if ( old != null ) {
fw.write( old );
}
fw.write( "\n" );
}
fw.close();
} catch ( IOException ex ) {
// $FALL-THROUGH$
}
}
private Dimension preferredSize;
/**
* When the locale changes, we first ask the resources to reload the locale
* dependent resources and pass the event to the GUI.
*/
@Override public void localeChanged( LocaleEvent event ) {
RESOURCES.defaultLocaleChanged();
if ( runPane != null ) {
runPane.processLocaleEvent( event );
}
MenuItem.localeChanged( event );
}
/**
* If we do not ignore the native inputs, register the input and invokes
* a new thread to treat the input whenever possible without hogging the
* main thread.
*/
@Override public void nativeKeyPressed( final NativeKeyEvent event ) {
int keyCode = event.getKeyCode();
boolean hotkeysEnabler = ( keyCode == Settings.KEY_LOCK.get() );
if ( !ignoresNativeInputs() || hotkeysEnabler ) {
SwingUtilities.invokeLater( new Runnable() {
@Override public void run() {
actions.process( event );
}
} );
}
}
/**
* Creates and initializes the application. As with any Swing application
* this constructor should be called from within a thread to avoid
* dead-lock.
*/
private Llanfair() {
super( "Llanfair" );
LocaleDelegate.setDefault( Settings.GNR_LANG.get() );
LocaleDelegate.addLocaleListener( this );
@Override public void nativeKeyReleased( NativeKeyEvent event ) {}
//ResourceBundle b = ResourceBundle.getBundle("language");
@Override public void nativeKeyTyped( NativeKeyEvent event ) {}
/**
* A property change event might be fired from either the settings
* singleton or the run itself. In either case, we propagate the event to
* our children and update ourself with the new value of the given property.
*/
@Override public void propertyChange( PropertyChangeEvent event ) {
runPane.processPropertyChangeEvent( event );
String property = event.getPropertyName();
RESOURCES = new Resources();
registerFonts();
setLookAndFeel();
if ( Run.STATE_PROPERTY.equals( property ) ) {
MenuItem.setActiveState( run.getState() );
} else if ( Settings.GNR_ATOP.equals( property ) ) {
setAlwaysOnTop( Settings.GNR_ATOP.get() );
} else if (Settings.HST_ROWS.equals(property)
|| Settings.GPH_SHOW.equals(property)
|| Settings.FOO_SHOW.equals(property)
|| Settings.FOO_SPLT.equals(property)
|| Settings.COR_ICSZ.equals(property)
|| Settings.GNR_ACCY.equals(property)
|| Settings.HDR_TTLE.equals(property)
|| Settings.HDR_GOAL.equals(property)
|| Settings.HST_DLTA.equals(property)
|| Settings.HST_SFNT.equals(property)
|| Settings.HST_TFNT.equals(property)
|| Settings.HST_LIVE.equals(property)
|| Settings.HST_MERG.equals(property)
|| Settings.HST_BLNK.equals(property)
|| Settings.HST_ICON.equals(property)
|| Settings.HST_ICSZ.equals(property)
|| Settings.HST_LINE.equals(property)
|| Settings.COR_NAME.equals(property)
|| Settings.COR_SPLT.equals(property)
|| Settings.COR_SEGM.equals(property)
|| Settings.COR_BEST.equals(property)
|| Settings.COR_ICON.equals(property)
|| Settings.COR_TFNT.equals(property)
|| Settings.COR_SFNT.equals(property)
|| Settings.COR_STMR.equals(property)
|| Settings.FOO_BEST.equals(property)
|| Settings.FOO_DLBL.equals(property)
|| Settings.FOO_VERB.equals(property)
|| Settings.FOO_LINE.equals(property)
|| Run.NAME_PROPERTY.equals(property)) {
setPreferredSize(null);
pack();
}
}
/**
* When the run's table of segments is updated, we ask the main panel to
* update itself accordingly and repack the frame as its dimensions may
* have changed.
*/
@Override public void tableChanged( TableModelEvent event ) {
runPane.processTableModelEvent( event );
// No need to recompute the size if we receive a HEADER_ROW UPDATE
// as we only use them when a segment is moved up or down and when
// the user cancel any changes made to his run.
if ( event.getType() == TableModelEvent.UPDATE
&& event.getFirstRow() == TableModelEvent.HEADER_ROW ) {
setPreferredSize( preferredSize );
} else {
setPreferredSize( null );
}
pack();
}
/**
* When the user scrolls the mouse wheel, we update the graph's scale to
* zoom in or out depending on the direction of the scroll.
*/
@Override public void mouseWheelMoved( MouseWheelEvent event ) {
int rotations = event.getWheelRotation();
float percent = Settings.GPH_SCAL.get();
if ( percent == 0.5F ) {
percent = 1.0F;
rotations--;
}
float newValue = Math.max( 0.5F, percent + rotations );
Settings.GPH_SCAL.set( newValue );
}
run = new Run();
runPane = null;
ignoreNativeInputs = false;
preferredSize = null;
actions = new Actions( this );
/**
* When the user clicks on the mouse's right-button, we bring up the
* context menu at the click's location.
*/
@Override public void mousePressed( MouseEvent event ) {
super.mousePressed( event );
if ( SwingUtilities.isRightMouseButton( event ) ) {
popupMenu.show( this, event.getX(), event.getY() );
}
}
/**
* Whenever the frame is being disposed of, we save the settings and
* unregister the native hook of {@code JNativeHook}.
*/
@Override public void windowClosed( WindowEvent event ) {
Settings.save();
GlobalScreen.unregisterNativeHook();
}
setMenu();
setBehavior();
setRun( run );
@Override public void windowClosing(WindowEvent event) {}
setVisible( true );
}
@Override public void windowOpened(WindowEvent event) {}
/**
* Main entry point of the application. This is the method called by Java
* when a user executes the JAR. Simply instantiantes a new Llanfair object.
* If an argument is passed, the program will not launch but will instead
* enter localization mode, dumping all language variables for the specified
* locale.
*
* @param args array of command line parameters supplied at launch
*/
public static void main( String[] args ) {
if ( args.length > 0 ) {
String locale = args[0];
LocaleDelegate.setDefault( new Locale( locale ) );
RESOURCES = new Resources();
dumpLocalization();
System.exit( 0 );
}
SwingUtilities.invokeLater( new Runnable() {
@Override public void run() {
new Llanfair();
}
} );
}
@Override public void windowActivated(WindowEvent event) {}
/**
* Grabs the resources of Llanfair. The Resources object is a front-end
* for every classpath resources associated with the application, including
* localization strings, icons, and properties.
*
* @return the resources object
*/
public static Resources getResources() {
return RESOURCES;
}
@Override public void windowDeactivated(WindowEvent event) {}
/**
* Returns the run currently associated with this instance of Llanfair.
* While the run can be empty, it cannot be {@code null}.
*
* @return the current run
*/
Run getRun() {
return run;
}
@Override public void windowIconified(WindowEvent event) {}
/**
* Sets the run to represent in this application to the given run. If the
* GUI does not exist (in other words, we are registering the first run) it
* is created on the fly.
*
* @param run the run to represent, cannot be {@code null}
*/
public final void setRun( Run run ) {
if ( run == null ) {
throw new NullPointerException( "Null run" );
}
this.run = run;
// If we have a GUI, set the new model; else, create the GUI
if ( runPane != null ) {
runPane.setRun( run );
} else {
runPane = new RunPane( run );
add( runPane );
}
Settings.setRun( run );
run.addTableModelListener( this );
run.addPropertyChangeListener( this );
MenuItem.setActiveState( run.getState() );
@Override public void windowDeiconified(WindowEvent event) {}
/**
* Action events are fired by clicking on the entries of the context menu.
*/
@Override public synchronized void actionPerformed( final ActionEvent ev ) {
MenuItem source = ( MenuItem ) ev.getSource();
setPreferredSize( preferredSize );
pack();
SwingUtilities.invokeLater( new Runnable() {
@Override public void run() {
actions.process( ev );
}
} );
// Replace the window to the run preferred location; center if none
Point location = Settings.GNR_COOR.get();
if ( location == null ) {
setLocationRelativeTo( null );
} else {
setLocation( location );
}
}
if (source.equals(MenuItem.EDIT)) {
/**
* Indicates whether or not Llanfair currently ignores all native inputs.
* Since native inputs can be caught even when the application does not have
* the focus, it is necessary to be able to lock the application when the
* user needs to do something else whilst not interfering with the behavior
* of Llanfair.
*
* @return {@code true} if the current instance ignores native inputs
*/
public synchronized boolean ignoresNativeInputs() {
return ignoreNativeInputs;
}
} else if (source.equals(MenuItem.RESIZE_DEFAULT)) {
setPreferredSize(null);
pack();
} else if (source.equals(MenuItem.RESIZE_PREFERRED)) {
setPreferredSize(preferredSize);
pack();
}
}
/**
* Tells Llanfair whether to ignore native input events or not. Since native
* inputs can be caught even when the application does not have the focus,
* it is necessary to be able to lock the application when the user needs
* to do something else whilst not interfering with the behavior of
* Llanfair.
*
* @param ignore if Llanfair must ignore the native inputs or not
*/
public synchronized void setIgnoreNativeInputs( boolean ignore ) {
ignoreNativeInputs = ignore;
}
/**
* Sets the persistent behavior of the application and its components.
*
* @throws IllegalStateException if JNativeHook cannot be registered.
*/
private void setBehavior() {
try {
GlobalScreen.registerNativeHook();
} catch (NativeHookException e) {
throw new IllegalStateException("cannot register native hook");
}
setAlwaysOnTop(Settings.GNR_ATOP.get());
addWindowListener(this);
addMouseWheelListener(this);
Settings.addPropertyChangeListener(this);
GlobalScreen.getInstance().addNativeKeyListener(this);
}
/**
* Outputs the given error in a dialog box. Only errors that are made for
* and useful to the user need to be displayed that way.
*
* @param message the localized error message
*/
void showError( String message ) {
JOptionPane.showMessageDialog(
this, message, Language.ERROR.get(), JOptionPane.ERROR_MESSAGE
);
}
/**
* Initializes the right-click context menu.
*/
private void setMenu() {
popupMenu = MenuItem.getPopupMenu();
MenuItem.addActionListener( this );
MenuItem.populateRecentlyOpened();
}
/**
* Sets the look and feel of the application. Provides a task bar icon and
* a general system dependent theme.
*/
private void setLookAndFeel() {
setIconImage( RESOURCES.getImage( "Llanfair" ) );
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName()
);
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
/**
* Register the fonts provided with Llanfair with its environment.
*/
private void registerFonts() {
InputStream fontFile = RESOURCES.getStream( "digitalism.ttf" );
try {
Font digitalism = Font.createFont( Font.TRUETYPE_FONT, fontFile );
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(
digitalism
);
} catch ( Exception ex ) {
// $FALL-THROUGH$
}
}
/**
* Writes all values from the {@code Language} enum in a property file.
* This method will append all the newly defined entries to the list of
* already existing values.
*/
private static void dumpLocalization() {
try {
String iso = Locale.getDefault().getLanguage();
FileWriter fw = new FileWriter( "language_" + iso + ".properties" );
for ( Language lang : Language.values() ) {
String old = RESOURCES.getString( lang.name() );
fw.write( lang.name() + " = " );
if ( old != null ) {
fw.write( old );
}
fw.write( "\n" );
}
fw.close();
} catch ( IOException ex ) {
// $FALL-THROUGH$
}
}
/**
* When the locale changes, we first ask the resources to reload the locale
* dependent resources and pass the event to the GUI.
*/
@Override public void localeChanged( LocaleEvent event ) {
RESOURCES.defaultLocaleChanged();
if ( runPane != null ) {
runPane.processLocaleEvent( event );
}
MenuItem.localeChanged( event );
}
/**
* If we do not ignore the native inputs, register the input and invokes
* a new thread to treat the input whenever possible without hogging the
* main thread.
*/
@Override public void nativeKeyPressed( final NativeKeyEvent event ) {
int keyCode = event.getKeyCode();
boolean hotkeysEnabler = ( keyCode == Settings.KEY_LOCK.get() );
if ( !ignoresNativeInputs() || hotkeysEnabler ) {
SwingUtilities.invokeLater( new Runnable() {
@Override public void run() {
actions.process( event );
}
} );
}
}
@Override public void nativeKeyReleased( NativeKeyEvent event ) {}
@Override public void nativeKeyTyped( NativeKeyEvent event ) {}
/**
* A property change event might be fired from either the settings
* singleton or the run itself. In either case, we propagate the event to
* our children and update ourself with the new value of the given property.
*/
@Override public void propertyChange( PropertyChangeEvent event ) {
runPane.processPropertyChangeEvent( event );
String property = event.getPropertyName();
if ( Run.STATE_PROPERTY.equals( property ) ) {
MenuItem.setActiveState( run.getState() );
} else if ( Settings.GNR_ATOP.equals( property ) ) {
setAlwaysOnTop( Settings.GNR_ATOP.get() );
} else if (Settings.HST_ROWS.equals(property)
|| Settings.GPH_SHOW.equals(property)
|| Settings.FOO_SHOW.equals(property)
|| Settings.FOO_SPLT.equals(property)
|| Settings.COR_ICSZ.equals(property)
|| Settings.GNR_ACCY.equals(property)
|| Settings.HDR_TTLE.equals(property)
|| Settings.HDR_GOAL.equals(property)
|| Settings.HST_DLTA.equals(property)
|| Settings.HST_SFNT.equals(property)
|| Settings.HST_TFNT.equals(property)
|| Settings.HST_LIVE.equals(property)
|| Settings.HST_MERG.equals(property)
|| Settings.HST_BLNK.equals(property)
|| Settings.HST_ICON.equals(property)
|| Settings.HST_ICSZ.equals(property)
|| Settings.HST_LINE.equals(property)
|| Settings.COR_NAME.equals(property)
|| Settings.COR_SPLT.equals(property)
|| Settings.COR_SEGM.equals(property)
|| Settings.COR_BEST.equals(property)
|| Settings.COR_ICON.equals(property)
|| Settings.COR_TFNT.equals(property)
|| Settings.COR_SFNT.equals(property)
|| Settings.COR_STMR.equals(property)
|| Settings.FOO_BEST.equals(property)
|| Settings.FOO_DLBL.equals(property)
|| Settings.FOO_VERB.equals(property)
|| Settings.FOO_LINE.equals(property)
|| Run.NAME_PROPERTY.equals(property)) {
setPreferredSize(null);
pack();
}
}
/**
* When the run's table of segments is updated, we ask the main panel to
* update itself accordingly and repack the frame as its dimensions may
* have changed.
*/
@Override public void tableChanged( TableModelEvent event ) {
runPane.processTableModelEvent( event );
// No need to recompute the size if we receive a HEADER_ROW UPDATE
// as we only use them when a segment is moved up or down and when
// the user cancel any changes made to his run.
if ( event.getType() == TableModelEvent.UPDATE
&& event.getFirstRow() == TableModelEvent.HEADER_ROW ) {
setPreferredSize( preferredSize );
} else {
setPreferredSize( null );
}
pack();
}
/**
* When the user scrolls the mouse wheel, we update the graph's scale to
* zoom in or out depending on the direction of the scroll.
*/
@Override public void mouseWheelMoved( MouseWheelEvent event ) {
int rotations = event.getWheelRotation();
float percent = Settings.GPH_SCAL.get();
if ( percent == 0.5F ) {
percent = 1.0F;
rotations--;
}
float newValue = Math.max( 0.5F, percent + rotations );
Settings.GPH_SCAL.set( newValue );
}
/**
* When the user clicks on the mouse's right-button, we bring up the
* context menu at the click's location.
*/
@Override public void mousePressed( MouseEvent event ) {
super.mousePressed( event );
if ( SwingUtilities.isRightMouseButton( event ) ) {
popupMenu.show( this, event.getX(), event.getY() );
}
}
/**
* Whenever the frame is being disposed of, we save the settings and
* unregister the native hook of {@code JNativeHook}.
*/
@Override public void windowClosed( WindowEvent event ) {
Settings.save();
GlobalScreen.unregisterNativeHook();
}
@Override public void windowClosing(WindowEvent event) {}
@Override public void windowOpened(WindowEvent event) {}
@Override public void windowActivated(WindowEvent event) {}
@Override public void windowDeactivated(WindowEvent event) {}
@Override public void windowIconified(WindowEvent event) {}
@Override public void windowDeiconified(WindowEvent event) {}
/**
* Action events are fired by clicking on the entries of the context menu.
*/
@Override public synchronized void actionPerformed( final ActionEvent ev ) {
MenuItem source = ( MenuItem ) ev.getSource();
SwingUtilities.invokeLater( new Runnable() {
@Override public void run() {
actions.process( ev );
}
} );
if (source.equals(MenuItem.EDIT)) {
} else if (source.equals(MenuItem.RESIZE_DEFAULT)) {
setPreferredSize(null);
pack();
} else if (source.equals(MenuItem.RESIZE_PREFERRED)) {
setPreferredSize(preferredSize);
pack();
}
}
/**
* Sets the persistent behavior of the application and its components.
*
* @throws IllegalStateException if JNativeHook cannot be registered.
*/
private void setBehavior() {
try {
GlobalScreen.registerNativeHook();
} catch (NativeHookException e) {
throw new IllegalStateException("cannot register native hook");
}
setAlwaysOnTop(Settings.GNR_ATOP.get());
addWindowListener(this);
addMouseWheelListener(this);
Settings.addPropertyChangeListener(this);
GlobalScreen.getInstance().addNativeKeyListener(this);
}
/**
* Initializes the right-click context menu.
*/
private void setMenu() {
popupMenu = MenuItem.getPopupMenu();
MenuItem.addActionListener( this );
MenuItem.populateRecentlyOpened();
}
}

View file

@ -1,19 +1,16 @@
package org.fenix.llanfair;
import org.fenix.llanfair.Run.State;
import org.fenix.llanfair.config.Settings;
import org.fenix.utils.locale.LocaleEvent;
import javax.swing.*;
import javax.swing.event.EventListenerList;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.event.EventListenerList;
import org.fenix.utils.locale.LocaleEvent;
import org.fenix.llanfair.Run.State;
import org.fenix.llanfair.config.Settings;
/**
* Enumerates the menu items available in the right-click context menu of
@ -25,207 +22,207 @@ import org.fenix.llanfair.config.Settings;
*/
enum MenuItem implements ActionListener {
EDIT( false, State.NULL, State.READY ),
NEW( true, State.NULL, State.READY, State.STOPPED ),
OPEN( false, State.NULL, State.READY, State.STOPPED ),
OPEN_RECENT( false, State.NULL, State.READY, State.STOPPED ),
IMPORT( false, State.NULL, State.READY, State.STOPPED ),
SAVE( false, State.READY, State.STOPPED ),
SAVE_AS( true, State.READY ),
RESET( true, State.ONGOING, State.STOPPED, State.PAUSED ),
LOCK( false, State.NULL, State.READY, State.STOPPED, State.ONGOING ),
UNLOCK( false, State.NULL, State.READY, State.STOPPED, State.ONGOING ),
RESIZE_DEFAULT( false, State.NULL, State.READY ),
RESIZE_PREFERRED( true, State.NULL, State.READY ),
SETTINGS( true, State.NULL, State.READY, State.STOPPED ),
ABOUT( true, State.NULL, State.READY, State.STOPPED, State.ONGOING ),
EXIT( false, State.NULL, State.READY, State.STOPPED, State.ONGOING );
EDIT( false, State.NULL, State.READY ),
NEW( true, State.NULL, State.READY, State.STOPPED ),
OPEN( false, State.NULL, State.READY, State.STOPPED ),
OPEN_RECENT( false, State.NULL, State.READY, State.STOPPED ),
IMPORT( false, State.NULL, State.READY, State.STOPPED ),
SAVE( false, State.READY, State.STOPPED ),
SAVE_AS( true, State.READY ),
RESET( true, State.ONGOING, State.STOPPED, State.PAUSED ),
LOCK( false, State.NULL, State.READY, State.STOPPED, State.ONGOING ),
UNLOCK( false, State.NULL, State.READY, State.STOPPED, State.ONGOING ),
RESIZE_DEFAULT( false, State.NULL, State.READY ),
RESIZE_PREFERRED( true, State.NULL, State.READY ),
SETTINGS( true, State.NULL, State.READY, State.STOPPED ),
ABOUT( true, State.NULL, State.READY, State.STOPPED, State.ONGOING ),
EXIT( false, State.NULL, State.READY, State.STOPPED, State.ONGOING );
/**
* Static list of listeners that listen to all the menu items. Whenever
* one item fires an {@code ActionEvent}, the type statically fires an
* other {@code ActionEvent} with the {@code MenuItem} enumerate as the
* source component.
*/
private static EventListenerList listeners = new EventListenerList();
/**
* Static list of listeners that listen to all the menu items. Whenever
* one item fires an {@code ActionEvent}, the type statically fires an
* other {@code ActionEvent} with the {@code MenuItem} enumerate as the
* source component.
*/
private static EventListenerList listeners = new EventListenerList();
private static final int MAX_FILES = 5;
private static final int TRUNCATE = 30;
private boolean isEndOfGroup;
private List<State> activeStates;
private JMenuItem menuItem;
private static final int MAX_FILES = 5;
private static final int TRUNCATE = 30;
/**
* Internal constructor used to set the attributes. Only called by the
* enum type itself.
*
* @param isEndOfGroup indicates if this item is a group ender
* @param activeStates list of active run states of this item
*/
private MenuItem( boolean isEndOfGroup, Run.State... activeStates ) {
this.isEndOfGroup = isEndOfGroup;
this.activeStates = Arrays.asList( activeStates );
private boolean isEndOfGroup;
private List<State> activeStates;
private JMenuItem menuItem;
if ( name().equals( "OPEN_RECENT" ) ) {
menuItem = new JMenu( toString() );
} else {
menuItem = new JMenuItem( toString() );
}
Icon icon = Llanfair.getResources().getIcon( "jmi/" + name() + ".png" );
menuItem.setIcon( icon );
menuItem.addActionListener( this );
// LOCK & UNLOCK mask each other and Llanfair always start unlocked
if ( name().equals( "UNLOCK" ) ) {
menuItem.setVisible( false );
}
}
/**
* Returns a popup menu composed of all the menu items. A separator is
* inserted after each item which are indicated as end of their group.
*
* @return a popup menu containing every menu item
*/
static JPopupMenu getPopupMenu() {
JPopupMenu menu = new JPopupMenu();
for ( MenuItem item : values() ) {
menu.add( item.menuItem );
if ( item.isEndOfGroup ) {
menu.add( new JSeparator() );
}
}
return menu;
}
/**
* Internal constructor used to set the attributes. Only called by the
* enum type itself.
*
* @param isEndOfGroup indicates if this item is a group ender
* @param activeStates list of active run states of this item
*/
MenuItem(boolean isEndOfGroup, Run.State... activeStates) {
this.isEndOfGroup = isEndOfGroup;
this.activeStates = Arrays.asList( activeStates );
/**
* Enables or disables every menu items depending on the given run state.
* If this state is present in the list of active states for an item, then
* its GUI component is enabled, else it is disabled.
*
* @param state the current run state, cannot be {@code null}
*/
static void setActiveState( Run.State state ) {
if ( state == null ) {
throw new IllegalArgumentException( "Null run state" );
}
for ( MenuItem item : values() ) {
item.menuItem.setEnabled( item.activeStates.contains( state ) );
}
}
if ( name().equals( "OPEN_RECENT" ) ) {
menuItem = new JMenu( toString() );
} else {
menuItem = new JMenuItem( toString() );
}
Icon icon = Llanfair.getResources().getIcon( "jmi/" + name() + ".png" );
menuItem.setIcon( icon );
menuItem.addActionListener( this );
// LOCK & UNLOCK mask each other and Llanfair always start unlocked
if ( name().equals( "UNLOCK" ) ) {
menuItem.setVisible( false );
}
}
/**
* Registers the given {@code ActionListener} to the list of listeners
* interested in capturing events from every menu items.
*
* @param listener the action listener to register
*/
static void addActionListener( ActionListener listener ) {
listeners.add( ActionListener.class, listener );
}
/**
* 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.
*
* @param path the name of the recently opened file
*/
static void recentlyOpened( String path ) {
assert ( path != null );
List<String> recentFiles = Settings.GNR_RCNT.get();
if ( recentFiles.contains( path ) ) {
recentFiles.remove( path );
}
recentFiles.add( 0, path );
/**
* Returns a popup menu composed of all the menu items. A separator is
* inserted after each item which are indicated as end of their group.
*
* @return a popup menu containing every menu item
*/
static JPopupMenu getPopupMenu() {
JPopupMenu menu = new JPopupMenu();
for ( MenuItem item : values() ) {
menu.add( item.menuItem );
if ( item.isEndOfGroup ) {
menu.add( new JSeparator() );
}
}
return menu;
}
if ( recentFiles.size() > MAX_FILES ) {
recentFiles.remove( MAX_FILES );
}
Settings.GNR_RCNT.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.
*/
static void populateRecentlyOpened() {
List<String> recentFiles = Settings.GNR_RCNT.get();
for ( int i = MAX_FILES; i < recentFiles.size(); i++ ) {
recentFiles.remove( i - 1 );
}
OPEN_RECENT.menuItem.removeAll();
for ( String fileName : Settings.GNR_RCNT.get() ) {
String text = fileName;
int index = text.lastIndexOf( File.separatorChar );
if ( index == -1 ) {
int length = text.length();
int start = length - Math.min( length, TRUNCATE );
text = text.substring( start );
if ( start == 0 ) {
text = "[...]" + text;
}
} else {
text = text.substring( index + 1 );
}
JMenuItem jmi = new JMenuItem( text );
jmi.setName( "RECENT" + fileName );
jmi.addActionListener( OPEN_RECENT );
OPEN_RECENT.menuItem.add( jmi );
}
}
/**
* Returns the localized name of this menu item.
*/
@Override public String toString() {
return Language.valueOf( "menuItem_" + name().toLowerCase() ).get();
}
/**
* Enables or disables every menu items depending on the given run state.
* If this state is present in the list of active states for an item, then
* its GUI component is enabled, else it is disabled.
*
* @param state the current run state, cannot be {@code null}
*/
static void setActiveState( Run.State state ) {
if ( state == null ) {
throw new IllegalArgumentException( "Null run state" );
}
for ( MenuItem item : values() ) {
item.menuItem.setEnabled( item.activeStates.contains( state ) );
}
}
/**
* When a GUI component fires an action event, capture it and fire it
* back, setting the source as the enumerate value instead of the GUI
* component.
*/
@Override public void actionPerformed( ActionEvent event ) {
Object source = event.getSource();
if ( source.equals( LOCK.menuItem ) ) {
LOCK.menuItem.setVisible( false );
UNLOCK.menuItem.setVisible( true );
} else if ( source.equals( UNLOCK.menuItem ) ) {
LOCK.menuItem.setVisible( true );
UNLOCK.menuItem.setVisible( false );
}
/**
* Registers the given {@code ActionListener} to the list of listeners
* interested in capturing events from every menu items.
*
* @param listener the action listener to register
*/
static void addActionListener( ActionListener listener ) {
listeners.add( ActionListener.class, listener );
}
JMenuItem jmi = ( JMenuItem ) source;
String name = jmi.getName();
ActionListener[] als = listeners.getListeners( ActionListener.class );
if ( name != null && name.startsWith( "RECENT" ) ) {
event = new ActionEvent(
this, ActionEvent.ACTION_PERFORMED, name.substring( 6 )
);
} else {
event = new ActionEvent(
this, ActionEvent.ACTION_PERFORMED, jmi.getText()
);
}
for ( ActionListener al : als ) {
al.actionPerformed( event );
}
}
/**
* 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.
*
* @param path the name of the recently opened file
*/
static void recentlyOpened( String path ) {
assert ( path != null );
List<String> recentFiles = Settings.GNR_RCNT.get();
if ( recentFiles.contains( path ) ) {
recentFiles.remove( path );
}
recentFiles.add( 0, path );
if ( recentFiles.size() > MAX_FILES ) {
recentFiles.remove( MAX_FILES );
}
Settings.GNR_RCNT.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.
*/
static void populateRecentlyOpened() {
List<String> recentFiles = Settings.GNR_RCNT.get();
for ( int i = MAX_FILES; i < recentFiles.size(); i++ ) {
recentFiles.remove( i - 1 );
}
OPEN_RECENT.menuItem.removeAll();
for ( String fileName : Settings.GNR_RCNT.get() ) {
String text = fileName;
int index = text.lastIndexOf( File.separatorChar );
if ( index == -1 ) {
int length = text.length();
int start = length - Math.min( length, TRUNCATE );
text = text.substring( start );
if ( start == 0 ) {
text = "[...]" + text;
}
} else {
text = text.substring( index + 1 );
}
JMenuItem jmi = new JMenuItem( text );
jmi.setName( "RECENT" + fileName );
jmi.addActionListener( OPEN_RECENT );
OPEN_RECENT.menuItem.add( jmi );
}
}
/**
* Returns the localized name of this menu item.
*/
@Override public String toString() {
return Language.valueOf( "menuItem_" + name().toLowerCase() ).get();
}
/**
* When a GUI component fires an action event, capture it and fire it
* back, setting the source as the enumerate value instead of the GUI
* component.
*/
@Override public void actionPerformed( ActionEvent event ) {
Object source = event.getSource();
if ( source.equals( LOCK.menuItem ) ) {
LOCK.menuItem.setVisible( false );
UNLOCK.menuItem.setVisible( true );
} else if ( source.equals( UNLOCK.menuItem ) ) {
LOCK.menuItem.setVisible( true );
UNLOCK.menuItem.setVisible( false );
}
JMenuItem jmi = ( JMenuItem ) source;
String name = jmi.getName();
ActionListener[] als = listeners.getListeners( ActionListener.class );
if ( name != null && name.startsWith( "RECENT" ) ) {
event = new ActionEvent(
this, ActionEvent.ACTION_PERFORMED, name.substring( 6 )
);
} else {
event = new ActionEvent(
this, ActionEvent.ACTION_PERFORMED, jmi.getText()
);
}
for ( ActionListener al : als ) {
al.actionPerformed( event );
}
}
/**
* When the locale changes, every menu item must be updated to enforce the
* new locale setting.
*/
public static void localeChanged( LocaleEvent event ) {
for ( MenuItem item : values() ) {
item.menuItem.setText( "" + item );
}
}
/**
* When the locale changes, every menu item must be updated to enforce the
* new locale setting.
*/
public static void localeChanged( LocaleEvent event ) {
for ( MenuItem item : values() ) {
item.menuItem.setText( "" + item );
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -17,311 +17,311 @@ import java.io.Serializable;
*/
public class Segment implements Cloneable, Serializable {
// -------------------------------------------------------------- CONSTANTS
// -------------------------------------------------------------- CONSTANTS
/**
* The serial version identifier used to determine the compatibility of the
* different serialized versions of this type. This identifier must change
* when modifications that break backward-compatibility are made to the
* type.
*/
private static final long serialVersionUID = 1001L;
/**
* The serial version identifier used to determine the compatibility of the
* different serialized versions of this type. This identifier must change
* when modifications that break backward-compatibility are made to the
* type.
*/
private static final long serialVersionUID = 1001L;
/**
* Array of legit display sizes for the segments icons.
*/
public static final Integer[] ICON_SIZES = new Integer[] {
16, 24, 32, 40, 48, 56, 64
};
/**
* Array of legit display sizes for the segments icons.
*/
public static final Integer[] ICON_SIZES = new Integer[] {
16, 24, 32, 40, 48, 56, 64
};
/**
* Maximum size allowed for the segments icons. When setting an icon for
* a segment, it will be scaled down to that size if its bigger, but will
* not be scaled up if its smaller.
*/
public static final int ICON_MAX_SIZE = ICON_SIZES[ICON_SIZES.length - 1];
/**
* Maximum size allowed for the segments icons. When setting an icon for
* a segment, it will be scaled down to that size if its bigger, but will
* not be scaled up if its smaller.
*/
public static final int ICON_MAX_SIZE = ICON_SIZES[ICON_SIZES.length - 1];
/**
* Identifier for the time of the segment as defined by the currently set
* compare method.
*/
public static final int SET = 0;
/**
* Identifier for the time of the segment as defined by the currently set
* compare method.
*/
public static final int SET = 0;
/**
* Identifier for the registered time of the segment.
*/
public static final int RUN = 1;
/**
* Identifier for the registered time of the segment.
*/
public static final int RUN = 1;
/**
* Identifier for the best ever registered time of the segment.
*/
public static final int BEST = 2;
/**
* Identifier for the best ever registered time of the segment.
*/
public static final int BEST = 2;
/**
* Identifier for the live time realized on this segment.
*/
public static final int LIVE = 3;
/**
* Identifier for the live time realized on this segment.
*/
public static final int LIVE = 3;
/**
* Identifier for the delta between the live time and the time as defined
* by the currently set compare method, i.e. {@code LIVE - SET}.
*/
public static final int DELTA = 4;
/**
* Identifier for the delta between the live time and the time as defined
* by the currently set compare method, i.e. {@code LIVE - SET}.
*/
public static final int DELTA = 4;
/**
* Identifier for the delta between the live time and the registered time
* of the segment, i.e. {@code LIVE - RUN}.
*/
public static final int DELTA_RUN = 5;
/**
* Identifier for the delta between the live time and the registered time
* of the segment, i.e. {@code LIVE - RUN}.
*/
public static final int DELTA_RUN = 5;
/**
* Identifier for the delta between the live time and the best ever
* registered time of the segment, i.e. {@code LIVE - BEST}.
*/
public static final int DELTA_BEST = 6;
/**
* Identifier for the delta between the live time and the best ever
* registered time of the segment, i.e. {@code LIVE - BEST}.
*/
public static final int DELTA_BEST = 6;
// ------------------------------------------------------------- ATTRIBUTES
// ------------------------------------------------------------- ATTRIBUTES
/**
* Name of the segment.
*/
private String name;
/**
* Name of the segment.
*/
private String name;
/**
* Icon associated with this segment. Can be {@code null} if no icon is to
* be displayed.
*/
private Icon icon;
/**
* Icon associated with this segment. Can be {@code null} if no icon is to
* be displayed.
*/
private Icon icon;
/**
* Registered time for this segment during the best run.
*/
private Time runTime;
/**
* Registered time for this segment during the best run.
*/
private Time runTime;
/**
* Best time ever registered for this segment.
*/
private Time bestTime;
/**
* Best time ever registered for this segment.
*/
private Time bestTime;
/**
* Live time realized on this segment during a run. This value is never
* saved as is but can overwrite {@code runTime} or {@code bestTime}.
*/
private transient Time liveTime;
/**
* Number of milliseconds on the clock when the segment started.
*/
private transient long startTime;
/**
* Live time realized on this segment during a run. This value is never
* saved as is but can overwrite {@code runTime} or {@code bestTime}.
*/
private transient Time liveTime;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Number of milliseconds on the clock when the segment started.
*/
private transient long startTime;
/**
* Creates a new segment of given name and undefined times.
*
* @param name - the name of the segment.
*/
public Segment(String name) {
if (name == null) {
throw new NullPointerException("null segment name");
}
this.name = name;
icon = null;
runTime = null;
bestTime = null;
initializeTransients();
}
/**
* Creates a default segment with a default name and undefined times.
*/
public Segment() {
this("" + Language.UNTITLED);
}
// ----------------------------------------------------------- CONSTRUCTORS
// ---------------------------------------------------------------- GETTERS
/**
* Creates a new segment of given name and undefined times.
*
* @param name - the name of the segment.
*/
public Segment(String name) {
if (name == null) {
throw new NullPointerException("null segment name");
}
this.name = name;
icon = null;
runTime = null;
bestTime = null;
initializeTransients();
}
/**
* Returns the name of the segment.
*
* @return the name of the segment.
*/
public String getName() {
return name;
}
/**
* Returns the icon associated with this segment. Can be {@code null}.
*
* @return the icon of the segment or {@code null}.
*/
public Icon getIcon() {
return icon;
}
/**
* Returns the number of milliseconds on the clock when the segment started.
*
* @return the start time of this segment.
*/
public long getStartTime() {
return startTime;
}
/**
* Returns the given type of time for this segment.
*
* @param type - one of the type identifier.
* @return the segment time of given type.
*/
public Time getTime(int type) {
switch (type) {
case BEST:
return bestTime;
case LIVE:
return liveTime;
case RUN:
return runTime;
case DELTA_RUN:
if (runTime == null) {
return null;
}
return Time.getDelta(liveTime, runTime);
case DELTA_BEST:
if (bestTime == null) {
return null;
}
return Time.getDelta(liveTime, bestTime);
case DELTA:
Time time = getTime();
return (time == null ? null : Time.getDelta(liveTime, time));
default:
return getTime();
}
}
/**
* As specified by {@code Cloneable}, returns a deep copy of the segment.
*/
public Segment clone() {
Segment segment = new Segment(name);
segment.icon = icon;
segment.runTime = (runTime == null ? null : runTime.clone());
segment.bestTime = (bestTime == null ? null : bestTime.clone());
segment.liveTime = (liveTime == null ? null : liveTime.clone());
segment.startTime = startTime;
return segment;
}
// ---------------------------------------------------------------- SETTERS
/**
* Sets the name of the segment to the given string.
*
* @param name - the new name of the segment.
*/
public void setName(String name) {
if (name == null) {
throw new NullPointerException("null name");
}
this.name = name;
}
/**
* Sets the current icon of this segment. Can be {@code null} to remove
* the current icon or indicate that no icon should be used. The icon wil
* be scale down to {@code ICON_MAX_SIZE} if its bigger.
*
* @param icon - the new icon for this segment.
*/
public void setIcon(Icon icon) {
if (icon == null) {
this.icon = null;
} else {
this.icon = Images.rescale(icon, ICON_MAX_SIZE);
}
}
/**
* Sets the number of milliseconds on the clock when the segment started.
* Should only be called by the run owning this segment.
*
* @param startTime - the starting time of this segment.
* @throws IllegalArgumentException if the time is negative.
*/
void setStartTime(long startTime) {
if (startTime < 0L) {
throw new IllegalArgumentException("negative start time");
}
this.startTime = startTime;
}
/**
* Sets the given type of time to the new value. Note that some type of
* times cannot be set (such as {@code DELTA}s.) The new value can be
* {@code null} to indicate undefined times.
*
* @param time - the new time value for the given type.
* @param type - one of the type identifier.
* @throws IllegalArgumentException if the new time value is lower than or
* equal to zero.
*/
public void setTime(Time time, int type, boolean bypass) {
if (!bypass) {
if (time != null && time.compareTo(Time.ZERO) <= 0) {
throw new IllegalArgumentException("" + Language.ILLEGAL_TIME);
}
}
switch (type) {
case BEST: bestTime = time; break;
case LIVE: liveTime = time; break;
case RUN: runTime = time; break;
}
}
public void setTime(Time time, int type) {
setTime(time, type, false);
}
/**
* Creates a default segment with a default name and undefined times.
*/
public Segment() {
this("" + Language.UNTITLED);
}
// ---------------------------------------------------------------- GETTERS
/**
* Returns the name of the segment.
*
* @return the name of the segment.
*/
public String getName() {
return name;
}
/**
* Returns the icon associated with this segment. Can be {@code null}.
*
* @return the icon of the segment or {@code null}.
*/
public Icon getIcon() {
return icon;
}
/**
* Returns the number of milliseconds on the clock when the segment started.
*
* @return the start time of this segment.
*/
public long getStartTime() {
return startTime;
}
/**
* Returns the given type of time for this segment.
*
* @param type - one of the type identifier.
* @return the segment time of given type.
*/
public Time getTime(int type) {
switch (type) {
case BEST:
return bestTime;
case LIVE:
return liveTime;
case RUN:
return runTime;
case DELTA_RUN:
if (runTime == null) {
return null;
}
return Time.getDelta(liveTime, runTime);
case DELTA_BEST:
if (bestTime == null) {
return null;
}
return Time.getDelta(liveTime, bestTime);
case DELTA:
Time time = getTime();
return (time == null ? null : Time.getDelta(liveTime, time));
default:
return getTime();
}
}
/**
* As specified by {@code Cloneable}, returns a deep copy of the segment.
*/
public Segment clone() {
Segment segment = new Segment(name);
segment.icon = icon;
segment.runTime = (runTime == null ? null : runTime.clone());
segment.bestTime = (bestTime == null ? null : bestTime.clone());
segment.liveTime = (liveTime == null ? null : liveTime.clone());
segment.startTime = startTime;
return segment;
}
// ---------------------------------------------------------------- SETTERS
/**
* Sets the name of the segment to the given string.
*
* @param name - the new name of the segment.
*/
public void setName(String name) {
if (name == null) {
throw new NullPointerException("null name");
}
this.name = name;
}
/**
* Sets the current icon of this segment. Can be {@code null} to remove
* the current icon or indicate that no icon should be used. The icon wil
* be scale down to {@code ICON_MAX_SIZE} if its bigger.
*
* @param icon - the new icon for this segment.
*/
public void setIcon(Icon icon) {
if (icon == null) {
this.icon = null;
} else {
this.icon = Images.rescale(icon, ICON_MAX_SIZE);
}
}
/**
* Sets the number of milliseconds on the clock when the segment started.
* Should only be called by the run owning this segment.
*
* @param startTime - the starting time of this segment.
* @throws IllegalArgumentException if the time is negative.
*/
void setStartTime(long startTime) {
if (startTime < 0L) {
throw new IllegalArgumentException("negative start time");
}
this.startTime = startTime;
}
/**
* Sets the given type of time to the new value. Note that some type of
* times cannot be set (such as {@code DELTA}s.) The new value can be
* {@code null} to indicate undefined times.
*
* @param time - the new time value for the given type.
* @param type - one of the type identifier.
* @throws IllegalArgumentException if the new time value is lower than or
* equal to zero.
*/
public void setTime(Time time, int type, boolean bypass) {
if (!bypass) {
if (time != null && time.compareTo(Time.ZERO) <= 0) {
throw new IllegalArgumentException("" + Language.ILLEGAL_TIME);
}
}
switch (type) {
case BEST: bestTime = time; break;
case LIVE: liveTime = time; break;
case RUN: runTime = time; break;
}
}
public void setTime(Time time, int type) {
setTime(time, type, false);
}
// -------------------------------------------------------------- UTILITIES
/**
* Initialize all transient fields.
*/
private void initializeTransients() {
liveTime = null;
startTime = 0L;
}
/**
* Returns the time of this segment as specified by the currently set
* compare method.
*
* @return the time as defined by the current compare method.
*/
private Time getTime() {
switch (Settings.GNR_COMP.get()) {
case BEST_OVERALL_RUN: return runTime;
case SUM_OF_BEST_SEGMENTS: return bestTime;
}
// Should not be reached.
return null;
}
// -------------------------------------------------------------- UTILITIES
/**
* Initialize all transient fields.
*/
private void initializeTransients() {
liveTime = null;
startTime = 0L;
}
/**
* Returns the time of this segment as specified by the currently set
* compare method.
*
* @return the time as defined by the current compare method.
*/
private Time getTime() {
switch (Settings.GNR_COMP.get()) {
case BEST_OVERALL_RUN: return runTime;
case SUM_OF_BEST_SEGMENTS: return bestTime;
}
// Should not be reached.
return null;
}
/**
* Deserialization process. Redefined to initialize transients fields upon
* deserialization.
*/
* Deserialization process. Redefined to initialize transients fields upon
* deserialization.
*/
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
in.defaultReadObject();
initializeTransients();
throws IOException, ClassNotFoundException {
in.defaultReadObject();
initializeTransients();
}
}

View file

@ -23,276 +23,276 @@ import org.fenix.llanfair.config.Settings;
* @version 1.1
*/
public class Time implements Cloneable, Comparable<Time>, Serializable {
/**
* A time of zero milliseconds. This constant can be used for comparisons
* with other times and nothing else.
*/
public static final Time ZERO = new Time();
private static final long serialVersionUID = 1000L;
/**
* A time of zero milliseconds. This constant can be used for comparisons
* with other times and nothing else.
*/
public static final Time ZERO = new Time();
private long milliseconds;
private static final long serialVersionUID = 1000L;
/**
* Creates a default time of zero milliseconds.
*/
public Time() {
milliseconds = 0L;
}
private long milliseconds;
/**
* Creates a time representing a given number of milliseconds. This number
* is truncated to the milliseconds to prevent discrepencies resulting from
* successive calls to {@link System.currentTimeMillis()} and thus ensures
* that the compare method remains accurate.
*
* @param ms the number of milliseconds to represent
*/
public Time(long ms) {
milliseconds = (ms / 10L) * 10L;
}
/**
* Creates a time representing the given decimal number of seconds. This
* number is truncated to the milliseconds to prevent discrepencies
* resulting from successive calls to {@link System.currentTimeMillis()}
* and thus ensures that the compare method remains accurate.
*
* @param seconds the number of seconds to represent
*/
public Time(double seconds) {
this((long) (seconds * 1000.0));
}
/**
* Creates a default time of zero milliseconds.
*/
public Time() {
milliseconds = 0L;
}
/**
* Creates a time representing the given time-stamp. The time-stamp is a
* string describing the time in a format understandable by the user. The
* time-stamp must be formatted according to the following:
*
* <pre>((H)H:)((M)M:)(S)S(.T(H(M)))</pre>
*
* Where a letter represents a digit and parenthesis indicate optionality.
* H stands for hours, M for minutes, S for seconds, T for tenths, H for
* hundredth and M for milliseconds.
*
* @param timestamp a string representation of the time to parse
* @throws IllegalArgumentException if the timestamp cannot be parsed
*/
public Time(String timeStamp) {
try {
milliseconds = parseTimeStamp(timeStamp);
} catch (Exception ex) {
throw new IllegalArgumentException(
Language.INVALID_TIME_STAMP.get(timeStamp)
);
}
}
/**
* Creates a time representing a given number of milliseconds. This number
* is truncated to the milliseconds to prevent discrepencies resulting from
* successive calls to {@link System.currentTimeMillis()} and thus ensures
* that the compare method remains accurate.
*
* @param ms the number of milliseconds to represent
*/
public Time(long ms) {
milliseconds = (ms / 10L) * 10L;
}
/**
* Returns the delta of time between two times. The returned time is
* equivalent to, but more convenient than, the following code:
* {@code new Time(t1.getMilliseconds() - t2.getMilliseconds())}.
*
* @param t1 the first time
* @param t2 the time to substract from the first
* @return the delta of time between the two times
*/
public static Time getDelta(Time t1, Time t2) {
if (t1 == null) {
t1 = new Time();
} else if (t2 == null) {
t2 = new Time();
}
return new Time(t1.milliseconds - t2.milliseconds);
}
/**
* Creates a time representing the given decimal number of seconds. This
* number is truncated to the milliseconds to prevent discrepencies
* resulting from successive calls to {@link System.currentTimeMillis()}
* and thus ensures that the compare method remains accurate.
*
* @param seconds the number of seconds to represent
*/
public Time(double seconds) {
this((long) (seconds * 1000.0));
}
/**
* Returns the number of milliseconds represented by that time.
*
* @return the number of milliseconds represented by that time
*/
public long getMilliseconds() {
return milliseconds;
}
/**
* Adds the given time to this time. The number of milliseconds
* represented by this time object is now equals to:
* {@code getMilliseconds() + time.getMilliseconds()}
*
* @param time the time object to add to this time
*/
public void add(Time time) {
milliseconds += (time == null ? 0L : time.milliseconds);
}
public String toString(boolean signed, Accuracy accuracy) {
if (signed) {
return (milliseconds > 0L ? "+" : "-") + toString(false, accuracy);
}
long time = Math.abs(milliseconds);
long cen = (time % 1000L) / 10L;
long sec;
// Round to the nearest tenth.
if (accuracy == Accuracy.TENTH) {
cen = Math.round((double) cen / 10L);
if (cen == 10L) {
cen = 0L;
time = time + 1000L;
}
}
// Round to the nearest second.
if (accuracy == Accuracy.SECONDS) {
sec = Math.round((double) time / 1000);
} else {
sec = time / 1000L;
}
long min = sec / 60L;
sec = sec % 60L;
long hou = min / 60L;
min = min % 60L;
/**
* Creates a time representing the given time-stamp. The time-stamp is a
* string describing the time in a format understandable by the user. The
* time-stamp must be formatted according to the following:
*
* <pre>((H)H:)((M)M:)(S)S(.T(H(M)))</pre>
*
* Where a letter represents a digit and parenthesis indicate optionality.
* H stands for hours, M for minutes, S for seconds, T for tenths, H for
* hundredth and M for milliseconds.
*
* @param timestamp a string representation of the time to parse
* @throws IllegalArgumentException if the timestamp cannot be parsed
*/
public Time(String timeStamp) {
try {
milliseconds = parseTimeStamp(timeStamp);
} catch (Exception ex) {
throw new IllegalArgumentException(
Language.INVALID_TIME_STAMP.get(timeStamp)
);
}
}
if (hou == 0L) {
if (min == 0L) {
switch (accuracy) {
case HUNDREDTH:
return String.format("%d.%02d", sec, cen);
case TENTH:
return String.format("%d.%d", sec, cen);
case SECONDS:
return String.format("%d", sec);
}
}
switch (accuracy) {
case HUNDREDTH:
return String.format("%d:%02d.%02d", min, sec, cen);
case TENTH:
return String.format("%d:%02d.%d", min, sec, cen);
case SECONDS:
return String.format("%d:%02d", min, sec);
}
}
switch (accuracy) {
case HUNDREDTH:
return String.format("%d:%02d:%02d.%02d", hou, min, sec, cen);
case TENTH:
return String.format("%d:%02d:%02d.%d", hou, min, sec, cen);
case SECONDS:
return String.format("%d:%02d:%02d", hou, min, sec);
}
// Should not be reachable.
return null;
}
/**
* A time represents itself as a string using the traditional format
* {@code H:M:S}. The global accuracy determines the presence of tenths
* or hundredths of a second and if the get should be rounded. Every digit
* of higher-order than a second is only displayed if necessary. The sign
* parameter determines if the string should be preceded by a plus or minus
* sign depending on the number of milliseconds. If not, only the absolute
* number is used.
*
* @param signed if this time is to be displayed as a delta of time
* @return a string representation of this time object
*/
public String toString(boolean signed) {
return toString(signed, Settings.GNR_ACCY.get());
}
/**
* A time represents itself as a string using the traditional format
* {@code H:M:S}. The accuracy parameter determines the presence of tenths
* or hundredths of a second and if the get should be rounded. Every digit
* of higher-order than a second is only displayed if necessary.
*
* @param accuracy the target accuracy to display this time in
* @return a string representation of this time object
*/
public String toString(Accuracy accuracy) {
return toString(false, accuracy);
}
/**
* A time represents itself as a string using the traditional format
* {@code H:M:S}. The accuracy setting determines the presence of tenths
* or hundredths of a second and if the get should be rounded. Every digit
* of higher-order than a second is only displayed if necessary.
*
* @return a string representation of this time object
*/
@Override public String toString() {
return toString(false);
}
/**
* {@inheritDoc} Returns a deep-copy of this time object.
*/
@Override public Time clone() {
return new Time(milliseconds);
}
/**
* Returns the delta of time between two times. The returned time is
* equivalent to, but more convenient than, the following code:
* {@code new Time(t1.getMilliseconds() - t2.getMilliseconds())}.
*
* @param t1 the first time
* @param t2 the time to substract from the first
* @return the delta of time between the two times
*/
public static Time getDelta(Time t1, Time t2) {
if (t1 == null) {
t1 = new Time();
} else if (t2 == null) {
t2 = new Time();
}
return new Time(t1.milliseconds - t2.milliseconds);
}
/**
* {@inheritDoc} Two time objects are equal if and only if they represent
* the same amount of milliseconds.
*/
@Override public boolean equals(Object obj) {
if (!(obj instanceof Time)) {
return false;
}
Time time = (Time) obj;
return (milliseconds == time.milliseconds);
}
/**
* Returns the number of milliseconds represented by that time.
*
* @return the number of milliseconds represented by that time
*/
public long getMilliseconds() {
return milliseconds;
}
/**
* {@inheritDoc} The hash code of a time object is its number of
* milliseconds cast into an integer get. As such, the load factor of a
* table storing time objects is floored by {@code Integer#MAX_VALUE}.
*/
@Override public int hashCode() {
return (int) milliseconds;
}
/**
* Adds the given time to this time. The number of milliseconds
* represented by this time object is now equals to:
* {@code getMilliseconds() + time.getMilliseconds()}
*
* @param time the time object to add to this time
*/
public void add(Time time) {
milliseconds += (time == null ? 0L : time.milliseconds);
}
/**
* {@inheritDoc} Time objects are compared using their amount of
* milliseconds
*/
@Override public int compareTo(Time time) {
if (time == null) {
return -1;
}
return ((Long) milliseconds).compareTo(time.milliseconds);
}
public String toString(boolean signed, Accuracy accuracy) {
if (signed) {
return (milliseconds > 0L ? "+" : "-") + toString(false, accuracy);
}
long time = Math.abs(milliseconds);
long cen = (time % 1000L) / 10L;
long sec;
/**
* Parses a given time-stamp and converts it to a number of milliseconds.
*
* @param timestamp the timestamp to parse
* @return the number of milliseconds represented by the stamp
*/
private long parseTimeStamp(String timestamp) {
String seconds = timestamp;
long millis = 0L;
// Round to the nearest tenth.
if (accuracy == Accuracy.TENTH) {
cen = Math.round((double) cen / 10L);
if (cen == 10L) {
cen = 0L;
time = time + 1000L;
}
}
// Hours or minutes in the stamp.
if (timestamp.contains(":")) {
String[] split = timestamp.split(":");
if (split.length > 3) {
throw new IllegalArgumentException();
}
// We keep the number of seconds and lower for later.
seconds = split[split.length - 1];
// Round to the nearest second.
if (accuracy == Accuracy.SECONDS) {
sec = Math.round((double) time / 1000);
} else {
sec = time / 1000L;
}
long min = sec / 60L;
sec = sec % 60L;
long hou = min / 60L;
min = min % 60L;
for (int i = 0; i < split.length - 1; i++) {
long power = (long) Math.pow(60, split.length - i - 1);
millis += Long.valueOf(split[i]) * power * 1000L;
}
}
double value = Double.parseDouble(seconds);
return (millis + (long) (value * 1000.0));
}
if (hou == 0L) {
if (min == 0L) {
switch (accuracy) {
case HUNDREDTH:
return String.format("%d.%02d", sec, cen);
case TENTH:
return String.format("%d.%d", sec, cen);
case SECONDS:
return String.format("%d", sec);
}
}
switch (accuracy) {
case HUNDREDTH:
return String.format("%d:%02d.%02d", min, sec, cen);
case TENTH:
return String.format("%d:%02d.%d", min, sec, cen);
case SECONDS:
return String.format("%d:%02d", min, sec);
}
}
switch (accuracy) {
case HUNDREDTH:
return String.format("%d:%02d:%02d.%02d", hou, min, sec, cen);
case TENTH:
return String.format("%d:%02d:%02d.%d", hou, min, sec, cen);
case SECONDS:
return String.format("%d:%02d:%02d", hou, min, sec);
}
// Should not be reachable.
return null;
}
/**
* A time represents itself as a string using the traditional format
* {@code H:M:S}. The global accuracy determines the presence of tenths
* or hundredths of a second and if the get should be rounded. Every digit
* of higher-order than a second is only displayed if necessary. The sign
* parameter determines if the string should be preceded by a plus or minus
* sign depending on the number of milliseconds. If not, only the absolute
* number is used.
*
* @param signed if this time is to be displayed as a delta of time
* @return a string representation of this time object
*/
public String toString(boolean signed) {
return toString(signed, Settings.GNR_ACCY.get());
}
/**
* A time represents itself as a string using the traditional format
* {@code H:M:S}. The accuracy parameter determines the presence of tenths
* or hundredths of a second and if the get should be rounded. Every digit
* of higher-order than a second is only displayed if necessary.
*
* @param accuracy the target accuracy to display this time in
* @return a string representation of this time object
*/
public String toString(Accuracy accuracy) {
return toString(false, accuracy);
}
/**
* A time represents itself as a string using the traditional format
* {@code H:M:S}. The accuracy setting determines the presence of tenths
* or hundredths of a second and if the get should be rounded. Every digit
* of higher-order than a second is only displayed if necessary.
*
* @return a string representation of this time object
*/
@Override public String toString() {
return toString(false);
}
/**
* {@inheritDoc} Returns a deep-copy of this time object.
*/
@Override public Time clone() {
return new Time(milliseconds);
}
/**
* {@inheritDoc} Two time objects are equal if and only if they represent
* the same amount of milliseconds.
*/
@Override public boolean equals(Object obj) {
if (!(obj instanceof Time)) {
return false;
}
Time time = (Time) obj;
return (milliseconds == time.milliseconds);
}
/**
* {@inheritDoc} The hash code of a time object is its number of
* milliseconds cast into an integer get. As such, the load factor of a
* table storing time objects is floored by {@code Integer#MAX_VALUE}.
*/
@Override public int hashCode() {
return (int) milliseconds;
}
/**
* {@inheritDoc} Time objects are compared using their amount of
* milliseconds
*/
@Override public int compareTo(Time time) {
if (time == null) {
return -1;
}
return ((Long) milliseconds).compareTo(time.milliseconds);
}
/**
* Parses a given time-stamp and converts it to a number of milliseconds.
*
* @param timestamp the timestamp to parse
* @return the number of milliseconds represented by the stamp
*/
private long parseTimeStamp(String timestamp) {
String seconds = timestamp;
long millis = 0L;
// Hours or minutes in the stamp.
if (timestamp.contains(":")) {
String[] split = timestamp.split(":");
if (split.length > 3) {
throw new IllegalArgumentException();
}
// We keep the number of seconds and lower for later.
seconds = split[split.length - 1];
for (int i = 0; i < split.length - 1; i++) {
long power = (long) Math.pow(60, split.length - i - 1);
millis += Long.valueOf(split[i]) * power * 1000L;
}
}
double value = Double.parseDouble(seconds);
return (millis + (long) (value * 1000.0));
}
}

View file

@ -1,20 +1,21 @@
package org.fenix.llanfair.config;
import java.io.Serializable;
import org.fenix.llanfair.Language;
import java.io.Serializable;
public enum Accuracy implements Serializable {
SECONDS,
TENTH,
HUNDREDTH;
private static final long serialVersionUID = 1000L;
@Override public String toString() {
return Language.valueOf("accuracy_" + name().toLowerCase()).get();
}
SECONDS,
TENTH,
HUNDREDTH;
private static final long serialVersionUID = 1000L;
@Override public String toString() {
return Language.valueOf("accuracy_" + name().toLowerCase()).get();
}
}

View file

@ -1,22 +1,23 @@
package org.fenix.llanfair.config;
import java.io.Serializable;
import org.fenix.llanfair.Language;
import java.io.Serializable;
/**
*
* @author Xavier
*/
public enum Compare implements Serializable {
BEST_OVERALL_RUN,
SUM_OF_BEST_SEGMENTS;
private static final long serialVersionUID = 1000L;
@Override public String toString() {
return Language.valueOf(
"compare_" + name().toLowerCase().replaceAll("\\.", "_")
).get();
}
BEST_OVERALL_RUN,
SUM_OF_BEST_SEGMENTS;
private static final long serialVersionUID = 1000L;
@Override public String toString() {
return Language.valueOf(
"compare_" + name().toLowerCase().replaceAll("\\.", "_")
).get();
}
}

View file

@ -1,21 +1,22 @@
package org.fenix.llanfair.config;
import java.io.Serializable;
import org.fenix.llanfair.Language;
import java.io.Serializable;
/**
*
* @author Xavier
*/
public enum Merge implements Serializable {
NONE,
LIVE,
DELTA;
private static final long serialVersionUID = 1000L;
@Override public String toString() {
return Language.valueOf("merge_" + name().toLowerCase()).get();
}
NONE,
LIVE,
DELTA;
private static final long serialVersionUID = 1000L;
@Override public String toString() {
return Language.valueOf("merge_" + name().toLowerCase()).get();
}
}

View file

@ -26,403 +26,403 @@ import org.fenix.utils.config.Configuration;
* @version 1.3
*/
public class Settings {
/* GENERIC properties */
public static final Property<Boolean> GNR_ATOP =
new Property<Boolean>( "alwaysOnTop" );
public static final Property<Locale> GNR_LANG =
new Property<Locale>( "language" );
public static final Property<Locale> GNR_VLNG =
new Property<Locale>( "viewerLanguage" );
public static final Property<List<String>> GNR_RCNT =
new Property<List<String>>( "recentFiles" );
public static final Property<Point> GNR_COOR =
new Property<Point>( "coordinates" );
public static final Property<Dimension> GNR_SIZE =
new Property<Dimension>( "dimension" );
public static final Property<Compare> GNR_COMP =
new Property<Compare>( "compareMethod" );
public static final Property<Accuracy> GNR_ACCY =
new Property<Accuracy>( "accuracy" );
public static final Property<Boolean> GNR_WARN =
new Property<Boolean>( "warnOnReset" );
/* COLOR properties */
public static final Property<Color> CLR_BACK =
new Property<Color>( "color.background" );
public static final Property<Color> CLR_FORE =
new Property<Color>( "color.foreground" );
public static final Property<Color> CLR_TIME =
new Property<Color>( "color.time" );
public static final Property<Color> CLR_TIMR =
new Property<Color>( "color.timer" );
public static final Property<Color> CLR_GAIN =
new Property<Color>( "color.timeGained" );
public static final Property<Color> CLR_LOST =
new Property<Color>( "color.timeLost" );
public static final Property<Color> CLR_RCRD =
new Property<Color>( "color.newRecord" );
public static final Property<Color> CLR_TITL =
new Property<Color>( "color.title" );
public static final Property<Color> CLR_HIGH =
new Property<Color>( "color.highlight" );
public static final Property<Color> CLR_SPRT =
new Property<Color>( "color.separators" );
/* HOTKEY properties */
public static final Property<Integer> KEY_SPLT =
new Property<Integer>( "hotkey.split" );
public static final Property<Integer> KEY_USPL =
new Property<Integer>( "hotkey.unsplit" );
public static final Property<Integer> KEY_SKIP =
new Property<Integer>( "hotkey.skip" );
public static final Property<Integer> KEY_RSET =
new Property<Integer>( "hotkey.reset" );
public static final Property<Integer> KEY_STOP =
new Property<Integer>( "hotkey.stop" );
public static final Property<Integer> KEY_PAUS =
new Property<Integer>( "hotkey.pause" );
public static final Property<Integer> KEY_LOCK =
new Property<Integer>( "hotkey.lock" );
/* HEADER properties */
public static final Property<Boolean> HDR_TTLE =
new Property<Boolean>( "header.goal" );
public static final Property<Boolean> HDR_GOAL =
new Property<Boolean>( "header.title" );
/* HISTORY properties */
public static final Property<Integer> HST_ROWS =
new Property<Integer>( "history.rowCount" );
public static final Property<Boolean> HST_TABL =
new Property<Boolean>( "history.tabular" );
public static final Property<Boolean> HST_BLNK =
new Property<Boolean>( "history.blankRows" );
public static final Property<Boolean> HST_LINE =
new Property<Boolean>( "history.multiline" );
public static final Property<Merge> HST_MERG =
new Property<Merge>( "history.merge" );
public static final Property<Boolean> HST_LIVE =
new Property<Boolean>( "history.liveTimes" );
public static final Property<Boolean> HST_DLTA =
new Property<Boolean>( "history.deltas" );
public static final Property<Boolean> HST_ICON =
new Property<Boolean>( "history.icons" );
public static final Property<Integer> HST_ICSZ =
new Property<Integer>( "history.iconSize" );
public static final Property<Integer> HST_OFFS =
new Property<Integer>( "history.offset" );
public static final Property<Boolean> HST_LAST =
new Property<Boolean>( "history.alwaysShowLast" );
public static final Property<Font> HST_SFNT =
new Property<Font>( "history.segmentFont" );
public static final Property<Font> HST_TFNT =
new Property<Font>( "history.timeFont" );
/* CORE properties */
public static final Property<Accuracy> COR_ACCY =
new Property<Accuracy>( "core.accuracy" );
public static final Property<Boolean> COR_ICON =
new Property<Boolean>( "core.icons" );
public static final Property<Integer> COR_ICSZ =
new Property<Integer>( "core.iconSize" );
public static final Property<Boolean> COR_NAME =
new Property<Boolean>( "core.segmentName" );
public static final Property<Boolean> COR_SPLT =
new Property<Boolean>( "core.splitTime" );
public static final Property<Boolean> COR_SEGM =
new Property<Boolean>( "core.segmentTime" );
public static final Property<Boolean> COR_BEST =
new Property<Boolean>( "core.bestTime" );
public static final Property<Boolean> COR_STMR =
new Property<Boolean>( "core.segmentTimer" );
public static final Property<Font> COR_TFNT =
new Property<Font>( "core.timerFont" );
public static final Property<Font> COR_SFNT =
new Property<Font>( "core.segmentTimerFont" );
/* GRAPH properties */
public static final Property<Boolean> GPH_SHOW =
new Property<Boolean>( "graph.display" );
public static final Property<Float> GPH_SCAL =
new Property<Float>( "graph.scale" );
/* FOOTER properties */
public static final Property<Boolean> FOO_SHOW =
new Property<Boolean>( "footer.display" );
public static final Property<Boolean> FOO_SPLT =
new Property<Boolean>( "footer.useSplitData" );
public static final Property<Boolean> FOO_VERB =
new Property<Boolean>( "footer.verbose" );
public static final Property<Boolean> FOO_BEST =
new Property<Boolean>( "footer.bestTime" );
public static final Property<Boolean> FOO_LINE =
new Property<Boolean>( "footer.multiline" );
public static final Property<Boolean> FOO_DLBL =
new Property<Boolean>( "footer.deltaLabels" );
private static Configuration global = null;
private static Run run = null;
/**
* Sets the currently opened run. The run will be asked for its local
* configuration first when retrieving a property. If the run does not have
* the given property its value will be taken from the global configuration.
*
* @param run the run currently viewed by Llanfair, cannot be {@code null}
*/
public static void setRun( Run run ) {
if ( run == null ) {
throw new NullPointerException( "Null run" );
}
Settings.run = run;
}
/* GENERIC properties */
/**
* Returns a list of all the properties whose key starts with the given
* prefix. If given an empty string, every properties are returned.
*
* @param prefix the prefix for which a list of property is asked
* @return the list of all properties whose key starts with the prefix
*/
public static List<Property<?>> getAll( String prefix ) {
if ( prefix == null ) {
throw new NullPointerException( "Null prefix string" );
}
List<Property<?>> list = new ArrayList<Property<?>>();
for ( Property<?> property : Property.P ) {
if ( property.key.startsWith( prefix ) ) {
list.add( property );
}
}
return list;
}
public static final Property<Boolean> GNR_ATOP =
new Property<Boolean>( "alwaysOnTop" );
public static final Property<Locale> GNR_LANG =
new Property<Locale>( "language" );
public static final Property<Locale> GNR_VLNG =
new Property<Locale>( "viewerLanguage" );
public static final Property<List<String>> GNR_RCNT =
new Property<List<String>>( "recentFiles" );
public static final Property<Point> GNR_COOR =
new Property<Point>( "coordinates" );
public static final Property<Dimension> GNR_SIZE =
new Property<Dimension>( "dimension" );
public static final Property<Compare> GNR_COMP =
new Property<Compare>( "compareMethod" );
public static final Property<Accuracy> GNR_ACCY =
new Property<Accuracy>( "accuracy" );
public static final Property<Boolean> GNR_WARN =
new Property<Boolean>( "warnOnReset" );
/* COLOR properties */
public static final Property<Color> CLR_BACK =
new Property<Color>( "color.background" );
public static final Property<Color> CLR_FORE =
new Property<Color>( "color.foreground" );
public static final Property<Color> CLR_TIME =
new Property<Color>( "color.time" );
public static final Property<Color> CLR_TIMR =
new Property<Color>( "color.timer" );
public static final Property<Color> CLR_GAIN =
new Property<Color>( "color.timeGained" );
public static final Property<Color> CLR_LOST =
new Property<Color>( "color.timeLost" );
public static final Property<Color> CLR_RCRD =
new Property<Color>( "color.newRecord" );
public static final Property<Color> CLR_TITL =
new Property<Color>( "color.title" );
public static final Property<Color> CLR_HIGH =
new Property<Color>( "color.highlight" );
public static final Property<Color> CLR_SPRT =
new Property<Color>( "color.separators" );
/* HOTKEY properties */
public static final Property<Integer> KEY_SPLT =
new Property<Integer>( "hotkey.split" );
public static final Property<Integer> KEY_USPL =
new Property<Integer>( "hotkey.unsplit" );
public static final Property<Integer> KEY_SKIP =
new Property<Integer>( "hotkey.skip" );
public static final Property<Integer> KEY_RSET =
new Property<Integer>( "hotkey.reset" );
public static final Property<Integer> KEY_STOP =
new Property<Integer>( "hotkey.stop" );
public static final Property<Integer> KEY_PAUS =
new Property<Integer>( "hotkey.pause" );
public static final Property<Integer> KEY_LOCK =
new Property<Integer>( "hotkey.lock" );
/* HEADER properties */
public static final Property<Boolean> HDR_TTLE =
new Property<Boolean>( "header.goal" );
public static final Property<Boolean> HDR_GOAL =
new Property<Boolean>( "header.title" );
/* HISTORY properties */
public static final Property<Integer> HST_ROWS =
new Property<Integer>( "history.rowCount" );
public static final Property<Boolean> HST_TABL =
new Property<Boolean>( "history.tabular" );
public static final Property<Boolean> HST_BLNK =
new Property<Boolean>( "history.blankRows" );
public static final Property<Boolean> HST_LINE =
new Property<Boolean>( "history.multiline" );
public static final Property<Merge> HST_MERG =
new Property<Merge>( "history.merge" );
public static final Property<Boolean> HST_LIVE =
new Property<Boolean>( "history.liveTimes" );
public static final Property<Boolean> HST_DLTA =
new Property<Boolean>( "history.deltas" );
public static final Property<Boolean> HST_ICON =
new Property<Boolean>( "history.icons" );
public static final Property<Integer> HST_ICSZ =
new Property<Integer>( "history.iconSize" );
public static final Property<Integer> HST_OFFS =
new Property<Integer>( "history.offset" );
public static final Property<Boolean> HST_LAST =
new Property<Boolean>( "history.alwaysShowLast" );
public static final Property<Font> HST_SFNT =
new Property<Font>( "history.segmentFont" );
public static final Property<Font> HST_TFNT =
new Property<Font>( "history.timeFont" );
/* CORE properties */
public static final Property<Accuracy> COR_ACCY =
new Property<Accuracy>( "core.accuracy" );
public static final Property<Boolean> COR_ICON =
new Property<Boolean>( "core.icons" );
public static final Property<Integer> COR_ICSZ =
new Property<Integer>( "core.iconSize" );
public static final Property<Boolean> COR_NAME =
new Property<Boolean>( "core.segmentName" );
public static final Property<Boolean> COR_SPLT =
new Property<Boolean>( "core.splitTime" );
public static final Property<Boolean> COR_SEGM =
new Property<Boolean>( "core.segmentTime" );
public static final Property<Boolean> COR_BEST =
new Property<Boolean>( "core.bestTime" );
public static final Property<Boolean> COR_STMR =
new Property<Boolean>( "core.segmentTimer" );
public static final Property<Font> COR_TFNT =
new Property<Font>( "core.timerFont" );
public static final Property<Font> COR_SFNT =
new Property<Font>( "core.segmentTimerFont" );
/* GRAPH properties */
public static final Property<Boolean> GPH_SHOW =
new Property<Boolean>( "graph.display" );
public static final Property<Float> GPH_SCAL =
new Property<Float>( "graph.scale" );
/* FOOTER properties */
public static final Property<Boolean> FOO_SHOW =
new Property<Boolean>( "footer.display" );
public static final Property<Boolean> FOO_SPLT =
new Property<Boolean>( "footer.useSplitData" );
public static final Property<Boolean> FOO_VERB =
new Property<Boolean>( "footer.verbose" );
public static final Property<Boolean> FOO_BEST =
new Property<Boolean>( "footer.bestTime" );
public static final Property<Boolean> FOO_LINE =
new Property<Boolean>( "footer.multiline" );
public static final Property<Boolean> FOO_DLBL =
new Property<Boolean>( "footer.deltaLabels" );
private static Configuration global = null;
private static Run run = null;
/**
* Sets the currently opened run. The run will be asked for its local
* configuration first when retrieving a property. If the run does not have
* the given property its value will be taken from the global configuration.
*
* @param run the run currently viewed by Llanfair, cannot be {@code null}
*/
public static void setRun( Run run ) {
if ( run == null ) {
throw new NullPointerException( "Null run" );
}
Settings.run = run;
}
/**
* Returns a list of all the properties whose key starts with the given
* prefix. If given an empty string, every properties are returned.
*
* @param prefix the prefix for which a list of property is asked
* @return the list of all properties whose key starts with the prefix
*/
public static List<Property<?>> getAll( String prefix ) {
if ( prefix == null ) {
throw new NullPointerException( "Null prefix string" );
}
List<Property<?>> list = new ArrayList<Property<?>>();
for ( Property<?> property : Property.P ) {
if ( property.key.startsWith( prefix ) ) {
list.add( property );
}
}
return list;
}
/**
* Registers a new {@code PropertyChangeListener} with the settings.
* Whenever a property sees its value updated globally or locally, listeners
* are warned of the update.
*
* @param pcl the listener to register with the settings
*/
public static void addPropertyChangeListener( PropertyChangeListener pcl ) {
if ( pcl == null ) {
throw new NullPointerException( "Null property listener" );
}
global.addPropertyChangeListener( pcl );
if ( run != null ) {
run.addSettingChangeListener( pcl );
}
}
/**
* Registers a new {@code PropertyChangeListener} with the settings.
* Whenever a property sees its value updated globally or locally, listeners
* are warned of the update.
*
* @param pcl the listener to register with the settings
*/
public static void addPropertyChangeListener( PropertyChangeListener pcl ) {
if ( pcl == null ) {
throw new NullPointerException( "Null property listener" );
}
global.addPropertyChangeListener( pcl );
if ( run != null ) {
run.addSettingChangeListener( pcl );
}
}
/**
* Saves the global configuration in {@code llanfair.xml} in the working
* directory. If such a file does not exist, it is created.
*/
public static void save() {
global.serialize();
}
/**
* 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
* 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( "./llanfair.xml" ) );
if ( global.isEmpty() ) {
setDefaultValues();
}
}
/**
* Saves the global configuration in {@code llanfair.xml} in the working
* directory. If such a file does not exist, it is created.
*/
public static void save() {
global.serialize();
}
/**
* 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.
*/
private static void setDefaultValues() {
global.put( GNR_ATOP.key, true );
global.put( GNR_LANG.key, Locale.ENGLISH );
global.put( GNR_VLNG.key, Locale.ENGLISH );
global.put( GNR_RCNT.key, new ArrayList<String>() );
global.put( GNR_COOR.key, null );
global.put( GNR_SIZE.key, null );
global.put( GNR_COMP.key, Compare.BEST_OVERALL_RUN );
global.put( GNR_ACCY.key, Accuracy.TENTH );
global.put( GNR_WARN.key, true );
global.put( CLR_BACK.key, Color.decode( "0x000000" ) );
global.put( CLR_FORE.key, Color.decode( "0xc0c0c0" ) );
global.put( CLR_TIME.key, Color.decode( "0xffffff" ) );
global.put( CLR_TIMR.key, Color.decode( "0x22cc22" ) );
global.put( CLR_GAIN.key, Color.decode( "0x6295fc" ) );
global.put( CLR_LOST.key, Color.decode( "0xe82323" ) );
global.put( CLR_RCRD.key, Color.decode( "0xf0b012" ) );
global.put( CLR_TITL.key, Color.decode( "0xf0b012" ) );
global.put( CLR_HIGH.key, Color.decode( "0xffffff" ) );
global.put( CLR_SPRT.key, Color.decode( "0x666666" ) );
global.put( KEY_SPLT.key, -1 );
global.put( KEY_USPL.key, -1 );
global.put( KEY_SKIP.key, -1 );
global.put( KEY_RSET.key, -1 );
global.put( KEY_STOP.key, -1 );
global.put( KEY_PAUS.key, -1 );
global.put( KEY_LOCK.key, -1 );
global.put( HDR_TTLE.key, true );
global.put( HDR_GOAL.key, true );
global.put( HST_ROWS.key, 8 );
global.put( HST_TABL.key, true );
global.put( HST_BLNK.key, false );
global.put( HST_LINE.key, false );
global.put( HST_MERG.key, Merge.LIVE );
global.put( HST_LIVE.key, true );
global.put( HST_DLTA.key, true );
global.put( HST_ICON.key, true );
global.put( HST_ICSZ.key, 16 );
global.put( HST_OFFS.key, 0 );
global.put( HST_LAST.key, true );
global.put( HST_SFNT.key, Font.decode( "Arial-12" ) );
global.put( HST_TFNT.key, Font.decode( "Arial-11" ) );
global.put( COR_ACCY.key, Accuracy.HUNDREDTH );
global.put( COR_ICON.key, true );
global.put( COR_ICSZ.key, 40 );
global.put( COR_NAME.key, true );
global.put( COR_SPLT.key, false );
global.put( COR_SEGM.key, true );
global.put( COR_BEST.key, true );
global.put( COR_STMR.key, true );
global.put( COR_TFNT.key, Font.decode( "Digitalism-32" ) );
global.put( COR_SFNT.key, Font.decode( "Digitalism-18" ) );
global.put( GPH_SHOW.key, true );
global.put( GPH_SCAL.key, 3.0F );
global.put( FOO_SHOW.key, true );
global.put( FOO_VERB.key, true );
global.put( FOO_SPLT.key, false );
global.put( FOO_BEST.key, true );
global.put( FOO_LINE.key, true );
global.put( FOO_DLBL.key, true );
}
/**
* Polymorphic property object. It is merely a string identifying the
* property and serving as an interface to the configuration. When asked
* for its value, the property will return the global value unless a local
* value has been defined. Will return a statically typecasted value.
*
* @param <T> the type of the values of the property
* @author Xavier "Xunkar" Sencert
* @version 1.0
*/
public static class Property<T> {
private static final List<Property<?>> P = new ArrayList<Property<?>>();
private String key;
/**
* Creates a new property of given key. If the key contains a dot, the
* property name is interpreted as {@code section.key} allowing callers
* to grab a submap of the configuration with all properties starting
* with {@code section}.
*
* @param fullKey the name of the property
*/
private Property( String fullKey ) {
this.key = fullKey;
P.add( this );
}
/**
* 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
* 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( "./llanfair.xml" ) );
if ( global.isEmpty() ) {
setDefaultValues();
}
}
/**
* Returns the key string of this property.
*
* @return the key string of this property
*/
public String getKey() {
return key;
}
/**
* Returns the value assigned to this property. The value will be first
* read from the local configuration, and if no value has been defined
* for this property, it will be read from the global configuration. The
* first property to call this method will trigger the global
* configuration to be read and loaded in memory.
*
* @return the local value of this property, or the global one if there
* isn't a locally defined value
*/
public T get() {
if ( global == null ) {
retrieve();
}
if ( run != null && run.containsSetting( key ) ) {
return run.<T>getSetting( key );
}
return global.<T>get( key );
}
/**
* 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.
*/
private static void setDefaultValues() {
global.put( GNR_ATOP.key, true );
global.put( GNR_LANG.key, Locale.ENGLISH );
global.put( GNR_VLNG.key, Locale.ENGLISH );
global.put( GNR_RCNT.key, new ArrayList<String>() );
global.put( GNR_COOR.key, null );
global.put( GNR_SIZE.key, null );
global.put( GNR_COMP.key, Compare.BEST_OVERALL_RUN );
global.put( GNR_ACCY.key, Accuracy.TENTH );
global.put( GNR_WARN.key, true );
/**
* Sets the value of this property in the global configuration.
*
* @param value the value to assign to this property
*/
public void set( T value ) {
set( value, false );
}
/**
* Sets the value of this property. The value of {@code locally}
* determines if the value must be stored in the local or global
* configuration.
*
* @param valuethe value to assign to this property
* @param locally if the value must be stored in the local configuration
*/
public void set( T value, boolean locally ) {
if ( locally ) {
run.putSetting( key, value );
} else {
global.put( key, value );
}
}
/**
* Compares a property to a given string. This property is equal to
* the given string if and only if the string is equal to the full key
* of this property.
*
* @param str the string to compare this property against
* @return {@code true} if the string equals this property full key
*/
public boolean equals( String str ) {
return key.equals( str );
}
global.put( CLR_BACK.key, Color.decode( "0x000000" ) );
global.put( CLR_FORE.key, Color.decode( "0xc0c0c0" ) );
global.put( CLR_TIME.key, Color.decode( "0xffffff" ) );
global.put( CLR_TIMR.key, Color.decode( "0x22cc22" ) );
global.put( CLR_GAIN.key, Color.decode( "0x6295fc" ) );
global.put( CLR_LOST.key, Color.decode( "0xe82323" ) );
global.put( CLR_RCRD.key, Color.decode( "0xf0b012" ) );
global.put( CLR_TITL.key, Color.decode( "0xf0b012" ) );
global.put( CLR_HIGH.key, Color.decode( "0xffffff" ) );
global.put( CLR_SPRT.key, Color.decode( "0x666666" ) );
/**
* Returns the localized name of this property.
*/
@Override public String toString() {
return Language.valueOf(
"setting_" + key.replaceAll( "\\.", "_" )
).get();
}
}
global.put( KEY_SPLT.key, -1 );
global.put( KEY_USPL.key, -1 );
global.put( KEY_SKIP.key, -1 );
global.put( KEY_RSET.key, -1 );
global.put( KEY_STOP.key, -1 );
global.put( KEY_PAUS.key, -1 );
global.put( KEY_LOCK.key, -1 );
global.put( HDR_TTLE.key, true );
global.put( HDR_GOAL.key, true );
global.put( HST_ROWS.key, 8 );
global.put( HST_TABL.key, true );
global.put( HST_BLNK.key, false );
global.put( HST_LINE.key, false );
global.put( HST_MERG.key, Merge.LIVE );
global.put( HST_LIVE.key, true );
global.put( HST_DLTA.key, true );
global.put( HST_ICON.key, true );
global.put( HST_ICSZ.key, 16 );
global.put( HST_OFFS.key, 0 );
global.put( HST_LAST.key, true );
global.put( HST_SFNT.key, Font.decode( "Arial-12" ) );
global.put( HST_TFNT.key, Font.decode( "Arial-11" ) );
global.put( COR_ACCY.key, Accuracy.HUNDREDTH );
global.put( COR_ICON.key, true );
global.put( COR_ICSZ.key, 40 );
global.put( COR_NAME.key, true );
global.put( COR_SPLT.key, false );
global.put( COR_SEGM.key, true );
global.put( COR_BEST.key, true );
global.put( COR_STMR.key, true );
global.put( COR_TFNT.key, Font.decode( "Digitalism-32" ) );
global.put( COR_SFNT.key, Font.decode( "Digitalism-18" ) );
global.put( GPH_SHOW.key, true );
global.put( GPH_SCAL.key, 3.0F );
global.put( FOO_SHOW.key, true );
global.put( FOO_VERB.key, true );
global.put( FOO_SPLT.key, false );
global.put( FOO_BEST.key, true );
global.put( FOO_LINE.key, true );
global.put( FOO_DLBL.key, true );
}
/**
* Polymorphic property object. It is merely a string identifying the
* property and serving as an interface to the configuration. When asked
* for its value, the property will return the global value unless a local
* value has been defined. Will return a statically typecasted value.
*
* @param <T> the type of the values of the property
* @author Xavier "Xunkar" Sencert
* @version 1.0
*/
public static class Property<T> {
private static final List<Property<?>> P = new ArrayList<Property<?>>();
private String key;
/**
* Creates a new property of given key. If the key contains a dot, the
* property name is interpreted as {@code section.key} allowing callers
* to grab a submap of the configuration with all properties starting
* with {@code section}.
*
* @param fullKey the name of the property
*/
private Property( String fullKey ) {
this.key = fullKey;
P.add( this );
}
/**
* Returns the key string of this property.
*
* @return the key string of this property
*/
public String getKey() {
return key;
}
/**
* Returns the value assigned to this property. The value will be first
* read from the local configuration, and if no value has been defined
* for this property, it will be read from the global configuration. The
* first property to call this method will trigger the global
* configuration to be read and loaded in memory.
*
* @return the local value of this property, or the global one if there
* isn't a locally defined value
*/
public T get() {
if ( global == null ) {
retrieve();
}
if ( run != null && run.containsSetting( key ) ) {
return run.getSetting(key);
}
return global.get(key);
}
/**
* Sets the value of this property in the global configuration.
*
* @param value the value to assign to this property
*/
public void set( T value ) {
set( value, false );
}
/**
* Sets the value of this property. The value of {@code locally}
* determines if the value must be stored in the local or global
* configuration.
*
* @param valuethe value to assign to this property
* @param locally if the value must be stored in the local configuration
*/
public void set( T value, boolean locally ) {
if ( locally ) {
run.putSetting( key, value );
} else {
global.put( key, value );
}
}
/**
* Compares a property to a given string. This property is equal to
* the given string if and only if the string is equal to the full key
* of this property.
*
* @param str the string to compare this property against
* @return {@code true} if the string equals this property full key
*/
public boolean equals( String str ) {
return key.equals( str );
}
/**
* Returns the localized name of this property.
*/
@Override public String toString() {
return Language.valueOf(
"setting_" + key.replaceAll( "\\.", "_" )
).get();
}
}
}

View file

@ -50,463 +50,463 @@ import org.fenix.utils.gui.GBC;
public class EditRun extends LlanfairDialog
implements ActionListener, ListSelectionListener {
// ------------------------------------------------------------- CONSTANTES
// ------------------------------------------------------------- CONSTANTES
/**
* Largeurs en pixels des colonnes de la table dédition des segments. Les
* largeurs sont données dans lodre du modèle.
*/
private static final int[] TABLE_COLUMN_WIDTHS = new int[] {
Segment.ICON_MAX_SIZE, 130, 100, 100, 100
};
/**
* Largeurs en pixels des colonnes de la table dédition des segments. Les
* largeurs sont données dans lodre du modèle.
*/
private static final int[] TABLE_COLUMN_WIDTHS = new int[] {
Segment.ICON_MAX_SIZE, 130, 100, 100, 100
};
/**
* Dimension dun petit bouton dont létiquette nest quun caractère.
*/
private static final Dimension SMALL_BUTTON_SIZE = new Dimension(40, 25);
/**
* Dimension dun petit bouton dont létiquette nest quun caractère.
*/
private static final Dimension SMALL_BUTTON_SIZE = new Dimension(40, 25);
// -------------------------------------------------------------- ATTRIBUTS
// -------------------------------------------------------------- ATTRIBUTS
/**
* Course éditée par cette boîte de dialogue.
*/
private Run run;
/**
* Course éditée par cette boîte de dialogue.
*/
private Run run;
/**
* Zone dédition du titre de la course.
*/
private JTextField runTitle;
/**
* Zone dédition du titre de la course.
*/
private JTextField runTitle;
/**
* Étiquette du {@link runTitle}.
*/
private JLabel runTitleLabel;
/**
* Étiquette du {@link runTitle}.
*/
private JLabel runTitleLabel;
/**
* Table dédition des segments de la course.
*/
private JTable segments;
/**
* Table dédition des segments de la course.
*/
private JTable segments;
/**
* Panneau avec ascenseur dans lequel est inséré la table {@link segments}.
*/
private JScrollPane scrollPane;
/**
* Panneau avec ascenseur dans lequel est inséré la table {@link segments}.
*/
private JScrollPane scrollPane;
/**
* Étiquette de {@code segments}.
*/
private JLabel segmentsLabel;
/**
* Étiquette de {@code segments}.
*/
private JLabel segmentsLabel;
/**
* Bouton permettant dinsérer un nouveau segment.
*/
private JButton addSegment;
/**
* Bouton permettant dinsérer un nouveau segment.
*/
private JButton addSegment;
/**
* Bouton permettant de supprimer un segment.
*/
private JButton remSegment;
private JCheckBox segmented;
/**
* Bouton permettant de supprimer un segment.
*/
private JButton remSegment;
/**
* Bouton permettant denregistrer lédition et de quitter le dialogue.
*/
private JButton save;
private JCheckBox segmented;
/**
* Bouton permettant de quitter le dialogue sans modifier la course.
*/
private JButton cancel;
/**
* Button moving the currently selected segment one position up.
*/
private JButton moveUp;
/**
* Button moving the currently selected segment one position down.
*/
private JButton moveDown;
private JTextField runGoal;
/**
* Bouton permettant denregistrer lédition et de quitter le dialogue.
*/
private JButton save;
// ----------------------------------------------------------- CONSTRUCTEURS
/**
* Bouton permettant de quitter le dialogue sans modifier la course.
*/
private JButton cancel;
/**
* Création dune boîte de dialogue permettant déditer la course fournie.
*
* @param run - la course a éditer.
*/
public EditRun(Run run) {
super();
if (run == null) {
throw new NullPointerException("EditDialog.EditDialog(): null run");
}
this.run = run;
run.saveBackup();
/**
* Button moving the currently selected segment one position up.
*/
private JButton moveUp;
setTitle(Language.EDITING.get());
/**
* Button moving the currently selected segment one position down.
*/
private JButton moveDown;
runTitle = new JTextField(run.getName(), 61);
runTitleLabel = new JLabel(Language.RUN_TITLE.get());
runGoal = new JTextField(run.getGoal(), 48);
segments = new JTable(run) {
@Override protected JTableHeader createDefaultTableHeader() {
return new JTableHeader(columnModel) {
@Override public String getToolTipText(MouseEvent event) {
int col = columnModel.getColumnIndexAtX(event.getX());
int ind = columnModel.getColumn(col).getModelIndex();
switch (ind) {
case Run.COLUMN_BEST:
return "" + Language.TT_COLUMN_BEST;
case Run.COLUMN_SEGMENT:
return "" + Language.TT_COLUMN_SEGMENT;
case Run.COLUMN_TIME:
return "" + Language.TT_COLUMN_TIME;
default:
return null;
}
}
};
}
};
segmentsLabel = new JLabel("" + Language.SEGMENTS);
addSegment = new JButton(Llanfair.getResources().getIcon("PLUS"));
remSegment = new JButton(Llanfair.getResources().getIcon("MINUS"));
save = new JButton("" + Language.menuItem_save);
cancel = new JButton("" + Language.CANCEL);
scrollPane = new JScrollPane(segments);
moveUp = new JButton(Llanfair.getResources().getIcon("ARROW_UP"));
moveDown = new JButton(Llanfair.getResources().getIcon("ARROW_DOWN"));
segmented = new JCheckBox("" + Language.ED_SEGMENTED, run.isSegmented());
private JTextField runGoal;
placeComponents();
setBehavior();
}
// ----------------------------------------------------------- CONSTRUCTEURS
// ---------------------------------------------------------------- MÉTHODES
/**
* Création dune boîte de dialogue permettant déditer la course fournie.
*
* @param run - la course a éditer.
*/
public EditRun(Run run) {
super();
if (run == null) {
throw new NullPointerException("EditDialog.EditDialog(): null run");
}
this.run = run;
run.saveBackup();
/**
* Dispose les sous-composants au sein de la boîte de dialogue.
*/
private void placeComponents() {
setLayout(new GridBagLayout());
add(runTitleLabel, GBC.grid(0, 0).insets(4, 4, 0, 4));
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));
add(runGoal, GBC.grid(1, 1).insets(4, 0, 0, 4).anchor(GBC.LS));
add(segmented, GBC.grid(2, 1, 2, 1).insets(4, 0, 0, 4).anchor(GBC.LS));
add(segmentsLabel, GBC.grid(0, 2, 4, 1).insets(5, 4, 4, 0)
.anchor(GBC.BL));
add(scrollPane, GBC.grid(0, 3, 3, 4).insets(0, 4, 0, 0));
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));
JPanel controls = new JPanel();
controls.add(save);
controls.add(cancel);
add(controls, GBC.grid(0, 7, 4, 1));
}
setTitle(Language.EDITING.get());
/**
* Définit le comportement des sous-composants du dialogue.
*/
private void setBehavior() {
// Ne pas permettre la fermeture du dialogue par la croix pour forcer
// lutilisateur à utiliser les boutons save et cancel.
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
runTitle = new JTextField(run.getName(), 61);
runTitleLabel = new JLabel(Language.RUN_TITLE.get());
runGoal = new JTextField(run.getGoal(), 48);
segments = new JTable(run) {
@Override protected JTableHeader createDefaultTableHeader() {
return new JTableHeader(columnModel) {
@Override public String getToolTipText(MouseEvent event) {
int col = columnModel.getColumnIndexAtX(event.getX());
int ind = columnModel.getColumn(col).getModelIndex();
switch (ind) {
case Run.COLUMN_BEST:
return "" + Language.TT_COLUMN_BEST;
case Run.COLUMN_SEGMENT:
return "" + Language.TT_COLUMN_SEGMENT;
case Run.COLUMN_TIME:
return "" + Language.TT_COLUMN_TIME;
default:
return null;
}
}
};
}
};
segmentsLabel = new JLabel("" + Language.SEGMENTS);
addSegment = new JButton(Llanfair.getResources().getIcon("PLUS"));
remSegment = new JButton(Llanfair.getResources().getIcon("MINUS"));
save = new JButton("" + Language.menuItem_save);
cancel = new JButton("" + Language.CANCEL);
scrollPane = new JScrollPane(segments);
moveUp = new JButton(Llanfair.getResources().getIcon("ARROW_UP"));
moveDown = new JButton(Llanfair.getResources().getIcon("ARROW_DOWN"));
segmented = new JCheckBox("" + Language.ED_SEGMENTED, run.isSegmented());
// Définir les dimensions des composants.
setResizable(false);
addSegment.setPreferredSize(SMALL_BUTTON_SIZE);
remSegment.setPreferredSize(SMALL_BUTTON_SIZE);
moveUp.setPreferredSize(SMALL_BUTTON_SIZE);
moveDown.setPreferredSize(SMALL_BUTTON_SIZE);
placeComponents();
setBehavior();
}
segments.setRowHeight(Segment.ICON_MAX_SIZE);
for (int i = 0; i < run.getColumnCount(); i++) {
TableColumn column = segments.getColumnModel().getColumn(i);
column.setPreferredWidth(TABLE_COLUMN_WIDTHS[i]);
}
// ---------------------------------------------------------------- MÉTHODES
int totalWidth = 0;
for (int width : TABLE_COLUMN_WIDTHS) {
totalWidth += width;
}
Dimension size = new Dimension(totalWidth, Segment.ICON_MAX_SIZE * 5);
segments.setPreferredScrollableViewportSize(size);
/**
* Dispose les sous-composants au sein de la boîte de dialogue.
*/
private void placeComponents() {
setLayout(new GridBagLayout());
add(runTitleLabel, GBC.grid(0, 0).insets(4, 4, 0, 4));
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));
add(runGoal, GBC.grid(1, 1).insets(4, 0, 0, 4).anchor(GBC.LS));
add(segmented, GBC.grid(2, 1, 2, 1).insets(4, 0, 0, 4).anchor(GBC.LS));
add(segmentsLabel, GBC.grid(0, 2, 4, 1).insets(5, 4, 4, 0)
.anchor(GBC.BL));
add(scrollPane, GBC.grid(0, 3, 3, 4).insets(0, 4, 0, 0));
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));
// Enregistrement des écouteurs des composants.
addSegment.addActionListener(this);
remSegment.addActionListener(this);
moveUp.addActionListener(this);
moveDown.addActionListener(this);
cancel.addActionListener(this);
save.addActionListener(this);
JPanel controls = new JPanel();
controls.add(save);
controls.add(cancel);
add(controls, GBC.grid(0, 7, 4, 1));
}
// Insertion des délégués de rendus et dédition.
segments.setDefaultRenderer(Icon.class, new IconRenderer());
segments.setDefaultEditor(Icon.class, new FileChooserEditor(this));
segments.setDefaultEditor(Time.class, new TimeEditor());
/**
* Définit le comportement des sous-composants du dialogue.
*/
private void setBehavior() {
// Ne pas permettre la fermeture du dialogue par la croix pour forcer
// lutilisateur à utiliser les boutons save et cancel.
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
// Ajuster le comportement de la table.
segments.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
segments.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
segments.getSelectionModel().addListSelectionListener(this);
// Définir les dimensions des composants.
setResizable(false);
addSegment.setPreferredSize(SMALL_BUTTON_SIZE);
remSegment.setPreferredSize(SMALL_BUTTON_SIZE);
moveUp.setPreferredSize(SMALL_BUTTON_SIZE);
moveDown.setPreferredSize(SMALL_BUTTON_SIZE);
addSegment.setToolTipText("" + Language.TT_ADD_SEGMENT);
remSegment.setToolTipText("" + Language.TT_REMOVE_SEGMENT);
moveDown.setToolTipText("" + Language.TT_MOVE_SEGMENT_DOWN);
moveUp.setToolTipText("" + Language.TT_MOVE_SEGMENT_UP);
segmented.setToolTipText("" + Language.TT_ED_SEGMENTED);
updateButtons();
}
segments.setRowHeight(Segment.ICON_MAX_SIZE);
for (int i = 0; i < run.getColumnCount(); i++) {
TableColumn column = segments.getColumnModel().getColumn(i);
column.setPreferredWidth(TABLE_COLUMN_WIDTHS[i]);
}
/**
* Procédure à invoquer lorsque ce composant réalise une action. Sont donc
* capturés ici, tous les évènements daction des sous-composants, comme
* lappui sur un bouton.
*
* @param event - lévènement daction.
* @see ActionListener
*/
@Override public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(addSegment)) {
run.addSegment(new Segment());
Rectangle rect = segments.getCellRect(run.getRowCount() - 1, 0, true);
segments.scrollRectToVisible(rect);
updateButtons();
} else if (source.equals(remSegment)) {
run.removeSegment(segments.getSelectedRow());
updateButtons();
int totalWidth = 0;
for (int width : TABLE_COLUMN_WIDTHS) {
totalWidth += width;
}
Dimension size = new Dimension(totalWidth, Segment.ICON_MAX_SIZE * 5);
segments.setPreferredScrollableViewportSize(size);
} else if (source.equals(save)) {
run.setName(runTitle.getText());
run.setGoal(runGoal.getText());
run.setSegmented(segmented.isSelected());
dispose();
// Enregistrement des écouteurs des composants.
addSegment.addActionListener(this);
remSegment.addActionListener(this);
moveUp.addActionListener(this);
moveDown.addActionListener(this);
cancel.addActionListener(this);
save.addActionListener(this);
} else if (source.equals(cancel)) {
if (!segments.isEditing()) {
run.loadBackup();
dispose();
}
} else if (source.equals(moveUp)) {
int selected = segments.getSelectedRow();
run.moveSegmentUp(selected);
segments.setRowSelectionInterval(selected - 1, selected - 1);
} else if (source.equals(moveDown)) {
int selected = segments.getSelectedRow();
run.moveSegmentDown(selected);
segments.setRowSelectionInterval(selected + 1, selected + 1);
}
}
// Insertion des délégués de rendus et dédition.
segments.setDefaultRenderer(Icon.class, new IconRenderer());
segments.setDefaultEditor(Icon.class, new FileChooserEditor(this));
segments.setDefaultEditor(Time.class, new TimeEditor());
/**
* Méthode invoquée lors dun changement de sélection dans la table.
*
* @param event - lévènement de sélection.
*/
public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) {
updateButtons();
}
}
private void updateButtons() {
int selected = segments.getSelectedRow();
boolean enabled = (selected >= 0);
remSegment.setEnabled(enabled);
moveUp.setEnabled(enabled && selected > 0);
moveDown.setEnabled(enabled && selected < run.getRowCount() - 1);
}
// Ajuster le comportement de la table.
segments.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
segments.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
segments.getSelectionModel().addListSelectionListener(this);
// ---------------------------------------------------------- CLASSE INTERNE
addSegment.setToolTipText("" + Language.TT_ADD_SEGMENT);
remSegment.setToolTipText("" + Language.TT_REMOVE_SEGMENT);
moveDown.setToolTipText("" + Language.TT_MOVE_SEGMENT_DOWN);
moveUp.setToolTipText("" + Language.TT_MOVE_SEGMENT_UP);
segmented.setToolTipText("" + Language.TT_ED_SEGMENTED);
/**
* Gestionnaire de rendu capable dafficher une valeur de type {@link Icon}
* au sein dune table.
*
* @author Xavier Sencert
* @see TableCellRenderer
*/
private class IconRenderer extends DefaultTableCellRenderer {
updateButtons();
}
/**
* Construction dun gestionnaire de rendu dicônes.
*/
public IconRenderer() {
super();
}
/**
* Procédure à invoquer lorsque ce composant réalise une action. Sont donc
* capturés ici, tous les évènements daction des sous-composants, comme
* lappui sur un bouton.
*
* @param event - lévènement daction.
* @see ActionListener
*/
@Override public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(addSegment)) {
run.addSegment(new Segment());
Rectangle rect = segments.getCellRect(run.getRowCount() - 1, 0, true);
segments.scrollRectToVisible(rect);
updateButtons();
/**
* Retourne le composant de rendu à afficher dans la table. Le composant
* est ici préparé selon la valeur de la cellule et différentes
* informations sur létat du composant.
*/
@Override public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
// Préparer le label comme un label par défaut.
JLabel label = (JLabel) super.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
// Modifier licône du label par celle de la valeur.
label.setIcon((value == null) ? null : (Icon) value);
label.setText("");
label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
}
} else if (source.equals(remSegment)) {
run.removeSegment(segments.getSelectedRow());
updateButtons();
// ---------------------------------------------------------- CLASSE INTERNE
} else if (source.equals(save)) {
run.setName(runTitle.getText());
run.setGoal(runGoal.getText());
run.setSegmented(segmented.isSelected());
dispose();
/**
* Délégué dédition des temps de course et de segments. Cette classe permet
* de récupérer les exceptions potentiellement levés lorsque lédition se
* termine et que la valeur est modifié dans la table des segments.
*
* @author Xavier "Xunkar" Sencert
*/
private class TimeEditor extends DefaultCellEditor {
} else if (source.equals(cancel)) {
if (!segments.isEditing()) {
run.loadBackup();
dispose();
}
} else if (source.equals(moveUp)) {
int selected = segments.getSelectedRow();
run.moveSegmentUp(selected);
segments.setRowSelectionInterval(selected - 1, selected - 1);
/**
* Composant dédition. Conservé ici en doublon pour éviter de devoir le
* caster à chaque fois.
*/
private JTextField editor;
} else if (source.equals(moveDown)) {
int selected = segments.getSelectedRow();
run.moveSegmentDown(selected);
segments.setRowSelectionInterval(selected + 1, selected + 1);
}
}
/**
* Construction dun délégué par défaut.
*/
public TimeEditor() {
super(new JTextField());
editor = (JTextField) getComponent();
}
/**
* Méthode invoquée lors dun changement de sélection dans la table.
*
* @param event - lévènement de sélection.
*/
public void valueChanged(ListSelectionEvent event) {
if (!event.getValueIsAdjusting()) {
updateButtons();
}
}
/**
* Retourne la valeur entrée par lutilisateur.
*
* @return la valeur entrée par lutilisateur.
*/
public Object getCellEditorValue() {
String text = editor.getText();
return text.equals("") ? null : new Time(text);
}
private void updateButtons() {
int selected = segments.getSelectedRow();
boolean enabled = (selected >= 0);
remSegment.setEnabled(enabled);
moveUp.setEnabled(enabled && selected > 0);
moveDown.setEnabled(enabled && selected < run.getRowCount() - 1);
}
/**
* Retourne le composant dédition, formatté comme il se doit selon les
* informations de la table et de la cellule.
*
* @param table - la table source demandant lédition.
* @param get - la valeur actuellement présente à représenter.
* @param isSelected - indique si la ligne est sélectionnée.
* @param row - indice de ligne la cellule éditée.
* @param column - indice de colonne de la cellule éditée.
* @return le composant dédition.
*/
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
editor.setText(value == null ? "" : value.toString());
editor.selectAll();
return editor;
}
// ---------------------------------------------------------- CLASSE INTERNE
/**
* Arrête lédition de la cellule. Le délégué récupère ici les exceptions
* levées et les remonte à lutilisateur. Tant que lédition est en erreur
* lédition persiste.
*
* @return {@code true} si lédition sest arrêtée.
*/
public boolean stopCellEditing() {
try {
return super.stopCellEditing();
} catch (Exception e) {
JOptionPane.showMessageDialog(editor, e.getMessage(),
Language.ERROR.get(), JOptionPane.ERROR_MESSAGE);
return false;
}
}
/**
* Gestionnaire de rendu capable dafficher une valeur de type {@link Icon}
* au sein dune table.
*
* @author Xavier Sencert
* @see TableCellRenderer
*/
private class IconRenderer extends DefaultTableCellRenderer {
}
/**
* Construction dun gestionnaire de rendu dicônes.
*/
public IconRenderer() {
super();
}
// ---------------------------------------------------------- CLASSE INTERNE
/**
* Retourne le composant de rendu à afficher dans la table. Le composant
* est ici préparé selon la valeur de la cellule et différentes
* informations sur létat du composant.
*/
@Override public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column) {
// Préparer le label comme un label par défaut.
JLabel label = (JLabel) super.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
// Modifier licône du label par celle de la valeur.
label.setIcon((value == null) ? null : (Icon) value);
label.setText("");
label.setHorizontalAlignment(JLabel.CENTER);
return label;
}
}
/**
* Éditeur de cellules permettant de sélectionner un fichier via un
* {@code JFileChooser}.
*
* @author Xavier Sencert
* @see TableCellEditor
* @see ActionListener
*/
private class FileChooserEditor extends AbstractCellEditor
implements TableCellEditor, ActionListener {
// ---------------------------------------------------------- CLASSE INTERNE
/**
* Gestionnaire de sélection de fichier.
*/
private JFileChooser chooser;
/**
* Délégué dédition des temps de course et de segments. Cette classe permet
* de récupérer les exceptions potentiellement levés lorsque lédition se
* termine et que la valeur est modifié dans la table des segments.
*
* @author Xavier "Xunkar" Sencert
*/
private class TimeEditor extends DefaultCellEditor {
/**
* Composant dédition de léditeur. Il sagit dun {@code JButton}
* ce qui permet de capturer le clic de lutilisateur et ainsi douvrir
* le gestionnaire de sélection de fichier.
*/
private JButton editor;
private Window owner;
/**
* Composant dédition. Conservé ici en doublon pour éviter de devoir le
* caster à chaque fois.
*/
private JTextField editor;
/**
* Création dun éditeur par défaut.
*/
public FileChooserEditor(Window owner) {
super();
this.owner = owner;
chooser = new JFileChooser(".");
editor = new JButton();
chooser.setFileFilter(new FileNameExtensionFilter(
"" + Language.IMAGE, "gif", "jpg", "jpeg", "png"));
editor.addActionListener(this);
}
/**
* Construction dun délégué par défaut.
*/
public TimeEditor() {
super(new JTextField());
editor = (JTextField) getComponent();
}
/**
* Retourne le composant dédition à afficher au sein de la table, il
* sagit donc du bouton {@link #editor}.
*/
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
return editor;
}
/**
* Retourne la valeur entrée par lutilisateur.
*
* @return la valeur entrée par lutilisateur.
*/
public Object getCellEditorValue() {
String text = editor.getText();
return text.equals("") ? null : new Time(text);
}
/**
* Retourne la valeur stockée par léditeur à savoir le fichier
* sélectionner par lutilisateur sil y en a un.
*/
public Object getCellEditorValue() {
if (chooser.getSelectedFile() == null) {
return null;
}
return new ImageIcon(chooser.getSelectedFile().getPath());
}
/**
* Retourne le composant dédition, formatté comme il se doit selon les
* informations de la table et de la cellule.
*
* @param table - la table source demandant lédition.
* @param get - la valeur actuellement présente à représenter.
* @param isSelected - indique si la ligne est sélectionnée.
* @param row - indice de ligne la cellule éditée.
* @param column - indice de colonne de la cellule éditée.
* @return le composant dédition.
*/
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
editor.setText(value == null ? "" : value.toString());
editor.selectAll();
return editor;
}
/**
* Lors du clic de lutilisateur sur le bouton, on affiche le
* gestionnaire de sélection de fichier puis lon force la fin de
* lédition lorsque celui-ci retourne.
*/
@Override public void actionPerformed(ActionEvent e) {
chooser.showOpenDialog(owner);
fireEditingStopped();
}
/**
* Arrête lédition de la cellule. Le délégué récupère ici les exceptions
* levées et les remonte à lutilisateur. Tant que lédition est en erreur
* lédition persiste.
*
* @return {@code true} si lédition sest arrêtée.
*/
public boolean stopCellEditing() {
try {
return super.stopCellEditing();
} catch (Exception e) {
JOptionPane.showMessageDialog(editor, e.getMessage(),
Language.ERROR.get(), JOptionPane.ERROR_MESSAGE);
return false;
}
}
}
}
// ---------------------------------------------------------- CLASSE INTERNE
/**
* Éditeur de cellules permettant de sélectionner un fichier via un
* {@code JFileChooser}.
*
* @author Xavier Sencert
* @see TableCellEditor
* @see ActionListener
*/
private class FileChooserEditor extends AbstractCellEditor
implements TableCellEditor, ActionListener {
/**
* Gestionnaire de sélection de fichier.
*/
private JFileChooser chooser;
/**
* Composant dédition de léditeur. Il sagit dun {@code JButton}
* ce qui permet de capturer le clic de lutilisateur et ainsi douvrir
* le gestionnaire de sélection de fichier.
*/
private JButton editor;
private Window owner;
/**
* Création dun éditeur par défaut.
*/
public FileChooserEditor(Window owner) {
super();
this.owner = owner;
chooser = new JFileChooser(".");
editor = new JButton();
chooser.setFileFilter(new FileNameExtensionFilter(
"" + Language.IMAGE, "gif", "jpg", "jpeg", "png"));
editor.addActionListener(this);
}
/**
* Retourne le composant dédition à afficher au sein de la table, il
* sagit donc du bouton {@link #editor}.
*/
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
return editor;
}
/**
* Retourne la valeur stockée par léditeur à savoir le fichier
* sélectionner par lutilisateur sil y en a un.
*/
public Object getCellEditorValue() {
if (chooser.getSelectedFile() == null) {
return null;
}
return new ImageIcon(chooser.getSelectedFile().getPath());
}
/**
* Lors du clic de lutilisateur sur le bouton, on affiche le
* gestionnaire de sélection de fichier puis lon force la fin de
* lédition lorsque celui-ci retourne.
*/
@Override public void actionPerformed(ActionEvent e) {
chooser.showOpenDialog(owner);
fireEditingStopped();
}
}
}

View file

@ -27,127 +27,127 @@ import org.fenix.utils.locale.LocaleListener;
* @date 22 juil. 2012
*/
public class EditSettings extends LlanfairDialog
implements ActionListener, LocaleListener, WindowListener {
implements ActionListener, LocaleListener, WindowListener {
// ATTRIBUTS
// ATTRIBUTS
/**
* Bouton permettant de valider et de fermer la boîte de dialogue.
*/
private JButton actionOK;
/**
* Bouton permettant de valider et de fermer la boîte de dialogue.
*/
private JButton actionOK;
private JButton reset;
private JButton reset;
private List<SettingsTab> settingsTabs;
private List<SettingsTab> settingsTabs;
// CONSTRUCTEURS
// CONSTRUCTEURS
/**
* Construction dune boîte de dialogue dédition de paramètres.
*/
public EditSettings() {
settingsTabs = new ArrayList<SettingsTab>();
settingsTabs.add(new TabGeneral());
settingsTabs.add(new TabLook());
settingsTabs.add(new TabHotkeys());
settingsTabs.add(new TabHistory());
settingsTabs.add(new TabComponents());
createResources();
placeComponents();
setPersistentBehavior();
LocaleDelegate.addLocaleListener(this);
}
@Override public void localeChanged(LocaleEvent event) {
/**
* Construction dune boîte de dialogue dédition de paramètres.
*/
public EditSettings() {
settingsTabs = new ArrayList<SettingsTab>();
settingsTabs.add(new TabGeneral());
settingsTabs.add(new TabLook());
settingsTabs.add(new TabHotkeys());
settingsTabs.add(new TabHistory());
settingsTabs.add(new TabComponents());
createResources();
placeComponents();
setPersistentBehavior();
LocaleDelegate.addLocaleListener(this);
}
@Override public void localeChanged(LocaleEvent event) {
// for (SettingsTab tab : settingsTabs) {
// tab.processLocaleEvent(event);
// }
}
}
// MÉTHODES
// MÉTHODES
/**
* Instanciation des sous-composants utilisés par cette boîte de dialogue.
*/
private void createResources() {
reset = new JButton("" + Language.setting_hotkey_reset);
actionOK = new JButton(Language.ACCEPT.get());
}
/**
* Instanciation des sous-composants utilisés par cette boîte de dialogue.
*/
private void createResources() {
reset = new JButton("" + Language.setting_hotkey_reset);
actionOK = new JButton(Language.ACCEPT.get());
}
/**
* Dispose les sous-composants au sein de ce panneau. Les sous-composants
* sont placés à laide dun {@link GridBagLayout} dont laccès est
* simplifié par la classe-proxy {@link GBC}.
*/
private void placeComponents() {
setLayout(new GridBagLayout());
JTabbedPane tabPane = new JTabbedPane(); {
for (SettingsTab tab : settingsTabs) {
tabPane.add(tab.toString(), tab);
}
}
JPanel controls = new JPanel(); {
controls.add(actionOK);
controls.add(reset);
}
add(tabPane, GBC.grid(0, 0));
add(controls, GBC.grid(0, 1).insets(6, 0, 4, 0));
}
/**
* Dispose les sous-composants au sein de ce panneau. Les sous-composants
* sont placés à laide dun {@link GridBagLayout} dont laccès est
* simplifié par la classe-proxy {@link GBC}.
*/
private void placeComponents() {
setLayout(new GridBagLayout());
/**
* Définit le comportement persistant (non sujet aux variations détats du
* modèle ou de la configuration de lapplication) pour ce composant et
* ses sous-composants.
*/
private void setPersistentBehavior() {
setResizable(false);
setTitle(Language.menuItem_settings.get());
actionOK.addActionListener(this);
reset.addActionListener(this);
addWindowListener(this);
}
JTabbedPane tabPane = new JTabbedPane(); {
for (SettingsTab tab : settingsTabs) {
tabPane.add(tab.toString(), tab);
}
}
JPanel controls = new JPanel(); {
controls.add(actionOK);
controls.add(reset);
}
add(tabPane, GBC.grid(0, 0));
add(controls, GBC.grid(0, 1).insets(6, 0, 4, 0));
}
/**
* Définit le comportement persistant (non sujet aux variations détats du
* modèle ou de la configuration de lapplication) pour ce composant et
* ses sous-composants.
*/
private void setPersistentBehavior() {
setResizable(false);
setTitle(Language.menuItem_settings.get());
actionOK.addActionListener(this);
reset.addActionListener(this);
addWindowListener(this);
}
/**
* Procédure à invoquer lorsquun sous-composant réalise une action. Cela
* signifie pour nous que lutilisateur à effectuer un réglage de paramètre.
*
* @param evt - lévènement daction.
* @see ActionListener
*/
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source.equals(actionOK)) {
for (SettingsTab tab : settingsTabs) {
tab.doDelayedSettingChange();
}
dispose();
} else if (source.equals(reset)) {
int option = JOptionPane.showConfirmDialog(this,
"" + Language.WARN_RESET_SETTINGS);
if (option == JOptionPane.YES_OPTION) {
/**
* Procédure à invoquer lorsquun sous-composant réalise une action. Cela
* signifie pour nous que lutilisateur à effectuer un réglage de paramètre.
*
* @param evt - lévènement daction.
* @see ActionListener
*/
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source.equals(actionOK)) {
for (SettingsTab tab : settingsTabs) {
tab.doDelayedSettingChange();
}
dispose();
} else if (source.equals(reset)) {
int option = JOptionPane.showConfirmDialog(this,
"" + Language.WARN_RESET_SETTINGS);
if (option == JOptionPane.YES_OPTION) {
// Settings.reset();
dispose();
}
}
dispose();
}
}
}
@Override public void windowActivated(WindowEvent e) {}
@Override public void windowClosed(WindowEvent e) {}
@Override public void windowDeactivated(WindowEvent e) {}
@Override public void windowDeiconified(WindowEvent e) {}
@Override public void windowIconified(WindowEvent e) {}
@Override public void windowOpened(WindowEvent e) {}
@Override public void windowClosing(WindowEvent e) {
for (SettingsTab tab : settingsTabs) {
tab.doDelayedSettingChange();
}
dispose();
}
}
@Override public void windowActivated(WindowEvent e) {}
@Override public void windowClosed(WindowEvent e) {}
@Override public void windowDeactivated(WindowEvent e) {}
@Override public void windowDeiconified(WindowEvent e) {}
@Override public void windowIconified(WindowEvent e) {}
@Override public void windowOpened(WindowEvent e) {}
@Override public void windowClosing(WindowEvent e) {
for (SettingsTab tab : settingsTabs) {
tab.doDelayedSettingChange();
}
dispose();
}
}

View file

@ -1,12 +1,11 @@
package org.fenix.llanfair.dialog;
import org.fenix.llanfair.Llanfair;
import javax.swing.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JDialog;
import org.fenix.llanfair.Llanfair;
/**
* LlanfairDialog
*
@ -15,22 +14,22 @@ import org.fenix.llanfair.Llanfair;
*/
public class LlanfairDialog extends JDialog {
// ATTRIBUTS
public void display(boolean lockNativeInputs, final Llanfair llanfair) {
if (lockNativeInputs) {
llanfair.setIgnoreNativeInputs(true);
addWindowListener(new WindowAdapter() {
@Override public void windowClosed(WindowEvent e) {
llanfair.setIgnoreNativeInputs(false);
}
});
}
setAlwaysOnTop(true);
setModalityType(ModalityType.APPLICATION_MODAL);
pack();
setLocationRelativeTo(getOwner());
setVisible(true);
}
// ATTRIBUTS
public void display(boolean lockNativeInputs, final Llanfair llanfair) {
if (lockNativeInputs) {
llanfair.setIgnoreNativeInputs(true);
addWindowListener(new WindowAdapter() {
@Override public void windowClosed(WindowEvent e) {
llanfair.setIgnoreNativeInputs(false);
}
});
}
setAlwaysOnTop(true);
setModalityType(ModalityType.APPLICATION_MODAL);
pack();
setLocationRelativeTo(getOwner());
setVisible(true);
}
}

View file

@ -1,43 +1,43 @@
package org.fenix.llanfair.dialog;
import org.fenix.llanfair.config.Settings;
import org.fenix.utils.gui.LinkedCheckBox;
import javax.swing.*;
import java.awt.event.ItemEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JPanel;
import org.fenix.llanfair.Language;
import org.fenix.llanfair.config.Settings;
import org.fenix.utils.gui.LinkedCheckBox;
/**
*
* @author Xavier "Xunkar" Sencert
*/
abstract class SettingsTab extends JPanel {
protected Map<String, SCheckBox> checkBoxes;
protected SettingsTab() {
checkBoxes = new HashMap<String, SCheckBox>();
}
abstract void doDelayedSettingChange();
protected class SCheckBox extends LinkedCheckBox {
private Settings.Property<Boolean> setting;
SCheckBox(Settings.Property<Boolean> setting) {
super();
this.setting = setting;
setText("" + setting);
setSelected(setting.get());
}
@Override public void itemStateChanged(ItemEvent e) {
super.itemStateChanged(e);
setting.set(isSelected());
}
}
protected Map<String, SCheckBox> checkBoxes;
protected SettingsTab() {
checkBoxes = new HashMap<String, SCheckBox>();
}
abstract void doDelayedSettingChange();
protected class SCheckBox extends LinkedCheckBox {
private Settings.Property<Boolean> setting;
SCheckBox(Settings.Property<Boolean> setting) {
super();
this.setting = setting;
setText("" + setting);
setSelected(setting.get());
}
@Override public void itemStateChanged(ItemEvent e) {
super.itemStateChanged(e);
setting.set(isSelected());
}
}
}

View file

@ -1,242 +1,232 @@
package org.fenix.llanfair.dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.fenix.llanfair.Language;
import org.fenix.llanfair.Segment;
import org.fenix.llanfair.config.Settings;
import org.fenix.utils.gui.GBC;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Xavier "Xunkar" Sencert
*/
public class TabComponents extends SettingsTab
implements ActionListener, ChangeListener {
private static final List<Settings.Property<Boolean>> SCB_SETTINGS
= new ArrayList<Settings.Property<Boolean>>();
static {
SCB_SETTINGS.add(Settings.FOO_SHOW);
SCB_SETTINGS.add(Settings.FOO_SPLT);
SCB_SETTINGS.add(Settings.FOO_DLBL);
SCB_SETTINGS.add(Settings.FOO_BEST);
SCB_SETTINGS.add(Settings.FOO_LINE);
SCB_SETTINGS.add(Settings.FOO_VERB);
SCB_SETTINGS.add(Settings.COR_NAME);
SCB_SETTINGS.add(Settings.COR_SPLT);
SCB_SETTINGS.add(Settings.COR_SEGM);
SCB_SETTINGS.add(Settings.COR_BEST);
SCB_SETTINGS.add(Settings.HDR_GOAL);
SCB_SETTINGS.add(Settings.GPH_SHOW);
SCB_SETTINGS.add(Settings.COR_STMR);
SCB_SETTINGS.add(Settings.COR_ICON);
SCB_SETTINGS.add(Settings.HDR_TTLE);
};
private JComboBox iconSizes;
private JComboBox timerFont;
private JSpinner timerSize;
private JComboBox timerSegFont;
private JSpinner timerSegSize;
private JCheckBox timerSameFont;
TabComponents() {
super();
for (Settings.Property<Boolean> setting : SCB_SETTINGS) {
checkBoxes.put(setting.getKey(), new SCheckBox(setting));
}
// Checkboxes side effects
checkBoxes.get(Settings.FOO_SPLT.getKey()).deactivates(
checkBoxes.get(Settings.FOO_BEST.getKey())
);
// Checkboxes requirements
checkBoxes.get(Settings.FOO_BEST.getKey()).requires(
checkBoxes.get(Settings.FOO_SPLT.getKey()), false
);
checkBoxes.get(Settings.FOO_LINE.getKey()).requires(
checkBoxes.get(Settings.FOO_BEST.getKey()), true
);
iconSizes = new JComboBox(Segment.ICON_SIZES);
iconSizes.setSelectedItem(Settings.COR_ICSZ.get());
iconSizes.addActionListener(this);
implements ActionListener, ChangeListener {
GraphicsEnvironment gEnv = GraphicsEnvironment
.getLocalGraphicsEnvironment();
String mainFont = Settings.COR_TFNT.get().getName();
timerFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
timerFont.setSelectedItem(mainFont);
timerFont.setPreferredSize(new Dimension(130, 22));
timerFont.addActionListener(this);
String segFont = Settings.COR_SFNT.get().getName();
timerSegFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
timerSegFont.setSelectedItem(segFont);
timerSegFont.setPreferredSize(new Dimension(130, 22));
timerSegFont.addActionListener(this);
timerSize = new JSpinner(new SpinnerNumberModel(
Settings.COR_TFNT.get().getSize(), 8, 240, 1)
);
timerSize.addChangeListener(this);
timerSegSize = new JSpinner(new SpinnerNumberModel(
Settings.COR_SFNT.get().getSize(), 8, 240, 1)
);
timerSegSize.addChangeListener(this);
timerSameFont = new JCheckBox("" + Language.USE_MAIN_FONT);
timerSameFont.setSelected(segFont.equals(mainFont));
timerSegFont.setEnabled(!timerSameFont.isSelected());
timerSameFont.addActionListener(this);
place();
}
// -------------------------------------------------------------- CALLBACKS
@Override public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(iconSizes)) {
Settings.COR_ICSZ.set((Integer) iconSizes.getSelectedItem());
} else if (source.equals(timerFont)) {
String fontName = timerFont.getSelectedItem().toString();
Font font = Font.decode(fontName).deriveFont(
(float) Settings.COR_TFNT.get().getSize()
);
Settings.COR_TFNT.set(font);
if (timerSameFont.isSelected()) {
timerSegFont.setSelectedItem(fontName);
}
} else if (source.equals(timerSegFont)) {
String fontName = timerSegFont.getSelectedItem().toString();
Font font = Font.decode(fontName).deriveFont(
(float) Settings.COR_SFNT.get().getSize()
);
Settings.COR_SFNT.set(font);
} else if (source.equals(timerSameFont)) {
timerSegFont.setEnabled(!timerSameFont.isSelected());
if (timerSameFont.isSelected()) {
int size = (Integer) timerSegSize.getValue();
Settings.COR_SFNT.set(
Settings.COR_TFNT.get().deriveFont((float) size)
);
}
}
}
@Override public void stateChanged(ChangeEvent event) {
Object source = event.getSource();
if (source.equals(timerSize)) {
int size = (Integer) timerSize.getValue();
Settings.COR_TFNT.set(
Settings.COR_TFNT.get().deriveFont((float) size)
);
} else if (source.equals(timerSegSize)) {
int size = (Integer) timerSegSize.getValue();
Settings.COR_SFNT.set(
Settings.COR_SFNT.get().deriveFont((float) size)
);
}
}
// -------------------------------------------------------------- INHERITED
@Override void doDelayedSettingChange() {}
@Override public String toString() {
return "" + Language.COMPONENTS;
}
// -------------------------------------------------------------- UTILITIES
private void place() {
setLayout(new GridBagLayout());
JPanel fontPanel = new JPanel(new GridBagLayout()); {
fontPanel.add(
new JLabel("" + Language.setting_core_timerFont),
GBC.grid(0, 0).anchor(GBC.LS).insets(0, 5)
);
fontPanel.add(timerFont, GBC.grid(1, 0));
fontPanel.add(timerSize, GBC.grid(2, 0).insets(0, 5));
fontPanel.add(
new JLabel("" + Language.setting_core_segmentTimerFont),
GBC.grid(0, 1).anchor(GBC.LS).insets(3, 5)
);
fontPanel.add(
timerSameFont,
GBC.grid(1, 1, 2, 1).anchor(GBC.LS).insets(3, 0)
);
fontPanel.add(timerSegFont, GBC.grid(1, 2));
fontPanel.add(timerSegSize, GBC.grid(2, 2).insets(0, 5));
fontPanel.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_FONTS)
);
}
JPanel timerPanel = new JPanel(new GridBagLayout()); {
timerPanel.add(
new JLabel("" + Language.setting_core_iconSize),
GBC.grid(0, 1).anchor(GBC.LE).insets(5, 5)
);
timerPanel.add(iconSizes, GBC.grid(1, 1).insets(5, 5));
timerPanel.add(checkBoxes.get(Settings.COR_ICON.getKey()), GBC.grid(0, 2, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_NAME.getKey()), GBC.grid(0, 3, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_SPLT.getKey()), GBC.grid(0, 4, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_SEGM.getKey()), GBC.grid(0, 5, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_BEST.getKey()), GBC.grid(0, 6, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_STMR.getKey()), GBC.grid(0, 7, 2, 1).anchor(GBC.LS));
timerPanel.setBorder(
BorderFactory.createTitledBorder("" + Language.TIMER)
);
}
JPanel footerPanel = new JPanel(new GridBagLayout()); {
footerPanel.add(checkBoxes.get(Settings.FOO_SHOW.getKey()), GBC.grid(0, 0).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_SPLT.getKey()), GBC.grid(0, 1).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_VERB.getKey()), GBC.grid(0, 2).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_DLBL.getKey()), GBC.grid(1, 0).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_BEST.getKey()), GBC.grid(1, 1).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_LINE.getKey()), GBC.grid(1, 2).anchor(GBC.LS));
footerPanel.setBorder(
BorderFactory.createTitledBorder("" + Language.FOOTER)
);
}
JPanel miscPanel = new JPanel(new GridBagLayout()); {
miscPanel.add(checkBoxes.get(Settings.HDR_TTLE.getKey()), GBC.grid(0, 0).anchor(GBC.LS));
miscPanel.add(checkBoxes.get(Settings.HDR_GOAL.getKey()), GBC.grid(0, 1).anchor(GBC.LS));
miscPanel.add(checkBoxes.get(Settings.GPH_SHOW.getKey()), GBC.grid(0, 2).anchor(GBC.LS));
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));
}
private static final List<Settings.Property<Boolean>> SCB_SETTINGS
= new ArrayList<Settings.Property<Boolean>>();
static {
SCB_SETTINGS.add(Settings.FOO_SHOW);
SCB_SETTINGS.add(Settings.FOO_SPLT);
SCB_SETTINGS.add(Settings.FOO_DLBL);
SCB_SETTINGS.add(Settings.FOO_BEST);
SCB_SETTINGS.add(Settings.FOO_LINE);
SCB_SETTINGS.add(Settings.FOO_VERB);
SCB_SETTINGS.add(Settings.COR_NAME);
SCB_SETTINGS.add(Settings.COR_SPLT);
SCB_SETTINGS.add(Settings.COR_SEGM);
SCB_SETTINGS.add(Settings.COR_BEST);
SCB_SETTINGS.add(Settings.HDR_GOAL);
SCB_SETTINGS.add(Settings.GPH_SHOW);
SCB_SETTINGS.add(Settings.COR_STMR);
SCB_SETTINGS.add(Settings.COR_ICON);
SCB_SETTINGS.add(Settings.HDR_TTLE);
}
private JComboBox iconSizes;
private JComboBox timerFont;
private JSpinner timerSize;
private JComboBox timerSegFont;
private JSpinner timerSegSize;
private JCheckBox timerSameFont;
TabComponents() {
super();
for (Settings.Property<Boolean> setting : SCB_SETTINGS) {
checkBoxes.put(setting.getKey(), new SCheckBox(setting));
}
// Checkboxes side effects
checkBoxes.get(Settings.FOO_SPLT.getKey()).deactivates(
checkBoxes.get(Settings.FOO_BEST.getKey())
);
// Checkboxes requirements
checkBoxes.get(Settings.FOO_BEST.getKey()).requires(
checkBoxes.get(Settings.FOO_SPLT.getKey()), false
);
checkBoxes.get(Settings.FOO_LINE.getKey()).requires(
checkBoxes.get(Settings.FOO_BEST.getKey()), true
);
iconSizes = new JComboBox(Segment.ICON_SIZES);
iconSizes.setSelectedItem(Settings.COR_ICSZ.get());
iconSizes.addActionListener(this);
GraphicsEnvironment gEnv = GraphicsEnvironment
.getLocalGraphicsEnvironment();
String mainFont = Settings.COR_TFNT.get().getName();
timerFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
timerFont.setSelectedItem(mainFont);
timerFont.setPreferredSize(new Dimension(130, 22));
timerFont.addActionListener(this);
String segFont = Settings.COR_SFNT.get().getName();
timerSegFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
timerSegFont.setSelectedItem(segFont);
timerSegFont.setPreferredSize(new Dimension(130, 22));
timerSegFont.addActionListener(this);
timerSize = new JSpinner(new SpinnerNumberModel(
Settings.COR_TFNT.get().getSize(), 8, 240, 1)
);
timerSize.addChangeListener(this);
timerSegSize = new JSpinner(new SpinnerNumberModel(
Settings.COR_SFNT.get().getSize(), 8, 240, 1)
);
timerSegSize.addChangeListener(this);
timerSameFont = new JCheckBox("" + Language.USE_MAIN_FONT);
timerSameFont.setSelected(segFont.equals(mainFont));
timerSegFont.setEnabled(!timerSameFont.isSelected());
timerSameFont.addActionListener(this);
place();
}
// -------------------------------------------------------------- CALLBACKS
@Override public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(iconSizes)) {
Settings.COR_ICSZ.set((Integer) iconSizes.getSelectedItem());
} else if (source.equals(timerFont)) {
String fontName = timerFont.getSelectedItem().toString();
Font font = Font.decode(fontName).deriveFont(
(float) Settings.COR_TFNT.get().getSize()
);
Settings.COR_TFNT.set(font);
if (timerSameFont.isSelected()) {
timerSegFont.setSelectedItem(fontName);
}
} else if (source.equals(timerSegFont)) {
String fontName = timerSegFont.getSelectedItem().toString();
Font font = Font.decode(fontName).deriveFont(
(float) Settings.COR_SFNT.get().getSize()
);
Settings.COR_SFNT.set(font);
} else if (source.equals(timerSameFont)) {
timerSegFont.setEnabled(!timerSameFont.isSelected());
if (timerSameFont.isSelected()) {
int size = (Integer) timerSegSize.getValue();
Settings.COR_SFNT.set(
Settings.COR_TFNT.get().deriveFont((float) size)
);
}
}
}
@Override public void stateChanged(ChangeEvent event) {
Object source = event.getSource();
if (source.equals(timerSize)) {
int size = (Integer) timerSize.getValue();
Settings.COR_TFNT.set(
Settings.COR_TFNT.get().deriveFont((float) size)
);
} else if (source.equals(timerSegSize)) {
int size = (Integer) timerSegSize.getValue();
Settings.COR_SFNT.set(
Settings.COR_SFNT.get().deriveFont((float) size)
);
}
}
// -------------------------------------------------------------- INHERITED
@Override void doDelayedSettingChange() {}
@Override public String toString() {
return "" + Language.COMPONENTS;
}
// -------------------------------------------------------------- UTILITIES
private void place() {
setLayout(new GridBagLayout());
JPanel fontPanel = new JPanel(new GridBagLayout()); {
fontPanel.add(
new JLabel("" + Language.setting_core_timerFont),
GBC.grid(0, 0).anchor(GBC.LS).insets(0, 5)
);
fontPanel.add(timerFont, GBC.grid(1, 0));
fontPanel.add(timerSize, GBC.grid(2, 0).insets(0, 5));
fontPanel.add(
new JLabel("" + Language.setting_core_segmentTimerFont),
GBC.grid(0, 1).anchor(GBC.LS).insets(3, 5)
);
fontPanel.add(
timerSameFont,
GBC.grid(1, 1, 2, 1).anchor(GBC.LS).insets(3, 0)
);
fontPanel.add(timerSegFont, GBC.grid(1, 2));
fontPanel.add(timerSegSize, GBC.grid(2, 2).insets(0, 5));
fontPanel.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_FONTS)
);
}
JPanel timerPanel = new JPanel(new GridBagLayout()); {
timerPanel.add(
new JLabel("" + Language.setting_core_iconSize),
GBC.grid(0, 1).anchor(GBC.LE).insets(5, 5)
);
timerPanel.add(iconSizes, GBC.grid(1, 1).insets(5, 5));
timerPanel.add(checkBoxes.get(Settings.COR_ICON.getKey()), GBC.grid(0, 2, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_NAME.getKey()), GBC.grid(0, 3, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_SPLT.getKey()), GBC.grid(0, 4, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_SEGM.getKey()), GBC.grid(0, 5, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_BEST.getKey()), GBC.grid(0, 6, 2, 1).anchor(GBC.LS));
timerPanel.add(checkBoxes.get(Settings.COR_STMR.getKey()), GBC.grid(0, 7, 2, 1).anchor(GBC.LS));
timerPanel.setBorder(
BorderFactory.createTitledBorder("" + Language.TIMER)
);
}
JPanel footerPanel = new JPanel(new GridBagLayout()); {
footerPanel.add(checkBoxes.get(Settings.FOO_SHOW.getKey()), GBC.grid(0, 0).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_SPLT.getKey()), GBC.grid(0, 1).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_VERB.getKey()), GBC.grid(0, 2).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_DLBL.getKey()), GBC.grid(1, 0).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_BEST.getKey()), GBC.grid(1, 1).anchor(GBC.LS));
footerPanel.add(checkBoxes.get(Settings.FOO_LINE.getKey()), GBC.grid(1, 2).anchor(GBC.LS));
footerPanel.setBorder(
BorderFactory.createTitledBorder("" + Language.FOOTER)
);
}
JPanel miscPanel = new JPanel(new GridBagLayout()); {
miscPanel.add(checkBoxes.get(Settings.HDR_TTLE.getKey()), GBC.grid(0, 0).anchor(GBC.LS));
miscPanel.add(checkBoxes.get(Settings.HDR_GOAL.getKey()), GBC.grid(0, 1).anchor(GBC.LS));
miscPanel.add(checkBoxes.get(Settings.GPH_SHOW.getKey()), GBC.grid(0, 2).anchor(GBC.LS));
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));
}
}

View file

@ -1,191 +1,179 @@
package org.fenix.llanfair.dialog;
import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import org.fenix.llanfair.Language;
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.gui.GBC;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import java.util.Locale;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import org.fenix.llanfair.Language;
import org.fenix.llanfair.Llanfair;
import org.fenix.llanfair.config.Settings;
import org.fenix.llanfair.config.Compare;
import org.fenix.llanfair.config.Accuracy;
import org.fenix.utils.gui.GBC;
import org.fenix.utils.Resources;
/**
*
* @author Xavier "Xunkar" Sencert
*/
public class TabGeneral extends SettingsTab implements ActionListener {
// ------------------------------------------------------------- ATTRIBUTES
private JComboBox language;
private JLabel languageText;
private JCheckBox alwaysOnTop;
private JLabel alwaysOnTopText;
private ButtonGroup compare;
private JLabel compareText;
private ButtonGroup accuracy;
private JLabel accuracyText;
private JCheckBox warnOnReset;
// ----------------------------------------------------------- CONSTRUCTORS
TabGeneral() {
language = new JComboBox(Language.LANGUAGES);
language.setRenderer(new LocaleListRenderer());
language.setSelectedItem(Settings.GNR_LANG.get());
language.addActionListener(this);
alwaysOnTop = new JCheckBox("" + Language.setting_alwaysOnTop);
alwaysOnTop.setSelected(Settings.GNR_ATOP.get());
compare = new ButtonGroup();
Compare setCmp = Settings.GNR_COMP.get();
for (Compare method : Compare.values()) {
JRadioButton radio = new JRadioButton("" + method);
radio.setName(method.name());
radio.setSelected(setCmp == method);
radio.addActionListener(this);
compare.add(radio);
}
accuracy = new ButtonGroup();
Accuracy setAcc = Settings.GNR_ACCY.get();
for (Accuracy value : Accuracy.values()) {
JRadioButton radio = new JRadioButton("" + value);
radio.setName(value.name());
radio.setSelected(setAcc == value);
radio.addActionListener(this);
accuracy.add(radio);
}
warnOnReset = new JCheckBox("" + Language.setting_warnOnReset);
warnOnReset.setSelected(Settings.GNR_WARN.get());
warnOnReset.addActionListener(this);
languageText = new JLabel("" + Language.setting_language);
alwaysOnTopText = new JLabel("" + Language.APPLICATION);
compareText = new JLabel("" + Language.COMPARE_METHOD);
accuracyText = new JLabel("" + Language.ACCURACY);
place();
}
// -------------------------------------------------------------- CALLBACKS
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(language)) {
Settings.GNR_LANG.set((Locale) language.getSelectedItem());
} else if (source instanceof JRadioButton) {
JRadioButton radio = (JRadioButton) source;
try {
Settings.GNR_COMP.set(
Compare.valueOf(radio.getName())
);
} catch (Exception e) {
Settings.GNR_ACCY.set(Accuracy.valueOf(radio.getName()));
}
} else if (source.equals(warnOnReset)) {
Settings.GNR_WARN.set(warnOnReset.isSelected());
}
}
// -------------------------------------------------------------- INHERITED
void doDelayedSettingChange() {
Settings.GNR_ATOP.set(alwaysOnTop.isSelected());
}
/**
* Returns the localized name of this tab.
*/
@Override public String toString() {
return "" + Language.GENERAL;
}
// -------------------------------------------------------------- UTILITIES
/**
* Places all sub-components within this panel.
*/
private void place() {
setLayout(new GridBagLayout());
add(
alwaysOnTopText, GBC.grid(0, 0).anchor(GBC.LE).insets(5, 10)
);
add(alwaysOnTop, GBC.grid(1, 0).anchor(GBC.LS));
add(warnOnReset, GBC.grid(1, 1).anchor(GBC.LS));
add(languageText, GBC.grid(0, 2).anchor(GBC.LE).insets(10, 10));
add(language, GBC.grid(1, 2).fill(GBC.H));
JPanel comparePanel = new JPanel(new GridLayout(0, 1)); {
Enumeration<AbstractButton> buttons = compare.getElements();
while (buttons.hasMoreElements()) {
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));
JPanel accuracyPanel = new JPanel(new GridLayout(0, 1)); {
Enumeration<AbstractButton> buttons = accuracy.getElements();
while (buttons.hasMoreElements()) {
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));
}
// --------------------------------------------------------- INTERNAL TYPES
class LocaleListRenderer extends DefaultListCellRenderer {
public LocaleListRenderer() {
setVerticalAlignment(CENTER);
}
@Override public Component getListCellRendererComponent(
JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus
) {
super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus
);
Locale selected = (Locale) value;
setIcon(Llanfair.getResources().getIcon("" + selected));
setText(Language.LOCALE_NAMES.get("" + selected));
return this;
}
}
// ------------------------------------------------------------- ATTRIBUTES
private JComboBox language;
private JLabel languageText;
private JCheckBox alwaysOnTop;
private JLabel alwaysOnTopText;
private ButtonGroup compare;
private JLabel compareText;
private ButtonGroup accuracy;
private JLabel accuracyText;
private JCheckBox warnOnReset;
// ----------------------------------------------------------- CONSTRUCTORS
TabGeneral() {
language = new JComboBox(Language.LANGUAGES);
language.setRenderer(new LocaleListRenderer());
language.setSelectedItem(Settings.GNR_LANG.get());
language.addActionListener(this);
alwaysOnTop = new JCheckBox("" + Language.setting_alwaysOnTop);
alwaysOnTop.setSelected(Settings.GNR_ATOP.get());
compare = new ButtonGroup();
Compare setCmp = Settings.GNR_COMP.get();
for (Compare method : Compare.values()) {
JRadioButton radio = new JRadioButton("" + method);
radio.setName(method.name());
radio.setSelected(setCmp == method);
radio.addActionListener(this);
compare.add(radio);
}
accuracy = new ButtonGroup();
Accuracy setAcc = Settings.GNR_ACCY.get();
for (Accuracy value : Accuracy.values()) {
JRadioButton radio = new JRadioButton("" + value);
radio.setName(value.name());
radio.setSelected(setAcc == value);
radio.addActionListener(this);
accuracy.add(radio);
}
warnOnReset = new JCheckBox("" + Language.setting_warnOnReset);
warnOnReset.setSelected(Settings.GNR_WARN.get());
warnOnReset.addActionListener(this);
languageText = new JLabel("" + Language.setting_language);
alwaysOnTopText = new JLabel("" + Language.APPLICATION);
compareText = new JLabel("" + Language.COMPARE_METHOD);
accuracyText = new JLabel("" + Language.ACCURACY);
place();
}
// -------------------------------------------------------------- CALLBACKS
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(language)) {
Settings.GNR_LANG.set((Locale) language.getSelectedItem());
} else if (source instanceof JRadioButton) {
JRadioButton radio = (JRadioButton) source;
try {
Settings.GNR_COMP.set(
Compare.valueOf(radio.getName())
);
} catch (Exception e) {
Settings.GNR_ACCY.set(Accuracy.valueOf(radio.getName()));
}
} else if (source.equals(warnOnReset)) {
Settings.GNR_WARN.set(warnOnReset.isSelected());
}
}
// -------------------------------------------------------------- INHERITED
void doDelayedSettingChange() {
Settings.GNR_ATOP.set(alwaysOnTop.isSelected());
}
/**
* Returns the localized name of this tab.
*/
@Override public String toString() {
return "" + Language.GENERAL;
}
// -------------------------------------------------------------- UTILITIES
/**
* Places all sub-components within this panel.
*/
private void place() {
setLayout(new GridBagLayout());
add(
alwaysOnTopText, GBC.grid(0, 0).anchor(GBC.LE).insets(5, 10)
);
add(alwaysOnTop, GBC.grid(1, 0).anchor(GBC.LS));
add(warnOnReset, GBC.grid(1, 1).anchor(GBC.LS));
add(languageText, GBC.grid(0, 2).anchor(GBC.LE).insets(10, 10));
add(language, GBC.grid(1, 2).fill(GBC.H));
JPanel comparePanel = new JPanel(new GridLayout(0, 1)); {
Enumeration<AbstractButton> buttons = compare.getElements();
while (buttons.hasMoreElements()) {
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));
JPanel accuracyPanel = new JPanel(new GridLayout(0, 1)); {
Enumeration<AbstractButton> buttons = accuracy.getElements();
while (buttons.hasMoreElements()) {
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));
}
// --------------------------------------------------------- INTERNAL TYPES
class LocaleListRenderer extends DefaultListCellRenderer {
public LocaleListRenderer() {
setVerticalAlignment(CENTER);
}
@Override public Component getListCellRendererComponent(
JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus
) {
super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus
);
Locale selected = (Locale) value;
setIcon(Llanfair.getResources().getIcon("" + selected));
setText(Language.LOCALE_NAMES.get("" + selected));
return this;
}
}
}

View file

@ -1,348 +1,337 @@
package org.fenix.llanfair.dialog;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.fenix.llanfair.Language;
import org.fenix.llanfair.Segment;
import org.fenix.llanfair.config.Settings;
import org.fenix.llanfair.config.Merge;
import org.fenix.llanfair.config.Settings;
import org.fenix.utils.gui.GBC;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
class TabHistory extends SettingsTab implements ActionListener, ChangeListener {
// ------------------------------------------------------------- ATTRIBUTES
/**
* Text field allowing the user to edit the height of the history. The
* height is provided as a number of segments to display.
*/
private JTextField rows;
/**
* Label displaying the name of the text field {@code height}.
*/
private JLabel heightText;
/**
* Label displaying the unit of the value of the text field {@code height}.
*/
private JLabel heightUnit;
/**
* Combox box displaying the available merging methods, determining which,
* if any, column should be merged.
*/
private JComboBox merge;
/**
* Check box determining wether or not the history should display the
* delta column.
*/
private JCheckBox deltas;
/**
* Check box determining wether or not the history should display the
* live times column.
*/
private JCheckBox lives;
private JCheckBox blankRows;
private JCheckBox icons;
private JComboBox iconSize;
private JCheckBox showLast;
private JSpinner offset;
private JLabel offsetHelper;
private JCheckBox twoLines;
private JCheckBox tabular;
private JComboBox nameFont;
private JSpinner nameSize;
private JComboBox timeFont;
private JSpinner timeSize;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Creates the "History" settings tab. Only called by {@link EditSettings}.
*/
TabHistory() {
rows = new JTextField();
rows.setHorizontalAlignment(JTextField.TRAILING);
rows.setText("" + Settings.HST_ROWS.get());
deltas = new JCheckBox("" + Language.setting_history_deltas);
deltas.setSelected(Settings.HST_DLTA.get());
deltas.addActionListener(this);
lives = new JCheckBox("" + Language.setting_history_liveTimes);
lives.setSelected(Settings.HST_LIVE.get());
lives.addActionListener(this);
twoLines = new JCheckBox("" + Language.setting_history_multiline);
twoLines.setSelected(Settings.HST_LINE.get());
twoLines.addActionListener(this);
blankRows = new JCheckBox("" + Language.setting_history_blankRows);
blankRows.setSelected(Settings.HST_BLNK.get());
blankRows.addActionListener(this);
icons = new JCheckBox("" + Language.setting_history_icons);
icons.setSelected(Settings.HST_ICON.get());
icons.addActionListener(this);
tabular = new JCheckBox("" + Language.setting_history_tabular);
tabular.setSelected(Settings.HST_TABL.get());
tabular.addActionListener(this);
showLast = new JCheckBox("" + Language.setting_history_alwaysShowLast);
showLast.setSelected(Settings.HST_LAST.get());
showLast.addActionListener(this);
merge = new JComboBox(Merge.values());
merge.setSelectedItem(Settings.HST_MERG.get());
merge.addActionListener(this);
iconSize = new JComboBox(Segment.ICON_SIZES);
iconSize.setSelectedItem(Settings.HST_ICSZ.get());
iconSize.addActionListener(this);
offset = new JSpinner(new SpinnerNumberModel(
(int) Settings.HST_OFFS.get(), -5, 5, 1)
);
offset.addChangeListener(this);
offsetHelper = new JLabel("<html>[<a href=''>?</a>]</html>");
offsetHelper.setToolTipText(
"<html><div width=200>" + Language.TT_HS_OFFSET + "</div></html>"
);
GraphicsEnvironment gEnv = GraphicsEnvironment
.getLocalGraphicsEnvironment();
String mainFont = Settings.HST_SFNT.get().getName();
nameFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
nameFont.setSelectedItem(mainFont);
nameFont.setPreferredSize(new Dimension(130, 22));
nameFont.addActionListener(this);
String font = Settings.HST_TFNT.get().getName();
timeFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
timeFont.setSelectedItem(font);
timeFont.setPreferredSize(new Dimension(130, 22));
timeFont.addActionListener(this);
nameSize = new JSpinner(new SpinnerNumberModel(
Settings.HST_SFNT.get().getSize(), 8, 240, 1)
);
nameSize.addChangeListener(this);
timeSize = new JSpinner(new SpinnerNumberModel(
Settings.HST_TFNT.get().getSize(), 8, 240, 1)
);
timeSize.addChangeListener(this);
place();
}
// -------------------------------------------------------------- CALLBACKS
/**
* Update the settings with the user input when he validates.
*/
@Override public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(merge)) {
Merge value = (Merge) merge.getSelectedItem();
Settings.HST_MERG.set(value);
if (value == Merge.DELTA) {
Settings.HST_DLTA.set(false);
deltas.setSelected(false);
} else if (value == Merge.LIVE) {
Settings.HST_LIVE.set(false);
lives.setSelected(false);
}
} else if (source.equals(deltas)) {
Settings.HST_DLTA.set(deltas.isSelected());
} else if (source.equals(lives)) {
Settings.HST_LIVE.set(lives.isSelected());
} else if (source.equals(blankRows)) {
Settings.HST_BLNK.set(blankRows.isSelected());
} else if (source.equals(icons)) {
Settings.HST_ICON.set(icons.isSelected());
} else if (source.equals(showLast)) {
Settings.HST_LAST.set(showLast.isSelected());
} else if (source.equals(twoLines)) {
Settings.HST_LINE.set(twoLines.isSelected());
} else if (source.equals(tabular)) {
Settings.HST_TABL.set(tabular.isSelected());
} else if (source.equals(iconSize)) {
Settings.HST_ICSZ.set(
(Integer) iconSize.getSelectedItem()
);
} else if (source.equals(nameFont)) {
String fontName = nameFont.getSelectedItem().toString();
Font font = Font.decode(fontName).deriveFont(
(float) Settings.HST_SFNT.get().getSize()
);
Settings.HST_SFNT.set(font);
} else if (source.equals(timeFont)) {
String fontName = timeFont.getSelectedItem().toString();
Font font = Font.decode(fontName).deriveFont(
(float) Settings.HST_TFNT.get().getSize()
);
Settings.HST_TFNT.set(font);
}
}
public void stateChanged(ChangeEvent event) {
Object source = event.getSource();
if (source.equals(offset)) {
int size = (Integer) offset.getValue();
Settings.HST_OFFS.set(size);
} else if (source.equals(nameSize)) {
int size = (Integer) nameSize.getValue();
Settings.HST_SFNT.set(
Settings.HST_SFNT.get().deriveFont((float) size)
);
} else if (source.equals(timeSize)) {
int size = (Integer) timeSize.getValue();
Settings.HST_TFNT.set(
Settings.HST_TFNT.get().deriveFont((float) size)
);
}
}
// -------------------------------------------------------------- INHERITED
// ------------------------------------------------------------- ATTRIBUTES
@Override void doDelayedSettingChange() {
int input = 0;
try {
input = Integer.parseInt(rows.getText());
} catch (Exception e) {
//$FALL-THROUGH$
} finally {
if (input <= 0) {
input = 0;
rows.setText("0");
}
Settings.HST_ROWS.set(input);
}
}
/**
* Returns the localized name of this tab.
*/
@Override public String toString() {
return "" + Language.HISTORY;
}
// -------------------------------------------------------------- UTILITIES
/**
* Places all sub-components within this panel.
*/
private void place() {
setLayout(new GridBagLayout());
// Set Components Orientation
rows.setHorizontalAlignment(JLabel.CENTER);
twoLines.setHorizontalTextPosition(JLabel.LEADING);
blankRows.setHorizontalTextPosition(JLabel.LEADING);
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));
/**
* Text field allowing the user to edit the height of the history. The
* height is provided as a number of segments to display.
*/
private JTextField rows;
/**
* Label displaying the name of the text field {@code height}.
*/
private JLabel heightText;
/**
* Label displaying the unit of the value of the text field {@code height}.
*/
private JLabel heightUnit;
/**
* Combox box displaying the available merging methods, determining which,
* if any, column should be merged.
*/
private JComboBox merge;
/**
* Check box determining wether or not the history should display the
* delta column.
*/
private JCheckBox deltas;
/**
* Check box determining wether or not the history should display the
* live times column.
*/
private JCheckBox lives;
private JCheckBox blankRows;
private JCheckBox icons;
private JComboBox iconSize;
private JCheckBox showLast;
private JSpinner offset;
private JLabel offsetHelper;
private JCheckBox twoLines;
private JCheckBox tabular;
private JComboBox nameFont;
private JSpinner nameSize;
private JComboBox timeFont;
private JSpinner timeSize;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Creates the "History" settings tab. Only called by {@link EditSettings}.
*/
TabHistory() {
rows = new JTextField();
rows.setHorizontalAlignment(JTextField.TRAILING);
rows.setText("" + Settings.HST_ROWS.get());
deltas = new JCheckBox("" + Language.setting_history_deltas);
deltas.setSelected(Settings.HST_DLTA.get());
deltas.addActionListener(this);
lives = new JCheckBox("" + Language.setting_history_liveTimes);
lives.setSelected(Settings.HST_LIVE.get());
lives.addActionListener(this);
twoLines = new JCheckBox("" + Language.setting_history_multiline);
twoLines.setSelected(Settings.HST_LINE.get());
twoLines.addActionListener(this);
blankRows = new JCheckBox("" + Language.setting_history_blankRows);
blankRows.setSelected(Settings.HST_BLNK.get());
blankRows.addActionListener(this);
icons = new JCheckBox("" + Language.setting_history_icons);
icons.setSelected(Settings.HST_ICON.get());
icons.addActionListener(this);
tabular = new JCheckBox("" + Language.setting_history_tabular);
tabular.setSelected(Settings.HST_TABL.get());
tabular.addActionListener(this);
showLast = new JCheckBox("" + Language.setting_history_alwaysShowLast);
showLast.setSelected(Settings.HST_LAST.get());
showLast.addActionListener(this);
merge = new JComboBox(Merge.values());
merge.setSelectedItem(Settings.HST_MERG.get());
merge.addActionListener(this);
iconSize = new JComboBox(Segment.ICON_SIZES);
iconSize.setSelectedItem(Settings.HST_ICSZ.get());
iconSize.addActionListener(this);
offset = new JSpinner(new SpinnerNumberModel(
(int) Settings.HST_OFFS.get(), -5, 5, 1)
);
offset.addChangeListener(this);
offsetHelper = new JLabel("<html>[<a href=''>?</a>]</html>");
offsetHelper.setToolTipText(
"<html><div width=200>" + Language.TT_HS_OFFSET + "</div></html>"
);
GraphicsEnvironment gEnv = GraphicsEnvironment
.getLocalGraphicsEnvironment();
String mainFont = Settings.HST_SFNT.get().getName();
nameFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
nameFont.setSelectedItem(mainFont);
nameFont.setPreferredSize(new Dimension(130, 22));
nameFont.addActionListener(this);
String font = Settings.HST_TFNT.get().getName();
timeFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
timeFont.setSelectedItem(font);
timeFont.setPreferredSize(new Dimension(130, 22));
timeFont.addActionListener(this);
nameSize = new JSpinner(new SpinnerNumberModel(
Settings.HST_SFNT.get().getSize(), 8, 240, 1)
);
nameSize.addChangeListener(this);
timeSize = new JSpinner(new SpinnerNumberModel(
Settings.HST_TFNT.get().getSize(), 8, 240, 1)
);
timeSize.addChangeListener(this);
place();
}
// -------------------------------------------------------------- CALLBACKS
/**
* Update the settings with the user input when he validates.
*/
@Override public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(merge)) {
Merge value = (Merge) merge.getSelectedItem();
Settings.HST_MERG.set(value);
if (value == Merge.DELTA) {
Settings.HST_DLTA.set(false);
deltas.setSelected(false);
} else if (value == Merge.LIVE) {
Settings.HST_LIVE.set(false);
lives.setSelected(false);
}
} else if (source.equals(deltas)) {
Settings.HST_DLTA.set(deltas.isSelected());
} else if (source.equals(lives)) {
Settings.HST_LIVE.set(lives.isSelected());
} else if (source.equals(blankRows)) {
Settings.HST_BLNK.set(blankRows.isSelected());
} else if (source.equals(icons)) {
Settings.HST_ICON.set(icons.isSelected());
} else if (source.equals(showLast)) {
Settings.HST_LAST.set(showLast.isSelected());
} else if (source.equals(twoLines)) {
Settings.HST_LINE.set(twoLines.isSelected());
} else if (source.equals(tabular)) {
Settings.HST_TABL.set(tabular.isSelected());
} else if (source.equals(iconSize)) {
Settings.HST_ICSZ.set(
(Integer) iconSize.getSelectedItem()
);
} else if (source.equals(nameFont)) {
String fontName = nameFont.getSelectedItem().toString();
Font font = Font.decode(fontName).deriveFont(
(float) Settings.HST_SFNT.get().getSize()
);
Settings.HST_SFNT.set(font);
} else if (source.equals(timeFont)) {
String fontName = timeFont.getSelectedItem().toString();
Font font = Font.decode(fontName).deriveFont(
(float) Settings.HST_TFNT.get().getSize()
);
Settings.HST_TFNT.set(font);
}
}
public void stateChanged(ChangeEvent event) {
Object source = event.getSource();
if (source.equals(offset)) {
int size = (Integer) offset.getValue();
Settings.HST_OFFS.set(size);
} else if (source.equals(nameSize)) {
int size = (Integer) nameSize.getValue();
Settings.HST_SFNT.set(
Settings.HST_SFNT.get().deriveFont((float) size)
);
} else if (source.equals(timeSize)) {
int size = (Integer) timeSize.getValue();
Settings.HST_TFNT.set(
Settings.HST_TFNT.get().deriveFont((float) size)
);
}
}
// -------------------------------------------------------------- INHERITED
@Override void doDelayedSettingChange() {
int input = 0;
try {
input = Integer.parseInt(rows.getText());
} catch (Exception e) {
//$FALL-THROUGH$
} finally {
if (input <= 0) {
input = 0;
rows.setText("0");
}
Settings.HST_ROWS.set(input);
}
}
/**
* Returns the localized name of this tab.
*/
@Override public String toString() {
return "" + Language.HISTORY;
}
// -------------------------------------------------------------- UTILITIES
/**
* Places all sub-components within this panel.
*/
private void place() {
setLayout(new GridBagLayout());
// Set Components Orientation
rows.setHorizontalAlignment(JLabel.CENTER);
twoLines.setHorizontalTextPosition(JLabel.LEADING);
blankRows.setHorizontalTextPosition(JLabel.LEADING);
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.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_DISPLAY)
);
}
// Dimension
JPanel dimension = new JPanel(new GridBagLayout()); {
dimension.add(
new JLabel("" + Language.setting_history_iconSize),
GBC.grid(0, 0).anchor(GBC.LE)
);
dimension.add(iconSize, GBC.grid(1, 0).anchor(GBC.LE).insets(2, 3));
dimension.add(
new JLabel("" + Language.setting_history_rowCount),
GBC.grid(0, 1).anchor(GBC.LE)
);
dimension.add(
rows, GBC.grid(1, 1).anchor(GBC.LE).fill(GBC.H).insets(2, 3)
);
dimension.add(blankRows, GBC.grid(0, 2, 2, 1).anchor(GBC.LE));
dimension.add(
twoLines , GBC.grid(0, 3, 2, 1).anchor(GBC.LE).insets(0, 1)
);
dimension.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_DIMENSION)
);
}
// Fonts
JPanel fonts = new JPanel(new GridBagLayout()); {
fonts.add(
new JLabel("" + Language.setting_history_segmentFont),
GBC.grid(0, 0).anchor(GBC.LE)
);
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(
new JLabel("" + Language.setting_history_timeFont),
GBC.grid(0, 1).anchor(GBC.LE)
);
fonts.add(timeFont, GBC.grid(1, 1).anchor(GBC.LS).insets(5, 5));
fonts.add(timeSize, GBC.grid(2, 1).anchor(GBC.LS));
fonts.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_FONTS)
);
}
// Scrolling
JPanel scrolling = new JPanel(new GridBagLayout()); {
scrolling.add(
offsetHelper, GBC.grid(0, 0).anchor(GBC.LE).insets(0, 8)
);
scrolling.add(
new JLabel("" + Language.setting_history_offset),
GBC.grid(1, 0).anchor(GBC.LE)
);
scrolling.add(offset , GBC.grid(2, 0).anchor(GBC.LE));
scrolling.add(showLast, GBC.grid(0, 1, 3, 1).anchor(GBC.LE));
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));
}
display.add(merge , GBC.grid(0, 4).anchor(GBC.LS));
display.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_DISPLAY)
);
}
// Dimension
JPanel dimension = new JPanel(new GridBagLayout()); {
dimension.add(
new JLabel("" + Language.setting_history_iconSize),
GBC.grid(0, 0).anchor(GBC.LE)
);
dimension.add(iconSize, GBC.grid(1, 0).anchor(GBC.LE).insets(2, 3));
dimension.add(
new JLabel("" + Language.setting_history_rowCount),
GBC.grid(0, 1).anchor(GBC.LE)
);
dimension.add(
rows, GBC.grid(1, 1).anchor(GBC.LE).fill(GBC.H).insets(2, 3)
);
dimension.add(blankRows, GBC.grid(0, 2, 2, 1).anchor(GBC.LE));
dimension.add(
twoLines , GBC.grid(0, 3, 2, 1).anchor(GBC.LE).insets(0, 1)
);
dimension.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_DIMENSION)
);
}
// Fonts
JPanel fonts = new JPanel(new GridBagLayout()); {
fonts.add(
new JLabel("" + Language.setting_history_segmentFont),
GBC.grid(0, 0).anchor(GBC.LE)
);
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(
new JLabel("" + Language.setting_history_timeFont),
GBC.grid(0, 1).anchor(GBC.LE)
);
fonts.add(timeFont, GBC.grid(1, 1).anchor(GBC.LS).insets(5, 5));
fonts.add(timeSize, GBC.grid(2, 1).anchor(GBC.LS));
fonts.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_FONTS)
);
}
// Scrolling
JPanel scrolling = new JPanel(new GridBagLayout()); {
scrolling.add(
offsetHelper, GBC.grid(0, 0).anchor(GBC.LE).insets(0, 8)
);
scrolling.add(
new JLabel("" + Language.setting_history_offset),
GBC.grid(1, 0).anchor(GBC.LE)
);
scrolling.add(offset , GBC.grid(2, 0).anchor(GBC.LE));
scrolling.add(showLast, GBC.grid(0, 1, 3, 1).anchor(GBC.LE));
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));
}
}

View file

@ -1,16 +1,5 @@
package org.fenix.llanfair.dialog;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JTextField;
import org.fenix.llanfair.Language;
import org.fenix.llanfair.config.Settings;
import org.fenix.utils.gui.GBC;
@ -18,194 +7,201 @@ import org.jnativehook.GlobalScreen;
import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Xavier "Xunkar" Sencert
*/
class TabHotkeys extends SettingsTab {
// ------------------------------------------------------------- ATTRIBUTES
/**
* List of all key fields customizable by the user.
*/
private List<KeyField> keyFields;
/**
* List of labels displaying the name of each key field.
*/
private List<JLabel> keyLabels;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Creates the "Hotkeys" settings tab. Only called by {@link EditSettings}.
*/
TabHotkeys() {
keyFields = new ArrayList<KeyField>();
keyLabels = new ArrayList<JLabel>();
int index = 0;
for (Settings.Property<?> setting : Settings.getAll("hotkey")) {
keyFields.add(new KeyField(
index, (Settings.Property<Integer>) setting)
);
keyLabels.add(new JLabel("" + setting));
index++;
}
place();
}
// -------------------------------------------------------------- INHERITED
@Override void doDelayedSettingChange() {}
/**
* Returns the localized name of this tab.
*/
@Override public String toString() {
return "" + Language.INPUTS;
}
// -------------------------------------------------------------- UTILITIES
/**
* Places all sub-components within this panel.
*/
private void place() {
setLayout(new GridBagLayout());
for (int row = 0; row < keyFields.size(); row++) {
add(
keyLabels.get(row),
GBC.grid(0, row).insets(10, 0, 10, 10).anchor(GBC.LE)
);
add(keyFields.get(row), GBC.grid(1, row));
}
}
// --------------------------------------------------------- INTERNAL TYPES
/**
* A text field representing a hotkey setting. Clicking such field allows
* the user to define a key for this particular setting, using the tables
* from {@code JNativeHook}.
*
* @author Xavier "Xunkar" Sencert
*/
static class KeyField extends JTextField
implements MouseListener, NativeKeyListener {
// ------------------------------------------------------------- ATTRIBUTES
// ----------------------------------------------------- CONSTANTS
/**
* Preferred dimension of a text field.
*/
private static final Dimension SIZE = new Dimension(85, 20);
// ---------------------------------------------------- ATTRIBUTES
private static boolean isEditing = false;
/**
* Internal index of this field, used by the superclass.
*/
private int index;
/**
* Setting represented by this key field.
*/
private Settings.Property<Integer> setting;
// -------------------------------------------------- CONSTRUCTORS
/**
* Creates a new key field for the given setting. Only called by
* {@link TabHotkeys}.
*
* @param index - internal index to identify this field.
* @param setting - setting represented by this field.
*/
KeyField(int index, Settings.Property<Integer> setting) {
this.index = index;
this.setting = setting;
setEditable(false);
setName(setting.getKey());
setPreferredSize(SIZE);
setHorizontalAlignment(CENTER);
String text = NativeKeyEvent.getKeyText(setting.get());
setText(setting.get() == -1 ? "" + Language.DISABLED : text);
addMouseListener(this);
}
// ------------------------------------------------------- GETTERS
/**
* Returns the setting represented by this key field.
*/
public Settings.Property<Integer> getSetting() {
return setting;
}
// ----------------------------------------------------- CALLBACKS
/**
* When a key field is clicked, we register ourselves as native key
* listener to capture the new key setting. The background adorns a
* new color to signify that this field is now listening.
*/
public void mouseClicked(MouseEvent event) {
if (!isEditing) {
setBackground(Color.YELLOW);
GlobalScreen.getInstance().addNativeKeyListener(this);
isEditing = true;
}
}
// $UNUSED$
public void mouseEntered(MouseEvent event) {}
// $UNUSED$
public void mouseExited(MouseEvent event) {}
// $UNUSED$
public void mousePressed(MouseEvent event) {}
// $UNUSED$
public void mouseReleased(MouseEvent event) {}
/**
* When a key is pressed we set the captured key as the new value
* for the setting we represent. If escape is pressed, the hotkey
* becomes disabled. After registering the update, we no longer
* listen for native key events.
*/
public void nativeKeyPressed(NativeKeyEvent event) {
int code = event.getKeyCode();
String text = null;
if (code == NativeKeyEvent.VK_ESCAPE) {
code = -1;
text = "" + Language.DISABLED;
} else {
text = NativeKeyEvent.getKeyText(code);
}
setText(text);
setting.set(code);
setBackground(Color.GREEN);
GlobalScreen.getInstance().removeNativeKeyListener(this);
isEditing = false;
}
// $UNUSED$
public void nativeKeyReleased(NativeKeyEvent event) {}
// $UNUSED$
public void nativeKeyTyped(NativeKeyEvent event) {}
}
/**
* List of all key fields customizable by the user.
*/
private List<KeyField> keyFields;
/**
* List of labels displaying the name of each key field.
*/
private List<JLabel> keyLabels;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Creates the "Hotkeys" settings tab. Only called by {@link EditSettings}.
*/
TabHotkeys() {
keyFields = new ArrayList<KeyField>();
keyLabels = new ArrayList<JLabel>();
int index = 0;
for (Settings.Property<?> setting : Settings.getAll("hotkey")) {
keyFields.add(new KeyField(
index, (Settings.Property<Integer>) setting)
);
keyLabels.add(new JLabel("" + setting));
index++;
}
place();
}
// -------------------------------------------------------------- INHERITED
@Override void doDelayedSettingChange() {}
/**
* Returns the localized name of this tab.
*/
@Override public String toString() {
return "" + Language.INPUTS;
}
// -------------------------------------------------------------- UTILITIES
/**
* Places all sub-components within this panel.
*/
private void place() {
setLayout(new GridBagLayout());
for (int row = 0; row < keyFields.size(); row++) {
add(
keyLabels.get(row),
GBC.grid(0, row).insets(10, 0, 10, 10).anchor(GBC.LE)
);
add(keyFields.get(row), GBC.grid(1, row));
}
}
// --------------------------------------------------------- INTERNAL TYPES
/**
* A text field representing a hotkey setting. Clicking such field allows
* the user to define a key for this particular setting, using the tables
* from {@code JNativeHook}.
*
* @author Xavier "Xunkar" Sencert
*/
static class KeyField extends JTextField
implements MouseListener, NativeKeyListener {
// ----------------------------------------------------- CONSTANTS
/**
* Preferred dimension of a text field.
*/
private static final Dimension SIZE = new Dimension(85, 20);
// ---------------------------------------------------- ATTRIBUTES
private static boolean isEditing = false;
/**
* Internal index of this field, used by the superclass.
*/
private int index;
/**
* Setting represented by this key field.
*/
private Settings.Property<Integer> setting;
// -------------------------------------------------- CONSTRUCTORS
/**
* Creates a new key field for the given setting. Only called by
* {@link TabHotkeys}.
*
* @param index - internal index to identify this field.
* @param setting - setting represented by this field.
*/
KeyField(int index, Settings.Property<Integer> setting) {
this.index = index;
this.setting = setting;
setEditable(false);
setName(setting.getKey());
setPreferredSize(SIZE);
setHorizontalAlignment(CENTER);
String text = NativeKeyEvent.getKeyText(setting.get());
setText(setting.get() == -1 ? "" + Language.DISABLED : text);
addMouseListener(this);
}
// ------------------------------------------------------- GETTERS
/**
* Returns the setting represented by this key field.
*/
public Settings.Property<Integer> getSetting() {
return setting;
}
// ----------------------------------------------------- CALLBACKS
/**
* When a key field is clicked, we register ourselves as native key
* listener to capture the new key setting. The background adorns a
* new color to signify that this field is now listening.
*/
public void mouseClicked(MouseEvent event) {
if (!isEditing) {
setBackground(Color.YELLOW);
GlobalScreen.getInstance().addNativeKeyListener(this);
isEditing = true;
}
}
// $UNUSED$
public void mouseEntered(MouseEvent event) {}
// $UNUSED$
public void mouseExited(MouseEvent event) {}
// $UNUSED$
public void mousePressed(MouseEvent event) {}
// $UNUSED$
public void mouseReleased(MouseEvent event) {}
/**
* When a key is pressed we set the captured key as the new value
* for the setting we represent. If escape is pressed, the hotkey
* becomes disabled. After registering the update, we no longer
* listen for native key events.
*/
public void nativeKeyPressed(NativeKeyEvent event) {
int code = event.getKeyCode();
String text = null;
if (code == NativeKeyEvent.VK_ESCAPE) {
code = -1;
text = "" + Language.DISABLED;
} else {
text = NativeKeyEvent.getKeyText(code);
}
setText(text);
setting.set(code);
setBackground(Color.GREEN);
GlobalScreen.getInstance().removeNativeKeyListener(this);
isEditing = false;
}
// $UNUSED$
public void nativeKeyReleased(NativeKeyEvent event) {}
// $UNUSED$
public void nativeKeyTyped(NativeKeyEvent event) {}
}
}

View file

@ -28,296 +28,296 @@ import org.fenix.utils.gui.GBC;
* @author Xavier "Xunkar" Sencert
*/
class TabLook extends SettingsTab implements ActionListener, ChangeListener {
// -------------------------------------------------------------- CONSTANTS
/**
* Dimension of the revert button.
*/
private static final Dimension REVERT_SIZE = new Dimension(18, 18);
// ------------------------------------------------------------- ATTRIBUTES
/**
* List of all color buttons, inserted in the order of their name.
*/
private List<ColorButton> colorButtons;
/**
* List of labels displaying the name of the color button.
*/
private List<JLabel> colorTexts;
/**
* Panel allowing the user to select a color.
*/
private JColorChooser colorChooser;
/**
* The index of the currently selected color button. Any color change will
* be made to this button.
*/
private int selected;
/**
* Label displaying a text explaining to the user that he must first
* select a color button before editing its color.
*/
private JLabel helperText;
/**
* Panel listing the color buttons and their name label.
*/
private JPanel colorPanel;
/**
* Small button displayed next to the selected color button and resetting
* its color to its original value.
*/
private JButton revert;
/**
* Invisible panel the size of the revert button used to take up space when
* the button is not showing.
*/
private JPanel placeHolder;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Creates the "Look" settings tab. Only called by {@link EditSettings}.
*/
TabLook() {
selected = -1;
helperText = new JLabel("" + Language.TT_COLOR_PICK);
colorTexts = new ArrayList<JLabel>();
colorButtons = new ArrayList<ColorButton>();
// -------------------------------------------------------------- CONSTANTS
int index = 0;
for (Settings.Property<?> colorSetting : Settings.getAll("color")) {
ColorButton colorButton = new ColorButton(
index, (Settings.Property<Color>) colorSetting
);
colorButton.addActionListener(this);
colorButtons.add(colorButton);
colorTexts.add(new JLabel("" + colorSetting));
index++;
}
colorChooser = new JColorChooser();
colorChooser.setPreviewPanel(new JPanel());
colorChooser.getSelectionModel().addChangeListener(this);
colorChooser.setEnabled(false);
revert = new JButton(Llanfair.getResources().getIcon("REVERT"));
revert.setPreferredSize(REVERT_SIZE);
revert.addActionListener(this);
placeHolder = new JPanel();
placeHolder.setPreferredSize(REVERT_SIZE);
place();
}
// -------------------------------------------------------------- CALLBACKS
/**
* When a color button is pressed, we bring up the color chooser and
* change the color of the text indicate its been selected. We also
* display the revert button next to the selected color. If the event
* emanates from a revert button, we revert its associated color.
*/
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(revert)) {
colorButtons.get(selected).resetColor();
} else {
if (selected != -1) {
colorTexts.get(selected).setForeground(Color.BLACK);
colorPanel.remove(revert);
} else {
colorPanel.remove(placeHolder);
colorChooser.setEnabled(true);
}
selected = ((ColorButton) source).getIndex();
colorTexts.get(selected).setForeground(Color.RED);
colorPanel.add(revert, GBC.grid(2, selected).insets(0, 2));
colorChooser.setColor(colorButtons.get(selected).getColor());
revalidate();
}
}
/**
* When a color is selected in the color chooser, update the selected
* color button (if any.)
*/
public void stateChanged(ChangeEvent event) {
if (selected != -1) {
colorButtons.get(selected).setColor(colorChooser.getColor());
}
}
// -------------------------------------------------------------- INHERITED
@Override void doDelayedSettingChange() {}
/**
* Returns the localized name of this tab.
*/
@Override public String toString() {
return "" + Language.COLORS;
}
// -------------------------------------------------------------- UTILITIES
/**
* Places all sub-components within this panel.
*/
private void place() {
setLayout(new GridBagLayout());
colorPanel = new JPanel(new GridBagLayout()); {
for (int row = 0; row < colorButtons.size(); row++) {
colorPanel.add(
colorTexts.get(row),
GBC.grid(0, row).anchor(GBC.LE).insets(1, 4)
);
colorPanel.add(
colorButtons.get(row), GBC.grid(1, row).insets(2, 0)
);
}
colorPanel.add(placeHolder, GBC.grid(2, 0).insets(0, 2));
}
JPanel swatchPanel = new JPanel(new GridBagLayout()); {
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));
}
// --------------------------------------------------------- INTERNAL TYPES
/**
* A kind of {@code JButton} that displays a color setting by painting
* itself in that color. When the color is programatically set, the color
* setting is automatically updated by the button. Each button stores an
* index only valid in the owning views context and the initial value it
* was constructed with, allowing for a reset.
*
* @author Xavier "Xunkar" Sencert
*/
class ColorButton extends JButton implements Comparable<ColorButton> {
/**
* Dimension of the revert button.
*/
private static final Dimension REVERT_SIZE = new Dimension(18, 18);
// ---------------------------------------------------- ATTRIBUTES
/**
* Index of this color button in the englobing model.
*/
private int index;
// ------------------------------------------------------------- ATTRIBUTES
/**
* Color currently represented by this button.
*/
private Color color;
/**
* Color initially represented by this button. Used to revert to the
* setting initial state.
*/
private Color initialColor;
/**
* Color setting that this button represent. When the user selectes
* a new color, this setting is updated with the user's choice.
*/
private Settings.Property<Color> setting;
// -------------------------------------------------- CONSTRUCTORS
/**
* List of all color buttons, inserted in the order of their name.
*/
private List<ColorButton> colorButtons;
/**
* Creates a button representing the given setting. An index must be
* supplied to identify this button. The name of the setting becomes
* the name of the button.
*
* @param index - the index of this button.
* @param setting - the setting represented by this button.
*/
ColorButton(int index, Settings.Property<Color> setting) {
super(".");
color = setting.get();
initialColor = color;
this.index = index;
this.setting = setting;
setName(setting.getKey());
}
/**
* List of labels displaying the name of the color button.
*/
private List<JLabel> colorTexts;
// ------------------------------------------------------- GETTERS
/**
* Panel allowing the user to select a color.
*/
private JColorChooser colorChooser;
/**
* Returns the current color of this button.
*
* @return the current color.
*/
Color getColor() {
return color;
}
/**
* Returns the index of this button.
*
* @return the index of this button.
*/
int getIndex() {
return index;
}
/**
* The index of the currently selected color button. Any color change will
* be made to this button.
*/
private int selected;
// ------------------------------------------------------- SETTERS
/**
* Label displaying a text explaining to the user that he must first
* select a color button before editing its color.
*/
private JLabel helperText;
/**
* Sets the color that this button should display. Updates the setting
* and repaints itself.
*
* @param color - the new color to display.
*/
void setColor(Color color) {
this.color = color;
setting.set(color);
repaint();
}
/**
* Resets the color to be displayed by this button to the initial
* color it was constructed with.
*/
void resetColor() {
setColor(initialColor);
}
// ----------------------------------------------------- INHERITED
/**
* Two {@code ColorButton}s are compared using the lexicographic
* comparison of their name.
*/
public int compareTo(ColorButton o) {
if (o == null) {
return 1;
}
return getName().compareTo(o.getName());
}
/**
* Panel listing the color buttons and their name label.
*/
private JPanel colorPanel;
/**
* A {@code ColorButton} paints itself in the color he should
* display and does not display any string of text.
*/
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
/**
* Small button displayed next to the selected color button and resetting
* its color to its original value.
*/
private JButton revert;
/**
* Invisible panel the size of the revert button used to take up space when
* the button is not showing.
*/
private JPanel placeHolder;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Creates the "Look" settings tab. Only called by {@link EditSettings}.
*/
TabLook() {
selected = -1;
helperText = new JLabel("" + Language.TT_COLOR_PICK);
colorTexts = new ArrayList<JLabel>();
colorButtons = new ArrayList<ColorButton>();
int index = 0;
for (Settings.Property<?> colorSetting : Settings.getAll("color")) {
ColorButton colorButton = new ColorButton(
index, (Settings.Property<Color>) colorSetting
);
colorButton.addActionListener(this);
colorButtons.add(colorButton);
colorTexts.add(new JLabel("" + colorSetting));
index++;
}
colorChooser = new JColorChooser();
colorChooser.setPreviewPanel(new JPanel());
colorChooser.getSelectionModel().addChangeListener(this);
colorChooser.setEnabled(false);
revert = new JButton(Llanfair.getResources().getIcon("REVERT"));
revert.setPreferredSize(REVERT_SIZE);
revert.addActionListener(this);
placeHolder = new JPanel();
placeHolder.setPreferredSize(REVERT_SIZE);
place();
}
// -------------------------------------------------------------- CALLBACKS
/**
* When a color button is pressed, we bring up the color chooser and
* change the color of the text indicate its been selected. We also
* display the revert button next to the selected color. If the event
* emanates from a revert button, we revert its associated color.
*/
public void actionPerformed(ActionEvent event) {
Object source = event.getSource();
if (source.equals(revert)) {
colorButtons.get(selected).resetColor();
} else {
if (selected != -1) {
colorTexts.get(selected).setForeground(Color.BLACK);
colorPanel.remove(revert);
} else {
colorPanel.remove(placeHolder);
colorChooser.setEnabled(true);
}
selected = ((ColorButton) source).getIndex();
colorTexts.get(selected).setForeground(Color.RED);
colorPanel.add(revert, GBC.grid(2, selected).insets(0, 2));
colorChooser.setColor(colorButtons.get(selected).getColor());
revalidate();
}
}
/**
* When a color is selected in the color chooser, update the selected
* color button (if any.)
*/
public void stateChanged(ChangeEvent event) {
if (selected != -1) {
colorButtons.get(selected).setColor(colorChooser.getColor());
}
}
// -------------------------------------------------------------- INHERITED
@Override void doDelayedSettingChange() {}
/**
* Returns the localized name of this tab.
*/
@Override public String toString() {
return "" + Language.COLORS;
}
// -------------------------------------------------------------- UTILITIES
/**
* Places all sub-components within this panel.
*/
private void place() {
setLayout(new GridBagLayout());
colorPanel = new JPanel(new GridBagLayout()); {
for (int row = 0; row < colorButtons.size(); row++) {
colorPanel.add(
colorTexts.get(row),
GBC.grid(0, row).anchor(GBC.LE).insets(1, 4)
);
colorPanel.add(
colorButtons.get(row), GBC.grid(1, row).insets(2, 0)
);
}
colorPanel.add(placeHolder, GBC.grid(2, 0).insets(0, 2));
}
JPanel swatchPanel = new JPanel(new GridBagLayout()); {
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));
}
// --------------------------------------------------------- INTERNAL TYPES
/**
* A kind of {@code JButton} that displays a color setting by painting
* itself in that color. When the color is programatically set, the color
* setting is automatically updated by the button. Each button stores an
* index only valid in the owning views context and the initial value it
* was constructed with, allowing for a reset.
*
* @author Xavier "Xunkar" Sencert
*/
class ColorButton extends JButton implements Comparable<ColorButton> {
// ---------------------------------------------------- ATTRIBUTES
/**
* Index of this color button in the englobing model.
*/
private int index;
/**
* Color currently represented by this button.
*/
private Color color;
/**
* Color initially represented by this button. Used to revert to the
* setting initial state.
*/
private Color initialColor;
/**
* Color setting that this button represent. When the user selectes
* a new color, this setting is updated with the user's choice.
*/
private Settings.Property<Color> setting;
// -------------------------------------------------- CONSTRUCTORS
/**
* Creates a button representing the given setting. An index must be
* supplied to identify this button. The name of the setting becomes
* the name of the button.
*
* @param index - the index of this button.
* @param setting - the setting represented by this button.
*/
ColorButton(int index, Settings.Property<Color> setting) {
super(".");
color = setting.get();
initialColor = color;
this.index = index;
this.setting = setting;
setName(setting.getKey());
}
// ------------------------------------------------------- GETTERS
/**
* Returns the current color of this button.
*
* @return the current color.
*/
Color getColor() {
return color;
}
/**
* Returns the index of this button.
*
* @return the index of this button.
*/
int getIndex() {
return index;
}
// ------------------------------------------------------- SETTERS
/**
* Sets the color that this button should display. Updates the setting
* and repaints itself.
*
* @param color - the new color to display.
*/
void setColor(Color color) {
this.color = color;
setting.set(color);
repaint();
}
/**
* Resets the color to be displayed by this button to the initial
* color it was constructed with.
*/
void resetColor() {
setColor(initialColor);
}
// ----------------------------------------------------- INHERITED
/**
* Two {@code ColorButton}s are compared using the lexicographic
* comparison of their name.
*/
public int compareTo(ColorButton o) {
if (o == null) {
return 1;
}
return getName().compareTo(o.getName());
}
/**
* A {@code ColorButton} paints itself in the color he should
* display and does not display any string of text.
*/
@Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(color);
g.fillRect(0, 0, getWidth(), getHeight());
}
}
}

View file

@ -1,13 +1,14 @@
package org.fenix.llanfair.extern;
import java.io.BufferedReader;
import java.io.IOException;
import javax.swing.ImageIcon;
import org.fenix.llanfair.Llanfair;
import org.fenix.llanfair.Run;
import org.fenix.llanfair.Segment;
import org.fenix.llanfair.Time;
import javax.swing.*;
import java.io.BufferedReader;
import java.io.IOException;
/**
* Utility class that provides method to interface with WSplit.
*
@ -15,95 +16,95 @@ import org.fenix.llanfair.Time;
* @version 1.1
*/
public class WSplit {
/**
* Parses the given stream opened on a WSplit run file and sets it as
* the currently opened run in Llanfair.
*
* @param master the Llanfair instance calling the parser
* @param in an opened stream on a WSplit run file
* @throws Exception if the reading operation cannot complete
*/
public static void parse( Llanfair master, BufferedReader in )
throws Exception {
if ( master == null ) {
throw new NullPointerException( "Null Llanfair instance" );
}
if ( in == null ) {
throw new NullPointerException( "Null file reader" );
}
try {
// Title
String line = in.readLine();
Run run = new Run( line.split( "=" )[1] );
// Attempts, Offset, Size
in.readLine();
in.readLine();
in.readLine();
// Segment List
line = parseSegments( in, run );
// Segment Icons
parseIcons( line, run );
master.setRun( run );
} catch ( Exception ex ) {
throw ex;
}
}
/**
* Parses the list of segment in a WSplit run file. The segment list is
* formatted like this: each segment is on one line and comprised of the
* following information: Name, Old Time, Best Time, Best Segment
* comma-separated.
*
* @param in the opened stream on a WSplit run file
* @param run the run currently built by the parser
* @return the currently read line marker
* @throws IOException if reading operations fail
*/
private static String parseSegments( BufferedReader in, Run run )
throws IOException {
String line;
while ( !( line = in.readLine() ).startsWith( "Icons" ) ) {
String[] args = line.split( "," );
Segment segment = new Segment( args[0] );
run.addSegment( segment );
double parsed = Double.parseDouble( args[2] );
run.setValueAt(
parsed == 0.0 ? null : new Time( parsed ),
run.getRowCount() - 1, Run.COLUMN_TIME
);
parsed = Double.parseDouble( args[3] );
segment.setTime(
parsed == 0.0 ? null : new Time(parsed), Segment.BEST
);
}
return line;
}
/**
* Parses the list of segment icons in a WSplit run file. The list is a
* single line, listing the icons in the segment order and formatted as
* follows: Icons="icon1","icon2",...,"iconN"
*
* @param line the line containing the icon list
* @param run the run currently built by the parser
* @throws IOException if reading operations fail
*/
private static void parseIcons( String line, Run run ) throws IOException {
line = line.substring( line.indexOf( "=" ) + 1 );
String[] args = line.split( "," );
for ( int i = 0; i < args.length; i++ ) {
ImageIcon icon = null;
String[] lst = args[i].split( "\\\"" );
if ( lst.length > 0 && !lst[1].equals( "" ) ) {
icon = new ImageIcon( lst[1] );
}
run.getSegment( i ).setIcon( icon );
}
}
/**
* Parses the given stream opened on a WSplit run file and sets it as
* the currently opened run in Llanfair.
*
* @param master the Llanfair instance calling the parser
* @param in an opened stream on a WSplit run file
* @throws Exception if the reading operation cannot complete
*/
public static void parse( Llanfair master, BufferedReader in )
throws Exception {
if ( master == null ) {
throw new NullPointerException( "Null Llanfair instance" );
}
if ( in == null ) {
throw new NullPointerException( "Null file reader" );
}
try {
// Title
String line = in.readLine();
Run run = new Run( line.split( "=" )[1] );
// Attempts, Offset, Size
in.readLine();
in.readLine();
in.readLine();
// Segment List
line = parseSegments( in, run );
// Segment Icons
parseIcons( line, run );
master.setRun( run );
} catch ( Exception ex ) {
throw ex;
}
}
/**
* Parses the list of segment in a WSplit run file. The segment list is
* formatted like this: each segment is on one line and comprised of the
* following information: Name, Old Time, Best Time, Best Segment
* comma-separated.
*
* @param in the opened stream on a WSplit run file
* @param run the run currently built by the parser
* @return the currently read line marker
* @throws IOException if reading operations fail
*/
private static String parseSegments( BufferedReader in, Run run )
throws IOException {
String line;
while ( !( line = in.readLine() ).startsWith( "Icons" ) ) {
String[] args = line.split( "," );
Segment segment = new Segment( args[0] );
run.addSegment( segment );
double parsed = Double.parseDouble( args[2] );
run.setValueAt(
parsed == 0.0 ? null : new Time( parsed ),
run.getRowCount() - 1, Run.COLUMN_TIME
);
parsed = Double.parseDouble( args[3] );
segment.setTime(
parsed == 0.0 ? null : new Time(parsed), Segment.BEST
);
}
return line;
}
/**
* Parses the list of segment icons in a WSplit run file. The list is a
* single line, listing the icons in the segment order and formatted as
* follows: Icons="icon1","icon2",...,"iconN"
*
* @param line the line containing the icon list
* @param run the run currently built by the parser
* @throws IOException if reading operations fail
*/
private static void parseIcons( String line, Run run ) throws IOException {
line = line.substring( line.indexOf( "=" ) + 1 );
String[] args = line.split( "," );
for ( int i = 0; i < args.length; i++ ) {
ImageIcon icon = null;
String[] lst = args[i].split( "\\\"" );
if ( lst.length > 0 && !lst[1].equals( "" ) ) {
icon = new ImageIcon( lst[1] );
}
run.getSegment( i ).setIcon( icon );
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -29,464 +29,464 @@ import org.fenix.utils.locale.LocaleEvent;
*/
class Footer extends JPanel {
private static final int ALL = 0xff;
private static final int TIME = 0x01;
private static final int DELTA = 0x02;
private static final int TEXT = 0x04;
private static final int BEST = 0x08;
private static final int VERBOSE = 0x10;
private static final int INSET = 3;
private static final int ALL = 0xff;
private static final int TIME = 0x01;
private static final int DELTA = 0x02;
private static final int TEXT = 0x04;
private static final int BEST = 0x08;
private static final int VERBOSE = 0x10;
private static final int INSET = 3;
private Run run;
private Time tmDlta;
private JLabel labelPrev; // P.Se:
private JLabel liveL; // Left-hand Live Time
private JLabel liveR; // Right-hand Live Time
private JLabel time; // Segment Set Time
private JLabel labelDelta; // Delta:
private JLabel labelLive; // Live:
private JLabel delta; // Delta Segment Set/Live Time
private JLabel labelBest; // P.Be:
private JLabel best; // Segment Best Time
private JLabel inlineBest; // Segment Best Time (inline)
private JLabel labelDeltaBest; // B.Delta:
private JLabel deltaBest; // Delta Segment Best/Live Time
private JLabel inlineDeltaBest; // Delta Segment Best/Live Time (inline)
private Run run;
private Time tmDlta;
private JPanel panelBest; // labelBest + best
private JPanel panelDeltaBest; // labelDeltaBest + deltaBest
private boolean resize;
private Dimension preferredSize;
private JLabel labelPrev; // P.Se:
private JLabel liveL; // Left-hand Live Time
private JLabel liveR; // Right-hand Live Time
private JLabel time; // Segment Set Time
/**
* Creates a default panel displaying informations for the given run.
*
* @param run - the run to represent.
*/
Footer(Run run) {
time = new JLabel();
liveL = new JLabel();
liveR = new JLabel();
delta = new JLabel();
best = new JLabel();
deltaBest = new JLabel();
inlineBest = new JLabel();
inlineDeltaBest = new JLabel();
labelLive = new JLabel();
labelPrev = new JLabel();
labelBest = new JLabel();
labelDelta = new JLabel();
labelDeltaBest = new JLabel();
preferredSize = null;
resize = false;
setRun(run);
setOpaque(false);
placeComponents();
updateValues(TEXT);
updateColors(ALL);
updateVisibility(ALL);
forceResize();
}
private JLabel labelDelta; // Delta:
private JLabel labelLive; // Live:
private JLabel delta; // Delta Segment Set/Live Time
@Override public Dimension getPreferredSize() {
Graphics graphics = getGraphics();
if (resize && (graphics != null)) {
FontMetrics metrics = graphics.getFontMetrics();
int timeW;
int timeH = metrics.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);
} else {
timeW = metrics.stringWidth("" + Time.ZERO);
smtmW = timeW;
}
private JLabel labelBest; // P.Be:
private JLabel best; // Segment Best Time
private JLabel inlineBest; // Segment Best Time (inline)
private JLabel labelDeltaBest; // B.Delta:
private JLabel deltaBest; // Delta Segment Best/Live Time
private JLabel inlineDeltaBest; // Delta Segment Best/Live Time (inline)
private JPanel panelBest; // labelBest + best
private JPanel panelDeltaBest; // labelDeltaBest + deltaBest
private boolean resize;
private Dimension preferredSize;
/**
* Creates a default panel displaying informations for the given run.
*
* @param run - the run to represent.
*/
Footer(Run run) {
time = new JLabel();
liveL = new JLabel();
liveR = new JLabel();
delta = new JLabel();
best = new JLabel();
deltaBest = new JLabel();
inlineBest = new JLabel();
inlineDeltaBest = new JLabel();
labelLive = new JLabel();
labelPrev = new JLabel();
labelBest = new JLabel();
labelDelta = new JLabel();
labelDeltaBest = new JLabel();
preferredSize = null;
resize = false;
setRun(run);
setOpaque(false);
placeComponents();
updateValues(TEXT);
updateColors(ALL);
updateVisibility(ALL);
forceResize();
}
@Override public Dimension getPreferredSize() {
Graphics graphics = getGraphics();
if (resize && (graphics != null)) {
FontMetrics metrics = graphics.getFontMetrics();
int timeW;
int timeH = metrics.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);
} else {
timeW = metrics.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);
boolean ftBest = Settings.FOO_BEST.get();
boolean ftLabels = Settings.FOO_DLBL.get();
boolean ftVerbose = Settings.FOO_VERB.get();
boolean ftTwoLines = Settings.FOO_LINE.get();
int height = timeH;
int width = prevW + timeW + smtmW + INSET * 2;
if (ftLabels) {
width += dltaW;
}
if (ftVerbose) {
width += timeW + liveW - (ftLabels ? 0 : dltaW)
+ metrics.stringWidth(" []");
}
if (ftBest) {
if (ftTwoLines) {
height *= 2;
int breakW = bestW + timeW + smtmW + (ftLabels ? dltbW : 0);
width = Math.max(width, breakW);
} else {
width += timeW + smtmW + metrics.stringWidth("| ");
}
if (ftVerbose) {
width += 5;
}
}
preferredSize = new Dimension(width, height);
setMinimumSize(new Dimension(50, height));
resize = false;
}
return (preferredSize == null ? getMinimumSize() : preferredSize);
}
/**
* Sets the run to represent. All components are resetted to their initial
* state.
*
* @param run - the new run to represent.
*/
final void setRun(Run run) {
this.run = run;
updateValues(ALL & ~TEXT);
}
// -------------------------------------------------------------- CALLBACKS
/**
* Callback invoked by the parent when the run or the application's
* settings have seen one of their properties updated.
*
* @param event - the event describing the update.
*/
void processPropertyChangeEvent(PropertyChangeEvent event) {
String property = event.getPropertyName();
if (Run.CURRENT_SEGMENT_PROPERTY.equals(property)) {
updateValues(ALL & ~TEXT);
updateColors(TIME | DELTA);
updateVisibility(ALL);
} else if (Settings.CLR_LOST.equals(property)
|| Settings.CLR_GAIN.equals(property)) {
updateColors(DELTA);
} else if (Settings.CLR_TIME.equals(property)
|| Settings.CLR_RCRD.equals(property)) {
updateColors(TIME | DELTA);
} else if (Settings.CLR_FORE.equals(property)) {
updateColors(TEXT);
} else if (Settings.GNR_ACCY.equals(property)
|| Settings.GNR_COMP.equals(property)) {
updateValues(ALL & ~TEXT);
forceResize();
} else if (Run.STATE_PROPERTY.equals(property)) {
if (run.getState() == State.NULL || run.getState() == State.READY) {
updateValues(ALL & ~TEXT);
}
updateVisibility(ALL);
} else if (Settings.FOO_SPLT.equals(property)) {
updateValues(ALL);
} else if (Settings.FOO_BEST.equals(property)
|| Settings.FOO_LINE.equals(property)) {
updateVisibility(BEST);
forceResize();
} else if (Settings.FOO_DLBL.equals(property)) {
updateVisibility(TEXT);
forceResize();
} else if (Settings.FOO_VERB.equals(property)) {
updateValues(DELTA);
updateVisibility(VERBOSE);
forceResize();
}
}
private void forceResize() {
resize = true;
revalidate();
}
/**
* Callback invoked by the parent when default local for this instance of
* the JVM has changed.
*
* @param event - the event describing the update.
*/
void processLocaleEvent(LocaleEvent event) {
updateValues(TEXT);
}
// -------------------------------------------------------------- UTILITIES
/**
* Places the sub-components within this component.
*/
private void placeComponents() {
setLayout(new GridBagLayout());
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);
JPanel timePanel = new JPanel(new GridBagLayout()); {
timePanel.add(
labelPrev,
GBC.grid(0, 0).anchor(GBC.LS).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(
inlineBest,
GBC.grid(3, 0).anchor(GBC.LS).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)
);
deltaPanel.add(
labelLive,
GBC.grid(1, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
);
deltaPanel.add(
liveR, GBC.grid(2, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
);
deltaPanel.add(delta, GBC.grid(3, 0).anchor(GBC.LE));
deltaPanel.add(
inlineDeltaBest,
GBC.grid(4, 0).anchor(GBC.LE).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)
);
panelBest.add(best, GBC.grid(1, 0).anchor(GBC.LS));
panelBest.setOpaque(false);
}
panelDeltaBest = new JPanel(new GridBagLayout()); {
panelDeltaBest.add(
labelDeltaBest,
GBC.grid(0, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
);
panelDeltaBest.add(deltaBest, GBC.grid(1, 0).anchor(GBC.LE));
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));
}
private void updateVisibility(int identifier) {
if ((identifier & BEST) == BEST) {
boolean ftTwoLines = Settings.FOO_LINE.get();
boolean ftBest = Settings.FOO_BEST.get();
panelBest.setVisible(ftTwoLines);
panelDeltaBest.setVisible(ftTwoLines);
inlineBest.setVisible(!ftTwoLines && ftBest);
inlineDeltaBest.setVisible(!ftTwoLines && ftBest);
}
if ((identifier & TEXT) == TEXT) {
boolean ftLabels = Settings.FOO_DLBL.get();
boolean ftVerbose = Settings.FOO_VERB.get();
labelLive.setVisible(ftLabels && ftVerbose);
labelDelta.setVisible(ftLabels && !ftVerbose);
labelDeltaBest.setVisible(ftLabels);
}
if ((identifier & VERBOSE) == VERBOSE) {
boolean ftVerbose = Settings.FOO_VERB.get();
boolean ftLabels = Settings.FOO_DLBL.get();
labelLive.setVisible(ftVerbose && ftLabels);
labelDelta.setVisible(!ftVerbose && ftLabels);
time.setVisible(ftVerbose);
liveL.setVisible(!ftVerbose);
liveR.setVisible(ftVerbose);
}
}
/**
* Updates the colors of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateColors(int identifier) {
if ((identifier & TIME) == TIME) {
Color colorTM = Settings.CLR_TIME.get();
Color colorNR = Settings.CLR_RCRD.get();
if (run.hasPreviousSegment() && run.isBestSegment(run.getPrevious())) {
liveL.setForeground(colorNR);
liveR.setForeground(colorNR);
} else {
liveL.setForeground(colorTM);
liveR.setForeground(colorTM);
}
time.setForeground(colorTM);
best.setForeground(colorTM);
inlineBest.setForeground(colorTM);
}
if ((identifier & DELTA) == DELTA) {
if (run.hasPreviousSegment()) {
Color colorTM = Settings.CLR_TIME.get();
deltaBest.setForeground(colorTM);
inlineDeltaBest.setForeground(colorTM);
if (delta.getText().equals("--")) {
delta.setForeground(colorTM);
} else if (run.isBestSegment(run.getPrevious())){
Color colorNR = Settings.CLR_RCRD.get();
delta.setForeground(colorNR);
deltaBest.setForeground(colorNR);
inlineDeltaBest.setForeground(colorNR);
} else {
int compare = tmDlta.compareTo(Time.ZERO);
if (compare > 0) {
delta.setForeground(Settings.CLR_LOST.get());
} else {
delta.setForeground(Settings.CLR_GAIN.get());
}
}
}
}
if ((identifier & TEXT) == TEXT) {
Color color = Settings.CLR_FORE.get();
labelPrev.setForeground(color);
labelDelta.setForeground(color);
labelLive.setForeground(color);
labelBest.setForeground(color);
labelDeltaBest.setForeground(color);
}
}
/**
* Updates the values of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateValues(int identifier) {
boolean useSplit = Settings.FOO_SPLT.get();
boolean hasPrevious = run.hasPreviousSegment();
int pIndex = run.getPrevious();
Segment pSegment = null;
Time live;
boolean ftBest = Settings.FOO_BEST.get();
boolean ftLabels = Settings.FOO_DLBL.get();
boolean ftVerbose = Settings.FOO_VERB.get();
boolean ftTwoLines = Settings.FOO_LINE.get();
int height = timeH;
int width = prevW + timeW + smtmW + INSET * 2;
if (ftLabels) {
width += dltaW;
}
if (ftVerbose) {
width += timeW + liveW - (ftLabels ? 0 : dltaW)
+ metrics.stringWidth(" []");
}
if (ftBest) {
if (ftTwoLines) {
height *= 2;
int breakW = bestW + timeW + smtmW + (ftLabels ? dltbW : 0);
width = Math.max(width, breakW);
} else {
width += timeW + smtmW + metrics.stringWidth("| ");
}
if (ftVerbose) {
width += 5;
}
}
preferredSize = new Dimension(width, height);
setMinimumSize(new Dimension(50, height));
resize = false;
}
return (preferredSize == null ? getMinimumSize() : preferredSize);
}
/**
* Sets the run to represent. All components are resetted to their initial
* state.
*
* @param run - the new run to represent.
*/
final void setRun(Run run) {
this.run = run;
updateValues(ALL & ~TEXT);
}
// -------------------------------------------------------------- CALLBACKS
/**
* Callback invoked by the parent when the run or the application's
* settings have seen one of their properties updated.
*
* @param event - the event describing the update.
*/
void processPropertyChangeEvent(PropertyChangeEvent event) {
String property = event.getPropertyName();
if (Run.CURRENT_SEGMENT_PROPERTY.equals(property)) {
updateValues(ALL & ~TEXT);
updateColors(TIME | DELTA);
updateVisibility(ALL);
} else if (Settings.CLR_LOST.equals(property)
|| Settings.CLR_GAIN.equals(property)) {
updateColors(DELTA);
} else if (Settings.CLR_TIME.equals(property)
|| Settings.CLR_RCRD.equals(property)) {
updateColors(TIME | DELTA);
} else if (Settings.CLR_FORE.equals(property)) {
updateColors(TEXT);
} else if (Settings.GNR_ACCY.equals(property)
|| Settings.GNR_COMP.equals(property)) {
updateValues(ALL & ~TEXT);
forceResize();
} else if (Run.STATE_PROPERTY.equals(property)) {
if (run.getState() == State.NULL || run.getState() == State.READY) {
updateValues(ALL & ~TEXT);
}
updateVisibility(ALL);
} else if (Settings.FOO_SPLT.equals(property)) {
updateValues(ALL);
} else if (Settings.FOO_BEST.equals(property)
|| Settings.FOO_LINE.equals(property)) {
updateVisibility(BEST);
forceResize();
} else if (Settings.FOO_DLBL.equals(property)) {
updateVisibility(TEXT);
forceResize();
} else if (Settings.FOO_VERB.equals(property)) {
updateValues(DELTA);
updateVisibility(VERBOSE);
forceResize();
}
}
private void forceResize() {
resize = true;
revalidate();
}
/**
* Callback invoked by the parent when default local for this instance of
* the JVM has changed.
*
* @param event - the event describing the update.
*/
void processLocaleEvent(LocaleEvent event) {
updateValues(TEXT);
}
// -------------------------------------------------------------- UTILITIES
/**
* Places the sub-components within this component.
*/
private void placeComponents() {
setLayout(new GridBagLayout());
JPanel timePanel = new JPanel(new GridBagLayout()); {
timePanel.add(
labelPrev,
GBC.grid(0, 0).anchor(GBC.LS).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(
inlineBest,
GBC.grid(3, 0).anchor(GBC.LS).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)
);
deltaPanel.add(
labelLive,
GBC.grid(1, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
);
deltaPanel.add(
liveR, GBC.grid(2, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
);
deltaPanel.add(delta, GBC.grid(3, 0).anchor(GBC.LE));
deltaPanel.add(
inlineDeltaBest,
GBC.grid(4, 0).anchor(GBC.LE).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)
);
panelBest.add(best, GBC.grid(1, 0).anchor(GBC.LS));
panelBest.setOpaque(false);
}
panelDeltaBest = new JPanel(new GridBagLayout()); {
panelDeltaBest.add(
labelDeltaBest,
GBC.grid(0, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
);
panelDeltaBest.add(deltaBest, GBC.grid(1, 0).anchor(GBC.LE));
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));
}
private void updateVisibility(int identifier) {
if ((identifier & BEST) == BEST) {
boolean ftTwoLines = Settings.FOO_LINE.get();
boolean ftBest = Settings.FOO_BEST.get();
panelBest.setVisible(ftTwoLines);
panelDeltaBest.setVisible(ftTwoLines);
inlineBest.setVisible(!ftTwoLines && ftBest);
inlineDeltaBest.setVisible(!ftTwoLines && ftBest);
}
if ((identifier & TEXT) == TEXT) {
boolean ftLabels = Settings.FOO_DLBL.get();
boolean ftVerbose = Settings.FOO_VERB.get();
labelLive.setVisible(ftLabels && ftVerbose);
labelDelta.setVisible(ftLabels && !ftVerbose);
labelDeltaBest.setVisible(ftLabels);
}
if ((identifier & VERBOSE) == VERBOSE) {
boolean ftVerbose = Settings.FOO_VERB.get();
boolean ftLabels = Settings.FOO_DLBL.get();
labelLive.setVisible(ftVerbose && ftLabels);
labelDelta.setVisible(!ftVerbose && ftLabels);
time.setVisible(ftVerbose);
liveL.setVisible(!ftVerbose);
liveR.setVisible(ftVerbose);
}
}
/**
* Updates the colors of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateColors(int identifier) {
if ((identifier & TIME) == TIME) {
Color colorTM = Settings.CLR_TIME.get();
Color colorNR = Settings.CLR_RCRD.get();
if (run.hasPreviousSegment() && run.isBestSegment(run.getPrevious())) {
liveL.setForeground(colorNR);
liveR.setForeground(colorNR);
} else {
liveL.setForeground(colorTM);
liveR.setForeground(colorTM);
}
time.setForeground(colorTM);
best.setForeground(colorTM);
inlineBest.setForeground(colorTM);
}
if ((identifier & DELTA) == DELTA) {
if (run.hasPreviousSegment()) {
Color colorTM = Settings.CLR_TIME.get();
deltaBest.setForeground(colorTM);
inlineDeltaBest.setForeground(colorTM);
if (delta.getText().equals("--")) {
delta.setForeground(colorTM);
} else if (run.isBestSegment(run.getPrevious())){
Color colorNR = Settings.CLR_RCRD.get();
delta.setForeground(colorNR);
deltaBest.setForeground(colorNR);
inlineDeltaBest.setForeground(colorNR);
} else {
int compare = tmDlta.compareTo(Time.ZERO);
if (compare > 0) {
delta.setForeground(Settings.CLR_LOST.get());
} else {
delta.setForeground(Settings.CLR_GAIN.get());
}
}
}
}
if ((identifier & TEXT) == TEXT) {
Color color = Settings.CLR_FORE.get();
labelPrev.setForeground(color);
labelDelta.setForeground(color);
labelLive.setForeground(color);
labelBest.setForeground(color);
labelDeltaBest.setForeground(color);
}
}
/**
* Updates the values of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateValues(int identifier) {
boolean useSplit = Settings.FOO_SPLT.get();
boolean hasPrevious = run.hasPreviousSegment();
int pIndex = run.getPrevious();
Segment pSegment = null;
Time live;
if (hasPrevious) {
pSegment = run.getSegment(pIndex);
}
if ((identifier & TIME) == TIME) {
Time set;
if (hasPrevious) {
if (useSplit) {
live = run.getTime(pIndex, Segment.LIVE);
set = run.getTime(pIndex, Segment.SET);
} else {
live = pSegment.getTime(Segment.LIVE);
set = pSegment.getTime(Segment.SET);
}
time.setText("" + (set == null ? "--" : set));
liveL.setText("" + (live == null ? "--" : live));
liveR.setText(liveL.getText());
Time bTime = pSegment.getTime(Segment.BEST);
inlineBest.setText("| " + (bTime == null ? "--" : bTime));
best.setText("" + (bTime == null ? "--" : bTime));
} else {
time.setText("");
liveL.setText("");
liveR.setText("");
best.setText("");
inlineBest.setText("");
}
}
if ((identifier & DELTA) == DELTA) {
if (hasPrevious) {
if (useSplit) {
tmDlta = run.getTime(pIndex, Segment.DELTA);
live = run.getTime(pIndex, Segment.LIVE);
if (tmDlta == null || live == null) {
delta.setText("--");
} else {
delta.setText(tmDlta.toString(true));
}
} else {
tmDlta = pSegment.getTime(Segment.DELTA);
live = pSegment.getTime(Segment.LIVE);
Time set = pSegment.getTime(Segment.SET);
Time dBst = pSegment.getTime(Segment.DELTA_BEST);
inlineDeltaBest.setText("| " + (dBst == null ? "--" : dBst.toString(true)));
deltaBest.setText("" + (dBst == null ? "--" : dBst.toString(true)));
if (set != null && pIndex > 1) {
set = set.clone();
for (int i = pIndex - 1; i >= 0; i--) {
Segment pSeg = run.getSegment(i);
Time ante = pSeg.getTime(Segment.LIVE);
if (ante == null) {
set.add(pSeg.getTime(Segment.SET));
} else {
break;
}
}
tmDlta = Time.getDelta(live, set);
}
if (tmDlta == null || live == null) {
delta.setText("--");
inlineDeltaBest.setText("| --");
deltaBest.setText("--");
} else {
delta.setText(tmDlta.toString(true));
}
if (pIndex > 0) {
Time sTime = run.getSegment(pIndex - 1)
.getTime(Segment.SET);
if (sTime == null) {
delta.setText("--");
}
}
}
if (Settings.FOO_VERB.get()) {
delta.setText("[" + delta.getText() + "]");
}
updateColors(DELTA);
} else {
delta.setText("");
inlineDeltaBest.setText("");
deltaBest.setText("");
}
}
if ((identifier & TEXT) == TEXT) {
if (useSplit) {
labelPrev.setText("" + Language.LB_FT_SPLIT);
} else {
labelPrev.setText("" + Language.LB_FT_SEGMENT);
}
labelLive.setText("" + Language.LB_FT_LIVE);
labelBest.setText("" + Language.LB_FT_BEST);
labelDelta.setText("" + Language.LB_FT_DELTA);
labelDeltaBest.setText("" + Language.LB_FT_DELTA_BEST);
}
}
if (hasPrevious) {
pSegment = run.getSegment(pIndex);
}
if ((identifier & TIME) == TIME) {
Time set;
if (hasPrevious) {
if (useSplit) {
live = run.getTime(pIndex, Segment.LIVE);
set = run.getTime(pIndex, Segment.SET);
} else {
live = pSegment.getTime(Segment.LIVE);
set = pSegment.getTime(Segment.SET);
}
time.setText("" + (set == null ? "--" : set));
liveL.setText("" + (live == null ? "--" : live));
liveR.setText(liveL.getText());
Time bTime = pSegment.getTime(Segment.BEST);
inlineBest.setText("| " + (bTime == null ? "--" : bTime));
best.setText("" + (bTime == null ? "--" : bTime));
} else {
time.setText("");
liveL.setText("");
liveR.setText("");
best.setText("");
inlineBest.setText("");
}
}
if ((identifier & DELTA) == DELTA) {
if (hasPrevious) {
if (useSplit) {
tmDlta = run.getTime(pIndex, Segment.DELTA);
live = run.getTime(pIndex, Segment.LIVE);
if (tmDlta == null || live == null) {
delta.setText("--");
} else {
delta.setText(tmDlta.toString(true));
}
} else {
tmDlta = pSegment.getTime(Segment.DELTA);
live = pSegment.getTime(Segment.LIVE);
Time set = pSegment.getTime(Segment.SET);
Time dBst = pSegment.getTime(Segment.DELTA_BEST);
inlineDeltaBest.setText("| " + (dBst == null ? "--" : dBst.toString(true)));
deltaBest.setText("" + (dBst == null ? "--" : dBst.toString(true)));
if (set != null && pIndex > 1) {
set = set.clone();
for (int i = pIndex - 1; i >= 0; i--) {
Segment pSeg = run.getSegment(i);
Time ante = pSeg.getTime(Segment.LIVE);
if (ante == null) {
set.add(pSeg.getTime(Segment.SET));
} else {
break;
}
}
tmDlta = Time.getDelta(live, set);
}
if (tmDlta == null || live == null) {
delta.setText("--");
inlineDeltaBest.setText("| --");
deltaBest.setText("--");
} else {
delta.setText(tmDlta.toString(true));
}
if (pIndex > 0) {
Time sTime = run.getSegment(pIndex - 1)
.getTime(Segment.SET);
if (sTime == null) {
delta.setText("--");
}
}
}
if (Settings.FOO_VERB.get()) {
delta.setText("[" + delta.getText() + "]");
}
updateColors(DELTA);
} else {
delta.setText("");
inlineDeltaBest.setText("");
deltaBest.setText("");
}
}
if ((identifier & TEXT) == TEXT) {
if (useSplit) {
labelPrev.setText("" + Language.LB_FT_SPLIT);
} else {
labelPrev.setText("" + Language.LB_FT_SEGMENT);
}
labelLive.setText("" + Language.LB_FT_LIVE);
labelBest.setText("" + Language.LB_FT_BEST);
labelDelta.setText("" + Language.LB_FT_DELTA);
labelDeltaBest.setText("" + Language.LB_FT_DELTA_BEST);
}
}
}

View file

@ -32,331 +32,331 @@ import org.fenix.utils.locale.LocaleEvent;
*/
class Graph extends JPanel {
// -------------------------------------------------------------- CONSTANTS
// -------------------------------------------------------------- CONSTANTS
/**
* Half the thickness of the stroke used to paint the graph. This value is
* used to make sure the stroke retains its full thickness when reaching
* the top or the bottom of the canvas.
*/
protected static final int HALF_THICKNESS = 1;
/**
* The stroke used to paint the graph in itself (i.e. the lines connecting
* the vertices.)
*/
protected static final Stroke GRAPH_STROKE = new BasicStroke(2.0F);
/**
* The dashed stroke used to paint the projection of the vertices.
*/
protected static final Stroke DASHED_STROKE = new BasicStroke(
1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0F,
new float[] { 2.0F }, 0.0F );
/**
* Update identifier for every category.
*/
private static final int ALL = 0xff;
/**
* Update identifier for text variables.
*/
private static final int TEXT = 0x01;
/**
* Update identifier for time variables.
*/
private static final int TIME = 0x02;
/**
* Minimum width in pixels of this component.
*/
private static final int PACK_WIDTH = 50;
/**
* Minimum height in pixels of this component.
*/
private static final int PACK_HEIGHT = 50;
/**
* Half the thickness of the stroke used to paint the graph. This value is
* used to make sure the stroke retains its full thickness when reaching
* the top or the bottom of the canvas.
*/
protected static final int HALF_THICKNESS = 1;
// ------------------------------------------------------------- ATTRIBUTES
/**
* The stroke used to paint the graph in itself (i.e. the lines connecting
* the vertices.)
*/
protected static final Stroke GRAPH_STROKE = new BasicStroke(2.0F);
/**
* Run instance represented by this component.
*/
protected Run run;
/**
* Canvas where the graph will be drawn.
*/
private Canvas canvas;
/**
* Label displaying the current scale of the graph. The scale actually
* displays the time represented by the maximum ordinate.
*/
private JLabel scale;
/**
* Label describing the scale value being displayed.
*/
private JLabel scaleText;
/**
* The dashed stroke used to paint the projection of the vertices.
*/
protected static final Stroke DASHED_STROKE = new BasicStroke(
1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0F,
new float[] { 2.0F }, 0.0F );
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Update identifier for every category.
*/
private static final int ALL = 0xff;
/**
* Creates a default graph panel representing the given run.
*
* @param run - the run to represent.
*/
Graph(Run run) {
canvas = new Canvas();
scale = new JLabel();
scaleText = new JLabel();
/**
* Update identifier for text variables.
*/
private static final int TEXT = 0x01;
setRun(run);
setOpaque(false);
updateValues(TEXT);
updateColors(ALL);
placeComponents();
Dimension size = new Dimension(PACK_WIDTH, PACK_HEIGHT);
setPreferredSize(size);
setMinimumSize(size);
}
/**
* Update identifier for time variables.
*/
private static final int TIME = 0x02;
// -------------------------------------------------------------- INTERFACE
/**
* Minimum width in pixels of this component.
*/
private static final int PACK_WIDTH = 50;
/**
* Sets the new run to represent.
*
* @param run - the new run to represent.
*/
void setRun(Run run) {
this.run = run;
updateValues(TIME);
}
/**
* Minimum height in pixels of this component.
*/
private static final int PACK_HEIGHT = 50;
/**
* Callback invoked by the parent when the run or the application's
* settings have seen one of their properties updated.
*
* @param event - the event describing the update.
*/
void processPropertyChangeEvent(PropertyChangeEvent event) {
String property = event.getPropertyName();
// Settings.COLOR_FOREGROUND
if (Settings.CLR_FORE.equals(property)) {
updateColors(TEXT);
canvas.repaint();
// Settings.COLOR_TIME
} else if (Settings.CLR_TIME.equals(property)) {
updateColors(TIME);
// Settings.COLOR_BACKGROUND, COLOR_TIME_LOST, COLOR_TIME_GAINED
// or Run.CURRENT_SEGMENT_PROPERTY
} else if (Settings.CLR_BACK.equals(property)
|| Settings.CLR_LOST.equals(property)
|| Settings.CLR_GAIN.equals(property)
|| Settings.CLR_RCRD.equals(property)
|| Run.CURRENT_SEGMENT_PROPERTY.equals(property)) {
canvas.repaint();
// Settings.COMPARE_PERCENT or Settings.COMPARE_METHOD
} else if (Settings.GPH_SCAL.equals(property)
|| Settings.GNR_COMP.equals(property)) {
updateValues(TIME);
canvas.repaint();
// Settings.ACCURACY
} else if (Settings.GNR_ACCY.equals(property)) {
updateValues(TIME);
// Run.STATE_PROPERTY
} else if (Run.STATE_PROPERTY.equals(property)) {
if (run.getState() == State.READY) {
canvas.repaint();
} else if (run.getState() == State.NULL) {
updateValues(TIME);
}
}
}
/**
* Callback invoked by the parent when the run table of segments is
* updated.
*
* @param event - the event describing the update.
*/
void processTableModelEvent(TableModelEvent event) {
int type = event.getType();
int column = event.getColumn();
if (type != TableModelEvent.UPDATE
|| column == TableModelEvent.ALL_COLUMNS
|| column == Run.COLUMN_BEST
|| column == Run.COLUMN_SEGMENT
|| column == Run.COLUMN_TIME) {
updateValues(TIME);
canvas.repaint();
}
}
/**
* Callback invoked by the parent when default local for this instance of
* the JVM has changed.
*
* @param event - the event describing the update.
*/
void processLocaleEvent(LocaleEvent event) {
updateValues(TEXT);
}
// -------------------------------------------------------------- UTILITIES
/**
* Returns a percent representing the delta split time of the segment of
* given index in relation to a set fraction of the whole run given by
* {@link Run#getCompareTime()}.
*
* @param index - the index of the segment to compare.
* @return the percent of the segment delta split time and the runs
* compare time.
*/
protected long getCompareTimePercent(int index) {
long compare = run.getCompareTime().getMilliseconds();
long delta = run.getTime(index, Segment.DELTA).getMilliseconds();
// ------------------------------------------------------------- ATTRIBUTES
return (delta * 100L) / compare;
}
/**
* Places the sub-components within this component.
*/
private void placeComponents() {
setLayout(new GridBagLayout());
JPanel scalePanel = new JPanel(); {
scalePanel.add(scaleText);
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));
}
/**
* Updates the values of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateValues(int identifier) {
// TIME
if ((identifier & TIME) == TIME) {
Time time = run.getCompareTime();
scale.setText("" + (time == null ? "???" : time));
}
// TEXT
if ((identifier & TEXT) == TEXT) {
scaleText.setText("" + Language.MAX_ORDINATE);
}
}
/**
* Updates the colors of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateColors(int identifier) {
// TIME
if ((identifier & TIME) == TIME) {
scale.setForeground(Settings.CLR_TIME.get());
}
// TEXT
if ((identifier & TEXT) == TEXT) {
scaleText.setForeground(Settings.CLR_FORE.get());
}
}
/**
* Run instance represented by this component.
*/
protected Run run;
// ---------------------------------------------------------- INTERNAL TYPE
/**
* Canvas where the graph will be drawn.
*/
private Canvas canvas;
/**
* A simple panel whose paint method has been overriden to draw the graph.
*
* @author Xavier "Xunkar" Sencert
*/
protected class Canvas extends JPanel {
// ----------------------------------------------------- INTERFACE
/**
* Label displaying the current scale of the graph. The scale actually
* displays the time represented by the maximum ordinate.
*/
private JLabel scale;
/**
* Draws the graph onto the canvas.
*/
@Override protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
/**
* Label describing the scale value being displayed.
*/
private JLabel scaleText;
int clipH = getHeight();
int clipW = getWidth();
int halfH = clipH / 2;
// ----------------------------------------------------------- CONSTRUCTORS
g2.setColor(Settings.CLR_BACK.get());
g2.fillRect(0, 0, clipW, clipH);
/**
* Creates a default graph panel representing the given run.
*
* @param run - the run to represent.
*/
Graph(Run run) {
canvas = new Canvas();
scale = new JLabel();
scaleText = new JLabel();
setRun(run);
setOpaque(false);
updateValues(TEXT);
updateColors(ALL);
placeComponents();
Dimension size = new Dimension(PACK_WIDTH, PACK_HEIGHT);
setPreferredSize(size);
setMinimumSize(size);
}
// -------------------------------------------------------------- INTERFACE
/**
* Sets the new run to represent.
*
* @param run - the new run to represent.
*/
void setRun(Run run) {
this.run = run;
updateValues(TIME);
}
/**
* Callback invoked by the parent when the run or the application's
* settings have seen one of their properties updated.
*
* @param event - the event describing the update.
*/
void processPropertyChangeEvent(PropertyChangeEvent event) {
String property = event.getPropertyName();
// Settings.COLOR_FOREGROUND
if (Settings.CLR_FORE.equals(property)) {
updateColors(TEXT);
canvas.repaint();
// Settings.COLOR_TIME
} else if (Settings.CLR_TIME.equals(property)) {
updateColors(TIME);
// Settings.COLOR_BACKGROUND, COLOR_TIME_LOST, COLOR_TIME_GAINED
// or Run.CURRENT_SEGMENT_PROPERTY
} else if (Settings.CLR_BACK.equals(property)
|| Settings.CLR_LOST.equals(property)
|| Settings.CLR_GAIN.equals(property)
|| Settings.CLR_RCRD.equals(property)
|| Run.CURRENT_SEGMENT_PROPERTY.equals(property)) {
canvas.repaint();
// Settings.COMPARE_PERCENT or Settings.COMPARE_METHOD
} else if (Settings.GPH_SCAL.equals(property)
|| Settings.GNR_COMP.equals(property)) {
updateValues(TIME);
canvas.repaint();
// Settings.ACCURACY
} else if (Settings.GNR_ACCY.equals(property)) {
updateValues(TIME);
// Run.STATE_PROPERTY
} else if (Run.STATE_PROPERTY.equals(property)) {
if (run.getState() == State.READY) {
canvas.repaint();
} else if (run.getState() == State.NULL) {
updateValues(TIME);
}
}
}
/**
* Callback invoked by the parent when the run table of segments is
* updated.
*
* @param event - the event describing the update.
*/
void processTableModelEvent(TableModelEvent event) {
int type = event.getType();
int column = event.getColumn();
if (type != TableModelEvent.UPDATE
|| column == TableModelEvent.ALL_COLUMNS
|| column == Run.COLUMN_BEST
|| column == Run.COLUMN_SEGMENT
|| column == Run.COLUMN_TIME) {
updateValues(TIME);
canvas.repaint();
}
}
/**
* Callback invoked by the parent when default local for this instance of
* the JVM has changed.
*
* @param event - the event describing the update.
*/
void processLocaleEvent(LocaleEvent event) {
updateValues(TEXT);
}
// -------------------------------------------------------------- UTILITIES
/**
* Returns a percent representing the delta split time of the segment of
* given index in relation to a set fraction of the whole run given by
* {@link Run#getCompareTime()}.
*
* @param index - the index of the segment to compare.
* @return the percent of the segment delta split time and the runs
* compare time.
*/
protected long getCompareTimePercent(int index) {
long compare = run.getCompareTime().getMilliseconds();
long delta = run.getTime(index, Segment.DELTA).getMilliseconds();
return (delta * 100L) / compare;
}
/**
* Places the sub-components within this component.
*/
private void placeComponents() {
setLayout(new GridBagLayout());
JPanel scalePanel = new JPanel(); {
scalePanel.add(scaleText);
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));
}
/**
* Updates the values of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateValues(int identifier) {
// TIME
if ((identifier & TIME) == TIME) {
Time time = run.getCompareTime();
scale.setText("" + (time == null ? "???" : time));
}
// TEXT
if ((identifier & TEXT) == TEXT) {
scaleText.setText("" + Language.MAX_ORDINATE);
}
}
/**
* Updates the colors of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateColors(int identifier) {
// TIME
if ((identifier & TIME) == TIME) {
scale.setForeground(Settings.CLR_TIME.get());
}
// TEXT
if ((identifier & TEXT) == TEXT) {
scaleText.setForeground(Settings.CLR_FORE.get());
}
}
// ---------------------------------------------------------- INTERNAL TYPE
/**
* A simple panel whose paint method has been overriden to draw the graph.
*
* @author Xavier "Xunkar" Sencert
*/
protected class Canvas extends JPanel {
// ----------------------------------------------------- INTERFACE
/**
* Draws the graph onto the canvas.
*/
@Override protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int clipH = getHeight();
int clipW = getWidth();
int halfH = clipH / 2;
g2.setColor(Settings.CLR_BACK.get());
g2.fillRect(0, 0, clipW, clipH);
Color colorFG = Settings.CLR_FORE.get();
Color colorTG = Settings.CLR_GAIN.get();
Color colorTL = Settings.CLR_LOST.get();
Color colorRC = Settings.CLR_RCRD.get();
Color colorFG = Settings.CLR_FORE.get();
Color colorTG = Settings.CLR_GAIN.get();
Color colorTL = Settings.CLR_LOST.get();
Color colorRC = Settings.CLR_RCRD.get();
// Draw the axis.
g2.setColor(colorFG);
g2.drawLine(0, halfH, clipW, halfH);
// Draw the axis.
g2.setColor(colorFG);
g2.drawLine(0, halfH, clipW, halfH);
if (run.getState() != State.NULL) {
int segCnt = run.getRowCount();
double segGap = (double) clipW / segCnt;
if (run.getState() != State.NULL) {
int segCnt = run.getRowCount();
double segGap = (double) clipW / segCnt;
if (run.hasPreviousSegment()) {
// Coordinates of the last drawn vertex.
int prevX = 0;
int prevY = halfH;
if (run.hasPreviousSegment()) {
// Coordinates of the last drawn vertex.
int prevX = 0;
int prevY = halfH;
for (int i = 0; i < run.getCurrent(); i++) {
Time delta = run.getTime(i, Segment.DELTA);
Time live = run.getTime(i, Segment.LIVE);
if (delta != null && live != null) {
int percent = (int) getCompareTimePercent(i);
g2.setColor(run.isBetterSegment(i) ? colorTG : colorTL);
if (run.isBestSegment(i)) {
g2.setColor(colorRC);
}
// Coordinates of this segments vertex.
int coordY = halfH - ((percent * halfH) / 100);
coordY = Math.min(clipH - HALF_THICKNESS, coordY);
coordY = Math.max(HALF_THICKNESS, coordY);
int coordX = (int) ((i + 1) * segGap);
// Set the brush depending on the delta.
g2.setStroke(GRAPH_STROKE);
// Make sure the last vertex reaches the panes end.
if (i == segCnt - 1) {
coordX = Math.min(coordX - 1, clipW);
}
g2.drawLine(prevX, prevY, coordX, coordY);
// Projection along the x axis.
g2.setColor(colorFG);
g2.setStroke(DASHED_STROKE);
g2.drawLine(coordX, halfH, coordX, coordY);
prevY = coordY;
prevX = coordX;
}
}
}
}
}
for (int i = 0; i < run.getCurrent(); i++) {
Time delta = run.getTime(i, Segment.DELTA);
Time live = run.getTime(i, Segment.LIVE);
if (delta != null && live != null) {
int percent = (int) getCompareTimePercent(i);
g2.setColor(run.isBetterSegment(i) ? colorTG : colorTL);
if (run.isBestSegment(i)) {
g2.setColor(colorRC);
}
}
// Coordinates of this segments vertex.
int coordY = halfH - ((percent * halfH) / 100);
coordY = Math.min(clipH - HALF_THICKNESS, coordY);
coordY = Math.max(HALF_THICKNESS, coordY);
int coordX = (int) ((i + 1) * segGap);
// Set the brush depending on the delta.
g2.setStroke(GRAPH_STROKE);
// Make sure the last vertex reaches the panes end.
if (i == segCnt - 1) {
coordX = Math.min(coordX - 1, clipW);
}
g2.drawLine(prevX, prevY, coordX, coordY);
// Projection along the x axis.
g2.setColor(colorFG);
g2.setStroke(DASHED_STROKE);
g2.drawLine(coordX, halfH, coordX, coordY);
prevY = coordY;
prevX = coordX;
}
}
}
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -30,378 +30,378 @@ import org.fenix.utils.locale.LocaleEvent;
*/
public class RunPane extends JPanel {
// -------------------------------------------------------------- CONSTANTS
// -------------------------------------------------------------- CONSTANTS
/**
* Font used to render the title of the run.
*/
private static final Font RUN_TITLE_FONT = Font.decode("Arial-12-BOLD");
/**
* Update identifier for every category.
*/
private static final int ALL = 0xff;
/**
* Update identifier for time variables.
*/
private static final int GOAL = 0x01;
/**
* Update identifier for separator variables.
*/
private static final int SEPARATOR = 0x02;
/**
* Update identifier for text variables.
*/
private static final int TEXT = 0x04;
/**
* Update identifier for title variables.
*/
private static final int TITLE = 0x08;
/**
* Update identifier for background variables.
*/
private static final int BACKGROUND = 0x10;
/**
* Update identifier for the graph component.
*/
private static final int GRAPH = 0x20;
/**
* Update identifier for the footer component.
*/
private static final int FOOTER = 0x40;
/**
* Font used to render the title of the run.
*/
private static final Font RUN_TITLE_FONT = Font.decode("Arial-12-BOLD");
// ------------------------------------------------------------- ATTRIBUTES
/**
* Update identifier for every category.
*/
private static final int ALL = 0xff;
/**
* Run instance represented by this component.
*/
private Run run;
/**
* Update identifier for time variables.
*/
private static final int GOAL = 0x01;
/**
* Label displaying the title of the current run.
*/
private JLabel title;
/**
* Update identifier for separator variables.
*/
private static final int SEPARATOR = 0x02;
/**
* Label displaying the current goal of run. By default its the time of
* the run were comparing against, but it can be a customized string.
*/
private JLabel goal;
/**
* Update identifier for text variables.
*/
private static final int TEXT = 0x04;
/**
* Label describing the goal value being displayed.
*/
private JLabel goalText;
/**
* Panel containing both goal value and text.
*/
private JPanel goalPane;
/**
* A list containing empty labels serving as separators.
*/
private List<JLabel> separators;
/**
* Update identifier for title variables.
*/
private static final int TITLE = 0x08;
/**
* Simple footer displaying information on the previous segment if any.
* Is only visible if {@link Settings#FOOTER_DISPLAY} is {@code true}.
*/
private Footer footer;
/**
* Update identifier for background variables.
*/
private static final int BACKGROUND = 0x10;
/**
* Panel displaying the core information like the current segment and the
* necessary timers.
*/
private Core core;
/**
* Update identifier for the graph component.
*/
private static final int GRAPH = 0x20;
/**
* Panel representing the current run as a graph.
*/
private Graph graph;
/**
* Update identifier for the footer component.
*/
private static final int FOOTER = 0x40;
/**
* Scrolling panel displaying information regarding every segment of the
* run up to the last segment or {@link Settings#DISPLAYED_SEGMENTS}.
*/
private History history;
// ----------------------------------------------------------- CONSTRUCTORS
// ------------------------------------------------------------- ATTRIBUTES
/**
* Creates a default panel representing the given run.
*
* @param run - the run to represent.
*/
public RunPane(Run run) {
super(new GridBagLayout());
if (run == null) {
throw new NullPointerException("null run");
}
title = new JLabel();
goal = new JLabel();
goalText = new JLabel();
core = new Core(run);
graph = new Graph(run);
history = new History(run);
footer = new Footer(run);
separators = new ArrayList<JLabel>();
placeComponents();
setRun(run);
updateValues(TEXT);
updateColors(ALL);
updateVisibility(ALL);
title.setFont(RUN_TITLE_FONT);
}
/**
* Run instance represented by this component.
*/
private Run run;
// -------------------------------------------------------------- INTERFACE
/**
* Label displaying the title of the current run.
*/
private JLabel title;
/**
* Sets the new run to represent.
*
* @param run - the new run to represent.
*/
public final void setRun(Run run) {
if (run == null) {
throw new NullPointerException("null run");
}
this.run = run;
core.setRun(run);
graph.setRun(run);
history.setRun(run);
footer.setRun(run);
/**
* Label displaying the current goal of run. By default its the time of
* the run were comparing against, but it can be a customized string.
*/
private JLabel goal;
updateValues(ALL & ~TEXT);
}
// -------------------------------------------------------------- CALLBACKS
/**
* Callback invoked by the parent when default local for this instance of
* the JVM has changed.
*
* @param event - the event describing the update.
*/
public void processLocaleEvent(LocaleEvent event) {
core.processLocaleEvent(event);
graph.processLocaleEvent(event);
footer.processLocaleEvent(event);
updateValues(TEXT);
}
/**
* Callback invoked by the parent when the run or the application's
* settings have seen one of their properties updated.
*
* @param event - the event describing the update.
*/
public void processPropertyChangeEvent(PropertyChangeEvent event) {
core.processPropertyChangeEvent(event);
graph.processPropertyChangeEvent(event);
history.processPropertyChangeEvent(event);
footer.processPropertyChangeEvent(event);
/**
* Label describing the goal value being displayed.
*/
private JLabel goalText;
String property = event.getPropertyName();
if (Run.STATE_PROPERTY.equals(property)) {
if (run.getState() == State.READY
|| run.getState() == State.NULL) {
updateValues(GOAL | SEPARATOR);
}
} else if (Run.NAME_PROPERTY.equals(property)) {
updateValues(TITLE);
} else if (Settings.CLR_BACK.equals(property)) {
updateColors(BACKGROUND);
} else if (Settings.CLR_FORE.equals(property)) {
updateColors(TEXT);
} else if (Settings.CLR_SPRT.equals(property)) {
updateColors(SEPARATOR);
} else if (Settings.HST_ROWS.equals(property)) {
updateValues(SEPARATOR);
} else if (Settings.CLR_TIME.equals(property)) {
updateColors(GOAL);
} else if (Settings.CLR_TITL.equals(property)) {
updateColors(TITLE);
} else if (Settings.GNR_COMP.equals(property)) {
updateValues(GOAL);
} else if (Settings.GPH_SHOW.equals(property)) {
updateVisibility(GRAPH);
} else if (Settings.FOO_SHOW.equals(property)) {
updateVisibility(FOOTER);
} else if (Settings.HDR_GOAL.equals(property)) {
updateVisibility(GOAL);
updateValues(SEPARATOR);
} else if (Settings.HDR_TTLE.equals(property)) {
updateVisibility(TITLE | GOAL);
updateValues(SEPARATOR);
} else if (Settings.GNR_ACCY.equals(property)
|| Run.GOAL_PROPERTY.equals(property)) {
updateValues(GOAL);
}
}
/**
* Callback invoked by the parent when the run table of segments is
* updated.
*
* @param event - the event describing the update.
*/
public void processTableModelEvent(TableModelEvent event) {
core.processTableModelEvent(event);
graph.processTableModelEvent(event);
history.processTableModelEvent(event);
if (event.getType() == TableModelEvent.INSERT
|| event.getType() == TableModelEvent.DELETE
|| event.getType() == TableModelEvent.UPDATE) {
updateValues(GOAL);
}
}
/**
* Panel containing both goal value and text.
*/
private JPanel goalPane;
// -------------------------------------------------------------- UTILITIES
/**
* A list containing empty labels serving as separators.
*/
private List<JLabel> separators;
/**
* Adds a new separator to the list of separators used by the component
* and returns it.
*
* @param a new separator.
*/
private JLabel createSeparator() {
JLabel label = new JLabel();
separators.add(label);
return label;
}
/**
* Simple footer displaying information on the previous segment if any.
* Is only visible if {@link Settings#FOOTER_DISPLAY} is {@code true}.
*/
private Footer footer;
/**
* Places the sub-components within this component.
*/
private void placeComponents() {
goalPane = new JPanel(new GridBagLayout()); {
goalPane.add(goalText, GBC.grid(0, 0));
goalPane.add(goal, GBC.grid(1, 0).insets(0, 3, 0, 0));
goalPane.setOpaque(false);
}
add(title,GBC.grid(0, 0).insets(3, 0, 1, 0));
add(createSeparator(), GBC.grid(0, 2).insets(3, 0).fill(GBC.H));
add(history, GBC.grid(0, 3).fill(GBC.H).insets(0, 5));
add(createSeparator(), GBC.grid(0, 4).insets(3, 0).fill(GBC.H));
add(createSeparator(), GBC.grid(0, 6).insets(3, 0, 0, 0).fill(GBC.H));
/**
* Panel displaying the core information like the current segment and the
* necessary timers.
*/
private Core core;
updateVisibility(ALL);
}
/**
* Updates the values of the group of components specified by the
* identifier.
*
* @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());
}
if ((identifier & SEPARATOR) == SEPARATOR) {
boolean hdTitle = Settings.HDR_TTLE.get();
boolean hdGoal = Settings.HDR_GOAL.get();
boolean hsRows = history.getRowCount() > 0;
/**
* Panel representing the current run as a graph.
*/
private Graph graph;
separators.get(0).setVisible(hdTitle || hdGoal);
separators.get(1).setVisible(hsRows);
}
}
/**
* Updates the colors of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateColors(int identifier) {
if ((identifier & GOAL) == GOAL) {
goal.setForeground(Settings.CLR_TIME.get());
}
if ((identifier & TEXT) == TEXT) {
goalText.setForeground(Settings.CLR_FORE.get());
}
if ((identifier & TITLE) == TITLE) {
title.setForeground(Settings.CLR_TITL.get());
}
if ((identifier & BACKGROUND) == BACKGROUND) {
setBackground(Settings.CLR_BACK.get());
}
if ((identifier & SEPARATOR) == SEPARATOR) {
Color color = Settings.CLR_SPRT.get();
for (JLabel separator : separators) {
separator.setBorder(
BorderFactory.createMatteBorder(1, 0, 0, 0, color));
}
}
}
/**
* Updates the visibility of the components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateVisibility(int identifier) {
if ((identifier & GRAPH) == GRAPH) {
if (Settings.GPH_SHOW.get()) {
remove(core);
add(core, GBC.grid(0, 5).insets(0, 5).fill(GBC.H));
add(graph, GBC.grid(0, 7).fill(GBC.B).insets(0, 0, 3, 0)
.weight(1.0, 1.0));
} else {
remove(graph);
remove(core);
add(core, GBC.grid(0, 5).insets(0, 5).fill(GBC.H)
.weight(1.0, 1.0));
}
}
if ((identifier & FOOTER) == FOOTER) {
if (Settings.FOO_SHOW.get()) {
add(footer, GBC.grid(0, 8).insets(0, 3).fill(GBC.H));
} else {
remove(footer);
}
}
if ((identifier & GOAL) == GOAL) {
if (Settings.HDR_GOAL.get()) {
if (Settings.HDR_TTLE.get()) {
add(goalPane, GBC.grid(0, 1));
} else {
add(goalPane, GBC.grid(0, 1).insets(3, 0, 0, 0));
}
} else {
remove(goalPane);
}
}
if ((identifier & TITLE) == TITLE) {
title.setVisible(Settings.HDR_TTLE.get());
}
revalidate();
repaint();
}
/**
* Scrolling panel displaying information regarding every segment of the
* run up to the last segment or {@link Settings#DISPLAYED_SEGMENTS}.
*/
private History history;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Creates a default panel representing the given run.
*
* @param run - the run to represent.
*/
public RunPane(Run run) {
super(new GridBagLayout());
if (run == null) {
throw new NullPointerException("null run");
}
title = new JLabel();
goal = new JLabel();
goalText = new JLabel();
core = new Core(run);
graph = new Graph(run);
history = new History(run);
footer = new Footer(run);
separators = new ArrayList<JLabel>();
placeComponents();
setRun(run);
updateValues(TEXT);
updateColors(ALL);
updateVisibility(ALL);
title.setFont(RUN_TITLE_FONT);
}
// -------------------------------------------------------------- INTERFACE
/**
* Sets the new run to represent.
*
* @param run - the new run to represent.
*/
public final void setRun(Run run) {
if (run == null) {
throw new NullPointerException("null run");
}
this.run = run;
core.setRun(run);
graph.setRun(run);
history.setRun(run);
footer.setRun(run);
updateValues(ALL & ~TEXT);
}
// -------------------------------------------------------------- CALLBACKS
/**
* Callback invoked by the parent when default local for this instance of
* the JVM has changed.
*
* @param event - the event describing the update.
*/
public void processLocaleEvent(LocaleEvent event) {
core.processLocaleEvent(event);
graph.processLocaleEvent(event);
footer.processLocaleEvent(event);
updateValues(TEXT);
}
/**
* Callback invoked by the parent when the run or the application's
* settings have seen one of their properties updated.
*
* @param event - the event describing the update.
*/
public void processPropertyChangeEvent(PropertyChangeEvent event) {
core.processPropertyChangeEvent(event);
graph.processPropertyChangeEvent(event);
history.processPropertyChangeEvent(event);
footer.processPropertyChangeEvent(event);
String property = event.getPropertyName();
if (Run.STATE_PROPERTY.equals(property)) {
if (run.getState() == State.READY
|| run.getState() == State.NULL) {
updateValues(GOAL | SEPARATOR);
}
} else if (Run.NAME_PROPERTY.equals(property)) {
updateValues(TITLE);
} else if (Settings.CLR_BACK.equals(property)) {
updateColors(BACKGROUND);
} else if (Settings.CLR_FORE.equals(property)) {
updateColors(TEXT);
} else if (Settings.CLR_SPRT.equals(property)) {
updateColors(SEPARATOR);
} else if (Settings.HST_ROWS.equals(property)) {
updateValues(SEPARATOR);
} else if (Settings.CLR_TIME.equals(property)) {
updateColors(GOAL);
} else if (Settings.CLR_TITL.equals(property)) {
updateColors(TITLE);
} else if (Settings.GNR_COMP.equals(property)) {
updateValues(GOAL);
} else if (Settings.GPH_SHOW.equals(property)) {
updateVisibility(GRAPH);
} else if (Settings.FOO_SHOW.equals(property)) {
updateVisibility(FOOTER);
} else if (Settings.HDR_GOAL.equals(property)) {
updateVisibility(GOAL);
updateValues(SEPARATOR);
} else if (Settings.HDR_TTLE.equals(property)) {
updateVisibility(TITLE | GOAL);
updateValues(SEPARATOR);
} else if (Settings.GNR_ACCY.equals(property)
|| Run.GOAL_PROPERTY.equals(property)) {
updateValues(GOAL);
}
}
/**
* Callback invoked by the parent when the run table of segments is
* updated.
*
* @param event - the event describing the update.
*/
public void processTableModelEvent(TableModelEvent event) {
core.processTableModelEvent(event);
graph.processTableModelEvent(event);
history.processTableModelEvent(event);
if (event.getType() == TableModelEvent.INSERT
|| event.getType() == TableModelEvent.DELETE
|| event.getType() == TableModelEvent.UPDATE) {
updateValues(GOAL);
}
}
// -------------------------------------------------------------- UTILITIES
/**
* Adds a new separator to the list of separators used by the component
* and returns it.
*
* @param a new separator.
*/
private JLabel createSeparator() {
JLabel label = new JLabel();
separators.add(label);
return label;
}
/**
* Places the sub-components within this component.
*/
private void placeComponents() {
goalPane = new JPanel(new GridBagLayout()); {
goalPane.add(goalText, GBC.grid(0, 0));
goalPane.add(goal, GBC.grid(1, 0).insets(0, 3, 0, 0));
goalPane.setOpaque(false);
}
add(title,GBC.grid(0, 0).insets(3, 0, 1, 0));
add(createSeparator(), GBC.grid(0, 2).insets(3, 0).fill(GBC.H));
add(history, GBC.grid(0, 3).fill(GBC.H).insets(0, 5));
add(createSeparator(), GBC.grid(0, 4).insets(3, 0).fill(GBC.H));
add(createSeparator(), GBC.grid(0, 6).insets(3, 0, 0, 0).fill(GBC.H));
updateVisibility(ALL);
}
/**
* Updates the values of the group of components specified by the
* identifier.
*
* @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());
}
if ((identifier & SEPARATOR) == SEPARATOR) {
boolean hdTitle = Settings.HDR_TTLE.get();
boolean hdGoal = Settings.HDR_GOAL.get();
boolean hsRows = history.getRowCount() > 0;
separators.get(0).setVisible(hdTitle || hdGoal);
separators.get(1).setVisible(hsRows);
}
}
/**
* Updates the colors of the group of components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateColors(int identifier) {
if ((identifier & GOAL) == GOAL) {
goal.setForeground(Settings.CLR_TIME.get());
}
if ((identifier & TEXT) == TEXT) {
goalText.setForeground(Settings.CLR_FORE.get());
}
if ((identifier & TITLE) == TITLE) {
title.setForeground(Settings.CLR_TITL.get());
}
if ((identifier & BACKGROUND) == BACKGROUND) {
setBackground(Settings.CLR_BACK.get());
}
if ((identifier & SEPARATOR) == SEPARATOR) {
Color color = Settings.CLR_SPRT.get();
for (JLabel separator : separators) {
separator.setBorder(
BorderFactory.createMatteBorder(1, 0, 0, 0, color));
}
}
}
/**
* Updates the visibility of the components specified by the
* identifier.
*
* @param identifier - one of the constant update identifier.
*/
private void updateVisibility(int identifier) {
if ((identifier & GRAPH) == GRAPH) {
if (Settings.GPH_SHOW.get()) {
remove(core);
add(core, GBC.grid(0, 5).insets(0, 5).fill(GBC.H));
add(graph, GBC.grid(0, 7).fill(GBC.B).insets(0, 0, 3, 0)
.weight(1.0, 1.0));
} else {
remove(graph);
remove(core);
add(core, GBC.grid(0, 5).insets(0, 5).fill(GBC.H)
.weight(1.0, 1.0));
}
}
if ((identifier & FOOTER) == FOOTER) {
if (Settings.FOO_SHOW.get()) {
add(footer, GBC.grid(0, 8).insets(0, 3).fill(GBC.H));
} else {
remove(footer);
}
}
if ((identifier & GOAL) == GOAL) {
if (Settings.HDR_GOAL.get()) {
if (Settings.HDR_TTLE.get()) {
add(goalPane, GBC.grid(0, 1));
} else {
add(goalPane, GBC.grid(0, 1).insets(3, 0, 0, 0));
}
} else {
remove(goalPane);
}
}
if ((identifier & TITLE) == TITLE) {
title.setVisible(Settings.HDR_TTLE.get());
}
revalidate();
repaint();
}
}

View file

@ -40,7 +40,7 @@ public class Images {
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.scale(scale, scale);
icon.paintIcon((Component)null, g2, 0, 0);
icon.paintIcon(null, g2, 0, 0);
g2.dispose();
return new ImageIcon(buffer);
}

View file

@ -64,7 +64,7 @@ public class TableModelSupport {
}
private void fire(TableModelEvent event) {
TableModelListener[] tableListeners = (TableModelListener[])this.listeners.getListeners(TableModelListener.class);
TableModelListener[] tableListeners = this.listeners.getListeners(TableModelListener.class);
TableModelListener[] arr$ = tableListeners;
int len$ = tableListeners.length;

View file

@ -16,8 +16,8 @@ import java.net.URL;
public class AboutDialog extends JDialog implements ActionListener {
private JLabel icon = new JLabel();
private JLabel message;
private HyperLabel website;
private HyperLabel donate;
private org.fenix.utils.about.HyperLabel website;
private org.fenix.utils.about.HyperLabel donate;
private JButton okButton;
public AboutDialog(Window owner, String title) {
@ -64,12 +64,12 @@ public class AboutDialog extends JDialog implements ActionListener {
if(url == null) {
throw new NullPointerException("Donate URL is null");
} else {
this.donate = new HyperLabel(url, icon);
this.donate = new org.fenix.utils.about.HyperLabel(url, icon);
}
}
public void setWebsite(URL url) {
this.setWebsite(url, (String)null);
this.setWebsite(url, null);
}
public void setWebsite(URL url, String text) {
@ -80,7 +80,7 @@ public class AboutDialog extends JDialog implements ActionListener {
text = url.toString();
}
this.website = new HyperLabel(url, text);
this.website = new org.fenix.utils.about.HyperLabel(url, text);
}
}

View file

@ -171,7 +171,6 @@ public class Configuration implements Serializable {
try {
stream.close();
} catch (Exception var11) {
;
}
}

View file

@ -40,7 +40,7 @@ public class LocaleDelegate {
}
private static void fireLocalChanged(Locale oldLocale, Locale newLocale) {
LocaleListener[] arr$ = (LocaleListener[])listeners.getListeners(LocaleListener.class);
LocaleListener[] arr$ = listeners.getListeners(LocaleListener.class);
int len$ = arr$.length;
for(int i$ = 0; i$ < len$; ++i$) {