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.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver; 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.config.Settings;
import org.fenix.llanfair.dialog.EditRun; import org.fenix.llanfair.dialog.EditRun;
import org.fenix.llanfair.dialog.EditSettings; import org.fenix.llanfair.dialog.EditSettings;
@ -26,6 +9,14 @@ import org.fenix.llanfair.extern.WSplit;
import org.fenix.utils.about.AboutDialog; import org.fenix.utils.about.AboutDialog;
import org.jnativehook.keyboard.NativeKeyEvent; 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. * Regroups all actions, in the meaning of {@link Action}, used by Llanfair.
* All inputs and menu items callbacks are processed by this delegate to * All inputs and menu items callbacks are processed by this delegate to
@ -35,410 +26,410 @@ import org.jnativehook.keyboard.NativeKeyEvent;
* @version 1.0 * @version 1.0
*/ */
final class Actions { 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;
/** private static final long GHOST_DELAY = 300L;
* Creates a new delegate. This constructor is package private since it private static ResourceBundle BUNDLE = null;
* 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 Llanfair master;
* 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 ) { private File file;
String message = betterRun private JFileChooser fileChooser;
? Language.WARN_BETTER_RUN.get()
: Language.WARN_BETTER_TIMES.get(); private volatile long lastUnsplit;
private volatile long lastSkip;
int option = JOptionPane.showConfirmDialog( master, message,
Language.WARNING.get(), JOptionPane.YES_NO_CANCEL_OPTION, /**
JOptionPane.WARNING_MESSAGE ); * Creates a new delegate. This constructor is package private since it
* only need be called by the main class.
if ( option == JOptionPane.CANCEL_OPTION ) { *
master.setIgnoreNativeInputs( false ); * @param owner the Llanfair instance owning this delegate
return false; */
} else if ( option == JOptionPane.YES_OPTION ) { Actions( Llanfair owner ) {
run.saveLiveTimes( !betterRun ); assert ( owner != null );
run.reset(); master = owner;
save();
} file = null;
} fileChooser = new JFileChooser( "." );
master.setIgnoreNativeInputs( before );
return true; lastUnsplit = 0L;
} lastSkip = 0L;
/** if ( BUNDLE == null ) {
* Opens the given file. If the file is {@code null}, the user is asked BUNDLE = Llanfair.getResources().getBundle( "llanfair" );
* 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 /**
*/ * Processes the given native key event. This method must be called from
void open( File file ) { * a thread to prevent possible deadlock.
if ( !confirmOverwrite() ) { *
return; * @param event the native key event to process
} */
if ( file == null ) { void process( NativeKeyEvent event ) {
if ( ( file = selectFile() ) == null ) { assert ( event != null );
return;
} int keyCode = event.getKeyCode();
} Run run = master.getRun();
this.file = file; Run.State state = run.getState();
String name = file.getName();
try { if ( keyCode == Settings.KEY_SPLT.get() ) {
open(); split();
} catch ( Exception ex ) { } else if ( keyCode == Settings.KEY_RSET.get() ) {
master.showError( Language.error_read_file.get( name ) ); reset();
this.file = null; } else if ( keyCode == Settings.KEY_USPL.get() ) {
} unsplit();
} } else if ( keyCode == Settings.KEY_SKIP.get() ) {
skip();
/** } else if ( keyCode == Settings.KEY_STOP.get() ) {
* Opens the currently selected file. This method will first try to open if ( state == Run.State.ONGOING ) {
* the file using the new method (XStream XML) and if it fails will try to run.stop();
* use the legacy method (Java ObjectStream.) }
* } else if ( keyCode == Settings.KEY_PAUS.get() ) {
* @throws Exception if the reading operation fails if ( state == Run.State.ONGOING ) {
*/ run.pause();
private void open() throws Exception { } else if ( state == Run.State.PAUSED ) {
BufferedInputStream in = null; run.resume();
try { }
in = new BufferedInputStream( new FileInputStream( file ) ); } else if ( keyCode == Settings.KEY_LOCK.get() ) {
try { master.setIgnoreNativeInputs( !master.ignoresNativeInputs() );
in.mark( Integer.MAX_VALUE ); }
xmlRead( in ); }
} catch ( Exception ex ) {
in.reset(); /**
legacyRead( new ObjectInputStream( in ) ); * 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
MenuItem.recentlyOpened( "" + file ); * a thread to prevent possible deadlock.
} catch ( Exception ex ) { *
throw ex; * @param event the event to process
} finally { */
try { void process( ActionEvent event ) {
in.close(); assert ( event != null );
} catch ( Exception ex ) {
//$FALL-THROUGH$ Run run = master.getRun();
} MenuItem source = ( MenuItem ) event.getSource();
}
} if ( source == MenuItem.EDIT ) {
EditRun dialog = new EditRun( run );
/** dialog.display( true, master );
* Reads a stream on a run file using the new (since 1.5) XML method. This } else if ( source == MenuItem.NEW ) {
* method will not be able to read a legacy run file and will throw an if ( confirmOverwrite() ) {
* exception if confronted with such run file. master.setRun( new Run() );
* }
* @param in the input stream on the run file } else if ( source == MenuItem.OPEN ) {
*/ open( null );
private void xmlRead( InputStream in ) { } else if ( source == MenuItem.OPEN_RECENT ) {
XStream xml = new XStream( new DomDriver() ); open( new File( event.getActionCommand() ) );
master.setRun( ( Run ) xml.fromXML( in ) ); } else if ( source == MenuItem.IMPORT ) {
} imprt();
} else if ( source == MenuItem.SAVE ) {
/** run.saveLiveTimes( !run.isPersonalBest() );
* Reads a stream on a run file using the legacy Java method. This method run.reset();
* might fail if the given file is not a Llanfair run. save();
* } else if ( source == MenuItem.SAVE_AS ) {
* @param in an input stream on the run file file = null;
* @throws Exception if the stream cannot be read save();
*/ } else if ( source == MenuItem.RESET ) {
private void legacyRead( ObjectInputStream in ) throws Exception { reset();
master.setRun( ( Run ) in.readObject() ); } else if ( source == MenuItem.LOCK ) {
try { master.setIgnoreNativeInputs( true );
Settings.GNR_SIZE.set( ( Dimension ) in.readObject(), true ); } else if ( source == MenuItem.UNLOCK ) {
} catch ( Exception ex ) { master.setIgnoreNativeInputs( false );
// $FALL-THROUGH$ } else if ( source == MenuItem.SETTINGS ) {
} EditSettings dialog = new EditSettings();
} dialog.display( true, master );
} else if ( source == MenuItem.ABOUT ) {
/** about();
* Saves the currently opened run to the currently selected file. If no } else if ( source == MenuItem.EXIT ) {
* file has been selected, the user is asked for one. if ( confirmOverwrite() ) {
*/ master.dispose();
private void save() { }
if ( file == null ) { }
if ( ( file = selectFile() ) == null ) { }
return;
} /**
} * Performs a split or starts the run if it is ready. Can also resume a
Settings.GNR_COOR.set( master.getLocationOnScreen(), true ); * paused run in case the run is segmented.
Settings.GNR_SIZE.set( master.getSize(), true ); */
private void split() {
String name = file.getName(); Run run = master.getRun();
BufferedOutputStream out = null; Run.State state = run.getState();
try { if ( state == Run.State.ONGOING ) {
XStream xml = new XStream( new DomDriver() ); long milli = System.nanoTime() / 1000000L;
out = new BufferedOutputStream( new FileOutputStream( file ) ); long start = run.getSegment( run.getCurrent() ).getStartTime();
xml.toXML( master.getRun(), out ); if ( milli - start > GHOST_DELAY ) {
} catch ( Exception ex ) { run.split();
master.showError( Language.error_write_file.get( name ) ); }
} finally { } else if ( state == Run.State.READY ) {
try { run.start();
out.close(); } else if ( state == Run.State.PAUSED && run.isSegmented() ) {
} catch ( Exception ex ) { run.resume();
// $FALL-THROUGH$ }
} }
}
} /**
* 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.
* 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 private void reset() {
* are supported. Run run = master.getRun();
*/ if ( run.getState() != Run.State.NULL ) {
private void imprt() { if ( !Settings.GNR_WARN.get() || confirmOverwrite() ) {
if ( !confirmOverwrite() ) { run.reset();
return; }
} }
if ( file == null ) { }
if ( ( file = selectFile() ) == null ) {
return; /**
} * 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
String name = file.getName(); * the timer, as if the split had not taken place.
BufferedReader in = null; */
try { private void unsplit() {
in = new BufferedReader( new FileReader( file ) ); Run run = master.getRun();
WSplit.parse( master, in ); Run.State state = run.getState();
} catch ( Exception ex ) { if ( state == Run.State.ONGOING || state == Run.State.STOPPED ) {
master.showError( Language.error_import_run.get( name ) ); long milli = System.nanoTime() / 1000000L;
} finally { if ( milli - lastUnsplit > GHOST_DELAY ) {
try { lastUnsplit = milli;
in.close(); run.unsplit();
} catch ( Exception ex ) { }
// $FALL-THROUGH$ }
} }
}
} /**
* 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
* Displays the "about" dialog. The dialog displays the version of Llanfair, * segment with the following one.
* the creative commons licence, the credits of development, a link to */
* Llanfair's website and a link to donate. private void skip() {
*/ Run run = master.getRun();
private void about() { if ( run.getState() == Run.State.ONGOING ) {
AboutDialog dialog = new AboutDialog( long milli = System.nanoTime() / 1000000L;
master, Language.title_about.get() ); if ( milli - lastSkip > GHOST_DELAY ) {
dialog.setMessage( BUNDLE.getString( "about" )); lastSkip = milli;
try { run.skip();
dialog.setWebsite( new URL( BUNDLE.getString( "website" ) ) ); }
} catch ( MalformedURLException ex ) { }
// $FALL-THROUGH$ }
}
try { /**
dialog.setDonateLink( new URL( BUNDLE.getString( "donate" ) ), * Displays a dialog to let the user select a file. The user is able to
Llanfair.getResources().getIcon( "donate" ) ); * cancel this action, which results in a {@code null} being returned.
} catch ( MalformedURLException ex ) { *
// $FALL-THROUGH$ * @return a file selected by the user or {@code null} if he canceled
} */
dialog.display(); 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; 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.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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 * @author Xavier "Xunkar" Sencert
*/ */
public class Counters implements TableModel, Serializable { public class Counters implements TableModel, Serializable {
// -------------------------------------------------------------- CONSTANTES // -------------------------------------------------------------- CONSTANTES
public static final long serialVersionUID = 1000L; public static final long serialVersionUID = 1000L;
public static final int COLUMN_ICON = 0; public static final int COLUMN_ICON = 0;
public static final int COLUMN_NAME = 1; public static final int COLUMN_NAME = 1;
public static final int COLUMN_START = 2; public static final int COLUMN_START = 2;
public static final int COLUMN_INCREMENT = 3; public static final int COLUMN_INCREMENT = 3;
public static final int COLUMN_COUNT = 4; public static final int COLUMN_COUNT = 4;
// -------------------------------------------------------------- ATTRIBUTS // -------------------------------------------------------------- ATTRIBUTS
private List<Counter> data; private List<Counter> data;
private TableModelSupport tmSupport; private TableModelSupport tmSupport;
// ---------------------------------------------------------- CONSTRUCTEURS // ---------------------------------------------------------- CONSTRUCTEURS
public Counters() { public Counters() {
data = new ArrayList<Counter>(); data = new ArrayList<Counter>();
tmSupport = new TableModelSupport(this); tmSupport = new TableModelSupport(this);
} }
public int getColumnCount() { public int getColumnCount() {
return COLUMN_COUNT; return COLUMN_COUNT;
} }
public int getRowCount() { public int getRowCount() {
return data.size(); return data.size();
} }
public Object getValueAt(int rowIndex, int columnIndex) { public Object getValueAt(int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= data.size()) { if (rowIndex < 0 || rowIndex >= data.size()) {
throw new IllegalArgumentException("illegal counter id " + rowIndex); throw new IllegalArgumentException("illegal counter id " + rowIndex);
} }
return data.get(rowIndex).get(columnIndex); return data.get(rowIndex).get(columnIndex);
} }
public String getColumnName(int columnIndex) { public String getColumnName(int columnIndex) {
switch (columnIndex) { switch (columnIndex) {
case COLUMN_ICON: return "" + Language.ICON; case COLUMN_ICON: return "" + Language.ICON;
case COLUMN_INCREMENT: return "" + Language.INCREMENT; case COLUMN_INCREMENT: return "" + Language.INCREMENT;
case COLUMN_NAME: return "" + Language.NAME; case COLUMN_NAME: return "" + Language.NAME;
case COLUMN_START: return "" + Language.START_VALUE; case COLUMN_START: return "" + Language.START_VALUE;
} }
return null; return null;
} }
public Class<?> getColumnClass(int columnIndex) { public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) { switch (columnIndex) {
case COLUMN_ICON: return Icon.class; case COLUMN_ICON: return Icon.class;
case COLUMN_INCREMENT: return Integer.class; case COLUMN_INCREMENT: return Integer.class;
case COLUMN_NAME: return String.class; case COLUMN_NAME: return String.class;
case COLUMN_START: return Integer.class; case COLUMN_START: return Integer.class;
} }
return null; return null;
} }
public boolean isCellEditable(int rowIndex, int columnIndex) { public boolean isCellEditable(int rowIndex, int columnIndex) {
return true; return true;
} }
public void setValueAt(Object aValue, int rowIndex, int columnIndex) { public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (rowIndex < 0 || rowIndex >= data.size()) { if (rowIndex < 0 || rowIndex >= data.size()) {
throw new IllegalArgumentException("illegal counter id " + rowIndex); throw new IllegalArgumentException("illegal counter id " + rowIndex);
} }
data.get(rowIndex).set(columnIndex, aValue); data.get(rowIndex).set(columnIndex, aValue);
} }
public void addTableModelListener(TableModelListener l) { public void addTableModelListener(TableModelListener l) {
tmSupport.addTableModelListener(l); tmSupport.addTableModelListener(l);
} }
public void removeTableModelListener(TableModelListener l) { public void removeTableModelListener(TableModelListener l) {
tmSupport.removeTableModelListener(l); tmSupport.removeTableModelListener(l);
} }
// ----------------------------------------------------------- CLASSES // ----------------------------------------------------------- CLASSES
public static class Counter implements Serializable { public static class Counter implements Serializable {
public static final long serialVersionUID = 1000L; public static final long serialVersionUID = 1000L;
private String name; private String name;
private Icon icon; private Icon icon;
private int increment; private int increment;
private int start; private int start;
private int saved; private int saved;
private int live; private int live;
public Counter() { public Counter() {
name = "" + Language.UNTITLED; name = "" + Language.UNTITLED;
icon = null; icon = null;
start = 0; start = 0;
live = 0; live = 0;
increment = 1; increment = 1;
} }
public Object get(int columnIndex) { public Object get(int columnIndex) {
switch (columnIndex) { switch (columnIndex) {
case COLUMN_ICON: return icon; case COLUMN_ICON: return icon;
case COLUMN_INCREMENT: return increment; case COLUMN_INCREMENT: return increment;
case COLUMN_NAME: return name; case COLUMN_NAME: return name;
case COLUMN_START: return start; case COLUMN_START: return start;
default: return name; default: return name;
} }
} }
public void set(int columnIndex, Object value) { public void set(int columnIndex, Object value) {
switch (columnIndex) { switch (columnIndex) {
case COLUMN_ICON: icon = (Icon) value; break; case COLUMN_ICON: icon = (Icon) value; break;
case COLUMN_INCREMENT: increment = (Integer) value; break; case COLUMN_INCREMENT: increment = (Integer) value; break;
case COLUMN_NAME: name = (String) value; break; case COLUMN_NAME: name = (String) value; break;
case COLUMN_START: start = (Integer) value; break; case COLUMN_START: start = (Integer) value; break;
} }
} }
public Icon getIcon() { public Icon getIcon() {
return icon; return icon;
} }
public String getName() { public String getName() {
return name; return name;
} }
public int getLive() { public int getLive() {
return live; return live;
} }
public int getStart() { public int getStart() {
return start; return start;
} }
public int getSaved() { public int getSaved() {
return saved; return saved;
} }
public void nextStep() { public void nextStep() {
live += increment; live += increment;
} }
} }
} }

View file

@ -1,9 +1,10 @@
package org.fenix.llanfair; package org.fenix.llanfair;
import org.fenix.utils.Resources;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.fenix.utils.Resources;
/** /**
* Enumeration of all externalized strings used by {@code Llanfair}. While it is * Enumeration of all externalized strings used by {@code Llanfair}. While it is
@ -15,278 +16,278 @@ import org.fenix.utils.Resources;
* @see Resources * @see Resources
*/ */
public enum Language { 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,
/* // Settings > Generic
* Run.State enumeration. setting_alwaysOnTop,
*/ setting_language,
RUN_NULL, setting_viewerLanguage,
RUN_OVER, setting_recentFiles,
RUN_READY, setting_coordinates,
RUN_STOPPED, setting_dimension,
setting_compareMethod,
setting_accuracy,
setting_locked,
setting_warnOnReset,
/* // Settings > Color
* Time.Accuracy enumeration. setting_color_background,
*/ setting_color_foreground,
ACCURACY, 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. * Miscellaneous tokens.
*/ */
ACCEPT, ACCEPT,
APPLICATION, APPLICATION,
BEST, BEST,
CANCEL, CANCEL,
COMPARE_METHOD, COMPARE_METHOD,
COMPONENTS, COMPONENTS,
DISABLED, DISABLED,
EDITING, EDITING,
ERROR, ERROR,
GOAL, GOAL,
IMAGE, IMAGE,
INPUTS, INPUTS,
MAX_ORDINATE, MAX_ORDINATE,
NAME, NAME,
RUN_TITLE, RUN_TITLE,
SEGMENT, SEGMENT,
SEGMENTS, SEGMENTS,
SPLIT, SPLIT,
TIME, TIME,
UNTITLED, UNTITLED,
WARNING, 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");
}
// -------------------------------------------------------------- INTERFACE /*
* 1.4
*/
INCREMENT,
START_VALUE;
/** public static final Locale[] LANGUAGES = new Locale[] {
* Returns the localized string get of this language element. Locale.ENGLISH,
* Locale.FRENCH,
* @return the localized string for this element. Locale.GERMAN,
*/ new Locale("nl"),
public String get() { new Locale("sv")
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 Map<String, String> LOCALE_NAMES =
* The string representation of an enumerate is the localized string new HashMap<String, String>();
* corresponding to its name. static {
*/ LOCALE_NAMES.put("de", "Deutsch");
@Override public String toString() { LOCALE_NAMES.put("en", "English");
return get(); 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 * @version 1.5
*/ */
public class Llanfair extends BorderlessFrame implements TableModelListener, public class Llanfair extends BorderlessFrame implements TableModelListener,
LocaleListener, MouseWheelListener, ActionListener, NativeKeyListener, LocaleListener, MouseWheelListener, ActionListener, NativeKeyListener,
PropertyChangeListener, WindowListener { PropertyChangeListener, WindowListener {
private static Resources RESOURCES = null; private static Resources RESOURCES = null;
static {
ToolTipManager.sharedInstance().setInitialDelay( 1000 );
ToolTipManager.sharedInstance().setDismissDelay( 7000 );
ToolTipManager.sharedInstance().setReshowDelay( 0 );
}
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 Run run;
private RunPane runPane;
private Dimension preferredSize;
/** private Actions actions;
* 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 );
//ResourceBundle b = ResourceBundle.getBundle("language"); private JPopupMenu popupMenu;
RESOURCES = new Resources();
registerFonts();
setLookAndFeel();
run = new Run(); private volatile boolean ignoreNativeInputs;
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 Dimension preferredSize;
* 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$
}
}
/** /**
* When the locale changes, we first ask the resources to reload the locale * Creates and initializes the application. As with any Swing application
* dependent resources and pass the event to the GUI. * this constructor should be called from within a thread to avoid
*/ * dead-lock.
@Override public void localeChanged( LocaleEvent event ) { */
RESOURCES.defaultLocaleChanged(); private Llanfair() {
if ( runPane != null ) { super( "Llanfair" );
runPane.processLocaleEvent( event ); LocaleDelegate.setDefault( Settings.GNR_LANG.get() );
} LocaleDelegate.addLocaleListener( this );
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 ) {} //ResourceBundle b = ResourceBundle.getBundle("language");
@Override public void nativeKeyTyped( NativeKeyEvent event ) {} RESOURCES = new Resources();
registerFonts();
/** setLookAndFeel();
* 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 ) ) { run = new Run();
MenuItem.setActiveState( run.getState() ); runPane = null;
} else if ( Settings.GNR_ATOP.equals( property ) ) { ignoreNativeInputs = false;
setAlwaysOnTop( Settings.GNR_ATOP.get() ); preferredSize = null;
} else if (Settings.HST_ROWS.equals(property) actions = new Actions( this );
|| 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 );
}
/** setMenu();
* When the user clicks on the mouse's right-button, we bring up the setBehavior();
* context menu at the click's location. setRun( run );
*/
@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) {} 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) {} setPreferredSize( preferredSize );
pack();
/**
* 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() { // Replace the window to the run preferred location; center if none
@Override public void run() { Point location = Settings.GNR_COOR.get();
actions.process( ev ); 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); * Tells Llanfair whether to ignore native input events or not. Since native
pack(); * 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
} else if (source.equals(MenuItem.RESIZE_PREFERRED)) { * to do something else whilst not interfering with the behavior of
setPreferredSize(preferredSize); * Llanfair.
pack(); *
} * @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. * 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.
* @throws IllegalStateException if JNativeHook cannot be registered. *
*/ * @param message the localized error message
private void setBehavior() { */
try { void showError( String message ) {
GlobalScreen.registerNativeHook(); JOptionPane.showMessageDialog(
} catch (NativeHookException e) { this, message, Language.ERROR.get(), JOptionPane.ERROR_MESSAGE
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. * Sets the look and feel of the application. Provides a task bar icon and
*/ * a general system dependent theme.
private void setMenu() { */
popupMenu = MenuItem.getPopupMenu(); private void setLookAndFeel() {
MenuItem.addActionListener( this ); setIconImage( RESOURCES.getImage( "Llanfair" ) );
MenuItem.populateRecentlyOpened(); 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; 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.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.io.File; import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; 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 * 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 { enum MenuItem implements ActionListener {
EDIT( false, State.NULL, State.READY ), EDIT( false, State.NULL, State.READY ),
NEW( true, State.NULL, State.READY, State.STOPPED ), NEW( true, State.NULL, State.READY, State.STOPPED ),
OPEN( false, State.NULL, State.READY, State.STOPPED ), OPEN( false, State.NULL, State.READY, State.STOPPED ),
OPEN_RECENT( false, State.NULL, State.READY, State.STOPPED ), OPEN_RECENT( false, State.NULL, State.READY, State.STOPPED ),
IMPORT( false, State.NULL, State.READY, State.STOPPED ), IMPORT( false, State.NULL, State.READY, State.STOPPED ),
SAVE( false, State.READY, State.STOPPED ), SAVE( false, State.READY, State.STOPPED ),
SAVE_AS( true, State.READY ), SAVE_AS( true, State.READY ),
RESET( true, State.ONGOING, State.STOPPED, State.PAUSED ), RESET( true, State.ONGOING, State.STOPPED, State.PAUSED ),
LOCK( false, State.NULL, State.READY, State.STOPPED, State.ONGOING ), LOCK( false, State.NULL, State.READY, State.STOPPED, State.ONGOING ),
UNLOCK( 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_DEFAULT( false, State.NULL, State.READY ),
RESIZE_PREFERRED( true, State.NULL, State.READY ), RESIZE_PREFERRED( true, State.NULL, State.READY ),
SETTINGS( true, State.NULL, State.READY, State.STOPPED ), SETTINGS( true, State.NULL, State.READY, State.STOPPED ),
ABOUT( true, State.NULL, State.READY, State.STOPPED, State.ONGOING ), ABOUT( true, State.NULL, State.READY, State.STOPPED, State.ONGOING ),
EXIT( false, 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 * Static list of listeners that listen to all the menu items. Whenever
* one item fires an {@code ActionEvent}, the type statically fires an * one item fires an {@code ActionEvent}, the type statically fires an
* other {@code ActionEvent} with the {@code MenuItem} enumerate as the * other {@code ActionEvent} with the {@code MenuItem} enumerate as the
* source component. * source component.
*/ */
private static EventListenerList listeners = new EventListenerList(); private static EventListenerList listeners = new EventListenerList();
private static final int MAX_FILES = 5; private static final int MAX_FILES = 5;
private static final int TRUNCATE = 30; private static final int TRUNCATE = 30;
private boolean isEndOfGroup;
private List<State> activeStates;
private JMenuItem menuItem;
/** private boolean isEndOfGroup;
* Internal constructor used to set the attributes. Only called by the private List<State> activeStates;
* enum type itself. private JMenuItem menuItem;
*
* @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 );
if ( name().equals( "OPEN_RECENT" ) ) { /**
menuItem = new JMenu( toString() ); * Internal constructor used to set the attributes. Only called by the
} else { * enum type itself.
menuItem = new JMenuItem( toString() ); *
} * @param isEndOfGroup indicates if this item is a group ender
Icon icon = Llanfair.getResources().getIcon( "jmi/" + name() + ".png" ); * @param activeStates list of active run states of this item
menuItem.setIcon( icon ); */
menuItem.addActionListener( this ); MenuItem(boolean isEndOfGroup, Run.State... activeStates) {
// LOCK & UNLOCK mask each other and Llanfair always start unlocked this.isEndOfGroup = isEndOfGroup;
if ( name().equals( "UNLOCK" ) ) { this.activeStates = Arrays.asList( activeStates );
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;
}
/** if ( name().equals( "OPEN_RECENT" ) ) {
* Enables or disables every menu items depending on the given run state. menuItem = new JMenu( toString() );
* If this state is present in the list of active states for an item, then } else {
* its GUI component is enabled, else it is disabled. menuItem = new JMenuItem( toString() );
* }
* @param state the current run state, cannot be {@code null} Icon icon = Llanfair.getResources().getIcon( "jmi/" + name() + ".png" );
*/ menuItem.setIcon( icon );
static void setActiveState( Run.State state ) { menuItem.addActionListener( this );
if ( state == null ) { // LOCK & UNLOCK mask each other and Llanfair always start unlocked
throw new IllegalArgumentException( "Null run state" ); if ( name().equals( "UNLOCK" ) ) {
} menuItem.setVisible( false );
for ( MenuItem item : values() ) { }
item.menuItem.setEnabled( item.activeStates.contains( state ) ); }
}
}
/** /**
* Registers the given {@code ActionListener} to the list of listeners * Returns a popup menu composed of all the menu items. A separator is
* interested in capturing events from every menu items. * inserted after each item which are indicated as end of their group.
* *
* @param listener the action listener to register * @return a popup menu containing every menu item
*/ */
static void addActionListener( ActionListener listener ) { static JPopupMenu getPopupMenu() {
listeners.add( ActionListener.class, listener ); JPopupMenu menu = new JPopupMenu();
} for ( MenuItem item : values() ) {
menu.add( item.menuItem );
/** if ( item.isEndOfGroup ) {
* Callback to invoke whenever a file is opened. This method will sort the menu.add( new JSeparator() );
* recent files menu to put the recently opened file at the top. }
* }
* @param path the name of the recently opened file return menu;
*/ }
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 ); * 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
Settings.GNR_RCNT.set( recentFiles ); * its GUI component is enabled, else it is disabled.
populateRecentlyOpened(); *
} * @param state the current run state, cannot be {@code null}
*/
/** static void setActiveState( Run.State state ) {
* Fills the {@code OPEN_RECENT} item with the list of recently opened if ( state == null ) {
* files. If {@code MAX_FILES} is somehow lower than the recent files list throw new IllegalArgumentException( "Null run state" );
* length, the overflowing files are removed. }
*/ for ( MenuItem item : values() ) {
static void populateRecentlyOpened() { item.menuItem.setEnabled( item.activeStates.contains( state ) );
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 * Registers the given {@code ActionListener} to the list of listeners
* back, setting the source as the enumerate value instead of the GUI * interested in capturing events from every menu items.
* component. *
*/ * @param listener the action listener to register
@Override public void actionPerformed( ActionEvent event ) { */
Object source = event.getSource(); static void addActionListener( ActionListener listener ) {
listeners.add( ActionListener.class, listener );
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(); * Callback to invoke whenever a file is opened. This method will sort the
ActionListener[] als = listeners.getListeners( ActionListener.class ); * recent files menu to put the recently opened file at the top.
*
if ( name != null && name.startsWith( "RECENT" ) ) { * @param path the name of the recently opened file
event = new ActionEvent( */
this, ActionEvent.ACTION_PERFORMED, name.substring( 6 ) static void recentlyOpened( String path ) {
); assert ( path != null );
} else { List<String> recentFiles = Settings.GNR_RCNT.get();
event = new ActionEvent(
this, ActionEvent.ACTION_PERFORMED, jmi.getText() if ( recentFiles.contains( path ) ) {
); recentFiles.remove( path );
} }
for ( ActionListener al : als ) { recentFiles.add( 0, path );
al.actionPerformed( event );
} 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 { public class Segment implements Cloneable, Serializable {
// -------------------------------------------------------------- CONSTANTS // -------------------------------------------------------------- CONSTANTS
/** /**
* The serial version identifier used to determine the compatibility of the * The serial version identifier used to determine the compatibility of the
* different serialized versions of this type. This identifier must change * different serialized versions of this type. This identifier must change
* when modifications that break backward-compatibility are made to the * when modifications that break backward-compatibility are made to the
* type. * type.
*/ */
private static final long serialVersionUID = 1001L; private static final long serialVersionUID = 1001L;
/** /**
* Array of legit display sizes for the segments icons. * Array of legit display sizes for the segments icons.
*/ */
public static final Integer[] ICON_SIZES = new Integer[] { public static final Integer[] ICON_SIZES = new Integer[] {
16, 24, 32, 40, 48, 56, 64 16, 24, 32, 40, 48, 56, 64
}; };
/** /**
* Maximum size allowed for the segments icons. When setting an icon for * 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 * a segment, it will be scaled down to that size if its bigger, but will
* not be scaled up if its smaller. * not be scaled up if its smaller.
*/ */
public static final int ICON_MAX_SIZE = ICON_SIZES[ICON_SIZES.length - 1]; 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 * Identifier for the time of the segment as defined by the currently set
* compare method. * compare method.
*/ */
public static final int SET = 0; public static final int SET = 0;
/** /**
* Identifier for the registered time of the segment. * Identifier for the registered time of the segment.
*/ */
public static final int RUN = 1; public static final int RUN = 1;
/** /**
* Identifier for the best ever registered time of the segment. * Identifier for the best ever registered time of the segment.
*/ */
public static final int BEST = 2; public static final int BEST = 2;
/** /**
* Identifier for the live time realized on this segment. * Identifier for the live time realized on this segment.
*/ */
public static final int LIVE = 3; public static final int LIVE = 3;
/** /**
* Identifier for the delta between the live time and the time as defined * Identifier for the delta between the live time and the time as defined
* by the currently set compare method, i.e. {@code LIVE - SET}. * by the currently set compare method, i.e. {@code LIVE - SET}.
*/ */
public static final int DELTA = 4; public static final int DELTA = 4;
/** /**
* Identifier for the delta between the live time and the registered time * Identifier for the delta between the live time and the registered time
* of the segment, i.e. {@code LIVE - RUN}. * of the segment, i.e. {@code LIVE - RUN}.
*/ */
public static final int DELTA_RUN = 5; public static final int DELTA_RUN = 5;
/** /**
* Identifier for the delta between the live time and the best ever * Identifier for the delta between the live time and the best ever
* registered time of the segment, i.e. {@code LIVE - BEST}. * registered time of the segment, i.e. {@code LIVE - BEST}.
*/ */
public static final int DELTA_BEST = 6; public static final int DELTA_BEST = 6;
// ------------------------------------------------------------- ATTRIBUTES // ------------------------------------------------------------- ATTRIBUTES
/** /**
* Name of the segment. * Name of the segment.
*/ */
private String name; private String name;
/** /**
* Icon associated with this segment. Can be {@code null} if no icon is to * Icon associated with this segment. Can be {@code null} if no icon is to
* be displayed. * be displayed.
*/ */
private Icon icon; private Icon icon;
/** /**
* Registered time for this segment during the best run. * Registered time for this segment during the best run.
*/ */
private Time runTime; private Time runTime;
/** /**
* Best time ever registered for this segment. * Best time ever registered for this segment.
*/ */
private Time bestTime; private Time bestTime;
/** /**
* Live time realized on this segment during a run. This value is never * Live time realized on this segment during a run. This value is never
* saved as is but can overwrite {@code runTime} or {@code bestTime}. * saved as is but can overwrite {@code runTime} or {@code bestTime}.
*/ */
private transient Time liveTime; private transient Time liveTime;
/**
* Number of milliseconds on the clock when the segment started.
*/
private transient long startTime;
// ----------------------------------------------------------- CONSTRUCTORS /**
* Number of milliseconds on the clock when the segment started.
*/
private transient long startTime;
/** // ----------------------------------------------------------- CONSTRUCTORS
* 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);
}
// ---------------------------------------------------------------- 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. * Creates a default segment with a default name and undefined times.
* */
* @return the name of the segment. public Segment() {
*/ this("" + Language.UNTITLED);
public String getName() { }
return name;
} // ---------------------------------------------------------------- GETTERS
/** /**
* Returns the icon associated with this segment. Can be {@code null}. * Returns the name of the segment.
* *
* @return the icon of the segment or {@code null}. * @return the name of the segment.
*/ */
public Icon getIcon() { public String getName() {
return icon; return name;
} }
/** /**
* Returns the number of milliseconds on the clock when the segment started. * Returns the icon associated with this segment. Can be {@code null}.
* *
* @return the start time of this segment. * @return the icon of the segment or {@code null}.
*/ */
public long getStartTime() { public Icon getIcon() {
return startTime; return icon;
} }
/** /**
* Returns the given type of time for this segment. * Returns the number of milliseconds on the clock when the segment started.
* *
* @param type - one of the type identifier. * @return the start time of this segment.
* @return the segment time of given type. */
*/ public long getStartTime() {
public Time getTime(int type) { return startTime;
switch (type) { }
case BEST:
return bestTime; /**
* Returns the given type of time for this segment.
case LIVE: *
return liveTime; * @param type - one of the type identifier.
* @return the segment time of given type.
case RUN: */
return runTime; public Time getTime(int type) {
switch (type) {
case DELTA_RUN: case BEST:
if (runTime == null) { return bestTime;
return null;
} case LIVE:
return Time.getDelta(liveTime, runTime); return liveTime;
case DELTA_BEST: case RUN:
if (bestTime == null) { return runTime;
return null;
} case DELTA_RUN:
return Time.getDelta(liveTime, bestTime); if (runTime == null) {
return null;
case DELTA: }
Time time = getTime(); return Time.getDelta(liveTime, runTime);
return (time == null ? null : Time.getDelta(liveTime, time));
case DELTA_BEST:
default: if (bestTime == null) {
return getTime(); return null;
} }
} return Time.getDelta(liveTime, bestTime);
/** case DELTA:
* As specified by {@code Cloneable}, returns a deep copy of the segment. Time time = getTime();
*/ return (time == null ? null : Time.getDelta(liveTime, time));
public Segment clone() {
Segment segment = new Segment(name); default:
segment.icon = icon; return getTime();
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; * As specified by {@code Cloneable}, returns a deep copy of the segment.
} */
public Segment clone() {
// ---------------------------------------------------------------- SETTERS Segment segment = new Segment(name);
segment.icon = icon;
/** segment.runTime = (runTime == null ? null : runTime.clone());
* Sets the name of the segment to the given string. segment.bestTime = (bestTime == null ? null : bestTime.clone());
* segment.liveTime = (liveTime == null ? null : liveTime.clone());
* @param name - the new name of the segment. segment.startTime = startTime;
*/ return segment;
public void setName(String name) { }
if (name == null) {
throw new NullPointerException("null name"); // ---------------------------------------------------------------- SETTERS
}
this.name = name; /**
} * Sets the name of the segment to the given string.
*
/** * @param name - the new name of the segment.
* 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 public void setName(String name) {
* be scale down to {@code ICON_MAX_SIZE} if its bigger. if (name == null) {
* throw new NullPointerException("null name");
* @param icon - the new icon for this segment. }
*/ this.name = name;
public void setIcon(Icon icon) { }
if (icon == null) {
this.icon = null; /**
} else { * Sets the current icon of this segment. Can be {@code null} to remove
this.icon = Images.rescale(icon, ICON_MAX_SIZE); * 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.
/** */
* Sets the number of milliseconds on the clock when the segment started. public void setIcon(Icon icon) {
* Should only be called by the run owning this segment. if (icon == null) {
* this.icon = null;
* @param startTime - the starting time of this segment. } else {
* @throws IllegalArgumentException if the time is negative. this.icon = Images.rescale(icon, ICON_MAX_SIZE);
*/ }
void setStartTime(long startTime) { }
if (startTime < 0L) {
throw new IllegalArgumentException("negative start time"); /**
} * Sets the number of milliseconds on the clock when the segment started.
this.startTime = startTime; * 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.
* 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 void setStartTime(long startTime) {
* {@code null} to indicate undefined times. if (startTime < 0L) {
* throw new IllegalArgumentException("negative start time");
* @param time - the new time value for the given type. }
* @param type - one of the type identifier. this.startTime = startTime;
* @throws IllegalArgumentException if the new time value is lower than or }
* equal to zero.
*/ /**
public void setTime(Time time, int type, boolean bypass) { * Sets the given type of time to the new value. Note that some type of
if (!bypass) { * times cannot be set (such as {@code DELTA}s.) The new value can be
if (time != null && time.compareTo(Time.ZERO) <= 0) { * {@code null} to indicate undefined times.
throw new IllegalArgumentException("" + Language.ILLEGAL_TIME); *
} * @param time - the new time value for the given type.
} * @param type - one of the type identifier.
switch (type) { * @throws IllegalArgumentException if the new time value is lower than or
case BEST: bestTime = time; break; * equal to zero.
case LIVE: liveTime = time; break; */
case RUN: runTime = time; break; 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);
public void setTime(Time time, int type) { }
setTime(time, type, false); }
} 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 process. Redefined to initialize transients fields upon
* deserialization. * deserialization.
*/ */
private void readObject(ObjectInputStream in) private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException { throws IOException, ClassNotFoundException {
in.defaultReadObject(); in.defaultReadObject();
initializeTransients(); initializeTransients();
} }
} }

View file

@ -23,276 +23,276 @@ import org.fenix.llanfair.config.Settings;
* @version 1.1 * @version 1.1
*/ */
public class Time implements Cloneable, Comparable<Time>, Serializable { 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;
/** private long milliseconds;
* Creates a default time of zero milliseconds.
*/
public Time() {
milliseconds = 0L;
}
/** /**
* Creates a time representing a given number of milliseconds. This number * Creates a default time of zero milliseconds.
* is truncated to the milliseconds to prevent discrepencies resulting from */
* successive calls to {@link System.currentTimeMillis()} and thus ensures public Time() {
* that the compare method remains accurate. milliseconds = 0L;
* }
* @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 time representing the given time-stamp. The time-stamp is a * Creates a time representing a given number of milliseconds. This number
* string describing the time in a format understandable by the user. The * is truncated to the milliseconds to prevent discrepencies resulting from
* time-stamp must be formatted according to the following: * successive calls to {@link System.currentTimeMillis()} and thus ensures
* * that the compare method remains accurate.
* <pre>((H)H:)((M)M:)(S)S(.T(H(M)))</pre> *
* * @param ms the number of milliseconds to represent
* 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 public Time(long ms) {
* hundredth and M for milliseconds. milliseconds = (ms / 10L) * 10L;
* }
* @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)
);
}
}
/** /**
* Returns the delta of time between two times. The returned time is * Creates a time representing the given decimal number of seconds. This
* equivalent to, but more convenient than, the following code: * number is truncated to the milliseconds to prevent discrepencies
* {@code new Time(t1.getMilliseconds() - t2.getMilliseconds())}. * resulting from successive calls to {@link System.currentTimeMillis()}
* * and thus ensures that the compare method remains accurate.
* @param t1 the first time *
* @param t2 the time to substract from the first * @param seconds the number of seconds to represent
* @return the delta of time between the two times */
*/ public Time(double seconds) {
public static Time getDelta(Time t1, Time t2) { this((long) (seconds * 1000.0));
if (t1 == null) { }
t1 = new Time();
} else if (t2 == null) {
t2 = new Time();
}
return new Time(t1.milliseconds - t2.milliseconds);
}
/** /**
* Returns the number of milliseconds represented by that time. * 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
* @return the number of milliseconds represented by that time * time-stamp must be formatted according to the following:
*/ *
public long getMilliseconds() { * <pre>((H)H:)((M)M:)(S)S(.T(H(M)))</pre>
return milliseconds; *
} * 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.
* Adds the given time to this time. The number of milliseconds *
* represented by this time object is now equals to: * @param timestamp a string representation of the time to parse
* {@code getMilliseconds() + time.getMilliseconds()} * @throws IllegalArgumentException if the timestamp cannot be parsed
* */
* @param time the time object to add to this time public Time(String timeStamp) {
*/ try {
public void add(Time time) { milliseconds = parseTimeStamp(timeStamp);
milliseconds += (time == null ? 0L : time.milliseconds); } catch (Exception ex) {
} throw new IllegalArgumentException(
Language.INVALID_TIME_STAMP.get(timeStamp)
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;
if (hou == 0L) { /**
if (min == 0L) { * Returns the delta of time between two times. The returned time is
switch (accuracy) { * equivalent to, but more convenient than, the following code:
case HUNDREDTH: * {@code new Time(t1.getMilliseconds() - t2.getMilliseconds())}.
return String.format("%d.%02d", sec, cen); *
case TENTH: * @param t1 the first time
return String.format("%d.%d", sec, cen); * @param t2 the time to substract from the first
case SECONDS: * @return the delta of time between the two times
return String.format("%d", sec); */
} public static Time getDelta(Time t1, Time t2) {
} if (t1 == null) {
switch (accuracy) { t1 = new Time();
case HUNDREDTH: } else if (t2 == null) {
return String.format("%d:%02d.%02d", min, sec, cen); t2 = new Time();
case TENTH: }
return String.format("%d:%02d.%d", min, sec, cen); return new Time(t1.milliseconds - t2.milliseconds);
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 * Returns the number of milliseconds represented by that time.
* the same amount of milliseconds. *
*/ * @return the number of milliseconds represented by that time
@Override public boolean equals(Object obj) { */
if (!(obj instanceof Time)) { public long getMilliseconds() {
return false; return milliseconds;
} }
Time time = (Time) obj;
return (milliseconds == time.milliseconds);
}
/** /**
* {@inheritDoc} The hash code of a time object is its number of * Adds the given time to this time. The number of milliseconds
* milliseconds cast into an integer get. As such, the load factor of a * represented by this time object is now equals to:
* table storing time objects is floored by {@code Integer#MAX_VALUE}. * {@code getMilliseconds() + time.getMilliseconds()}
*/ *
@Override public int hashCode() { * @param time the time object to add to this time
return (int) milliseconds; */
} public void add(Time time) {
milliseconds += (time == null ? 0L : time.milliseconds);
}
/** public String toString(boolean signed, Accuracy accuracy) {
* {@inheritDoc} Time objects are compared using their amount of if (signed) {
* milliseconds return (milliseconds > 0L ? "+" : "-") + toString(false, accuracy);
*/ }
@Override public int compareTo(Time time) { long time = Math.abs(milliseconds);
if (time == null) { long cen = (time % 1000L) / 10L;
return -1; long sec;
}
return ((Long) milliseconds).compareTo(time.milliseconds);
}
/** // Round to the nearest tenth.
* Parses a given time-stamp and converts it to a number of milliseconds. if (accuracy == Accuracy.TENTH) {
* cen = Math.round((double) cen / 10L);
* @param timestamp the timestamp to parse if (cen == 10L) {
* @return the number of milliseconds represented by the stamp cen = 0L;
*/ time = time + 1000L;
private long parseTimeStamp(String timestamp) { }
String seconds = timestamp; }
long millis = 0L;
// Hours or minutes in the stamp. // Round to the nearest second.
if (timestamp.contains(":")) { if (accuracy == Accuracy.SECONDS) {
String[] split = timestamp.split(":"); sec = Math.round((double) time / 1000);
if (split.length > 3) { } else {
throw new IllegalArgumentException(); sec = time / 1000L;
} }
// We keep the number of seconds and lower for later. long min = sec / 60L;
seconds = split[split.length - 1]; sec = sec % 60L;
long hou = min / 60L;
min = min % 60L;
for (int i = 0; i < split.length - 1; i++) { if (hou == 0L) {
long power = (long) Math.pow(60, split.length - i - 1); if (min == 0L) {
millis += Long.valueOf(split[i]) * power * 1000L; switch (accuracy) {
} case HUNDREDTH:
} return String.format("%d.%02d", sec, cen);
double value = Double.parseDouble(seconds); case TENTH:
return (millis + (long) (value * 1000.0)); 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; package org.fenix.llanfair.config;
import java.io.Serializable;
import org.fenix.llanfair.Language; import org.fenix.llanfair.Language;
import java.io.Serializable;
public enum Accuracy implements Serializable { public enum Accuracy implements Serializable {
SECONDS,
TENTH,
HUNDREDTH;
private static final long serialVersionUID = 1000L;
@Override public String toString() { SECONDS,
return Language.valueOf("accuracy_" + name().toLowerCase()).get(); 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; package org.fenix.llanfair.config;
import java.io.Serializable;
import org.fenix.llanfair.Language; import org.fenix.llanfair.Language;
import java.io.Serializable;
/** /**
* *
* @author Xavier * @author Xavier
*/ */
public enum Compare implements Serializable { public enum Compare implements Serializable {
BEST_OVERALL_RUN, BEST_OVERALL_RUN,
SUM_OF_BEST_SEGMENTS; SUM_OF_BEST_SEGMENTS;
private static final long serialVersionUID = 1000L; private static final long serialVersionUID = 1000L;
@Override public String toString() { @Override public String toString() {
return Language.valueOf( return Language.valueOf(
"compare_" + name().toLowerCase().replaceAll("\\.", "_") "compare_" + name().toLowerCase().replaceAll("\\.", "_")
).get(); ).get();
} }
} }

View file

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

View file

@ -26,403 +26,403 @@ import org.fenix.utils.config.Configuration;
* @version 1.3 * @version 1.3
*/ */
public class Settings { 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; /* GENERIC properties */
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;
}
/** public static final Property<Boolean> GNR_ATOP =
* Returns a list of all the properties whose key starts with the given new Property<Boolean>( "alwaysOnTop" );
* prefix. If given an empty string, every properties are returned. public static final Property<Locale> GNR_LANG =
* new Property<Locale>( "language" );
* @param prefix the prefix for which a list of property is asked public static final Property<Locale> GNR_VLNG =
* @return the list of all properties whose key starts with the prefix new Property<Locale>( "viewerLanguage" );
*/ public static final Property<List<String>> GNR_RCNT =
public static List<Property<?>> getAll( String prefix ) { new Property<List<String>>( "recentFiles" );
if ( prefix == null ) { public static final Property<Point> GNR_COOR =
throw new NullPointerException( "Null prefix string" ); new Property<Point>( "coordinates" );
} public static final Property<Dimension> GNR_SIZE =
List<Property<?>> list = new ArrayList<Property<?>>(); new Property<Dimension>( "dimension" );
for ( Property<?> property : Property.P ) { public static final Property<Compare> GNR_COMP =
if ( property.key.startsWith( prefix ) ) { new Property<Compare>( "compareMethod" );
list.add( property ); public static final Property<Accuracy> GNR_ACCY =
} new Property<Accuracy>( "accuracy" );
} public static final Property<Boolean> GNR_WARN =
return list; 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. * Registers a new {@code PropertyChangeListener} with the settings.
* Whenever a property sees its value updated globally or locally, listeners * Whenever a property sees its value updated globally or locally, listeners
* are warned of the update. * are warned of the update.
* *
* @param pcl the listener to register with the settings * @param pcl the listener to register with the settings
*/ */
public static void addPropertyChangeListener( PropertyChangeListener pcl ) { public static void addPropertyChangeListener( PropertyChangeListener pcl ) {
if ( pcl == null ) { if ( pcl == null ) {
throw new NullPointerException( "Null property listener" ); throw new NullPointerException( "Null property listener" );
} }
global.addPropertyChangeListener( pcl ); global.addPropertyChangeListener( pcl );
if ( run != null ) { if ( run != null ) {
run.addSettingChangeListener( pcl ); run.addSettingChangeListener( pcl );
} }
} }
/** /**
* Saves the global configuration in {@code llanfair.xml} in the working * Saves the global configuration in {@code llanfair.xml} in the working
* directory. If such a file does not exist, it is created. * directory. If such a file does not exist, it is created.
*/ */
public static void save() { public static void save() {
global.serialize(); 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();
}
}
/** /**
* Fills the global configuration with every property, assigning them their * Retrieves the configuration of Llanfair. The configuration is read from
* default value. This method can be called even when the global * {@code llanfair.xml} placed in the working directory. If such a file
* configuration is not empty, and will thus function as a reset. * cannot be found, a default configuration is loaded. No local
*/ * configuration is loaded here, a call to {@code setRun} is required to
private static void setDefaultValues() { * do just that. This method is lenient and called by the first property
global.put( GNR_ATOP.key, true ); * whose value is requested.
global.put( GNR_LANG.key, Locale.ENGLISH ); */
global.put( GNR_VLNG.key, Locale.ENGLISH ); private static void retrieve() {
global.put( GNR_RCNT.key, new ArrayList<String>() ); global = Configuration.newInstance( new File( "./llanfair.xml" ) );
global.put( GNR_COOR.key, null ); if ( global.isEmpty() ) {
global.put( GNR_SIZE.key, null ); setDefaultValues();
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 );
}
/** /**
* Returns the key string of this property. * Fills the global configuration with every property, assigning them their
* * default value. This method can be called even when the global
* @return the key string of this property * configuration is not empty, and will thus function as a reset.
*/ */
public String getKey() { private static void setDefaultValues() {
return key; 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>() );
* Returns the value assigned to this property. The value will be first global.put( GNR_COOR.key, null );
* read from the local configuration, and if no value has been defined global.put( GNR_SIZE.key, null );
* for this property, it will be read from the global configuration. The global.put( GNR_COMP.key, Compare.BEST_OVERALL_RUN );
* first property to call this method will trigger the global global.put( GNR_ACCY.key, Accuracy.TENTH );
* configuration to be read and loaded in memory. global.put( GNR_WARN.key, true );
*
* @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 );
}
/** global.put( CLR_BACK.key, Color.decode( "0x000000" ) );
* Sets the value of this property in the global configuration. global.put( CLR_FORE.key, Color.decode( "0xc0c0c0" ) );
* global.put( CLR_TIME.key, Color.decode( "0xffffff" ) );
* @param value the value to assign to this property global.put( CLR_TIMR.key, Color.decode( "0x22cc22" ) );
*/ global.put( CLR_GAIN.key, Color.decode( "0x6295fc" ) );
public void set( T value ) { global.put( CLR_LOST.key, Color.decode( "0xe82323" ) );
set( value, false ); 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" ) );
* 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( KEY_SPLT.key, -1 );
* Returns the localized name of this property. global.put( KEY_USPL.key, -1 );
*/ global.put( KEY_SKIP.key, -1 );
@Override public String toString() { global.put( KEY_RSET.key, -1 );
return Language.valueOf( global.put( KEY_STOP.key, -1 );
"setting_" + key.replaceAll( "\\.", "_" ) global.put( KEY_PAUS.key, -1 );
).get(); 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 public class EditRun extends LlanfairDialog
implements ActionListener, ListSelectionListener { implements ActionListener, ListSelectionListener {
// ------------------------------------------------------------- CONSTANTES // ------------------------------------------------------------- CONSTANTES
/** /**
* Largeurs en pixels des colonnes de la table dédition des segments. Les * Largeurs en pixels des colonnes de la table dédition des segments. Les
* largeurs sont données dans lodre du modèle. * largeurs sont données dans lodre du modèle.
*/ */
private static final int[] TABLE_COLUMN_WIDTHS = new int[] { private static final int[] TABLE_COLUMN_WIDTHS = new int[] {
Segment.ICON_MAX_SIZE, 130, 100, 100, 100 Segment.ICON_MAX_SIZE, 130, 100, 100, 100
}; };
/** /**
* Dimension dun petit bouton dont létiquette nest quun caractère. * Dimension dun petit bouton dont létiquette nest quun caractère.
*/ */
private static final Dimension SMALL_BUTTON_SIZE = new Dimension(40, 25); private static final Dimension SMALL_BUTTON_SIZE = new Dimension(40, 25);
// -------------------------------------------------------------- ATTRIBUTS // -------------------------------------------------------------- ATTRIBUTS
/** /**
* Course éditée par cette boîte de dialogue. * Course éditée par cette boîte de dialogue.
*/ */
private Run run; private Run run;
/** /**
* Zone dédition du titre de la course. * Zone dédition du titre de la course.
*/ */
private JTextField runTitle; private JTextField runTitle;
/** /**
* Étiquette du {@link runTitle}. * Étiquette du {@link runTitle}.
*/ */
private JLabel runTitleLabel; private JLabel runTitleLabel;
/** /**
* Table dédition des segments de la course. * Table dédition des segments de la course.
*/ */
private JTable segments; private JTable segments;
/** /**
* Panneau avec ascenseur dans lequel est inséré la table {@link segments}. * Panneau avec ascenseur dans lequel est inséré la table {@link segments}.
*/ */
private JScrollPane scrollPane; private JScrollPane scrollPane;
/** /**
* Étiquette de {@code segments}. * Étiquette de {@code segments}.
*/ */
private JLabel segmentsLabel; private JLabel segmentsLabel;
/** /**
* Bouton permettant dinsérer un nouveau segment. * Bouton permettant dinsérer un nouveau segment.
*/ */
private JButton addSegment; private JButton addSegment;
/** /**
* Bouton permettant de supprimer un segment. * Bouton permettant de supprimer un segment.
*/ */
private JButton remSegment; private JButton remSegment;
private JCheckBox segmented;
/** private JCheckBox segmented;
* Bouton permettant denregistrer lédition et de quitter le dialogue.
*/
private JButton save;
/** /**
* Bouton permettant de quitter le dialogue sans modifier la course. * Bouton permettant denregistrer lédition et de quitter le dialogue.
*/ */
private JButton cancel; private JButton save;
/**
* 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;
// ----------------------------------------------------------- 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. * Button moving the currently selected segment one position up.
* */
* @param run - la course a éditer. private JButton moveUp;
*/
public EditRun(Run run) {
super();
if (run == null) {
throw new NullPointerException("EditDialog.EditDialog(): null run");
}
this.run = run;
run.saveBackup();
setTitle(Language.EDITING.get()); /**
* Button moving the currently selected segment one position down.
*/
private JButton moveDown;
runTitle = new JTextField(run.getName(), 61); private JTextField runGoal;
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());
placeComponents(); // ----------------------------------------------------------- CONSTRUCTEURS
setBehavior();
}
// ---------------------------------------------------------------- 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();
/** setTitle(Language.EDITING.get());
* 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));
}
/** runTitle = new JTextField(run.getName(), 61);
* Définit le comportement des sous-composants du dialogue. runTitleLabel = new JLabel(Language.RUN_TITLE.get());
*/ runGoal = new JTextField(run.getGoal(), 48);
private void setBehavior() { segments = new JTable(run) {
// Ne pas permettre la fermeture du dialogue par la croix pour forcer @Override protected JTableHeader createDefaultTableHeader() {
// lutilisateur à utiliser les boutons save et cancel. return new JTableHeader(columnModel) {
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); @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. placeComponents();
setResizable(false); setBehavior();
addSegment.setPreferredSize(SMALL_BUTTON_SIZE); }
remSegment.setPreferredSize(SMALL_BUTTON_SIZE);
moveUp.setPreferredSize(SMALL_BUTTON_SIZE);
moveDown.setPreferredSize(SMALL_BUTTON_SIZE);
segments.setRowHeight(Segment.ICON_MAX_SIZE); // ---------------------------------------------------------------- MÉTHODES
for (int i = 0; i < run.getColumnCount(); i++) {
TableColumn column = segments.getColumnModel().getColumn(i);
column.setPreferredWidth(TABLE_COLUMN_WIDTHS[i]);
}
int totalWidth = 0; /**
for (int width : TABLE_COLUMN_WIDTHS) { * Dispose les sous-composants au sein de la boîte de dialogue.
totalWidth += width; */
} private void placeComponents() {
Dimension size = new Dimension(totalWidth, Segment.ICON_MAX_SIZE * 5); setLayout(new GridBagLayout());
segments.setPreferredScrollableViewportSize(size); 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. JPanel controls = new JPanel();
addSegment.addActionListener(this); controls.add(save);
remSegment.addActionListener(this); controls.add(cancel);
moveUp.addActionListener(this); add(controls, GBC.grid(0, 7, 4, 1));
moveDown.addActionListener(this); }
cancel.addActionListener(this);
save.addActionListener(this);
// Insertion des délégués de rendus et dédition. /**
segments.setDefaultRenderer(Icon.class, new IconRenderer()); * Définit le comportement des sous-composants du dialogue.
segments.setDefaultEditor(Icon.class, new FileChooserEditor(this)); */
segments.setDefaultEditor(Time.class, new TimeEditor()); 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. // Définir les dimensions des composants.
segments.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); setResizable(false);
segments.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); addSegment.setPreferredSize(SMALL_BUTTON_SIZE);
segments.getSelectionModel().addListSelectionListener(this); remSegment.setPreferredSize(SMALL_BUTTON_SIZE);
moveUp.setPreferredSize(SMALL_BUTTON_SIZE);
moveDown.setPreferredSize(SMALL_BUTTON_SIZE);
addSegment.setToolTipText("" + Language.TT_ADD_SEGMENT); segments.setRowHeight(Segment.ICON_MAX_SIZE);
remSegment.setToolTipText("" + Language.TT_REMOVE_SEGMENT); for (int i = 0; i < run.getColumnCount(); i++) {
moveDown.setToolTipText("" + Language.TT_MOVE_SEGMENT_DOWN); TableColumn column = segments.getColumnModel().getColumn(i);
moveUp.setToolTipText("" + Language.TT_MOVE_SEGMENT_UP); column.setPreferredWidth(TABLE_COLUMN_WIDTHS[i]);
segmented.setToolTipText("" + Language.TT_ED_SEGMENTED); }
updateButtons();
}
/** int totalWidth = 0;
* Procédure à invoquer lorsque ce composant réalise une action. Sont donc for (int width : TABLE_COLUMN_WIDTHS) {
* capturés ici, tous les évènements daction des sous-composants, comme totalWidth += width;
* lappui sur un bouton. }
* Dimension size = new Dimension(totalWidth, Segment.ICON_MAX_SIZE * 5);
* @param event - lévènement daction. segments.setPreferredScrollableViewportSize(size);
* @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();
} else if (source.equals(save)) { // Enregistrement des écouteurs des composants.
run.setName(runTitle.getText()); addSegment.addActionListener(this);
run.setGoal(runGoal.getText()); remSegment.addActionListener(this);
run.setSegmented(segmented.isSelected()); moveUp.addActionListener(this);
dispose(); moveDown.addActionListener(this);
cancel.addActionListener(this);
save.addActionListener(this);
} else if (source.equals(cancel)) { // Insertion des délégués de rendus et dédition.
if (!segments.isEditing()) { segments.setDefaultRenderer(Icon.class, new IconRenderer());
run.loadBackup(); segments.setDefaultEditor(Icon.class, new FileChooserEditor(this));
dispose(); segments.setDefaultEditor(Time.class, new TimeEditor());
}
} 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);
}
}
/** // Ajuster le comportement de la table.
* Méthode invoquée lors dun changement de sélection dans la table. segments.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
* segments.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
* @param event - lévènement de sélection. segments.getSelectionModel().addListSelectionListener(this);
*/
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);
}
// ---------------------------------------------------------- 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);
/** updateButtons();
* 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. * Procédure à invoquer lorsque ce composant réalise une action. Sont donc
*/ * capturés ici, tous les évènements daction des sous-composants, comme
public IconRenderer() { * lappui sur un bouton.
super(); *
} * @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)) {
* Retourne le composant de rendu à afficher dans la table. Le composant run.removeSegment(segments.getSelectedRow());
* est ici préparé selon la valeur de la cellule et différentes updateButtons();
* 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;
}
}
// ---------------------------------------------------------- CLASSE INTERNE } else if (source.equals(save)) {
run.setName(runTitle.getText());
run.setGoal(runGoal.getText());
run.setSegmented(segmented.isSelected());
dispose();
/** } else if (source.equals(cancel)) {
* Délégué dédition des temps de course et de segments. Cette classe permet if (!segments.isEditing()) {
* de récupérer les exceptions potentiellement levés lorsque lédition se run.loadBackup();
* termine et que la valeur est modifié dans la table des segments. dispose();
* }
* @author Xavier "Xunkar" Sencert } else if (source.equals(moveUp)) {
*/ int selected = segments.getSelectedRow();
private class TimeEditor extends DefaultCellEditor { run.moveSegmentUp(selected);
segments.setRowSelectionInterval(selected - 1, selected - 1);
/** } else if (source.equals(moveDown)) {
* Composant dédition. Conservé ici en doublon pour éviter de devoir le int selected = segments.getSelectedRow();
* caster à chaque fois. run.moveSegmentDown(selected);
*/ segments.setRowSelectionInterval(selected + 1, selected + 1);
private JTextField editor; }
}
/** /**
* Construction dun délégué par défaut. * Méthode invoquée lors dun changement de sélection dans la table.
*/ *
public TimeEditor() { * @param event - lévènement de sélection.
super(new JTextField()); */
editor = (JTextField) getComponent(); public void valueChanged(ListSelectionEvent event) {
} if (!event.getValueIsAdjusting()) {
updateButtons();
}
}
/** private void updateButtons() {
* Retourne la valeur entrée par lutilisateur. int selected = segments.getSelectedRow();
* boolean enabled = (selected >= 0);
* @return la valeur entrée par lutilisateur. remSegment.setEnabled(enabled);
*/ moveUp.setEnabled(enabled && selected > 0);
public Object getCellEditorValue() { moveDown.setEnabled(enabled && selected < run.getRowCount() - 1);
String text = editor.getText(); }
return text.equals("") ? null : new Time(text);
}
/** // ---------------------------------------------------------- CLASSE INTERNE
* 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;
}
/** /**
* Arrête lédition de la cellule. Le délégué récupère ici les exceptions * Gestionnaire de rendu capable dafficher une valeur de type {@link Icon}
* levées et les remonte à lutilisateur. Tant que lédition est en erreur * au sein dune table.
* lédition persiste. *
* * @author Xavier Sencert
* @return {@code true} si lédition sest arrêtée. * @see TableCellRenderer
*/ */
public boolean stopCellEditing() { private class IconRenderer extends DefaultTableCellRenderer {
try {
return super.stopCellEditing();
} catch (Exception e) {
JOptionPane.showMessageDialog(editor, e.getMessage(),
Language.ERROR.get(), JOptionPane.ERROR_MESSAGE);
return false;
}
}
} /**
* 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;
}
}
/** // ---------------------------------------------------------- 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. * 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
private JFileChooser chooser; * 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} * Composant dédition. Conservé ici en doublon pour éviter de devoir le
* ce qui permet de capturer le clic de lutilisateur et ainsi douvrir * caster à chaque fois.
* le gestionnaire de sélection de fichier. */
*/ private JTextField editor;
private JButton editor;
private Window owner;
/** /**
* Création dun éditeur par défaut. * Construction dun délégué par défaut.
*/ */
public FileChooserEditor(Window owner) { public TimeEditor() {
super(); super(new JTextField());
this.owner = owner; editor = (JTextField) getComponent();
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 * Retourne la valeur entrée par lutilisateur.
* sagit donc du bouton {@link #editor}. *
*/ * @return la valeur entrée par lutilisateur.
public Component getTableCellEditorComponent(JTable table, Object value, */
boolean isSelected, int row, int column) { public Object getCellEditorValue() {
return editor; String text = editor.getText();
} return text.equals("") ? null : new Time(text);
}
/** /**
* Retourne la valeur stockée par léditeur à savoir le fichier * Retourne le composant dédition, formatté comme il se doit selon les
* sélectionner par lutilisateur sil y en a un. * informations de la table et de la cellule.
*/ *
public Object getCellEditorValue() { * @param table - la table source demandant lédition.
if (chooser.getSelectedFile() == null) { * @param get - la valeur actuellement présente à représenter.
return null; * @param isSelected - indique si la ligne est sélectionnée.
} * @param row - indice de ligne la cellule éditée.
return new ImageIcon(chooser.getSelectedFile().getPath()); * @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 * Arrête lédition de la cellule. Le délégué récupère ici les exceptions
* gestionnaire de sélection de fichier puis lon force la fin de * levées et les remonte à lutilisateur. Tant que lédition est en erreur
* lédition lorsque celui-ci retourne. * lédition persiste.
*/ *
@Override public void actionPerformed(ActionEvent e) { * @return {@code true} si lédition sest arrêtée.
chooser.showOpenDialog(owner); */
fireEditingStopped(); 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 * @date 22 juil. 2012
*/ */
public class EditSettings extends LlanfairDialog 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. * Bouton permettant de valider et de fermer la boîte de dialogue.
*/ */
private JButton actionOK; 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. * Construction dune boîte de dialogue dédition de paramètres.
*/ */
public EditSettings() { public EditSettings() {
settingsTabs = new ArrayList<SettingsTab>(); settingsTabs = new ArrayList<SettingsTab>();
settingsTabs.add(new TabGeneral()); settingsTabs.add(new TabGeneral());
settingsTabs.add(new TabLook()); settingsTabs.add(new TabLook());
settingsTabs.add(new TabHotkeys()); settingsTabs.add(new TabHotkeys());
settingsTabs.add(new TabHistory()); settingsTabs.add(new TabHistory());
settingsTabs.add(new TabComponents()); settingsTabs.add(new TabComponents());
createResources(); createResources();
placeComponents(); placeComponents();
setPersistentBehavior(); setPersistentBehavior();
LocaleDelegate.addLocaleListener(this); LocaleDelegate.addLocaleListener(this);
} }
@Override public void localeChanged(LocaleEvent event) { @Override public void localeChanged(LocaleEvent event) {
// for (SettingsTab tab : settingsTabs) { // for (SettingsTab tab : settingsTabs) {
// tab.processLocaleEvent(event); // tab.processLocaleEvent(event);
// } // }
} }
// MÉTHODES // MÉTHODES
/** /**
* Instanciation des sous-composants utilisés par cette boîte de dialogue. * Instanciation des sous-composants utilisés par cette boîte de dialogue.
*/ */
private void createResources() { private void createResources() {
reset = new JButton("" + Language.setting_hotkey_reset); reset = new JButton("" + Language.setting_hotkey_reset);
actionOK = new JButton(Language.ACCEPT.get()); actionOK = new JButton(Language.ACCEPT.get());
} }
/** /**
* Dispose les sous-composants au sein de ce panneau. Les sous-composants * Dispose les sous-composants au sein de ce panneau. Les sous-composants
* sont placés à laide dun {@link GridBagLayout} dont laccès est * sont placés à laide dun {@link GridBagLayout} dont laccès est
* simplifié par la classe-proxy {@link GBC}. * simplifié par la classe-proxy {@link GBC}.
*/ */
private void placeComponents() { private void placeComponents() {
setLayout(new GridBagLayout()); 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));
}
/** JTabbedPane tabPane = new JTabbedPane(); {
* Définit le comportement persistant (non sujet aux variations détats du for (SettingsTab tab : settingsTabs) {
* modèle ou de la configuration de lapplication) pour ce composant et tabPane.add(tab.toString(), tab);
* ses sous-composants. }
*/ }
private void setPersistentBehavior() { JPanel controls = new JPanel(); {
setResizable(false); controls.add(actionOK);
setTitle(Language.menuItem_settings.get()); controls.add(reset);
actionOK.addActionListener(this); }
reset.addActionListener(this); add(tabPane, GBC.grid(0, 0));
addWindowListener(this); 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 * Procédure à invoquer lorsquun sous-composant réalise une action. Cela
* signifie pour nous que lutilisateur à effectuer un réglage de paramètre. * signifie pour nous que lutilisateur à effectuer un réglage de paramètre.
* *
* @param evt - lévènement daction. * @param evt - lévènement daction.
* @see ActionListener * @see ActionListener
*/ */
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
Object source = e.getSource(); Object source = e.getSource();
if (source.equals(actionOK)) { if (source.equals(actionOK)) {
for (SettingsTab tab : settingsTabs) { for (SettingsTab tab : settingsTabs) {
tab.doDelayedSettingChange(); tab.doDelayedSettingChange();
} }
dispose(); dispose();
} else if (source.equals(reset)) { } else if (source.equals(reset)) {
int option = JOptionPane.showConfirmDialog(this, int option = JOptionPane.showConfirmDialog(this,
"" + Language.WARN_RESET_SETTINGS); "" + Language.WARN_RESET_SETTINGS);
if (option == JOptionPane.YES_OPTION) { if (option == JOptionPane.YES_OPTION) {
// Settings.reset(); // Settings.reset();
dispose(); dispose();
} }
} }
} }
@Override public void windowActivated(WindowEvent e) {} @Override public void windowActivated(WindowEvent e) {}
@Override public void windowClosed(WindowEvent e) {} @Override public void windowClosed(WindowEvent e) {}
@Override public void windowDeactivated(WindowEvent e) {} @Override public void windowDeactivated(WindowEvent e) {}
@Override public void windowDeiconified(WindowEvent e) {} @Override public void windowDeiconified(WindowEvent e) {}
@Override public void windowIconified(WindowEvent e) {} @Override public void windowIconified(WindowEvent e) {}
@Override public void windowOpened(WindowEvent e) {} @Override public void windowOpened(WindowEvent e) {}
@Override public void windowClosing(WindowEvent e) { @Override public void windowClosing(WindowEvent e) {
for (SettingsTab tab : settingsTabs) { for (SettingsTab tab : settingsTabs) {
tab.doDelayedSettingChange(); tab.doDelayedSettingChange();
} }
dispose(); dispose();
} }
} }

View file

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

View file

@ -1,43 +1,43 @@
package org.fenix.llanfair.dialog; 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.awt.event.ItemEvent;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; 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 * @author Xavier "Xunkar" Sencert
*/ */
abstract class SettingsTab extends JPanel { 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) { protected Map<String, SCheckBox> checkBoxes;
super.itemStateChanged(e);
setting.set(isSelected()); 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; 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.Language;
import org.fenix.llanfair.Segment; import org.fenix.llanfair.Segment;
import org.fenix.llanfair.config.Settings; import org.fenix.llanfair.config.Settings;
import org.fenix.utils.gui.GBC; 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 * @author Xavier "Xunkar" Sencert
*/ */
public class TabComponents extends SettingsTab public class TabComponents extends SettingsTab
implements ActionListener, ChangeListener { 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);
GraphicsEnvironment gEnv = GraphicsEnvironment private static final List<Settings.Property<Boolean>> SCB_SETTINGS
.getLocalGraphicsEnvironment(); = new ArrayList<Settings.Property<Boolean>>();
String mainFont = Settings.COR_TFNT.get().getName(); static {
timerFont = new JComboBox(gEnv.getAvailableFontFamilyNames()); SCB_SETTINGS.add(Settings.FOO_SHOW);
timerFont.setSelectedItem(mainFont); SCB_SETTINGS.add(Settings.FOO_SPLT);
timerFont.setPreferredSize(new Dimension(130, 22)); SCB_SETTINGS.add(Settings.FOO_DLBL);
timerFont.addActionListener(this); SCB_SETTINGS.add(Settings.FOO_BEST);
SCB_SETTINGS.add(Settings.FOO_LINE);
String segFont = Settings.COR_SFNT.get().getName(); SCB_SETTINGS.add(Settings.FOO_VERB);
timerSegFont = new JComboBox(gEnv.getAvailableFontFamilyNames()); SCB_SETTINGS.add(Settings.COR_NAME);
timerSegFont.setSelectedItem(segFont); SCB_SETTINGS.add(Settings.COR_SPLT);
timerSegFont.setPreferredSize(new Dimension(130, 22)); SCB_SETTINGS.add(Settings.COR_SEGM);
timerSegFont.addActionListener(this); SCB_SETTINGS.add(Settings.COR_BEST);
SCB_SETTINGS.add(Settings.HDR_GOAL);
timerSize = new JSpinner(new SpinnerNumberModel( SCB_SETTINGS.add(Settings.GPH_SHOW);
Settings.COR_TFNT.get().getSize(), 8, 240, 1) SCB_SETTINGS.add(Settings.COR_STMR);
); SCB_SETTINGS.add(Settings.COR_ICON);
timerSize.addChangeListener(this); SCB_SETTINGS.add(Settings.HDR_TTLE);
}
timerSegSize = new JSpinner(new SpinnerNumberModel(
Settings.COR_SFNT.get().getSize(), 8, 240, 1) private JComboBox iconSizes;
);
timerSegSize.addChangeListener(this); private JComboBox timerFont;
timerSameFont = new JCheckBox("" + Language.USE_MAIN_FONT); private JSpinner timerSize;
timerSameFont.setSelected(segFont.equals(mainFont));
timerSegFont.setEnabled(!timerSameFont.isSelected()); private JComboBox timerSegFont;
timerSameFont.addActionListener(this);
private JSpinner timerSegSize;
place();
} private JCheckBox timerSameFont;
// -------------------------------------------------------------- CALLBACKS TabComponents() {
super();
@Override public void actionPerformed(ActionEvent event) {
Object source = event.getSource(); for (Settings.Property<Boolean> setting : SCB_SETTINGS) {
if (source.equals(iconSizes)) { checkBoxes.put(setting.getKey(), new SCheckBox(setting));
Settings.COR_ICSZ.set((Integer) iconSizes.getSelectedItem()); }
} else if (source.equals(timerFont)) { // Checkboxes side effects
String fontName = timerFont.getSelectedItem().toString(); checkBoxes.get(Settings.FOO_SPLT.getKey()).deactivates(
Font font = Font.decode(fontName).deriveFont( checkBoxes.get(Settings.FOO_BEST.getKey())
(float) Settings.COR_TFNT.get().getSize() );
); // Checkboxes requirements
Settings.COR_TFNT.set(font); checkBoxes.get(Settings.FOO_BEST.getKey()).requires(
if (timerSameFont.isSelected()) { checkBoxes.get(Settings.FOO_SPLT.getKey()), false
timerSegFont.setSelectedItem(fontName); );
} checkBoxes.get(Settings.FOO_LINE.getKey()).requires(
} else if (source.equals(timerSegFont)) { checkBoxes.get(Settings.FOO_BEST.getKey()), true
String fontName = timerSegFont.getSelectedItem().toString(); );
Font font = Font.decode(fontName).deriveFont(
(float) Settings.COR_SFNT.get().getSize() iconSizes = new JComboBox(Segment.ICON_SIZES);
); iconSizes.setSelectedItem(Settings.COR_ICSZ.get());
Settings.COR_SFNT.set(font); iconSizes.addActionListener(this);
} else if (source.equals(timerSameFont)) {
timerSegFont.setEnabled(!timerSameFont.isSelected()); GraphicsEnvironment gEnv = GraphicsEnvironment
if (timerSameFont.isSelected()) { .getLocalGraphicsEnvironment();
int size = (Integer) timerSegSize.getValue(); String mainFont = Settings.COR_TFNT.get().getName();
Settings.COR_SFNT.set( timerFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
Settings.COR_TFNT.get().deriveFont((float) size) timerFont.setSelectedItem(mainFont);
); timerFont.setPreferredSize(new Dimension(130, 22));
} timerFont.addActionListener(this);
}
} String segFont = Settings.COR_SFNT.get().getName();
timerSegFont = new JComboBox(gEnv.getAvailableFontFamilyNames());
@Override public void stateChanged(ChangeEvent event) { timerSegFont.setSelectedItem(segFont);
Object source = event.getSource(); timerSegFont.setPreferredSize(new Dimension(130, 22));
if (source.equals(timerSize)) { timerSegFont.addActionListener(this);
int size = (Integer) timerSize.getValue();
Settings.COR_TFNT.set( timerSize = new JSpinner(new SpinnerNumberModel(
Settings.COR_TFNT.get().deriveFont((float) size) Settings.COR_TFNT.get().getSize(), 8, 240, 1)
); );
} else if (source.equals(timerSegSize)) { timerSize.addChangeListener(this);
int size = (Integer) timerSegSize.getValue();
Settings.COR_SFNT.set( timerSegSize = new JSpinner(new SpinnerNumberModel(
Settings.COR_SFNT.get().deriveFont((float) size) Settings.COR_SFNT.get().getSize(), 8, 240, 1)
); );
} timerSegSize.addChangeListener(this);
}
timerSameFont = new JCheckBox("" + Language.USE_MAIN_FONT);
// -------------------------------------------------------------- INHERITED timerSameFont.setSelected(segFont.equals(mainFont));
timerSegFont.setEnabled(!timerSameFont.isSelected());
@Override void doDelayedSettingChange() {} timerSameFont.addActionListener(this);
@Override public String toString() { place();
return "" + Language.COMPONENTS; }
}
// -------------------------------------------------------------- CALLBACKS
// -------------------------------------------------------------- UTILITIES
@Override public void actionPerformed(ActionEvent event) {
private void place() { Object source = event.getSource();
setLayout(new GridBagLayout()); if (source.equals(iconSizes)) {
Settings.COR_ICSZ.set((Integer) iconSizes.getSelectedItem());
JPanel fontPanel = new JPanel(new GridBagLayout()); { } else if (source.equals(timerFont)) {
fontPanel.add( String fontName = timerFont.getSelectedItem().toString();
new JLabel("" + Language.setting_core_timerFont), Font font = Font.decode(fontName).deriveFont(
GBC.grid(0, 0).anchor(GBC.LS).insets(0, 5) (float) Settings.COR_TFNT.get().getSize()
); );
fontPanel.add(timerFont, GBC.grid(1, 0)); Settings.COR_TFNT.set(font);
fontPanel.add(timerSize, GBC.grid(2, 0).insets(0, 5)); if (timerSameFont.isSelected()) {
fontPanel.add( timerSegFont.setSelectedItem(fontName);
new JLabel("" + Language.setting_core_segmentTimerFont), }
GBC.grid(0, 1).anchor(GBC.LS).insets(3, 5) } else if (source.equals(timerSegFont)) {
); String fontName = timerSegFont.getSelectedItem().toString();
fontPanel.add( Font font = Font.decode(fontName).deriveFont(
timerSameFont, (float) Settings.COR_SFNT.get().getSize()
GBC.grid(1, 1, 2, 1).anchor(GBC.LS).insets(3, 0) );
); Settings.COR_SFNT.set(font);
fontPanel.add(timerSegFont, GBC.grid(1, 2)); } else if (source.equals(timerSameFont)) {
fontPanel.add(timerSegSize, GBC.grid(2, 2).insets(0, 5)); timerSegFont.setEnabled(!timerSameFont.isSelected());
fontPanel.setBorder( if (timerSameFont.isSelected()) {
BorderFactory.createTitledBorder("" + Language.PN_FONTS) int size = (Integer) timerSegSize.getValue();
); Settings.COR_SFNT.set(
} Settings.COR_TFNT.get().deriveFont((float) size)
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)); @Override public void stateChanged(ChangeEvent event) {
timerPanel.add(checkBoxes.get(Settings.COR_ICON.getKey()), GBC.grid(0, 2, 2, 1).anchor(GBC.LS)); Object source = event.getSource();
timerPanel.add(checkBoxes.get(Settings.COR_NAME.getKey()), GBC.grid(0, 3, 2, 1).anchor(GBC.LS)); if (source.equals(timerSize)) {
timerPanel.add(checkBoxes.get(Settings.COR_SPLT.getKey()), GBC.grid(0, 4, 2, 1).anchor(GBC.LS)); int size = (Integer) timerSize.getValue();
timerPanel.add(checkBoxes.get(Settings.COR_SEGM.getKey()), GBC.grid(0, 5, 2, 1).anchor(GBC.LS)); Settings.COR_TFNT.set(
timerPanel.add(checkBoxes.get(Settings.COR_BEST.getKey()), GBC.grid(0, 6, 2, 1).anchor(GBC.LS)); Settings.COR_TFNT.get().deriveFont((float) size)
timerPanel.add(checkBoxes.get(Settings.COR_STMR.getKey()), GBC.grid(0, 7, 2, 1).anchor(GBC.LS)); );
timerPanel.setBorder( } else if (source.equals(timerSegSize)) {
BorderFactory.createTitledBorder("" + Language.TIMER) int size = (Integer) timerSegSize.getValue();
); Settings.COR_SFNT.set(
} Settings.COR_SFNT.get().deriveFont((float) size)
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)); // -------------------------------------------------------------- INHERITED
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)); @Override void doDelayedSettingChange() {}
footerPanel.setBorder(
BorderFactory.createTitledBorder("" + Language.FOOTER) @Override public String toString() {
); return "" + Language.COMPONENTS;
} }
JPanel miscPanel = new JPanel(new GridBagLayout()); {
miscPanel.add(checkBoxes.get(Settings.HDR_TTLE.getKey()), GBC.grid(0, 0).anchor(GBC.LS)); // -------------------------------------------------------------- UTILITIES
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)); private void place() {
miscPanel.setBorder( setLayout(new GridBagLayout());
BorderFactory.createTitledBorder("" + Language.MISC)
); JPanel fontPanel = new JPanel(new GridBagLayout()); {
} fontPanel.add(
add(fontPanel, GBC.grid(0, 0, 2, 1).fill(GBC.B).anchor(GBC.FLS)); new JLabel("" + Language.setting_core_timerFont),
add(timerPanel, GBC.grid(2, 0, 1, 2).fill(GBC.B).anchor(GBC.FLS)); GBC.grid(0, 0).anchor(GBC.LS).insets(0, 5)
add(footerPanel, GBC.grid(0, 1).fill(GBC.B).anchor(GBC.FLS)); );
add(miscPanel, GBC.grid(1, 1).fill(GBC.B).anchor(GBC.FLS)); 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; package org.fenix.llanfair.dialog;
import java.awt.Component; import org.fenix.llanfair.Language;
import java.awt.GridBagLayout; import org.fenix.llanfair.Llanfair;
import java.awt.GridLayout; 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.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Locale; 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 * @author Xavier "Xunkar" Sencert
*/ */
public class TabGeneral extends SettingsTab implements ActionListener { public class TabGeneral extends SettingsTab implements ActionListener {
// ------------------------------------------------------------- ATTRIBUTES // ------------------------------------------------------------- ATTRIBUTES
private JComboBox language; private JComboBox language;
private JLabel languageText; private JLabel languageText;
private JCheckBox alwaysOnTop; private JCheckBox alwaysOnTop;
private JLabel alwaysOnTopText; private JLabel alwaysOnTopText;
private ButtonGroup compare; private ButtonGroup compare;
private JLabel compareText; private JLabel compareText;
private ButtonGroup accuracy; private ButtonGroup accuracy;
private JLabel accuracyText; private JLabel accuracyText;
private JCheckBox warnOnReset; private JCheckBox warnOnReset;
// ----------------------------------------------------------- CONSTRUCTORS // ----------------------------------------------------------- CONSTRUCTORS
TabGeneral() { TabGeneral() {
language = new JComboBox(Language.LANGUAGES); language = new JComboBox(Language.LANGUAGES);
language.setRenderer(new LocaleListRenderer()); language.setRenderer(new LocaleListRenderer());
language.setSelectedItem(Settings.GNR_LANG.get()); language.setSelectedItem(Settings.GNR_LANG.get());
language.addActionListener(this); language.addActionListener(this);
alwaysOnTop = new JCheckBox("" + Language.setting_alwaysOnTop); alwaysOnTop = new JCheckBox("" + Language.setting_alwaysOnTop);
alwaysOnTop.setSelected(Settings.GNR_ATOP.get()); alwaysOnTop.setSelected(Settings.GNR_ATOP.get());
compare = new ButtonGroup(); compare = new ButtonGroup();
Compare setCmp = Settings.GNR_COMP.get(); Compare setCmp = Settings.GNR_COMP.get();
for (Compare method : Compare.values()) { for (Compare method : Compare.values()) {
JRadioButton radio = new JRadioButton("" + method); JRadioButton radio = new JRadioButton("" + method);
radio.setName(method.name()); radio.setName(method.name());
radio.setSelected(setCmp == method); radio.setSelected(setCmp == method);
radio.addActionListener(this); radio.addActionListener(this);
compare.add(radio); compare.add(radio);
} }
accuracy = new ButtonGroup(); accuracy = new ButtonGroup();
Accuracy setAcc = Settings.GNR_ACCY.get(); Accuracy setAcc = Settings.GNR_ACCY.get();
for (Accuracy value : Accuracy.values()) { for (Accuracy value : Accuracy.values()) {
JRadioButton radio = new JRadioButton("" + value); JRadioButton radio = new JRadioButton("" + value);
radio.setName(value.name()); radio.setName(value.name());
radio.setSelected(setAcc == value); radio.setSelected(setAcc == value);
radio.addActionListener(this); radio.addActionListener(this);
accuracy.add(radio); accuracy.add(radio);
} }
warnOnReset = new JCheckBox("" + Language.setting_warnOnReset); warnOnReset = new JCheckBox("" + Language.setting_warnOnReset);
warnOnReset.setSelected(Settings.GNR_WARN.get()); warnOnReset.setSelected(Settings.GNR_WARN.get());
warnOnReset.addActionListener(this); warnOnReset.addActionListener(this);
languageText = new JLabel("" + Language.setting_language); languageText = new JLabel("" + Language.setting_language);
alwaysOnTopText = new JLabel("" + Language.APPLICATION); alwaysOnTopText = new JLabel("" + Language.APPLICATION);
compareText = new JLabel("" + Language.COMPARE_METHOD); compareText = new JLabel("" + Language.COMPARE_METHOD);
accuracyText = new JLabel("" + Language.ACCURACY); accuracyText = new JLabel("" + Language.ACCURACY);
place(); place();
} }
// -------------------------------------------------------------- CALLBACKS // -------------------------------------------------------------- CALLBACKS
public void actionPerformed(ActionEvent event) { public void actionPerformed(ActionEvent event) {
Object source = event.getSource(); Object source = event.getSource();
if (source.equals(language)) { if (source.equals(language)) {
Settings.GNR_LANG.set((Locale) language.getSelectedItem()); Settings.GNR_LANG.set((Locale) language.getSelectedItem());
} else if (source instanceof JRadioButton) { } else if (source instanceof JRadioButton) {
JRadioButton radio = (JRadioButton) source; JRadioButton radio = (JRadioButton) source;
try { try {
Settings.GNR_COMP.set( Settings.GNR_COMP.set(
Compare.valueOf(radio.getName()) Compare.valueOf(radio.getName())
); );
} catch (Exception e) { } catch (Exception e) {
Settings.GNR_ACCY.set(Accuracy.valueOf(radio.getName())); Settings.GNR_ACCY.set(Accuracy.valueOf(radio.getName()));
} }
} else if (source.equals(warnOnReset)) { } else if (source.equals(warnOnReset)) {
Settings.GNR_WARN.set(warnOnReset.isSelected()); Settings.GNR_WARN.set(warnOnReset.isSelected());
} }
} }
// -------------------------------------------------------------- INHERITED // -------------------------------------------------------------- INHERITED
void doDelayedSettingChange() { void doDelayedSettingChange() {
Settings.GNR_ATOP.set(alwaysOnTop.isSelected()); Settings.GNR_ATOP.set(alwaysOnTop.isSelected());
} }
/** /**
* Returns the localized name of this tab. * Returns the localized name of this tab.
*/ */
@Override public String toString() { @Override public String toString() {
return "" + Language.GENERAL; return "" + Language.GENERAL;
} }
// -------------------------------------------------------------- UTILITIES // -------------------------------------------------------------- UTILITIES
/** /**
* Places all sub-components within this panel. * Places all sub-components within this panel.
*/ */
private void place() { private void place() {
setLayout(new GridBagLayout()); setLayout(new GridBagLayout());
add( add(
alwaysOnTopText, GBC.grid(0, 0).anchor(GBC.LE).insets(5, 10) alwaysOnTopText, GBC.grid(0, 0).anchor(GBC.LE).insets(5, 10)
); );
add(alwaysOnTop, GBC.grid(1, 0).anchor(GBC.LS)); add(alwaysOnTop, GBC.grid(1, 0).anchor(GBC.LS));
add(warnOnReset, GBC.grid(1, 1).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(languageText, GBC.grid(0, 2).anchor(GBC.LE).insets(10, 10));
add(language, GBC.grid(1, 2).fill(GBC.H)); add(language, GBC.grid(1, 2).fill(GBC.H));
JPanel comparePanel = new JPanel(new GridLayout(0, 1)); { JPanel comparePanel = new JPanel(new GridLayout(0, 1)); {
Enumeration<AbstractButton> buttons = compare.getElements(); Enumeration<AbstractButton> buttons = compare.getElements();
while (buttons.hasMoreElements()) { while (buttons.hasMoreElements()) {
comparePanel.add(buttons.nextElement()); comparePanel.add(buttons.nextElement());
} }
} }
add(compareText, GBC.grid(0, 3).anchor(GBC.FLE).insets(14, 10)); add(compareText, GBC.grid(0, 3).anchor(GBC.FLE).insets(14, 10));
add(comparePanel, GBC.grid(1, 3).fill(GBC.H).insets(10, 0, 0, 0)); add(comparePanel, GBC.grid(1, 3).fill(GBC.H).insets(10, 0, 0, 0));
JPanel accuracyPanel = new JPanel(new GridLayout(0, 1)); { JPanel accuracyPanel = new JPanel(new GridLayout(0, 1)); {
Enumeration<AbstractButton> buttons = accuracy.getElements(); Enumeration<AbstractButton> buttons = accuracy.getElements();
while (buttons.hasMoreElements()) { while (buttons.hasMoreElements()) {
accuracyPanel.add(buttons.nextElement()); accuracyPanel.add(buttons.nextElement());
} }
} }
add(accuracyText, GBC.grid(0, 4).anchor(GBC.FLE).insets(14, 10)); add(accuracyText, GBC.grid(0, 4).anchor(GBC.FLE).insets(14, 10));
add(accuracyPanel, GBC.grid(1, 4).fill(GBC.H).insets(10, 0)); add(accuracyPanel, GBC.grid(1, 4).fill(GBC.H).insets(10, 0));
} }
// --------------------------------------------------------- INTERNAL TYPES // --------------------------------------------------------- INTERNAL TYPES
class LocaleListRenderer extends DefaultListCellRenderer { class LocaleListRenderer extends DefaultListCellRenderer {
public LocaleListRenderer() { public LocaleListRenderer() {
setVerticalAlignment(CENTER); setVerticalAlignment(CENTER);
} }
@Override public Component getListCellRendererComponent( @Override public Component getListCellRendererComponent(
JList list, Object value, int index, boolean isSelected, JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus boolean cellHasFocus
) { ) {
super.getListCellRendererComponent( super.getListCellRendererComponent(
list, value, index, isSelected, cellHasFocus list, value, index, isSelected, cellHasFocus
); );
Locale selected = (Locale) value; Locale selected = (Locale) value;
setIcon(Llanfair.getResources().getIcon("" + selected)); setIcon(Llanfair.getResources().getIcon("" + selected));
setText(Language.LOCALE_NAMES.get("" + selected)); setText(Language.LOCALE_NAMES.get("" + selected));
return this; return this;
} }
} }
} }

View file

@ -1,348 +1,337 @@
package org.fenix.llanfair.dialog; 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.Language;
import org.fenix.llanfair.Segment; import org.fenix.llanfair.Segment;
import org.fenix.llanfair.config.Settings;
import org.fenix.llanfair.config.Merge; import org.fenix.llanfair.config.Merge;
import org.fenix.llanfair.config.Settings;
import org.fenix.utils.gui.GBC; 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 { 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(); // ------------------------------------------------------------- ATTRIBUTES
}
// -------------------------------------------------------------- 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; * Text field allowing the user to edit the height of the history. The
try { * height is provided as a number of segments to display.
input = Integer.parseInt(rows.getText()); */
} catch (Exception e) { private JTextField rows;
//$FALL-THROUGH$
} finally { /**
if (input <= 0) { * Label displaying the name of the text field {@code height}.
input = 0; */
rows.setText("0"); private JLabel heightText;
}
Settings.HST_ROWS.set(input); /**
} * Label displaying the unit of the value of the text field {@code height}.
} */
private JLabel heightUnit;
/**
* Returns the localized name of this tab. /**
*/ * Combox box displaying the available merging methods, determining which,
@Override public String toString() { * if any, column should be merged.
return "" + Language.HISTORY; */
} private JComboBox merge;
// -------------------------------------------------------------- UTILITIES /**
* Check box determining wether or not the history should display the
/** * delta column.
* Places all sub-components within this panel. */
*/ private JCheckBox deltas;
private void place() {
setLayout(new GridBagLayout()); /**
// Set Components Orientation * Check box determining wether or not the history should display the
rows.setHorizontalAlignment(JLabel.CENTER); * live times column.
twoLines.setHorizontalTextPosition(JLabel.LEADING); */
blankRows.setHorizontalTextPosition(JLabel.LEADING); private JCheckBox lives;
showLast.setHorizontalTextPosition(JLabel.LEADING);
// Display private JCheckBox blankRows;
JPanel display = new JPanel(new GridBagLayout()); {
display.add(icons , GBC.grid(0, 0).anchor(GBC.LS)); private JCheckBox icons;
display.add(lives , GBC.grid(0, 1).anchor(GBC.LS));
display.add(deltas, GBC.grid(0, 2).anchor(GBC.LS)); 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(tabular, GBC.grid(0, 3).anchor(GBC.LS));
display.add(merge , GBC.grid(0, 4).anchor(GBC.LS)); display.add(merge , GBC.grid(0, 4).anchor(GBC.LS));
display.setBorder( display.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_DISPLAY) BorderFactory.createTitledBorder("" + Language.PN_DISPLAY)
); );
} }
// Dimension // Dimension
JPanel dimension = new JPanel(new GridBagLayout()); { JPanel dimension = new JPanel(new GridBagLayout()); {
dimension.add( dimension.add(
new JLabel("" + Language.setting_history_iconSize), new JLabel("" + Language.setting_history_iconSize),
GBC.grid(0, 0).anchor(GBC.LE) GBC.grid(0, 0).anchor(GBC.LE)
); );
dimension.add(iconSize, GBC.grid(1, 0).anchor(GBC.LE).insets(2, 3)); dimension.add(iconSize, GBC.grid(1, 0).anchor(GBC.LE).insets(2, 3));
dimension.add( dimension.add(
new JLabel("" + Language.setting_history_rowCount), new JLabel("" + Language.setting_history_rowCount),
GBC.grid(0, 1).anchor(GBC.LE) GBC.grid(0, 1).anchor(GBC.LE)
); );
dimension.add( dimension.add(
rows, GBC.grid(1, 1).anchor(GBC.LE).fill(GBC.H).insets(2, 3) 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(blankRows, GBC.grid(0, 2, 2, 1).anchor(GBC.LE));
dimension.add( dimension.add(
twoLines , GBC.grid(0, 3, 2, 1).anchor(GBC.LE).insets(0, 1) twoLines , GBC.grid(0, 3, 2, 1).anchor(GBC.LE).insets(0, 1)
); );
dimension.setBorder( dimension.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_DIMENSION) BorderFactory.createTitledBorder("" + Language.PN_DIMENSION)
); );
} }
// Fonts // Fonts
JPanel fonts = new JPanel(new GridBagLayout()); { JPanel fonts = new JPanel(new GridBagLayout()); {
fonts.add( fonts.add(
new JLabel("" + Language.setting_history_segmentFont), new JLabel("" + Language.setting_history_segmentFont),
GBC.grid(0, 0).anchor(GBC.LE) GBC.grid(0, 0).anchor(GBC.LE)
); );
fonts.add(nameFont, GBC.grid(1, 0).anchor(GBC.LS).insets(5, 5)); 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(nameSize, GBC.grid(2, 0).anchor(GBC.LS));
fonts.add( fonts.add(
new JLabel("" + Language.setting_history_timeFont), new JLabel("" + Language.setting_history_timeFont),
GBC.grid(0, 1).anchor(GBC.LE) GBC.grid(0, 1).anchor(GBC.LE)
); );
fonts.add(timeFont, GBC.grid(1, 1).anchor(GBC.LS).insets(5, 5)); fonts.add(timeFont, GBC.grid(1, 1).anchor(GBC.LS).insets(5, 5));
fonts.add(timeSize, GBC.grid(2, 1).anchor(GBC.LS)); fonts.add(timeSize, GBC.grid(2, 1).anchor(GBC.LS));
fonts.setBorder( fonts.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_FONTS) BorderFactory.createTitledBorder("" + Language.PN_FONTS)
); );
} }
// Scrolling // Scrolling
JPanel scrolling = new JPanel(new GridBagLayout()); { JPanel scrolling = new JPanel(new GridBagLayout()); {
scrolling.add( scrolling.add(
offsetHelper, GBC.grid(0, 0).anchor(GBC.LE).insets(0, 8) offsetHelper, GBC.grid(0, 0).anchor(GBC.LE).insets(0, 8)
); );
scrolling.add( scrolling.add(
new JLabel("" + Language.setting_history_offset), new JLabel("" + Language.setting_history_offset),
GBC.grid(1, 0).anchor(GBC.LE) GBC.grid(1, 0).anchor(GBC.LE)
); );
scrolling.add(offset , GBC.grid(2, 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.add(showLast, GBC.grid(0, 1, 3, 1).anchor(GBC.LE));
scrolling.setBorder( scrolling.setBorder(
BorderFactory.createTitledBorder("" + Language.PN_SCROLLING) BorderFactory.createTitledBorder("" + Language.PN_SCROLLING)
); );
} }
add(fonts , GBC.grid(0, 0).fill(GBC.B).padding(10, 10)); 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(dimension, GBC.grid(1, 0).fill(GBC.B).padding(10, 10));
add(display , GBC.grid(0, 1).fill(GBC.B).padding(10, 10)); add(display , GBC.grid(0, 1).fill(GBC.B).padding(10, 10));
add(scrolling, GBC.grid(1, 1).fill(GBC.B).padding(10, 10)); add(scrolling, GBC.grid(1, 1).fill(GBC.B).padding(10, 10));
} }
} }

View file

@ -1,16 +1,5 @@
package org.fenix.llanfair.dialog; 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.Language;
import org.fenix.llanfair.config.Settings; import org.fenix.llanfair.config.Settings;
import org.fenix.utils.gui.GBC; import org.fenix.utils.gui.GBC;
@ -18,194 +7,201 @@ import org.jnativehook.GlobalScreen;
import org.jnativehook.keyboard.NativeKeyEvent; import org.jnativehook.keyboard.NativeKeyEvent;
import org.jnativehook.keyboard.NativeKeyListener; 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 * @author Xavier "Xunkar" Sencert
*/ */
class TabHotkeys extends SettingsTab { 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
/** // ------------------------------------------------------------- ATTRIBUTES
* 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 /**
* List of all key fields customizable by the user.
/** */
* Preferred dimension of a text field. private List<KeyField> keyFields;
*/
private static final Dimension SIZE = new Dimension(85, 20); /**
* List of labels displaying the name of each key field.
// ---------------------------------------------------- ATTRIBUTES */
private List<JLabel> keyLabels;
private static boolean isEditing = false;
// ----------------------------------------------------------- CONSTRUCTORS
/**
* Internal index of this field, used by the superclass. /**
*/ * Creates the "Hotkeys" settings tab. Only called by {@link EditSettings}.
private int index; */
TabHotkeys() {
/** keyFields = new ArrayList<KeyField>();
* Setting represented by this key field. keyLabels = new ArrayList<JLabel>();
*/
private Settings.Property<Integer> setting; int index = 0;
for (Settings.Property<?> setting : Settings.getAll("hotkey")) {
// -------------------------------------------------- CONSTRUCTORS keyFields.add(new KeyField(
index, (Settings.Property<Integer>) setting)
/** );
* Creates a new key field for the given setting. Only called by keyLabels.add(new JLabel("" + setting));
* {@link TabHotkeys}. index++;
* }
* @param index - internal index to identify this field.
* @param setting - setting represented by this field. place();
*/ }
KeyField(int index, Settings.Property<Integer> setting) {
this.index = index; // -------------------------------------------------------------- INHERITED
this.setting = setting;
@Override void doDelayedSettingChange() {}
setEditable(false);
setName(setting.getKey()); /**
setPreferredSize(SIZE); * Returns the localized name of this tab.
setHorizontalAlignment(CENTER); */
String text = NativeKeyEvent.getKeyText(setting.get()); @Override public String toString() {
setText(setting.get() == -1 ? "" + Language.DISABLED : text); return "" + Language.INPUTS;
}
addMouseListener(this);
} // -------------------------------------------------------------- UTILITIES
// ------------------------------------------------------- GETTERS /**
* Places all sub-components within this panel.
/** */
* Returns the setting represented by this key field. private void place() {
*/ setLayout(new GridBagLayout());
public Settings.Property<Integer> getSetting() {
return setting; for (int row = 0; row < keyFields.size(); row++) {
} add(
keyLabels.get(row),
// ----------------------------------------------------- CALLBACKS GBC.grid(0, row).insets(10, 0, 10, 10).anchor(GBC.LE)
);
/** add(keyFields.get(row), GBC.grid(1, row));
* 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.
*/ // --------------------------------------------------------- INTERNAL TYPES
public void mouseClicked(MouseEvent event) {
if (!isEditing) { /**
setBackground(Color.YELLOW); * A text field representing a hotkey setting. Clicking such field allows
GlobalScreen.getInstance().addNativeKeyListener(this); * the user to define a key for this particular setting, using the tables
isEditing = true; * from {@code JNativeHook}.
} *
} * @author Xavier "Xunkar" Sencert
*/
// $UNUSED$ static class KeyField extends JTextField
public void mouseEntered(MouseEvent event) {} implements MouseListener, NativeKeyListener {
// $UNUSED$ // ----------------------------------------------------- CONSTANTS
public void mouseExited(MouseEvent event) {}
/**
// $UNUSED$ * Preferred dimension of a text field.
public void mousePressed(MouseEvent event) {} */
private static final Dimension SIZE = new Dimension(85, 20);
// $UNUSED$
public void mouseReleased(MouseEvent event) {} // ---------------------------------------------------- ATTRIBUTES
/** private static boolean isEditing = false;
* 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 * Internal index of this field, used by the superclass.
* listen for native key events. */
*/ private int index;
public void nativeKeyPressed(NativeKeyEvent event) {
int code = event.getKeyCode(); /**
String text = null; * Setting represented by this key field.
*/
if (code == NativeKeyEvent.VK_ESCAPE) { private Settings.Property<Integer> setting;
code = -1;
text = "" + Language.DISABLED; // -------------------------------------------------- CONSTRUCTORS
} else {
text = NativeKeyEvent.getKeyText(code); /**
} * Creates a new key field for the given setting. Only called by
setText(text); * {@link TabHotkeys}.
setting.set(code); *
* @param index - internal index to identify this field.
setBackground(Color.GREEN); * @param setting - setting represented by this field.
GlobalScreen.getInstance().removeNativeKeyListener(this); */
isEditing = false; KeyField(int index, Settings.Property<Integer> setting) {
} this.index = index;
this.setting = setting;
// $UNUSED$
public void nativeKeyReleased(NativeKeyEvent event) {} setEditable(false);
setName(setting.getKey());
// $UNUSED$ setPreferredSize(SIZE);
public void nativeKeyTyped(NativeKeyEvent event) {} 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 * @author Xavier "Xunkar" Sencert
*/ */
class TabLook extends SettingsTab implements ActionListener, ChangeListener { 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;
/** // -------------------------------------------------------------- CONSTANTS
* 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")) { * Dimension of the revert button.
ColorButton colorButton = new ColorButton( */
index, (Settings.Property<Color>) colorSetting private static final Dimension REVERT_SIZE = new Dimension(18, 18);
);
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 // ------------------------------------------------------------- ATTRIBUTES
/**
* Index of this color button in the englobing model.
*/
private int index;
/** /**
* Color currently represented by this button. * List of all color buttons, inserted in the order of their name.
*/ */
private Color color; private List<ColorButton> colorButtons;
/**
* 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 * List of labels displaying the name of the color button.
* supplied to identify this button. The name of the setting becomes */
* the name of the button. private List<JLabel> colorTexts;
*
* @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 /**
* Panel allowing the user to select a color.
*/
private JColorChooser colorChooser;
/** /**
* Returns the current color of this button. * The index of the currently selected color button. Any color change will
* * be made to this button.
* @return the current color. */
*/ private int selected;
Color getColor() {
return color;
}
/**
* Returns the index of this button.
*
* @return the index of this button.
*/
int getIndex() {
return index;
}
// ------------------------------------------------------- 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 * Panel listing the color buttons and their name label.
* and repaints itself. */
* private JPanel colorPanel;
* @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 * Small button displayed next to the selected color button and resetting
* display and does not display any string of text. * its color to its original value.
*/ */
@Override protected void paintComponent(Graphics g) { private JButton revert;
super.paintComponent(g);
g.setColor(color); /**
g.fillRect(0, 0, getWidth(), getHeight()); * 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; 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.Llanfair;
import org.fenix.llanfair.Run; import org.fenix.llanfair.Run;
import org.fenix.llanfair.Segment; import org.fenix.llanfair.Segment;
import org.fenix.llanfair.Time; 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. * Utility class that provides method to interface with WSplit.
* *
@ -15,95 +16,95 @@ import org.fenix.llanfair.Time;
* @version 1.1 * @version 1.1
*/ */
public class WSplit { 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( * Parses the given stream opened on a WSplit run file and sets it as
parsed == 0.0 ? null : new Time( parsed ), * the currently opened run in Llanfair.
run.getRowCount() - 1, Run.COLUMN_TIME *
); * @param master the Llanfair instance calling the parser
parsed = Double.parseDouble( args[3] ); * @param in an opened stream on a WSplit run file
segment.setTime( * @throws Exception if the reading operation cannot complete
parsed == 0.0 ? null : new Time(parsed), Segment.BEST */
); public static void parse( Llanfair master, BufferedReader in )
} throws Exception {
return line; if ( master == null ) {
} throw new NullPointerException( "Null Llanfair instance" );
}
/** if ( in == null ) {
* Parses the list of segment icons in a WSplit run file. The list is a throw new NullPointerException( "Null file reader" );
* single line, listing the icons in the segment order and formatted as }
* follows: Icons="icon1","icon2",...,"iconN" try {
* // Title
* @param line the line containing the icon list String line = in.readLine();
* @param run the run currently built by the parser Run run = new Run( line.split( "=" )[1] );
* @throws IOException if reading operations fail // Attempts, Offset, Size
*/ in.readLine();
private static void parseIcons( String line, Run run ) throws IOException { in.readLine();
line = line.substring( line.indexOf( "=" ) + 1 ); in.readLine();
String[] args = line.split( "," ); // Segment List
line = parseSegments( in, run );
for ( int i = 0; i < args.length; i++ ) { // Segment Icons
ImageIcon icon = null; parseIcons( line, run );
String[] lst = args[i].split( "\\\"" ); master.setRun( run );
} catch ( Exception ex ) {
if ( lst.length > 0 && !lst[1].equals( "" ) ) { throw ex;
icon = new ImageIcon( lst[1] ); }
} }
run.getSegment( i ).setIcon( icon );
} /**
} * 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 { class Footer extends JPanel {
private static final int ALL = 0xff; private static final int ALL = 0xff;
private static final int TIME = 0x01; private static final int TIME = 0x01;
private static final int DELTA = 0x02; private static final int DELTA = 0x02;
private static final int TEXT = 0x04; private static final int TEXT = 0x04;
private static final int BEST = 0x08; private static final int BEST = 0x08;
private static final int VERBOSE = 0x10; private static final int VERBOSE = 0x10;
private static final int INSET = 3; private static final int INSET = 3;
private Run run; private Run run;
private Time tmDlta; 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 JPanel panelBest; // labelBest + best private JLabel labelPrev; // P.Se:
private JPanel panelDeltaBest; // labelDeltaBest + deltaBest private JLabel liveL; // Left-hand Live Time
private JLabel liveR; // Right-hand Live Time
private boolean resize; private JLabel time; // Segment Set Time
private Dimension preferredSize;
/** private JLabel labelDelta; // Delta:
* Creates a default panel displaying informations for the given run. private JLabel labelLive; // Live:
* private JLabel delta; // Delta Segment Set/Live Time
* @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() { private JLabel labelBest; // P.Be:
Graphics graphics = getGraphics(); private JLabel best; // Segment Best Time
if (resize && (graphics != null)) { private JLabel inlineBest; // Segment Best Time (inline)
FontMetrics metrics = graphics.getFontMetrics();
private JLabel labelDeltaBest; // B.Delta:
int timeW; private JLabel deltaBest; // Delta Segment Best/Live Time
int timeH = metrics.getHeight(); private JLabel inlineDeltaBest; // Delta Segment Best/Live Time (inline)
int smtmW;
if (run.getRowCount() > 0) { private JPanel panelBest; // labelBest + best
Time segmentTime = run.getSegment(0).getTime(Segment.RUN); private JPanel panelDeltaBest; // labelDeltaBest + deltaBest
Time tenthTime = new Time(segmentTime.getMilliseconds() / 10L);
timeW = metrics.stringWidth("" + segmentTime); private boolean resize;
smtmW = metrics.stringWidth("" + tenthTime); private Dimension preferredSize;
} else {
timeW = metrics.stringWidth("" + Time.ZERO); /**
smtmW = timeW; * 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 liveW = metrics.stringWidth("" + Language.LB_FT_LIVE);
int prevW = metrics.stringWidth("" + Language.LB_FT_SEGMENT); int prevW = metrics.stringWidth("" + Language.LB_FT_SEGMENT);
int bestW = metrics.stringWidth("" + Language.LB_FT_BEST); int bestW = metrics.stringWidth("" + Language.LB_FT_BEST);
int dltaW = metrics.stringWidth("" + Language.LB_FT_DELTA); int dltaW = metrics.stringWidth("" + Language.LB_FT_DELTA);
int dltbW = metrics.stringWidth("" + Language.LB_FT_DELTA_BEST); 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());
JPanel timePanel = new JPanel(new GridBagLayout()); { boolean ftBest = Settings.FOO_BEST.get();
timePanel.add( boolean ftLabels = Settings.FOO_DLBL.get();
labelPrev, boolean ftVerbose = Settings.FOO_VERB.get();
GBC.grid(0, 0).anchor(GBC.LS).insets(0, 0, 0, INSET) boolean ftTwoLines = Settings.FOO_LINE.get();
);
timePanel.add(liveL, GBC.grid(1, 0).anchor(GBC.LS)); int height = timeH;
timePanel.add(time, GBC.grid(2, 0).anchor(GBC.LS)); int width = prevW + timeW + smtmW + INSET * 2;
timePanel.add( if (ftLabels) {
inlineBest, width += dltaW;
GBC.grid(3, 0).anchor(GBC.LS).insets(0, INSET, 0, 0) }
); if (ftVerbose) {
timePanel.setOpaque(false); width += timeW + liveW - (ftLabels ? 0 : dltaW)
} + metrics.stringWidth(" []");
JPanel deltaPanel = new JPanel(new GridBagLayout()); { }
deltaPanel.add( if (ftBest) {
labelDelta, if (ftTwoLines) {
GBC.grid(0, 0).anchor(GBC.LE).insets(0, 0, 0, INSET) height *= 2;
); int breakW = bestW + timeW + smtmW + (ftLabels ? dltbW : 0);
deltaPanel.add( width = Math.max(width, breakW);
labelLive, } else {
GBC.grid(1, 0).anchor(GBC.LE).insets(0, 0, 0, INSET) width += timeW + smtmW + metrics.stringWidth("| ");
); }
deltaPanel.add( if (ftVerbose) {
liveR, GBC.grid(2, 0).anchor(GBC.LE).insets(0, 0, 0, INSET) width += 5;
); }
deltaPanel.add(delta, GBC.grid(3, 0).anchor(GBC.LE)); }
deltaPanel.add( preferredSize = new Dimension(width, height);
inlineDeltaBest, setMinimumSize(new Dimension(50, height));
GBC.grid(4, 0).anchor(GBC.LE).insets(0, INSET, 0, 0) resize = false;
); }
deltaPanel.setOpaque(false); return (preferredSize == null ? getMinimumSize() : preferredSize);
} }
panelBest = new JPanel(new GridBagLayout()); {
panelBest.add(
labelBest,
GBC.grid(0, 0).anchor(GBC.LS).insets(0, 0, 0, INSET) /**
); * Sets the run to represent. All components are resetted to their initial
panelBest.add(best, GBC.grid(1, 0).anchor(GBC.LS)); * state.
panelBest.setOpaque(false); *
} * @param run - the new run to represent.
panelDeltaBest = new JPanel(new GridBagLayout()); { */
panelDeltaBest.add( final void setRun(Run run) {
labelDeltaBest, this.run = run;
GBC.grid(0, 0).anchor(GBC.LE).insets(0, 0, 0, INSET) updateValues(ALL & ~TEXT);
); }
panelDeltaBest.add(deltaBest, GBC.grid(1, 0).anchor(GBC.LE));
panelDeltaBest.setOpaque(false); // -------------------------------------------------------------- CALLBACKS
}
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)); * Callback invoked by the parent when the run or the application's
add(panelBest, GBC.grid(0, 1).anchor(GBC.LS).weight(0.5, 0.0)); * settings have seen one of their properties updated.
add(panelDeltaBest, GBC.grid(1, 1).anchor(GBC.LE).weight(0.5, 0.0)); *
} * @param event - the event describing the update.
*/
private void updateVisibility(int identifier) { void processPropertyChangeEvent(PropertyChangeEvent event) {
if ((identifier & BEST) == BEST) { String property = event.getPropertyName();
boolean ftTwoLines = Settings.FOO_LINE.get();
boolean ftBest = Settings.FOO_BEST.get(); if (Run.CURRENT_SEGMENT_PROPERTY.equals(property)) {
panelBest.setVisible(ftTwoLines); updateValues(ALL & ~TEXT);
panelDeltaBest.setVisible(ftTwoLines); updateColors(TIME | DELTA);
inlineBest.setVisible(!ftTwoLines && ftBest); updateVisibility(ALL);
inlineDeltaBest.setVisible(!ftTwoLines && ftBest); } else if (Settings.CLR_LOST.equals(property)
} || Settings.CLR_GAIN.equals(property)) {
if ((identifier & TEXT) == TEXT) { updateColors(DELTA);
boolean ftLabels = Settings.FOO_DLBL.get();
boolean ftVerbose = Settings.FOO_VERB.get(); } else if (Settings.CLR_TIME.equals(property)
labelLive.setVisible(ftLabels && ftVerbose); || Settings.CLR_RCRD.equals(property)) {
labelDelta.setVisible(ftLabels && !ftVerbose); updateColors(TIME | DELTA);
labelDeltaBest.setVisible(ftLabels);
} } else if (Settings.CLR_FORE.equals(property)) {
if ((identifier & VERBOSE) == VERBOSE) { updateColors(TEXT);
boolean ftVerbose = Settings.FOO_VERB.get();
boolean ftLabels = Settings.FOO_DLBL.get(); } else if (Settings.GNR_ACCY.equals(property)
labelLive.setVisible(ftVerbose && ftLabels); || Settings.GNR_COMP.equals(property)) {
labelDelta.setVisible(!ftVerbose && ftLabels); updateValues(ALL & ~TEXT);
time.setVisible(ftVerbose); forceResize();
liveL.setVisible(!ftVerbose);
liveR.setVisible(ftVerbose); } else if (Run.STATE_PROPERTY.equals(property)) {
} if (run.getState() == State.NULL || run.getState() == State.READY) {
} updateValues(ALL & ~TEXT);
}
/** updateVisibility(ALL);
* Updates the colors of the group of components specified by the } else if (Settings.FOO_SPLT.equals(property)) {
* identifier. updateValues(ALL);
* } else if (Settings.FOO_BEST.equals(property)
* @param identifier - one of the constant update identifier. || Settings.FOO_LINE.equals(property)) {
*/
private void updateColors(int identifier) { updateVisibility(BEST);
if ((identifier & TIME) == TIME) { forceResize();
Color colorTM = Settings.CLR_TIME.get(); } else if (Settings.FOO_DLBL.equals(property)) {
Color colorNR = Settings.CLR_RCRD.get(); updateVisibility(TEXT);
if (run.hasPreviousSegment() && run.isBestSegment(run.getPrevious())) { forceResize();
liveL.setForeground(colorNR); } else if (Settings.FOO_VERB.equals(property)) {
liveR.setForeground(colorNR); updateValues(DELTA);
} else { updateVisibility(VERBOSE);
liveL.setForeground(colorTM); forceResize();
liveR.setForeground(colorTM); }
} }
time.setForeground(colorTM);
best.setForeground(colorTM); private void forceResize() {
inlineBest.setForeground(colorTM); resize = true;
} revalidate();
if ((identifier & DELTA) == DELTA) { }
if (run.hasPreviousSegment()) {
Color colorTM = Settings.CLR_TIME.get(); /**
deltaBest.setForeground(colorTM); * Callback invoked by the parent when default local for this instance of
inlineDeltaBest.setForeground(colorTM); * the JVM has changed.
if (delta.getText().equals("--")) { *
delta.setForeground(colorTM); * @param event - the event describing the update.
} else if (run.isBestSegment(run.getPrevious())){ */
Color colorNR = Settings.CLR_RCRD.get(); void processLocaleEvent(LocaleEvent event) {
delta.setForeground(colorNR); updateValues(TEXT);
deltaBest.setForeground(colorNR); }
inlineDeltaBest.setForeground(colorNR);
} else { // -------------------------------------------------------------- UTILITIES
int compare = tmDlta.compareTo(Time.ZERO);
if (compare > 0) { /**
delta.setForeground(Settings.CLR_LOST.get()); * Places the sub-components within this component.
} else { */
delta.setForeground(Settings.CLR_GAIN.get()); private void placeComponents() {
} setLayout(new GridBagLayout());
}
} JPanel timePanel = new JPanel(new GridBagLayout()); {
} timePanel.add(
if ((identifier & TEXT) == TEXT) { labelPrev,
Color color = Settings.CLR_FORE.get(); GBC.grid(0, 0).anchor(GBC.LS).insets(0, 0, 0, INSET)
labelPrev.setForeground(color); );
labelDelta.setForeground(color); timePanel.add(liveL, GBC.grid(1, 0).anchor(GBC.LS));
labelLive.setForeground(color); timePanel.add(time, GBC.grid(2, 0).anchor(GBC.LS));
labelBest.setForeground(color); timePanel.add(
labelDeltaBest.setForeground(color); inlineBest,
} GBC.grid(3, 0).anchor(GBC.LS).insets(0, INSET, 0, 0)
} );
timePanel.setOpaque(false);
/** }
* Updates the values of the group of components specified by the JPanel deltaPanel = new JPanel(new GridBagLayout()); {
* identifier. deltaPanel.add(
* labelDelta,
* @param identifier - one of the constant update identifier. GBC.grid(0, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
*/ );
private void updateValues(int identifier) { deltaPanel.add(
boolean useSplit = Settings.FOO_SPLT.get(); labelLive,
boolean hasPrevious = run.hasPreviousSegment(); GBC.grid(1, 0).anchor(GBC.LE).insets(0, 0, 0, INSET)
int pIndex = run.getPrevious(); );
Segment pSegment = null; deltaPanel.add(
Time live; 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) { if (hasPrevious) {
pSegment = run.getSegment(pIndex); pSegment = run.getSegment(pIndex);
} }
if ((identifier & TIME) == TIME) { if ((identifier & TIME) == TIME) {
Time set; Time set;
if (hasPrevious) { if (hasPrevious) {
if (useSplit) { if (useSplit) {
live = run.getTime(pIndex, Segment.LIVE); live = run.getTime(pIndex, Segment.LIVE);
set = run.getTime(pIndex, Segment.SET); set = run.getTime(pIndex, Segment.SET);
} else { } else {
live = pSegment.getTime(Segment.LIVE); live = pSegment.getTime(Segment.LIVE);
set = pSegment.getTime(Segment.SET); set = pSegment.getTime(Segment.SET);
} }
time.setText("" + (set == null ? "--" : set)); time.setText("" + (set == null ? "--" : set));
liveL.setText("" + (live == null ? "--" : live)); liveL.setText("" + (live == null ? "--" : live));
liveR.setText(liveL.getText()); liveR.setText(liveL.getText());
Time bTime = pSegment.getTime(Segment.BEST); Time bTime = pSegment.getTime(Segment.BEST);
inlineBest.setText("| " + (bTime == null ? "--" : bTime)); inlineBest.setText("| " + (bTime == null ? "--" : bTime));
best.setText("" + (bTime == null ? "--" : bTime)); best.setText("" + (bTime == null ? "--" : bTime));
} else { } else {
time.setText(""); time.setText("");
liveL.setText(""); liveL.setText("");
liveR.setText(""); liveR.setText("");
best.setText(""); best.setText("");
inlineBest.setText(""); inlineBest.setText("");
} }
} }
if ((identifier & DELTA) == DELTA) { if ((identifier & DELTA) == DELTA) {
if (hasPrevious) { if (hasPrevious) {
if (useSplit) { if (useSplit) {
tmDlta = run.getTime(pIndex, Segment.DELTA); tmDlta = run.getTime(pIndex, Segment.DELTA);
live = run.getTime(pIndex, Segment.LIVE); live = run.getTime(pIndex, Segment.LIVE);
if (tmDlta == null || live == null) { if (tmDlta == null || live == null) {
delta.setText("--"); delta.setText("--");
} else { } else {
delta.setText(tmDlta.toString(true)); delta.setText(tmDlta.toString(true));
} }
} else { } else {
tmDlta = pSegment.getTime(Segment.DELTA); tmDlta = pSegment.getTime(Segment.DELTA);
live = pSegment.getTime(Segment.LIVE); live = pSegment.getTime(Segment.LIVE);
Time set = pSegment.getTime(Segment.SET); Time set = pSegment.getTime(Segment.SET);
Time dBst = pSegment.getTime(Segment.DELTA_BEST); Time dBst = pSegment.getTime(Segment.DELTA_BEST);
inlineDeltaBest.setText("| " + (dBst == null ? "--" : dBst.toString(true))); inlineDeltaBest.setText("| " + (dBst == null ? "--" : dBst.toString(true)));
deltaBest.setText("" + (dBst == null ? "--" : dBst.toString(true))); deltaBest.setText("" + (dBst == null ? "--" : dBst.toString(true)));
if (set != null && pIndex > 1) { if (set != null && pIndex > 1) {
set = set.clone(); set = set.clone();
for (int i = pIndex - 1; i >= 0; i--) { for (int i = pIndex - 1; i >= 0; i--) {
Segment pSeg = run.getSegment(i); Segment pSeg = run.getSegment(i);
Time ante = pSeg.getTime(Segment.LIVE); Time ante = pSeg.getTime(Segment.LIVE);
if (ante == null) { if (ante == null) {
set.add(pSeg.getTime(Segment.SET)); set.add(pSeg.getTime(Segment.SET));
} else { } else {
break; break;
} }
} }
tmDlta = Time.getDelta(live, set); tmDlta = Time.getDelta(live, set);
} }
if (tmDlta == null || live == null) { if (tmDlta == null || live == null) {
delta.setText("--"); delta.setText("--");
inlineDeltaBest.setText("| --"); inlineDeltaBest.setText("| --");
deltaBest.setText("--"); deltaBest.setText("--");
} else { } else {
delta.setText(tmDlta.toString(true)); delta.setText(tmDlta.toString(true));
} }
if (pIndex > 0) { if (pIndex > 0) {
Time sTime = run.getSegment(pIndex - 1) Time sTime = run.getSegment(pIndex - 1)
.getTime(Segment.SET); .getTime(Segment.SET);
if (sTime == null) { if (sTime == null) {
delta.setText("--"); delta.setText("--");
} }
} }
} }
if (Settings.FOO_VERB.get()) { if (Settings.FOO_VERB.get()) {
delta.setText("[" + delta.getText() + "]"); delta.setText("[" + delta.getText() + "]");
} }
updateColors(DELTA); updateColors(DELTA);
} else { } else {
delta.setText(""); delta.setText("");
inlineDeltaBest.setText(""); inlineDeltaBest.setText("");
deltaBest.setText(""); deltaBest.setText("");
} }
} }
if ((identifier & TEXT) == TEXT) { if ((identifier & TEXT) == TEXT) {
if (useSplit) { if (useSplit) {
labelPrev.setText("" + Language.LB_FT_SPLIT); labelPrev.setText("" + Language.LB_FT_SPLIT);
} else { } else {
labelPrev.setText("" + Language.LB_FT_SEGMENT); labelPrev.setText("" + Language.LB_FT_SEGMENT);
} }
labelLive.setText("" + Language.LB_FT_LIVE); labelLive.setText("" + Language.LB_FT_LIVE);
labelBest.setText("" + Language.LB_FT_BEST); labelBest.setText("" + Language.LB_FT_BEST);
labelDelta.setText("" + Language.LB_FT_DELTA); labelDelta.setText("" + Language.LB_FT_DELTA);
labelDeltaBest.setText("" + Language.LB_FT_DELTA_BEST); labelDeltaBest.setText("" + Language.LB_FT_DELTA_BEST);
} }
} }
} }

View file

@ -32,331 +32,331 @@ import org.fenix.utils.locale.LocaleEvent;
*/ */
class Graph extends JPanel { class Graph extends JPanel {
// -------------------------------------------------------------- CONSTANTS // -------------------------------------------------------------- CONSTANTS
/** /**
* Half the thickness of the stroke used to paint the graph. This value is * 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 * used to make sure the stroke retains its full thickness when reaching
* the top or the bottom of the canvas. * the top or the bottom of the canvas.
*/ */
protected static final int HALF_THICKNESS = 1; 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;
// ------------------------------------------------------------- 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. * The dashed stroke used to paint the projection of the vertices.
*/ */
protected Run run; protected static final Stroke DASHED_STROKE = new BasicStroke(
1.0F, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1.0F,
/** new float[] { 2.0F }, 0.0F );
* 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;
// ----------------------------------------------------------- CONSTRUCTORS /**
* Update identifier for every category.
*/
private static final int ALL = 0xff;
/** /**
* Creates a default graph panel representing the given run. * Update identifier for text variables.
* */
* @param run - the run to represent. private static final int TEXT = 0x01;
*/
Graph(Run run) {
canvas = new Canvas();
scale = new JLabel();
scaleText = new JLabel();
setRun(run); /**
setOpaque(false); * Update identifier for time variables.
*/
updateValues(TEXT); private static final int TIME = 0x02;
updateColors(ALL);
placeComponents();
Dimension size = new Dimension(PACK_WIDTH, PACK_HEIGHT);
setPreferredSize(size);
setMinimumSize(size);
}
// -------------------------------------------------------------- INTERFACE /**
* Minimum width in pixels of this component.
*/
private static final int PACK_WIDTH = 50;
/** /**
* Sets the new run to represent. * Minimum height in pixels of this component.
* */
* @param run - the new run to represent. private static final int PACK_HEIGHT = 50;
*/
void setRun(Run run) {
this.run = run;
updateValues(TIME);
}
/** // ------------------------------------------------------------- ATTRIBUTES
* 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; /**
} * Run instance represented by this component.
*/
/** protected Run run;
* 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 /**
* Canvas where the graph will be drawn.
*/
private Canvas canvas;
/** /**
* A simple panel whose paint method has been overriden to draw the graph. * Label displaying the current scale of the graph. The scale actually
* * displays the time represented by the maximum ordinate.
* @author Xavier "Xunkar" Sencert */
*/ private JLabel scale;
protected class Canvas extends JPanel {
// ----------------------------------------------------- INTERFACE
/** /**
* Draws the graph onto the canvas. * Label describing the scale value being displayed.
*/ */
@Override protected void paintComponent(Graphics g) { private JLabel scaleText;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int clipH = getHeight(); // ----------------------------------------------------------- CONSTRUCTORS
int clipW = getWidth();
int halfH = clipH / 2;
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 colorFG = Settings.CLR_FORE.get();
Color colorTG = Settings.CLR_GAIN.get(); Color colorTG = Settings.CLR_GAIN.get();
Color colorTL = Settings.CLR_LOST.get(); Color colorTL = Settings.CLR_LOST.get();
Color colorRC = Settings.CLR_RCRD.get(); Color colorRC = Settings.CLR_RCRD.get();
// Draw the axis. // Draw the axis.
g2.setColor(colorFG); g2.setColor(colorFG);
g2.drawLine(0, halfH, clipW, halfH); g2.drawLine(0, halfH, clipW, halfH);
if (run.getState() != State.NULL) { if (run.getState() != State.NULL) {
int segCnt = run.getRowCount(); int segCnt = run.getRowCount();
double segGap = (double) clipW / segCnt; double segGap = (double) clipW / segCnt;
if (run.hasPreviousSegment()) { if (run.hasPreviousSegment()) {
// Coordinates of the last drawn vertex. // Coordinates of the last drawn vertex.
int prevX = 0; int prevX = 0;
int prevY = halfH; int prevY = halfH;
for (int i = 0; i < run.getCurrent(); i++) { for (int i = 0; i < run.getCurrent(); i++) {
Time delta = run.getTime(i, Segment.DELTA); Time delta = run.getTime(i, Segment.DELTA);
Time live = run.getTime(i, Segment.LIVE); Time live = run.getTime(i, Segment.LIVE);
if (delta != null && live != null) { if (delta != null && live != null) {
int percent = (int) getCompareTimePercent(i); int percent = (int) getCompareTimePercent(i);
g2.setColor(run.isBetterSegment(i) ? colorTG : colorTL); g2.setColor(run.isBetterSegment(i) ? colorTG : colorTL);
if (run.isBestSegment(i)) { if (run.isBestSegment(i)) {
g2.setColor(colorRC); 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;
}
}
}
}
}
} // 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 { public class RunPane extends JPanel {
// -------------------------------------------------------------- CONSTANTS // -------------------------------------------------------------- CONSTANTS
/** /**
* Font used to render the title of the run. * Font used to render the title of the run.
*/ */
private static final Font RUN_TITLE_FONT = Font.decode("Arial-12-BOLD"); 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;
// ------------------------------------------------------------- ATTRIBUTES /**
* Update identifier for every category.
*/
private static final int ALL = 0xff;
/** /**
* Run instance represented by this component. * Update identifier for time variables.
*/ */
private Run run; private static final int GOAL = 0x01;
/** /**
* Label displaying the title of the current run. * Update identifier for separator variables.
*/ */
private JLabel title; private static final int SEPARATOR = 0x02;
/** /**
* Label displaying the current goal of run. By default its the time of * Update identifier for text variables.
* the run were comparing against, but it can be a customized string. */
*/ private static final int TEXT = 0x04;
private JLabel goal;
/** /**
* Label describing the goal value being displayed. * Update identifier for title variables.
*/ */
private JLabel goalText; private static final int TITLE = 0x08;
/**
* Panel containing both goal value and text.
*/
private JPanel goalPane;
/**
* A list containing empty labels serving as separators.
*/
private List<JLabel> separators;
/** /**
* Simple footer displaying information on the previous segment if any. * Update identifier for background variables.
* Is only visible if {@link Settings#FOOTER_DISPLAY} is {@code true}. */
*/ private static final int BACKGROUND = 0x10;
private Footer footer;
/** /**
* Panel displaying the core information like the current segment and the * Update identifier for the graph component.
* necessary timers. */
*/ private static final int GRAPH = 0x20;
private Core core;
/** /**
* Panel representing the current run as a graph. * Update identifier for the footer component.
*/ */
private Graph graph; 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. * Run instance represented by this component.
* */
* @param run - the run to represent. private Run run;
*/
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 /**
* Label displaying the title of the current run.
*/
private JLabel title;
/** /**
* Sets the new run to represent. * 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.
* @param run - the new run to represent. */
*/ private JLabel goal;
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); /**
} * Label describing the goal value being displayed.
*/
// -------------------------------------------------------------- CALLBACKS private JLabel goalText;
/**
* 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)) { * Panel containing both goal value and text.
if (run.getState() == State.READY */
|| run.getState() == State.NULL) { private JPanel goalPane;
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 /**
* 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 * Simple footer displaying information on the previous segment if any.
* and returns it. * Is only visible if {@link Settings#FOOTER_DISPLAY} is {@code true}.
* */
* @param a new separator. private Footer footer;
*/
private JLabel createSeparator() {
JLabel label = new JLabel();
separators.add(label);
return label;
}
/** /**
* Places the sub-components within this component. * Panel displaying the core information like the current segment and the
*/ * necessary timers.
private void placeComponents() { */
goalPane = new JPanel(new GridBagLayout()); { private Core core;
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); /**
} * Panel representing the current run as a graph.
*/
/** private Graph graph;
* 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); * Scrolling panel displaying information regarding every segment of the
} * run up to the last segment or {@link Settings#DISPLAYED_SEGMENTS}.
} */
private History history;
/**
* Updates the colors of the group of components specified by the // ----------------------------------------------------------- CONSTRUCTORS
* identifier.
* /**
* @param identifier - one of the constant update identifier. * Creates a default panel representing the given run.
*/ *
private void updateColors(int identifier) { * @param run - the run to represent.
if ((identifier & GOAL) == GOAL) { */
goal.setForeground(Settings.CLR_TIME.get()); public RunPane(Run run) {
} super(new GridBagLayout());
if ((identifier & TEXT) == TEXT) { if (run == null) {
goalText.setForeground(Settings.CLR_FORE.get()); throw new NullPointerException("null run");
} }
if ((identifier & TITLE) == TITLE) { title = new JLabel();
title.setForeground(Settings.CLR_TITL.get()); goal = new JLabel();
} goalText = new JLabel();
if ((identifier & BACKGROUND) == BACKGROUND) { core = new Core(run);
setBackground(Settings.CLR_BACK.get()); graph = new Graph(run);
} history = new History(run);
if ((identifier & SEPARATOR) == SEPARATOR) { footer = new Footer(run);
Color color = Settings.CLR_SPRT.get(); separators = new ArrayList<JLabel>();
for (JLabel separator : separators) {
separator.setBorder( placeComponents();
BorderFactory.createMatteBorder(1, 0, 0, 0, color)); setRun(run);
}
} updateValues(TEXT);
} updateColors(ALL);
updateVisibility(ALL);
/**
* Updates the visibility of the components specified by the title.setFont(RUN_TITLE_FONT);
* identifier. }
*
* @param identifier - one of the constant update identifier. // -------------------------------------------------------------- INTERFACE
*/
private void updateVisibility(int identifier) { /**
if ((identifier & GRAPH) == GRAPH) { * Sets the new run to represent.
if (Settings.GPH_SHOW.get()) { *
remove(core); * @param run - the new run to represent.
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) public final void setRun(Run run) {
.weight(1.0, 1.0)); if (run == null) {
} else { throw new NullPointerException("null run");
remove(graph); }
remove(core); this.run = run;
add(core, GBC.grid(0, 5).insets(0, 5).fill(GBC.H) core.setRun(run);
.weight(1.0, 1.0)); graph.setRun(run);
} history.setRun(run);
} footer.setRun(run);
if ((identifier & FOOTER) == FOOTER) {
if (Settings.FOO_SHOW.get()) { updateValues(ALL & ~TEXT);
add(footer, GBC.grid(0, 8).insets(0, 3).fill(GBC.H)); }
} else {
remove(footer); // -------------------------------------------------------------- CALLBACKS
}
} /**
if ((identifier & GOAL) == GOAL) { * Callback invoked by the parent when default local for this instance of
if (Settings.HDR_GOAL.get()) { * the JVM has changed.
if (Settings.HDR_TTLE.get()) { *
add(goalPane, GBC.grid(0, 1)); * @param event - the event describing the update.
} else { */
add(goalPane, GBC.grid(0, 1).insets(3, 0, 0, 0)); public void processLocaleEvent(LocaleEvent event) {
} core.processLocaleEvent(event);
} else { graph.processLocaleEvent(event);
remove(goalPane); footer.processLocaleEvent(event);
}
} updateValues(TEXT);
if ((identifier & TITLE) == TITLE) { }
title.setVisible(Settings.HDR_TTLE.get());
} /**
revalidate(); * Callback invoked by the parent when the run or the application's
repaint(); * 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.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.scale(scale, scale); g2.scale(scale, scale);
icon.paintIcon((Component)null, g2, 0, 0); icon.paintIcon(null, g2, 0, 0);
g2.dispose(); g2.dispose();
return new ImageIcon(buffer); return new ImageIcon(buffer);
} }

View file

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

View file

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

View file

@ -40,7 +40,7 @@ public class LocaleDelegate {
} }
private static void fireLocalChanged(Locale oldLocale, Locale newLocale) { 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; int len$ = arr$.length;
for(int i$ = 0; i$ < len$; ++i$) { for(int i$ = 0; i$ < len$; ++i$) {