Initial release

This commit is contained in:
Norberto Lopez
2013-04-22 19:51:15 +00:00
parent 5c30d9f5d9
commit 182cf3e76c
252 changed files with 29004 additions and 0 deletions

9
.classpath Normal file
View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="lib" path="lib/miglayout-3.7.2-swing.jar" sourcepath="lib/miglayout-3.7.2-sources.jar"/>
<classpathentry kind="lib" path="lib/dockingFramesCore.jar" sourcepath="lib/dockingFramesCore-src.jar"/>
<classpathentry kind="lib" path="lib/guava-12.0.jar" sourcepath="lib/guava-12.0-sources.jar"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

17
.project Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>glum</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

18
buildGlumBin.jardesc Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<jardesc>
<jar path="glum/bin/glum.jar"/>
<options buildIfNeeded="true" compress="true" descriptionLocation="/glum/buildGlumBin.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="false" storeRefactorings="false" useSourceFolders="false"/>
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
<selectedProjects/>
<manifest generateManifest="false" manifestLocation="/glum/src/Manifest.txt" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
<sealing sealJar="false">
<packagesToSeal/>
<packagesToUnSeal/>
</sealing>
</manifest>
<selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
<javaElement handleIdentifier="=glum/src"/>
<folder path="/glum/build/doc"/>
<folder path="/glum/build/classes"/>
</selectedElements>
</jardesc>

18
buildGlumSrc.jardesc Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<jardesc>
<jar path="glum/build/glum-src.jar"/>
<options buildIfNeeded="true" compress="true" descriptionLocation="/glum/buildGlumSrc.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="true" saveDescription="false" storeRefactorings="false" useSourceFolders="false"/>
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
<selectedProjects/>
<manifest generateManifest="false" manifestLocation="/glum/src/Manifest.txt" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="false">
<sealing sealJar="false">
<packagesToSeal/>
<packagesToUnSeal/>
</sealing>
</manifest>
<selectedElements exportClassFiles="false" exportJavaFiles="true" exportOutputFolder="false">
<javaElement handleIdentifier="=glum/src"/>
<folder path="/glum/build/doc"/>
<folder path="/glum/build/classes"/>
</selectedElements>
</jardesc>

Binary file not shown.

BIN
lib/dockingFramesCore.jar Normal file

Binary file not shown.

BIN
lib/guava-12.0-sources.jar Normal file

Binary file not shown.

BIN
lib/guava-12.0.jar Normal file

Binary file not shown.

Binary file not shown.

BIN
lib/miglayout-3.7.2-swing.jar Executable file

Binary file not shown.

1
src/Manifest.txt Normal file
View File

@@ -0,0 +1 @@
Class-Path: lib/guava-12.0.jar lib/miglayout-3.7.2-sources.jar

View File

@@ -0,0 +1,95 @@
package glum.coord;
/**
* Contains conversion multipliers to/from feet, yards, meters, data miles, and nautical miles, as well as angular
* values to/from degrees and radians. To convert a value <code>X</code> in units of <code>U</code> to units of
* <code>V</code>, use <code>X * Convert.U_TO_V</code>.
*/
public class Convert
{
public static final double FEET_TO_METERS = 0.3048;
public static final double DM_TO_METERS = 1828.8;
public static final double NM_TO_METERS = 1852.0;
public static final double MILES_TO_METERS = 1609.344;
public static final double YARDS_TO_METERS = 0.9144; // 3 * FEET_TO_METERS
public static final double METERS_TO_FEET = 1.0 / FEET_TO_METERS;
public static final double DM_TO_FEET = 6000.0;
public static final double NM_TO_FEET = NM_TO_METERS * METERS_TO_FEET;
public static final double MILES_TO_FEET = 5280.0;
public static final double YARDS_TO_FEET = 3.0;
public static final double METERS_TO_DM = 1.0 / DM_TO_METERS;
public static final double FEET_TO_DM = FEET_TO_METERS * METERS_TO_DM;
public static final double NM_TO_DM = NM_TO_METERS * METERS_TO_DM;
public static final double MILES_TO_DM = MILES_TO_METERS * METERS_TO_DM;
public static final double YARDS_TO_DM = YARDS_TO_METERS * METERS_TO_DM;
public static final double METERS_TO_NM = 1.0 / NM_TO_METERS;
public static final double FEET_TO_NM = FEET_TO_METERS * METERS_TO_NM;
public static final double DM_TO_NM = DM_TO_METERS * METERS_TO_NM;
public static final double MILES_TO_NM = MILES_TO_METERS * METERS_TO_NM;
public static final double YARDS_TO_NM = YARDS_TO_METERS * NM_TO_METERS;
public static final double METERS_TO_MILES = 1.0 / MILES_TO_METERS;
public static final double FEET_TO_MILES = FEET_TO_METERS * METERS_TO_MILES;
public static final double DM_TO_MILES = DM_TO_METERS * METERS_TO_MILES;
public static final double NM_TO_MILES = NM_TO_METERS * METERS_TO_MILES;
public static final double YARDS_TO_MILES = YARDS_TO_METERS * METERS_TO_MILES;
public static final double METERS_TO_YARDS = 1.0 / YARDS_TO_METERS;
public static final double FEET_TO_YARDS = 1.0 / 3.0;
public static final double DM_TO_YARDS = 2000.0;
public static final double NM_TO_YARDS = NM_TO_METERS * METERS_TO_YARDS;
public static final double MILES_TO_YARDS = 1760.0;
public static final double RAD_TO_DEG = 180.0 / Math.PI;
public static final double DEG_TO_RAD = Math.PI / 180.0;
public static final double SECS_TO_MSECS = 1000.0;
public static final double MSECS_TO_SECS = 1.0 / SECS_TO_MSECS;
public static final int MINS_TO_SECS = 60;
public static final int HOURS_TO_MINS = 60;
public static final int HOURS_TO_SECS = HOURS_TO_MINS * MINS_TO_SECS;
public static final double SECS_TO_MINS = 1 / MINS_TO_SECS;
public static final double MINS_TO_HOURS = 1 / HOURS_TO_MINS;
public static final double SECS_TO_HOURS = 1 / HOURS_TO_SECS;
/**
* Constructor
*/
private Convert()
{
}
/**
* Converts an angle to a bearing
*/
public static double angleToBearing(double aAngle)
{
double bearing;
bearing = 180 - (aAngle + 90);
if (bearing < 0)
bearing += 360;
return bearing;
}
/**
* Converts a bearing to an angle
*/
public static double bearingToAngle(double aBearing)
{
double angle;
angle = 180 - (aBearing + 90);
if (angle < 0)
angle += 360;
return angle;
}
}

View File

@@ -0,0 +1,197 @@
package glum.coord;
/** Provides a few useful functions on coordinates, such as converting
* to a user-presentable string.
*/
public class CoordUtil
{
/** Convert a Lat/Lon to a pair of DEG:MM:SS H strings. H is the
* hemisphere, N or S for lat, E or W for lon. The default separator
* string LL_SEP is used.
*/
public static String LatLonToString (LatLon ll)
{
return ll == null ? "" : LatLonToString (ll, LL_SEP);
}
/** Same as the other LatLonToString, excepts this one uses the
* given <code>sep</code> string to separate the Lat and Lon.
*/
public static String LatLonToString (LatLon ll, String sep)
{
return ll == null ? "" :
LatToString (ll.lat) + LL_SEP + LonToString (ll.lon);
}
/** Converts the given <code>lat</code> to DD:MM:SS H. */
public static String LatToString (double lat)
{
return LatToString (lat, true);
}
/** Converts the given <code>lat</code> to DD:MM:SS H if
* <code>include_seconds</code> is true. If it's false, then the
* :SS part is left off.
*/
public static String LatToString (double lat, boolean include_seconds)
{
DMS dms = new DMS (lat);
StringBuffer s = new StringBuffer();
if ( dms.degrees < 10 )
s.append ("0");
s.append (dms.degrees);
s.append (":");
if ( dms.minutes < 10 )
s.append ("0");
s.append (dms.minutes);
if ( include_seconds )
{
s.append (":");
if ( dms.seconds < 10 )
s.append ("0");
s.append (dms.seconds);
}
s.append (lat >= 0 ? " N" : " S");
return s.toString();
}
/** Similar to <code>LatToString</code> except that the degrees
* part is DDD instead of DD. */
public static String LonToString (double lon)
{
return LonToString (lon, true);
}
/** Similar to <code>LatToString</code> except that the degrees
* part is DDD instead of DD. */
public static String LonToString (double lon, boolean include_seconds)
{
DMS dms = new DMS (lon);
StringBuffer s = new StringBuffer();
if ( dms.degrees < 100 )
s.append ("0");
if ( dms.degrees < 10 )
s.append ("0");
s.append (dms.degrees);
s.append (":");
if ( dms.minutes < 10 )
s.append ("0");
s.append (dms.minutes);
if ( include_seconds )
{
s.append (":");
if ( dms.seconds < 10 )
s.append ("0");
s.append (dms.seconds);
}
s.append (lon >= 0 ? " E" : " W");
return s.toString();
}
/** Converts <code>dmsh_string</code> to a double value.
* The string format should match the output of the
* LatToString formats, including hemisphere.
* If a hemisphere character is not part of the string, the
* returned value will be non-negative.
*/
public static double StringToLat (String dmsh_string)
{
if ( dmsh_string == null || dmsh_string.length() == 0 )
return 0.0;
int dms [] = StringToDMS (dmsh_string);
if ( dms.length == 3 )
return new Degrees (dms[0], dms[1], dms[2]).degrees;
else return 0.0;
}
/** {@see StringToLat} */
public static double StringToLon (String dmsh_string)
{
// Because we aren't doing any range or hemisphere error
// checking, a lon value is identical to a lat value.
return StringToLat (dmsh_string);
}
/** Converts <code>dmsh_string</code> to a an array of
* 3 ints representing degrees, minutes, and seconds.
* if a hemisphere character is present (one of NSEW or
* nsew), and it represents a souther or western hemisphere,
* then the degrees value, in index 0 of the returned array,
* will be a non-positive number.
*/
public static int [] StringToDMS (String dmsh_string)
{
if ( dmsh_string == null || dmsh_string.length() == 0 )
return null;
char chars [] = dmsh_string.toCharArray();
int dms [] = new int [ 3 ];
dms[0] = 0;
for ( int i = 0, j = 0; i < chars.length; i++ )
{
char c = chars[i];
if ( c == ' ' || c == ' ' ) // Space or tab.
continue;
else if ( c >= '0' && c <= '9' && j < 3 )
dms[j] = dms[j] * 10 + c - '0';
else if ( c == ':' )
{
j++;
dms[j] = 0;
}
else if ( c == 'S' || c == 's' || c == 'W' || c == 'w' )
dms[0] = -dms[0];
}
return dms;
}
public static class DMS
{
public DMS (double deg)
{
if ( deg < 0 ) deg = -deg;
degrees = (int) deg;
minutes = (int) (deg * 60) % 60;
seconds = (int) (deg * 3600) % 60;
}
public int degrees, minutes, seconds;
}
public static class Degrees
{
public Degrees (int deg, int min, int sec)
{
degrees = Math.abs (deg) +
Math.abs(min) / 60.0 +
Math.abs(sec) / 3600.0;
if ( deg < 0 || min < 0 || sec < 0 )
degrees = -degrees;
}
public Degrees (int deg, int min, int sec, char hemisphere)
{
this (deg, min, sec);
if ( hemisphere == 'N' || hemisphere == 'n' ||
hemisphere == 'E' || hemisphere == 'e' )
degrees = Math.abs (degrees);
else if ( hemisphere == 'S' || hemisphere == 's' ||
hemisphere == 'W' || hemisphere == 'w' )
degrees = -Math.abs (degrees);
}
public double degrees;
}
public static String LL_SEP = " / ";
}

View File

@@ -0,0 +1,43 @@
package glum.coord;
/** Determines if two numbers are close, usually as a way to say
* that they are equal. Close is defined to mean that their difference
* is less than some small number, which is either supplied by the caller
* or is EPSILON.
* <p>For longitude near the equator, a difference of EPSILON is about
* 3.65 feet (where the earth's circumference is about 21913.3 DM, or
* about 60.87 DM per degree longitude). For DataMile measurements, it's
* about 0.72 inches.
*/
public class Epsilon
{
/** The measure of closeness; set to 0.00001. */
public static final double EPSILON = 0.00001;
public static boolean close (float a, float b)
{
float diff = a - b;
return diff < EPSILON && diff > -EPSILON;
}
public static boolean close (float a, float b, float epsilon)
{
float diff = a - b;
return diff < epsilon && diff > -epsilon;
}
public static boolean close (double a, double b)
{
double diff = a - b;
return diff < EPSILON && diff > -EPSILON;
}
public static boolean close (double a, double b, float epsilon)
{
double diff = a - b;
return diff < EPSILON && diff > -EPSILON;
}
private Epsilon () { }
}

View File

@@ -0,0 +1,97 @@
package glum.coord;
/**
* Contains a collection of utility methods to perform linear algebra using the objects from this package.
*/
public class GeoUtil
{
/**
* realSqr returns aNum*aNum
*/
public static double realSqr(double aNum)
{
return aNum * aNum;
}
/**
* computeDotProduct - Returns the dot product of vector1 and vector2
*/
public static double computeDotProduct(Point3D vector1, Point3D vector2)
{
return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z;
}
/**
* computeDistance - Returns the distance between pt1 and pt2
*/
public static double computeDistance(Point3D pt1, Point3D pt2)
{
return Math.sqrt(realSqr(pt1.x - pt2.x) + realSqr(pt1.y - pt2.y) + realSqr(pt1.z - pt2.z));
}
/**
* computeDistanceSquare - Returns the squared distance between pt1 and pt2
*/
public static double computeDistanceSquare(Point3D pt1, Point3D pt2)
{
return realSqr(pt1.x - pt2.x) + realSqr(pt1.y - pt2.y) + realSqr(pt1.z - pt2.z);
}
/**
* computeLength - Returns the magnitude of aVector
*/
public static double computeLength(Point3D aVector)
{
return Math.sqrt(realSqr(aVector.x) + realSqr(aVector.y) + realSqr(aVector.z));
}
/**
* computeNormal - Returns the R.H.R normal defined by the 3 points
*/
public static void computeNormal(Point3D pt1, Point3D pt2, Point3D pt3, Point3D aNormal)
{
Point3D vector1, vector2;
vector1 = new Point3D();
vector2 = new Point3D();
computeVector(pt1, pt3, vector1);
computeVector(pt3, pt2, vector2);
// ! Not sure why I have to negate all the values; Need to refer to linear alg.
//! aNormal.x = vector1.y*vector2.z - vector1.z*vector2.y;
//! aNormal.y = vector1.z*vector2.x - vector1.x*vector2.z;
//! aNormal.z = vector1.x*vector2.y - vector1.y*vector2.x;
aNormal.x = -(vector1.y * vector2.z - vector1.z * vector2.y);
aNormal.y = -(vector1.z * vector2.x - vector1.x * vector2.z);
aNormal.z = -(vector1.x * vector2.y - vector1.y * vector2.x);
// Normalize the vector
normalizeVector(aNormal);
}
/**
* computeVector - Returns the vector defined by the 2 points
*/
public static void computeVector(Point3D pt1, Point3D pt2, Point3D aVector)
{
aVector.x = pt2.x - pt1.x;
aVector.y = pt2.y - pt1.y;
aVector.z = pt2.z - pt1.z;
}
/**
* normalizeVector - Normalizes aVector so that its length is 1
*/
public static void normalizeVector(Point3D aVector)
{
double length;
length = computeLength(aVector);
// Normalize the vector
aVector.x = aVector.x / length;
aVector.y = aVector.y / length;
aVector.z = aVector.z / length;
}
}

111
src/glum/coord/LatLon.java Normal file
View File

@@ -0,0 +1,111 @@
package glum.coord;
/** Simple class for Lat/Lon values. */
public class LatLon
{
public double lat;
public double lon;
public LatLon()
{
}
public LatLon(LatLon latlon)
{
if (latlon != null)
{
lat = latlon.lat;
lon = latlon.lon;
}
}
public LatLon(double lat, double lon)
{
this.lat = lat;
this.lon = lon;
}
public LatLon(String lat_string, String lon_string)
{
set(lat_string, lon_string);
}
public void set(double lat, double lon)
{
this.lat = lat;
this.lon = lon;
}
public void set(LatLon latlon)
{
if (latlon != null)
{
lat = latlon.lat;
lon = latlon.lon;
}
}
public void set(String lat_string, String lon_string)
{
lat = CoordUtil.StringToLat(lat_string);
lon = CoordUtil.StringToLon(lon_string);
}
public void normalize()
{
if (lat > 90)
lat = 90;
else if (lat < -90)
lat = -90;
if (lon > 180)
lon -= 360;
else if (lon < -180)
lon += 360;
}
/**
* Tests to see if the given object is the same lat/lon as this position.
* "Same" really means "very, very close," as defined by {@link Epsilon}.
*
* @return True if obj is a LatLon and is very close to our lat/lon position.
* False otherwise.
*/
@Override
public boolean equals(Object obj)
{
return (obj instanceof LatLon) && Epsilon.close(lat, ((LatLon)obj).lat) && Epsilon.close(lon, ((LatLon)obj).lon);
}
@Override
public String toString()
{
return CoordUtil.LatLonToString(this);
}
/**
* Returns the change in latitude
*/
static public double computeDeltaLat(double lat1, double lat2)
{
return lat2 - lat1;
}
/**
* Returns the change in longitude
*/
static public double computeDeltaLon(double lon1, double lon2)
{
double dLon;
dLon = lon2 - lon1;
if (Math.abs(dLon) < 180)
return dLon;
if (dLon > 180)
return dLon - 360;
else
return dLon + 360;
}
}

View File

@@ -0,0 +1,37 @@
package glum.coord;
public class Point2D
{
public double x;
public double y;
public Point2D () { }
public Point2D (Point2D pt)
{ if ( pt != null ) { x = pt.x; y = pt.y; } }
public Point2D (double x, double y)
{ this.x = x; this.y = y; }
public void set (double x, double y)
{ this.x = x; this.y = y; }
public void set (Point2D pt)
{ if ( pt != null ) { x = pt.x; y = pt.y; } }
public double distance (Point2D aPt)
{
if (aPt == null)
return 0;
return Math.sqrt((aPt.x - x)*(aPt.x - x) + (aPt.y - y)*(aPt.y - y));
}
@Override
public boolean equals (Object obj)
{
return (obj instanceof Point2D) &&
Epsilon.close (x, ((Point2D) obj).x) &&
Epsilon.close (y, ((Point2D) obj).y);
}
}

View File

@@ -0,0 +1,27 @@
package glum.coord;
public class Point2Di
{
public int x;
public int y;
public Point2Di () { }
public Point2Di (Point2Di pt)
{ if ( pt != null ) { x = pt.x; y = pt.y; } }
public Point2Di (int x, int y) { this.x = x; this.y = y; }
public void set (int x, int y) { this.x = x; this.y = y; }
public void set (Point2Di pt)
{ if ( pt != null ) { x = pt.x; y = pt.y; } }
@Override
public boolean equals (Object obj)
{
return (obj instanceof Point2Di) &&
x == ((Point2Di) obj).x &&
y == ((Point2Di) obj).y;
}
}

View File

@@ -0,0 +1,42 @@
package glum.coord;
/** A class for representing any 3-Dimensional vector, which could be
* a position, a velocity, or a rotation. No information about units
* is assumed or implied.
*/
public class Point3D
{
public double x;
public double y;
public double z;
public Point3D () { }
public Point3D (Point3D pt)
{ if ( pt != null ) { x = pt.x; y = pt.y; z = pt.z; } }
public Point3D (double x, double y, double z)
{ this.x = x; this.y = y; this.z = z; }
public void set (double x, double y, double z)
{ this.x = x; this.y = y; this.z = z; }
public void set (Point3D pt)
{ if ( pt != null ) { x = pt.x; y = pt.y; z = pt.z; } }
@Override
public boolean equals (Object obj)
{
return (obj instanceof Point3D) &&
Epsilon.close (x, ((Point3D) obj).x) &&
Epsilon.close (y, ((Point3D) obj).y) &&
Epsilon.close (z, ((Point3D) obj).z);
}
@Override
public String toString()
{
return new String("(" + x + ", " + y + ", " + z + ")");
}
}

View File

@@ -0,0 +1,47 @@
package glum.coord;
public class RngBrg
{
public double rng;
public double brg;
public RngBrg() { }
public RngBrg(RngBrg pt)
{
if ( pt != null )
{
rng = pt.rng;
brg = pt.brg;
}
}
public RngBrg (double rng, double brg)
{
this.rng = rng;
this.brg = brg;
}
public void set (double rng, double brg)
{
this.rng = rng;
this.brg = brg;
}
public void set (RngBrg pt)
{
if ( pt != null )
{
rng = pt.rng;
brg = pt.brg;
}
}
@Override
public boolean equals (Object obj)
{
return (obj instanceof RngBrg) &&
Epsilon.close (rng, ((RngBrg) obj).rng) &&
Epsilon.close (brg, ((RngBrg) obj).brg);
}
}

26
src/glum/coord/UV.java Normal file
View File

@@ -0,0 +1,26 @@
package glum.coord;
public class UV extends Point2D
{
public UV ()
{
x = 0;
y = 0;
}
public UV (Point2D pt)
{
if (pt != null)
{
x = pt.x;
y = pt.y;
}
}
public UV (double x, double y)
{
this.x = x;
this.y = y;
}
}

View File

@@ -0,0 +1,15 @@
package glum.database;
public abstract interface QueryItem<G1 extends Enum<?>>
{
/**
* Returns the corresponding value associated with aEnum
*/
public Object getValue(G1 aEnum);
/**
* Sets in the aObj as the corresponding value to aEnum
*/
public void setValue(G1 aEnum, Object aObj);
}

View File

@@ -0,0 +1,62 @@
package glum.database;
import java.util.Comparator;
public class QueryItemComparator<G1 extends QueryItem<G2>, G2 extends Enum<?>> implements Comparator<G1>
{
private G2 sortKey;
public QueryItemComparator(G2 aSortKey)
{
sortKey = aSortKey;
}
@SuppressWarnings("unchecked")
@Override
public int compare(G1 item1, G1 item2)
{
Comparable<Object> value1, value2;
value1 = (Comparable<Object>)item1.getValue(sortKey);
value2 = (Comparable<Object>)item2.getValue(sortKey);
if (value1 == null && value2 == null)
return 0;
if (value1 == null)
return -1;
if (value2 == null)
return 1;
return value1.compareTo(value2);
}
/**
* Utility method to create a QueryItemComparator by specifying the class and sort Enum.
* <P>
* This logic is here due to Java's horrible implementation off generics.
*/
public static <G3 extends QueryItem<G4>, G4 extends Enum<?>> Comparator<G3> spawn(Class<G3> aClass, G4 aEnum)
{
QueryItemComparator<G3, G4> retComparator;
retComparator = new QueryItemComparator<G3, G4>(aEnum);
return retComparator;
}
/**
* Utility method to create a QueryItemComparator by specifying just the Enum. Note this method can not be used in a
* argument to another method; instead use: {@link #spawn(Class, Enum)}
* <P>
* This logic is here due to Java's horrible implementation off generics.
*/
public static <G3 extends QueryItem<G4>, G4 extends Enum<?>> Comparator<G3> spawn(G4 aEnum)
{
QueryItemComparator<G3, G4> retComparator;
retComparator = new QueryItemComparator<G3, G4>(aEnum);
return retComparator;
}
}

View File

@@ -0,0 +1,120 @@
package glum.filter;
import glum.zio.ZinStream;
import glum.zio.ZoutStream;
import glum.zio.raw.ZioRaw;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
public abstract class EnumFilter<G1, G2 extends Enum<?>> implements ZioRaw, Filter<G1>
{
// Static config vars
private Map<Integer, Enum<?>> fullMap;
// State vars
private Set<Enum<?>> validSet;
private boolean isEnabled;
public EnumFilter(Class<? extends Enum<?>> enumClass)
{
fullMap = Maps.newLinkedHashMap();
for (Enum<?> aEnum : enumClass.getEnumConstants())
fullMap.put(aEnum.ordinal(), aEnum);
validSet = Sets.newLinkedHashSet();
isEnabled = false;
}
/**
* Returns true if the filter is active.
*/
public boolean getIsEnabled()
{
return isEnabled;
}
/**
* Returns the list of valid enums for this filter.
*/
public List<Enum<?>> getSelectedItems()
{
return Lists.newArrayList(validSet);
}
/**
* Sets this filter to match aFilter
*/
public void set(EnumFilter<G1, G2> aFilter)
{
validSet = Sets.newLinkedHashSet(aFilter.validSet);
isEnabled = aFilter.getIsEnabled();
}
/**
* Sets whether the filter is active.
*/
public void setIsEnabled(boolean aBool)
{
isEnabled = aBool;
}
/**
* Sets the list of valid enums for this filter.
*/
public void setSetSelectedItems(List<Enum<?>> selectedItems)
{
validSet.clear();
validSet.addAll(selectedItems);
}
@Override
public void zioReadRaw(ZinStream aStream) throws IOException
{
int numItems;
aStream.readVersion(0);
// Read the payload
isEnabled = aStream.readBool();
validSet.clear();
numItems = aStream.readInt();
for (int c1 = 0; c1 < numItems; c1++)
validSet.add(fullMap.get(aStream.readInt()));
}
@Override
public void zioWriteRaw(ZoutStream aStream) throws IOException
{
int numItems;
aStream.writeVersion(0);
aStream.writeBool(isEnabled);
numItems = validSet.size();
aStream.writeInt(numItems);
for (Enum<?> aEnum : validSet)
aStream.writeInt(aEnum.ordinal());
}
/**
* Utility method that returns whether aValue is within the constraints
* specified by this filter.
*/
protected boolean testIsValid(G2 aEnum)
{
if (isEnabled == false)
return true;
return validSet.contains(aEnum);
}
}

View File

@@ -0,0 +1,12 @@
package glum.filter;
import glum.zio.raw.ZioRaw;
public interface Filter<G1> extends ZioRaw
{
/**
* Method that returns true if aItem passes this filter
*/
public boolean isValid(G1 aItem);
}

View File

@@ -0,0 +1,100 @@
package glum.filter;
import java.util.List;
import javax.swing.JCheckBox;
import com.google.common.collect.Lists;
import glum.gui.component.GList;
import glum.gui.component.GNumberField;
public class FilterUtil
{
/**
* Utility method to return a sublist of itemList based on aFilter.
*/
public static <G1> List<G1> applyFilter(List<G1> itemList, Filter<G1> aFilter)
{
List<G1> retList;
retList = Lists.newArrayList();
for (G1 aItem : itemList)
{
if (aFilter.isValid(aItem) == true)
retList.add(aItem);
}
return retList;
}
/**
* Utility method to synchronize the specified filter with the associated GUI controls.
*/
public static void getEnumFilter(EnumFilter<?, Enum<?>> aFilter, JCheckBox mainCB, GList<Enum<?>> mainList)
{
aFilter.setIsEnabled(mainCB.isSelected());
aFilter.setSetSelectedItems(mainList.getSelectedItems());
}
/**
* Utility method to synchronize the associated GUI controls with the specified filter.
*/
public static void setEnumFilter(EnumFilter<?, Enum<?>> aFilter, JCheckBox mainCB, GList<Enum<?>> mainList)
{
mainCB.setSelected(aFilter.getIsEnabled());
mainList.setSelectedItems(aFilter.getSelectedItems());
}
/**
* Utility method to synchronize the specified filter with the associated GUI controls.
*/
public static void getRangeFilter(RangeFilter<?> aFilter, JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, GNumberField minNF, GNumberField maxNF)
{
aFilter.setIsEnabled(mainCB.isSelected());
aFilter.setUseMin(minCB.isSelected());
aFilter.setUseMax(maxCB.isSelected());
aFilter.setMinValue(minNF.getValue());
aFilter.setMaxValue(maxNF.getValue());
}
/**
* Utility method to synchronize the associated GUI controls with the specified filter.
*/
public static void setRangeGui(RangeFilter<?> aFilter, JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, GNumberField minNF, GNumberField maxNF)
{
mainCB.setSelected(aFilter.getIsEnabled());
minCB.setSelected(aFilter.getUseMin());
maxCB.setSelected(aFilter.getUseMax());
minNF.setValue(aFilter.getMinValue());
maxNF.setValue(aFilter.getMaxValue());
}
/**
* Utility method to keep the various GUI components associated with an EnumFilter synchronized.
* The mainList will be enabled/disabled based on the selection state of mainCB.
*/
public static void syncEnumGui(JCheckBox mainCB, GList<Enum<?>> mainList)
{
boolean isEnabled;
isEnabled = mainCB.isSelected();
mainList.setEnabled(isEnabled);
}
/**
* Utility method to keep the various GUI components associated with an RangeFilter synchronized.
* Gui components will be enabled/disabled based on the various check boxes.
*/
public static void syncRangeGui(JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, GNumberField minNF, GNumberField maxNF)
{
boolean isEnabled;
isEnabled = mainCB.isSelected();
minCB.setEnabled(isEnabled);
maxCB.setEnabled(isEnabled);
minNF.setEnabled(isEnabled & minCB.isSelected());
maxNF.setEnabled(isEnabled & maxCB.isSelected());
}
}

View File

@@ -0,0 +1,32 @@
package glum.filter;
import glum.zio.ZinStream;
import glum.zio.ZoutStream;
import java.io.IOException;
/**
* A Filter which does not filter anything. Thus the method isValid() always returns true.
*/
public class NullFilter<G1> implements Filter<G1>
{
@Override
public boolean isValid(G1 aItem)
{
return true;
}
@Override
public void zioReadRaw(ZinStream aStream) throws IOException
{
; // Nothing to do
}
@Override
public void zioWriteRaw(ZoutStream aStream) throws IOException
{
; // Nothing to do
}
}

View File

@@ -0,0 +1,122 @@
package glum.filter;
import glum.zio.ZinStream;
import glum.zio.ZoutStream;
import glum.zio.raw.ZioRaw;
import java.io.IOException;
/**
* Abstract filter which is used to filter a single value between the specified min/max ranges. The only code to write
* is the isValid() method and to call the appropriate Constructor. In the isValid() method, you should delegate filter
* logic to the method testIsValid() with the quantity of interest, and return the result from the method call.
*/
public abstract class RangeFilter<G1> implements ZioRaw, Filter<G1>
{
private boolean isEnabled;
private boolean useMin, useMax;
private double minValue, maxValue;
/**
* @param aBinCode
* Unique identifier used during serialization. The value specified here should not collide with any other
* codes for which there is serialization.
*/
public RangeFilter()
{
isEnabled = false;
useMin = false;
useMax = false;
minValue = 0;
maxValue = 0;
}
/**
* Accessor methods
*/
// @formatter:off
public boolean getIsEnabled() { return isEnabled; }
public boolean getUseMin() { return useMin; }
public boolean getUseMax() { return useMax; }
public double getMinValue() { return minValue; }
public double getMaxValue() { return maxValue; }
public void setIsEnabled(boolean aBool) { isEnabled = aBool; }
public void setUseMin(boolean aBool) { useMin = aBool; }
public void setUseMax(boolean aBool) { useMax = aBool; }
public void setMinValue(double aValue) { minValue = aValue; }
public void setMaxValue(double aValue) { maxValue = aValue; }
// @formatter:on
/**
* Sets this filter to match aFilter
*/
public void set(RangeFilter<G1> aFilter)
{
isEnabled = aFilter.getIsEnabled();
useMin = aFilter.getUseMin();
useMax = aFilter.getUseMax();
minValue = aFilter.getMinValue();
maxValue = aFilter.getMaxValue();
}
@Override
public void zioReadRaw(ZinStream aStream) throws IOException
{
byte bSwitch;
aStream.readVersion(0);
bSwitch = aStream.readByte();
isEnabled = (bSwitch & 0x1) != 0;
useMin = (bSwitch & 0x2) != 0;
useMax = (bSwitch & 0x4) != 0;
// isEnabled = aStream.readBoolean();
// useMin = aStream.readBoolean();
// useMax = aStream.readBoolean();
minValue = aStream.readDouble();
maxValue = aStream.readDouble();
}
@Override
public void zioWriteRaw(ZoutStream aStream) throws IOException
{
byte bSwitch;
aStream.writeVersion(0);
bSwitch = 0;
if (isEnabled == true)
bSwitch |= 0x1;
if (useMin == true)
bSwitch |= 0x2;
if (useMax == true)
bSwitch |= 0x4;
aStream.writeByte(bSwitch);
// aStream.writeBoolean(isEnabled);
// aStream.writeBoolean(useMin);
// aStream.writeBoolean(useMax);
aStream.writeDouble(minValue);
aStream.writeDouble(maxValue);
}
/**
* Utility method that returns whether aValue is within the constraints specified by this filter.
*/
protected boolean testIsValid(double aValue)
{
if (isEnabled == false)
return true;
if (useMin == true && aValue < minValue)
return false;
if (useMax == true && aValue > maxValue)
return false;
return true;
}
}

View File

@@ -0,0 +1,66 @@
package glum.gui;
import javax.swing.*;
public class FocusUtil
{
/**
* Adds a keyboard shortcut to bind aKeyStroke for the specified action.
* This keyboard shortcut will be executed whenever a child component of aComp has the focus.
*/
public static void addAncestorKeyBinding(JComponent aComp, String aKeyStroke, Action aAction)
{
addAncestorKeyBinding(aComp, convertStringToKeyStroke(aKeyStroke), aAction);
}
public static void addAncestorKeyBinding(JComponent aComp, KeyStroke aKeyStroke, Action aAction)
{
aComp.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(aKeyStroke, aAction);
aComp.getActionMap().put(aAction, aAction);
}
/**
* Adds a keyboard shortcut to bind aKeyStroke for the specified action.
* This keyboard shortcut will be executed aComp has the focus.
*/
public static void addFocusKeyBinding(JComponent aComp, String aKeyStroke, Action aAction)
{
addFocusKeyBinding(aComp, convertStringToKeyStroke(aKeyStroke), aAction);
}
public static void addFocusKeyBinding(JComponent aComp, KeyStroke aKeyStroke, Action aAction)
{
aComp.getInputMap(JComponent.WHEN_FOCUSED).put(aKeyStroke, aAction);
aComp.getActionMap().put(aAction, aAction);
}
/**
* Adds a keyboard shortcut to bind aKeyStroke for the specified action.
* This keyboard shortcut will be executed whenever the parent Window has the focus.
*/
public static void addWindowKeyBinding(JComponent aComp, String aKeyStroke, Action aAction)
{
addWindowKeyBinding(aComp, convertStringToKeyStroke(aKeyStroke), aAction);
}
public static void addWindowKeyBinding(JComponent aComp, KeyStroke aKeyStroke, Action aAction)
{
aComp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(aKeyStroke, aAction);
aComp.getActionMap().put(aAction, aAction);
}
/**
* Converts a String to a valid KeyStroke.
*/
public static KeyStroke convertStringToKeyStroke(String aStr)
{
KeyStroke aKeyStroke;
aKeyStroke = KeyStroke.getKeyStroke(aStr);
if (aKeyStroke == null)
throw new RuntimeException("Failed to convert: [" + aStr + "] to a keystroke.");
return aKeyStroke;
}
}

688
src/glum/gui/GuiUtil.java Normal file
View File

@@ -0,0 +1,688 @@
package glum.gui;
import glum.gui.icon.IconUtil;
import glum.reflect.Function;
import glum.reflect.FunctionRunnable;
import java.awt.*;
import java.awt.event.ActionListener;
import java.util.Collection;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.event.ChangeListener;
public class GuiUtil
{
/**
* Method to examine the labels and returns the size of the largest button.
*/
public static Dimension computePreferredJButtonSize(String... labels)
{
Dimension tmpDim, maxDim;
JButton tmpB;
maxDim = null;
tmpB = new JButton("");
// Find the label that requires the largest dimension
for (String aStr : labels)
{
if (aStr == null)
aStr = "";
tmpB.setText(aStr);
tmpDim = tmpB.getPreferredSize();
if (maxDim == null || maxDim.getWidth() < tmpDim.getWidth())
maxDim = tmpDim;
}
return maxDim;
}
/**
* Creates a JButton with the specified settings
*/
public static JButton createJButton(String aTitle, ActionListener aActionListener)
{
JButton tmpB;
tmpB = new JButton(aTitle);
tmpB.addActionListener(aActionListener);
return tmpB;
}
public static JButton createJButton(String aTitle, ActionListener aActionListener, Font aFont)
{
JButton tmpB;
tmpB = new JButton(aTitle);
tmpB.addActionListener(aActionListener);
if (aFont != null)
tmpB.setFont(aFont);
return tmpB;
}
public static JButton createJButton(String aTitle, ActionListener aActionListener, Dimension aDimension)
{
JButton tmpB;
tmpB = new JButton(aTitle);
tmpB.addActionListener(aActionListener);
// Force a dimension
if (aDimension != null)
{
tmpB.setMinimumSize(aDimension);
tmpB.setMaximumSize(aDimension);
tmpB.setPreferredSize(aDimension);
}
return tmpB;
}
public static JButton createJButton(Icon aIcon, ActionListener aActionListener)
{
return createJButton(aIcon, aActionListener, null);
}
public static JButton createJButton(Icon aIcon, ActionListener aActionListener, String aToolTip)
{
JButton tmpB;
tmpB = new JButton(aIcon);
tmpB.addActionListener(aActionListener);
if (aToolTip != null)
tmpB.setToolTipText(aToolTip);
return tmpB;
}
/**
* Creates the JButton with the specified resource icon.
*/
public static JButton createJButtonViaResource(ActionListener aHandler, String aResourcePath)
{
return createJButtonViaResource(aHandler, aResourcePath, null);
}
public static JButton createJButtonViaResource(ActionListener aHandler, String aResourcePath, String aToolTip)
{
JButton tmpB;
tmpB = new JButton(IconUtil.loadIcon(aResourcePath));
tmpB.addActionListener(aHandler);
if (aToolTip != null)
tmpB.setToolTipText(aToolTip);
return tmpB;
}
/**
* Creates a JCheckBox with the specified settings
*/
public static JCheckBox createJCheckBox(String aTitle, ActionListener aActionListener)
{
return createJCheckBox(aTitle, aActionListener, null);
}
public static JCheckBox createJCheckBox(String aTitle, ActionListener aActionListener, Font aFont)
{
JCheckBox tmpCB;
tmpCB = new JCheckBox(aTitle);
tmpCB.addActionListener(aActionListener);
if (aFont != null)
tmpCB.setFont(aFont);
return tmpCB;
}
/**
* Creates a JComboBox with the specified settings
*/
public static JComboBox createJComboBox(ActionListener aListener, Font aFont, Object... itemArr)
{
JComboBox tmpBox;
tmpBox = new JComboBox();
for (Object aItem : itemArr)
tmpBox.addItem(aItem);
if (aFont != null)
tmpBox.setFont(aFont);
tmpBox.addActionListener(aListener);
return tmpBox;
}
// /**
// * Creates a JComboBox with the specified settings
// */
// public static JComboBox createJComboBox(ActionListener aListener, Object... itemArr)
// {
// return createJComboBox(aListener, null, itemArr);
// }
/**
* Creates a JLabel with the specified settings
*/
public static JLabel createJLabel(String aTitle, Font aFont)
{
return createJLabel(aTitle, JLabel.LEADING, aFont);
}
public static JLabel createJLabel(String aTitle, int aAlignment, Font aFont)
{
JLabel tmpL;
tmpL = new JLabel(aTitle, aAlignment);
if (aFont != null)
tmpL.setFont(aFont);
return tmpL;
}
/**
* Creates the JRadioButton with the following attributes.
*/
public static JRadioButton createJRadioButton(String aLabel, ActionListener aListener)
{
return createJRadioButton(aLabel, aListener, null);
}
public static JRadioButton createJRadioButton(String aLabel, ActionListener aListener, Font aFont)
{
JRadioButton tmpRB;
tmpRB = new JRadioButton(aLabel);
tmpRB.addActionListener(aListener);
if (aFont != null)
tmpRB.setFont(aFont);
return tmpRB;
}
/**
* Utility method for creating a visual thin divider
*
* Typically added to MigLayout (or like manager) with: add(aComp, "growx,h 4!,span,wrap");
*/
public static JPanel createDivider()
{
JPanel tmpPanel;
tmpPanel = new JPanel();
tmpPanel.setBorder(new BevelBorder(BevelBorder.RAISED));
return tmpPanel;
}
/**
* Creates an uneditable JTextArea with no border, non-opaque, line wrap enabled and word wrap enabled.
*/
public static JTextArea createUneditableTextArea(int rows, int cols)
{
JTextArea tmpTA;
tmpTA = new JTextArea("", rows, cols);
tmpTA.setEditable(false);
tmpTA.setOpaque(false);
tmpTA.setLineWrap(true);
tmpTA.setWrapStyleWord(true);
return tmpTA;
}
/**
* Creates an uneditable JTextPane configured with non-opaque and content type of text/html.
*/
public static JTextPane createUneditableTextPane()
{
JTextPane tmpTP;
tmpTP = new JTextPane();
tmpTP.setEditable(false);
tmpTP.setOpaque(false);
tmpTP.setContentType("text/html");
return tmpTP;
}
/**
* Creates an uneditable JTextField
*/
public static JTextField createUneditableTextField(String aTitle)
{
JTextField tmpTF;
tmpTF = new JTextField(aTitle);
tmpTF.setBorder(null);
tmpTF.setEditable(false);
tmpTF.setOpaque(false);
return tmpTF;
}
/**
* Utility method to link a set of radio buttons together
*/
public static void linkRadioButtons(JRadioButton... buttonArr)
{
ButtonGroup tmpGroup;
tmpGroup = new ButtonGroup();
for (JRadioButton aItem : buttonArr)
tmpGroup.add(aItem);
}
/**
* Reads a boolean from a string with out throwing a exception
*/
public static boolean readBoolean(String aStr, boolean aVal)
{
if (aStr == null)
return aVal;
// Special case for 1 char strings
if (aStr.length() == 1)
{
char aChar;
aChar = aStr.charAt(0);
if (aChar == 'T' || aChar == 't' || aChar == '1')
return true;
return false;
}
try
{
return Boolean.valueOf(aStr).booleanValue();
}
catch(Exception e)
{
return aVal;
}
}
/**
* Reads a double from a string with out throwing a exception. Note aStr can have an number of separators: comma
* chars
*/
public static double readDouble(String aStr, double aVal)
{
try
{
aStr = aStr.replace(",", "");
return Double.parseDouble(aStr);
}
catch(Exception e)
{
return aVal;
}
}
/**
* Reads a float from a string with out throwing a exception. Note aStr can have an number of separators: comma chars
*/
public static float readFloat(String aStr, float aVal)
{
try
{
aStr = aStr.replace(",", "");
return Float.parseFloat(aStr);
}
catch(Exception e)
{
return aVal;
}
}
/**
* Reads an int from a string without throwing a exception Note aStr can have an number of separators: comma chars
*/
public static int readInt(String aStr, int aVal)
{
try
{
aStr = aStr.replace(",", "");
return Integer.parseInt(aStr);
}
catch(Exception e)
{
return aVal;
}
}
/**
* Reads a long from a string without throwing a exception Note aStr can have an number of separators: comma chars
*/
public static long readLong(String aStr, long aVal)
{
try
{
aStr = aStr.replace(",", "");
return Long.parseLong(aStr);
}
catch(Exception e)
{
return aVal;
}
}
/**
* Reads an int (forced to fit within a range) from a string with out throwing a exception
*/
public static int readRangeInt(String aStr, int minVal, int maxVal, int aVal)
{
int aInt;
try
{
aInt = Integer.parseInt(aStr);
if (aInt < minVal)
aInt = minVal;
else if (aInt > maxVal)
aInt = maxVal;
return aInt;
}
catch(Exception e)
{
return aVal;
}
}
/**
* Utility method to locate the RootPaneContainer for the specified Component
*/
public static RootPaneContainer getRootPaneContainer(Component aComponent)
{
Container aParent;
// Check to see if the Component is an actual RootPaneContainer
if (aComponent instanceof RootPaneContainer)
return (RootPaneContainer)aComponent;
// Attempt to locate the RootPaneContainer (through our stack)
aParent = aComponent.getParent();
while (aParent != null && (aParent instanceof RootPaneContainer) == false)
aParent = aParent.getParent();
// Bail if we failed to find the RootPaneContainer
if (aParent instanceof RootPaneContainer == false)
throw new RuntimeException("No valid (grand)parent associated with GlassPane.");
return (RootPaneContainer)aParent;
}
/**
* Utility method to locate all of the subcomponents contained in aContainer which are an instance of searchClass
*/
public static void locateAllSubComponents(Container aContainer, Collection<Component> itemList, Class<?>... searchClassArr)
{
for (Component aComponent : aContainer.getComponents())
{
for (Class<?> aClass : searchClassArr)
{
if (aClass.isInstance(aComponent) == true)
{
itemList.add(aComponent);
break;
}
}
if (aComponent instanceof Container)
locateAllSubComponents((Container)aComponent, itemList, searchClassArr);
}
}
/**
* Utility method to force a Component to act as modal while it is visible Source:
* http://stackoverflow.com/questions/804023/how-do-i-simulate-a-modal-dialog-from-within-an-applet
*/
public static void modalWhileVisible(Component aComponent)
{
// Bail if not called from the EventDispatchThread
if (SwingUtilities.isEventDispatchThread() == false)
throw new RuntimeException("Visibility for modal components must be changed via the Event thread.");
synchronized(aComponent)
{
try
{
EventQueue theQueue = aComponent.getToolkit().getSystemEventQueue();
while (aComponent.isVisible())
{
//System.out.println("About to dispatch event... component.isVisible():" + aComponent.isVisible());
AWTEvent event = theQueue.getNextEvent();
Object source = event.getSource();
if (event instanceof ActiveEvent)
{
((ActiveEvent)event).dispatch();
}
else if (source instanceof Component)
{
((Component)source).dispatchEvent(event);
}
else if (source instanceof MenuComponent)
{
((MenuComponent)source).dispatchEvent(event);
}
else
{
System.err.println("Unable to dispatch: " + event);
}
}
}
catch(InterruptedException ignored)
{
}
}
}
/**
* Utility to call a specific method (methodName) with specific parameters (aParamArr) on aComp and on all of the
* child subcomponents. The method will only be called if the components are an instance of refMatchClass.
* <P>
* This is useful so that a component and all of its children can be disabled, hidden, etc<BR>
* Example: GuiUtil.callMethod(myPanel, setEnabled, false);
* <P>
* Be aware, this is rather expensive, so do not call in time critical applications.
*/
public static void callMethod(Component aComp, Class<?> refMatchClass, String aMethodName, Object... aParamArr)
{
Class<?>[] typeArr;
// Construct the associated type array
typeArr = new Class[0];
if (aParamArr.length > 0)
{
// Determine the types of the specified arguments
typeArr = new Class[aParamArr.length];
for (int c1 = 0; c1 < typeArr.length; c1++)
{
typeArr[c1] = null;
if (aParamArr[c1] != null)
typeArr[c1] = aParamArr[c1].getClass();
}
}
// Call the helper version
callMethodHelper(aComp, refMatchClass, aMethodName, typeArr, aParamArr);
}
/**
* Helper method to callMethod
*/
private static void callMethodHelper(Component aComp, Class<?> refMatchClass, String aMethodName, Class<?>[] aTypeArr, Object[] aParamArr)
{
Component[] subCompArr;
Function aFunction;
// Locate and call the actual method
if (refMatchClass.isInstance(aComp) == true)
{
try
{
aFunction = new Function(aComp, aMethodName, aTypeArr);
aFunction.invoke(aParamArr);
}
catch(NoSuchMethodException aExp1)
{
throw new RuntimeException("Failed to locate valid function. Method:" + aMethodName, aExp1);
}
catch(Exception aExp2)
{
throw new RuntimeException("Failed to execute function. Method:" + aMethodName, aExp2);
}
}
// Bail if we do not have subcomponents
if (aComp instanceof Container == false)
return;
// Recurse down our children
subCompArr = ((Container)aComp).getComponents();
for (Component aSubComp : subCompArr)
callMethodHelper(aSubComp, refMatchClass, aMethodName, aTypeArr, aParamArr);
}
/**
* Utility method to set all subcomponents to the specified enabled mode.
*/
// TODO: Phase this method out, replace with callMethod()
public static void setEnabled(Component aComp, boolean aBool)
{
Component[] subCompArr;
aComp.setEnabled(aBool);
if (aComp instanceof Container == false)
return;
subCompArr = ((Container)aComp).getComponents();
for (Component aSubComp : subCompArr)
GuiUtil.setEnabled(aSubComp, aBool);
}
/**
* Utility method to set the enabled switch on all of the specified components.
*/
public static void setEnabled(boolean aBool, Component... componentArr)
{
for (Component aComp : componentArr)
aComp.setEnabled(aBool);
}
/**
* Utility method that will call doClick() on the selected RadioButton
*/
public static void doClickSelectedButton(JRadioButton... buttonArr)
{
for (int c1 = 0; c1 < buttonArr.length; c1++)
{
if (buttonArr[c1].isSelected() == true)
buttonArr[c1].doClick();
}
}
/**
* Utility method that takes up to 8 buttons and converts all of their selection states to a single byte.
*/
public static byte getSelectionStateAsByte(AbstractButton... buttonArr)
{
byte retByte;
if (buttonArr.length > 8)
throw new RuntimeException("Improper API call. Max of 8 buttons supported. Passed: " + buttonArr.length);
retByte = 0;
for (int c1 = 0; c1 < buttonArr.length; c1++)
{
if (buttonArr[c1].isSelected() == true)
retByte |= 1 << c1;
}
return retByte;
}
/**
* Utility method that takes up to 8 buttons and configures the selection state of the buttons to match the bit
* pattern of aByte.
*/
public static void setSelectionState(byte aByte, AbstractButton... buttonArr)
{
boolean aBool;
if (buttonArr.length > 8)
throw new RuntimeException("Improper API call. Max of 8 buttons supported. Passed: " + buttonArr.length);
for (int c1 = 0; c1 < buttonArr.length; c1++)
{
aBool = false;
if (((0x01 << c1) & aByte) != 0)
aBool = true;
buttonArr[c1].setSelected(aBool);
}
}
/**
* Utility method to update a JSlider without triggering notifications to its registered listeners.
*/
public static void updateSlider(JSlider aSlider, int aVal)
{
ChangeListener[] tmpArr;
tmpArr = aSlider.getChangeListeners();
for (ChangeListener aListener : tmpArr)
aSlider.removeChangeListener(aListener);
aSlider.setValue(aVal);
for (ChangeListener aListener : tmpArr)
aSlider.addChangeListener(aListener);
}
/**
* Utility method that checks to ensure the current thread is running on the ATW thread. If it is NOT then the
* specified function will be posted so that it is called on the AWT thread. If it is running on the AWT thread then
* nothing will happen and this method will return false.
* <P>
* Typically this utility method is called at the start of a function to ensure it is on the AWT thread, and if not
* then schedule the function onto the AWT thread. Thus it is strongly advisable that if this method returns true the
* caller should immediately exit.
* <P>
* <B>Typical usage within a method:</B>
*
* <PRE>
* public void actionPerformed(aEvent)
* {
* // Ensure this method is run on the AWT thread
* if (redispatchOnAwtIfNeeded(this, "actionPerformed", aEvent) = true)
* return;
*
* // Do normal work ...
* }
* </PRE>
*/
public static boolean redispatchOnAwtIfNeeded(Object aObj, String methodName, Object... aArgArr)
{
FunctionRunnable aFunctionRunnable;
// Do nothing if this is the AWT thread
if (SwingUtilities.isEventDispatchThread() == true)
return false;
aFunctionRunnable = new FunctionRunnable(aObj, methodName, aArgArr);
SwingUtilities.invokeLater(aFunctionRunnable);
return true;
}
}

View File

@@ -0,0 +1,22 @@
package glum.gui.action;
import java.awt.event.*;
import javax.swing.*;
public class ClickAction extends AbstractAction
{
// State vars
protected AbstractButton target;
public ClickAction(AbstractButton aTarget)
{
target = aTarget;
}
@Override
public void actionPerformed(ActionEvent e)
{
target.doClick();
}
}

View File

@@ -0,0 +1,23 @@
package glum.gui.action;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class MakeVisibleAction extends AbstractAction
{
// State vars
protected Component target;
public MakeVisibleAction(Component aTarget)
{
target = aTarget;
}
@Override
public void actionPerformed(ActionEvent e)
{
target.setVisible(true);
}
}

View File

@@ -0,0 +1,171 @@
package glum.gui.component;
import glum.gui.component.model.GComboBoxModel;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.JComboBox;
import javax.swing.ListCellRenderer;
public class GComboBox<G1> extends JComboBox
{
// State vars
protected GComboBoxModel<G1> itemModel;
public GComboBox()
{
itemModel = new GComboBoxModel<G1>(new LinkedList<G1>());
setModel(itemModel);
}
public GComboBox(ActionListener aListener, ListCellRenderer aRenderer)
{
this();
addActionListener(aListener);
setRenderer(aRenderer);
}
public GComboBox(ActionListener aListener)
{
this();
addActionListener(aListener);
}
public GComboBox(ActionListener aListener, List<G1> aItemList)
{
itemModel = new GComboBoxModel<G1>(aItemList);
setModel(itemModel);
addActionListener(aListener);
}
public GComboBox(ActionListener aListener, G1... aItemArr)
{
itemModel = new GComboBoxModel<G1>(aItemArr);
setModel(itemModel);
addActionListener(aListener);
}
/**
* Returns the list of all items stored in the GComboBox
*/
public ArrayList<G1> getAllItems()
{
return itemModel.getAllItems();
}
/**
* Returns the items that is currently selected.
*/
public G1 getChosenItem()
{
return itemModel.getSelectedItem();
}
/**
* Sets in the currently selected item. This method will not trigger an ActionEvent.
* @see JComboBox#setSelectedItem
*/
public void setChosenItem(G1 aItem)
{
ActionListener[] listenerArr;
listenerArr = getActionListeners();
for (ActionListener aListener : listenerArr)
removeActionListener(aListener);
itemModel.setSelectedItem(aItem);
for (ActionListener aListener : listenerArr)
addActionListener(aListener);
}
/**
* Note aItem must be of the generified type. This method will not trigger an ActionEvent.
*/
@Override @SuppressWarnings("unchecked")
public void addItem(Object aItem)
{
ActionListener[] listenerArr;
listenerArr = getActionListeners();
for (ActionListener aListener : listenerArr)
removeActionListener(aListener);
itemModel.addItem((G1)aItem);
for (ActionListener aListener : listenerArr)
addActionListener(aListener);
}
/**
* Note aItem must be of the generified type. This method will not trigger an ActionEvent.
*/
public void addItems(G1... aItemArr)
{
ActionListener[] listenerArr;
listenerArr = getActionListeners();
for (ActionListener aListener : listenerArr)
removeActionListener(aListener);
for (G1 aItem : aItemArr)
itemModel.addItem(aItem);
for (ActionListener aListener : listenerArr)
addActionListener(aListener);
}
/**
* Note aItem must be of the generified type. This method will not trigger an ActionEvent.
*/
@Override @SuppressWarnings("unchecked")
public void removeItem(Object aItem)
{
ActionListener[] listenerArr;
listenerArr = getActionListeners();
for (ActionListener aListener : listenerArr)
removeActionListener(aListener);
itemModel.removeItem((G1)aItem);
for (ActionListener aListener : listenerArr)
addActionListener(aListener);
}
/**
* Removes all of the items from the model. This method will not trigger an ActionEvent.
*/
@Override
public void removeAllItems()
{
ActionListener[] listenerArr;
listenerArr = getActionListeners();
for (ActionListener aListener : listenerArr)
removeActionListener(aListener);
itemModel.removeAllItems();
for (ActionListener aListener : listenerArr)
addActionListener(aListener);
}
// Note you cannot do the below as internal methods within JComboBox make calls to
// the methods below.
// @Override
// public Object getSelectedItem()
// {
// throw new RuntimeException("Unsupported operation. Call getChosenItem()");
// }
//
// @Override
// public void setSelectedItem(Object aObj)
// {
// throw new RuntimeException("Unsupported operation. Call setChosenItem()");
// }
}

View File

@@ -0,0 +1,49 @@
package glum.gui.component;
import glum.gui.panel.generic.GenericCodes;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.JComponent;
import com.google.common.collect.Lists;
public class GComponent extends JComponent implements GenericCodes
{
// State vars
protected List<ActionListener> myListeners;
public GComponent()
{
super();
myListeners = Lists.newLinkedList();
}
/**
* Add an ActionListener to this GPanel
*/
public void addActionListener(ActionListener aListener)
{
myListeners.add(aListener);
}
/**
* Remove an ActionListener to this GPanel
*/
public void removeActionListener(ActionListener aListener)
{
myListeners.remove(aListener);
}
/**
* Send out notification to all of the ActionListeners
*/
public void notifyListeners(Object aSource, int aId, String aCommand)
{
for (ActionListener aListener : myListeners)
aListener.actionPerformed(new ActionEvent(aSource, aId, aCommand));
}
}

View File

@@ -0,0 +1,93 @@
package glum.gui.component;
import java.awt.Dimension;
import java.awt.Font;
import glum.unit.UnitListener;
import glum.unit.UnitProvider;
import javax.swing.JLabel;
public class GFancyLabel extends JLabel implements UnitListener
{
// State vars
protected UnitProvider[] unitProviderArr;
protected String[] formatStrArr;
protected Object[] refValueArr;
/**
* Fancy JLabel which provides auto formatting of objects. The constructor
* is provided with a formatStr which has unit place holders specified by "%u"
* There must be a corresponding UnitProvider for each occurrence of "%u". This
* is provided via the variable arguments of UnitProvider.
*/
public GFancyLabel(String formatStr, UnitProvider... aUnitProviderArr)
{
this(null, formatStr, aUnitProviderArr);
}
/**
* Fancy JLabel which provides auto formatting of objects. The constructor
* is provided with a formatStr which has unit place holders specified by "%u"
* There must be a corresponding UnitProvider for each occurrence of "%u". This
* is provided via the variable arguments of UnitProvider.
*/
public GFancyLabel(Font aFont, String aFormatStr, UnitProvider... aUnitProviderArr)
{
super();
if (aFont != null)
setFont(aFont);
formatStrArr = aFormatStr.split("%u", -1);
unitProviderArr = aUnitProviderArr;
for (UnitProvider aUnitProvider : unitProviderArr)
aUnitProvider.addListener(this);
// Insanity check
if (unitProviderArr.length != formatStrArr.length - 1)
throw new RuntimeException("Num place holders: " + (formatStrArr.length - 1) + " Num units: " + unitProviderArr.length);
refValueArr = new Object[unitProviderArr.length];
for (int c1 = 0; c1 < unitProviderArr.length; c1++)
refValueArr[c1] = null;
setMinimumSize(new Dimension(0, 0));
}
@Override
public void unitChanged(UnitProvider aProvider, String aKey)
{
setValues(refValueArr);
}
/**
* Method to set in the set of values which will be formatted with the associated UnitProviders
* which were specified via the constructor.
*/
public void setValues(Object... aValueArr)
{
String aStr;
// Ensure the number of objects matches the number of units
if (unitProviderArr.length != aValueArr.length)
throw new RuntimeException("Inproper number of arguments. Expected: " + unitProviderArr.length + " Recieved:" + aValueArr.length);
for (int c1 = 0; c1 < aValueArr.length; c1++)
refValueArr[c1] = aValueArr[c1];
aStr = "";
for (int c1 = 0; c1 < aValueArr.length; c1++)
{
aStr += formatStrArr[c1];
aStr += unitProviderArr[c1].getUnit().getString(aValueArr[c1], false);
}
if (formatStrArr.length > aValueArr.length)
aStr += formatStrArr[formatStrArr.length - 1];
setText(aStr);
}
}

View File

@@ -0,0 +1,82 @@
package glum.gui.component;
import java.awt.Dimension;
import java.awt.Font;
import glum.unit.ConstUnitProvider;
import glum.unit.Unit;
import glum.unit.UnitListener;
import glum.unit.UnitProvider;
import javax.swing.JLabel;
public class GLabel extends JLabel implements UnitListener
{
// State vars
protected UnitProvider refUnitProvider;
protected Object refValue;
protected boolean showLabel;
public GLabel(Font aFont)
{
this(null, aFont, false);
}
public GLabel(Unit aUnit, Font aFont)
{
this(new ConstUnitProvider(aUnit), aFont, false);
}
public GLabel(UnitProvider aUnitProvider, Font aFont)
{
this(aUnitProvider, aFont, false);
}
public GLabel(UnitProvider aUnitProvider, Font aFont, boolean aShowLabel)
{
super();
refUnitProvider = aUnitProvider;
if (refUnitProvider != null)
refUnitProvider.addListener(this);
refValue = null;
if (aFont != null)
setFont(aFont);
showLabel = aShowLabel;
setMinimumSize(new Dimension(0, 0));
}
@Override
public void unitChanged(UnitProvider aProvider, String aKey)
{
setValue(refValue);
}
/**
* Method to set in the value which will be formatted with the associated unit.
*/
public void setValue(Object aValue)
{
String aStr;
Unit aUnit;
refValue = aValue;
aUnit = null;
if (refUnitProvider != null)
aUnit = refUnitProvider.getUnit();
if (aUnit == null)
aStr = "" + aValue;
else if (showLabel == false)
aStr = aUnit.getString(aValue);
else
aStr = aUnit.getString(aValue, true);
setText(aStr);
}
}

View File

@@ -0,0 +1,159 @@
package glum.gui.component;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import glum.gui.component.model.GListModel;
import glum.gui.panel.generic.GenericCodes;
import com.google.common.collect.Lists;
public class GList<G1> extends JComponent implements GenericCodes, ListSelectionListener
{
// Gui vars
private JList refList;
private GListModel<G1> refModel;
// State vars
private ActionListener refActionListener;
private ListSelectionListener refListSelectionListener;
public GList(Object aListener, List<G1> aItemList)
{
refActionListener = null;
refListSelectionListener = null;
refModel = new GListModel<G1>(aItemList);
refList = new JList(refModel);
buildGui(aListener);
}
public GList(Object aListener, G1... aItemArr)
{
refActionListener = null;
refListSelectionListener = null;
refModel = new GListModel<G1>(aItemArr);
refList = new JList(refModel);
buildGui(aListener);
}
/**
* Returns the (first) selected item
*/
public G1 getSelectedItem()
{
int selectedIndex;
selectedIndex = refList.getSelectedIndex();
if (selectedIndex == -1)
return null;
return refModel.getElementAt(selectedIndex);
}
/**
* Returns all of the selected items
*/
public List<G1> getSelectedItems()
{
ArrayList<G1> retList;
int[] indexArr;
indexArr = refList.getSelectedIndices();
retList = Lists.newArrayList();
for (int aIndex : indexArr)
retList.add(refModel.getElementAt(aIndex));
retList.trimToSize();
return retList;
}
/**
* Replaces all of the items in the list with aItemList.
*/
public void setItems(Collection<G1> aItemList)
{
refList.removeListSelectionListener(this);
refList.setModel(new GListModel<G1>(aItemList));
refList.addListSelectionListener(this);
}
/**
* Sets aItem as the selected item.
*/
public void setSelectedItem(G1 aItem)
{
refList.removeListSelectionListener(this);
refList.setSelectedIndex(refModel.indexOf(aItem));
refList.addListSelectionListener(this);
}
/**
* Sets all of the items in aItemList as selected
*/
public void setSelectedItems(List<G1> aItemList)
{
int[] idArr;
int c1;
// Ensure we are executed only on the proper thread
if (SwingUtilities.isEventDispatchThread() == false)
throw new RuntimeException("GList.selectItems() not executed on the AWT event dispatch thread.");
refList.removeListSelectionListener(this);
c1 = 0;
idArr = new int[aItemList.size()];
for (G1 aItem : aItemList)
{
idArr[c1] = refModel.indexOf(aItem);
c1++;
}
refList.setSelectedIndices(idArr);
refList.addListSelectionListener(this);
}
@Override
public void setEnabled(boolean aBool)
{
refList.setEnabled(aBool);
}
@Override
public void valueChanged(ListSelectionEvent aEvent)
{
if (refListSelectionListener != null)
refListSelectionListener.valueChanged(new ListSelectionEvent(this, aEvent.getFirstIndex(), aEvent.getLastIndex(), aEvent.getValueIsAdjusting()));
if (refActionListener != null)
refActionListener.actionPerformed(new ActionEvent(this, ID_UPDATE, "update"));
}
/**
* Helper method used to build the GUI
*/
private void buildGui(Object aListener)
{
if (aListener instanceof ActionListener)
refActionListener = (ActionListener)aListener;
if (aListener instanceof ListSelectionListener)
refListSelectionListener = (ListSelectionListener)aListener;
setLayout(new BorderLayout());
add(refList);
}
}

View File

@@ -0,0 +1,298 @@
package glum.gui.component;
import java.awt.Color;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import glum.gui.document.NumberDocument;
import glum.unit.ConstUnitProvider;
import glum.unit.NumberUnit;
import glum.unit.Unit;
import glum.unit.UnitListener;
import glum.unit.UnitProvider;
public class GNumberField extends JTextField implements DocumentListener, UnitListener
{
// State vars
protected UnitProvider refUnitProvider;
protected double currValue, minValue, maxValue;
protected boolean isMutating;
// Gui vars
protected Color failColor, passColor;
protected NumberDocument myDocument;
/**
* Constructor
*
* @param aListener
* : Default ActionListener
* @param aUnit
* : Object used to format programatic entered values. Note aUnitProvider will also be used to determine if
* Floating or only Integral input is allowed.
* @param inputType
* : Type of input to accept (Integer, Double, etc...)
* @param aMinVal
* : Minimum value to accept
* @param aMaxVal
* : Maximum value to accept
*/
public GNumberField(ActionListener aListener, UnitProvider aUnitProvider, double aMinVal, double aMaxVal)
{
super("", 0);
refUnitProvider = aUnitProvider;
currValue = 0;
minValue = aMinVal;
maxValue = aMaxVal;
isMutating = false;
failColor = Color.RED.darker();
passColor = getForeground();
// Register the ActionListener
if (aListener != null)
addActionListener(aListener);
// Form the appropriate Document and initialize
myDocument = new NumberDocument(this, false);
super.setDocument(myDocument);
// Register for events of interest
myDocument.addDocumentListener(this);
refUnitProvider.addListener(this);
// Install our unit into the Document. Note this should be done last as the method installUnit() assumes that
// this GNumberField is already registered with myDocument (via the method call forceTF()).
installUnit();
}
public GNumberField(ActionListener aListener, Unit aUnit, double aMinVal, double aMaxVal)
{
this(aListener, new ConstUnitProvider(aUnit), aMinVal, aMaxVal);
}
/**
* Returns whether the current input is valid
*/
public boolean isValidInput()
{
Unit aUnit;
double modelVal;
// Ensure we have valid input
aUnit = refUnitProvider.getUnit();
modelVal = aUnit.parseString(this.getText(), Double.NaN);
if (Double.isNaN(modelVal) == true)
return false;
// Ensure the value is within range
if (modelVal < minValue || modelVal > maxValue)
return false;
return true;
}
/**
* Sets the currently stored model value to Double.NaN. Also clears out the input area. This method will not trigger
* an ActionEvent.
*/
public void clearValue()
{
currValue = Double.NaN;
myDocument.removeDocumentListener(this);
setText("");
setCaretPosition(0);
myDocument.addDocumentListener(this);
}
/**
* Returns the currently stored model value
*/
public double getValue()
{
return currValue;
}
/**
* Returns the currently stored model value as an integer. If the modelValue is NaN, then errorVal will be returned.
* The values MaxInt, MinInt are returned for Infinity.
*/
public int getValueAsInt(int errorVal)
{
if (Double.isNaN(currValue) == true)
return errorVal;
return (int)currValue;
}
/**
* Takes in a model value and will display it with respect to the active unit. This method will not trigger an
* ActionEvent.
* <P>
* Note this method will do nothing if the UI is being "mutated" when this method is called.
*/
public void setValue(final double aValue)
{
// Bail if we are being mutated. The alternative is to throw an exception like:
// throw new IllegalStateException("Attempt to mutate in notification");
if (isMutating == true)
return;
// Simple edit if we are not currently being mutated
forceTF(aValue);
updateGui();
}
/**
* Changes the range of acceptable values (in model units). Note the current value will be force to fit this range.
*/
public void setMinMaxValue(double aMinValue, double aMaxValue)
{
Unit aUnit;
minValue = aMinValue;
maxValue = aMaxValue;
if (currValue < minValue || currValue > maxValue)
currValue = minValue;
// Update our document
aUnit = refUnitProvider.getUnit();
myDocument.setMinMaxValue(aUnit.toUnit(minValue), aUnit.toUnit(maxValue));
}
@Override
public void setDocument(Document aDoc)
{
// throw new UnsupportedOperationException();
if (aDoc != null)
aDoc.addDocumentListener(this);
super.setDocument(aDoc);
}
@Override
public void changedUpdate(DocumentEvent aEvent)
{
syncValue(aEvent);
}
@Override
public void insertUpdate(DocumentEvent aEvent)
{
syncValue(aEvent);
}
@Override
public void removeUpdate(DocumentEvent aEvent)
{
syncValue(aEvent);
}
@Override
public void unitChanged(UnitProvider aProvider, String aKey)
{
installUnit();
}
/**
* Updates the internal model value and will update the display wrt to the active unit.
*/
protected void forceTF(double aValue)
{
Unit aUnit;
String aStr;
// Save off the new model value, and check the validity
currValue = aValue;
if (currValue < minValue || currValue > maxValue)
currValue = Double.NaN;
// throw new RuntimeException("Programatic input is invalid. Is unit compatible? Input: " + aValue);
// Invalid values shall just clear the text field and bail
if (Double.isNaN(currValue) == true)
{
clearValue();
return;
}
// Convert from model value to (unit) textual format
aUnit = refUnitProvider.getUnit();
aStr = aUnit.getString(currValue);
// Update the GUI internals
myDocument.removeDocumentListener(this);
setText(aStr);
setCaretPosition(0);
myDocument.addDocumentListener(this);
}
/**
* Helper method to update the associated Document whenever a Unit is changed
*/
protected void installUnit()
{
Unit aUnit;
boolean aBool;
// Ensure that we have a valid Unit
aUnit = refUnitProvider.getUnit();
if (aUnit instanceof NumberUnit == false)
throw new RuntimeException("refUnitProvider must return a Unit of type NumberUnit. Unit: " + aUnit);
// Update our Document to reflect whether this Unit supports floating point numbers
aBool = (aUnit instanceof NumberUnit) && (((NumberUnit)aUnit).isFloating() == true);
myDocument.setAllowFloats(aBool);
// Update the Document's MinMax values reflect the new Unit
setMinMaxValue(minValue, maxValue);
// Force myDocument's text to match the new unit
forceTF(currValue);
}
/**
* Keeps the "model" value conceptually linked to the GUI component. It will also trigger the actionEventListeners.
*/
protected void syncValue(DocumentEvent e)
{
Unit aUnit;
// Mark ourself as mutating
isMutating = true;
// Convert the textual (unit) value to the model value
aUnit = refUnitProvider.getUnit();
currValue = aUnit.parseString(this.getText(), Double.NaN);
// If the value is not in range then, it is invalid
if (currValue < minValue || currValue > maxValue)
currValue = Double.NaN;
// Notify our listeners and update the GUI
updateGui();
fireActionPerformed();
// We are no longer mutating
isMutating = false;
}
/**
* Helper method to update the GUI to reflect the current state of the NumberField
*/
protected void updateGui()
{
Color aColor;
aColor = passColor;
if (isValidInput() == false)
aColor = failColor;
setForeground(aColor);
}
}

View File

@@ -0,0 +1,106 @@
package glum.gui.component;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class GPasswordField extends JPasswordField implements DocumentListener
{
/**
* Constructor
*
* @param aListener
* : Default ActionListener
*/
public GPasswordField(ActionListener aListener)
{
super("", 0);
if (aListener != null)
addActionListener(aListener);
// Form the appropriate Document
Document doc;
doc = new PlainDocument();
super.setDocument(doc);
// Register for events of interest
doc.addDocumentListener(this);
}
/**
* Returns whether the current input is valid
*/
public boolean isValidInput()
{
if (getPassword().length < 2)
return false;
return true;
}
/**
* Sets in the value for the GTextField. Note this method will not trigger an ActionEvent.
*/
public void setValue(String aText)
{
forceTF(aText);
}
@Override
public void setDocument(Document aDoc)
{
// throw new UnsupportedOperationException();
if (aDoc != null)
aDoc.addDocumentListener(this);
super.setDocument(aDoc);
}
@Override
public void changedUpdate(DocumentEvent aEvent)
{
fireActionPerformed();
}
@Override
public void insertUpdate(DocumentEvent aEvent)
{
fireActionPerformed();
}
@Override
public void removeUpdate(DocumentEvent aEvent)
{
fireActionPerformed();
}
/**
* Updates the internal model value and will update the display wrt tho active unit.
*/
protected void forceTF(String aStr)
{
Document aDocument;
// Update the GUI internals
aDocument = this.getDocument();
if (aDocument != null)
aDocument.removeDocumentListener(this);
setText(aStr);
setCaretPosition(0);
if (aDocument != null)
aDocument.addDocumentListener(this);
}
/**
* Keeps the "model" value conceptually linked to the GUI component. It will also trigger the actionEventListeners.
*/
protected void syncValue(DocumentEvent aEvent)
{
fireActionPerformed();
}
}

View File

@@ -0,0 +1,91 @@
package glum.gui.component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class GSlider extends JSlider implements ChangeListener
{
private ActionListener myListener;
private double minVal, maxVal, rngVal;
private int maxSteps;
public GSlider(ActionListener aListener, int aMaxSteps, double aMinVal, double aMaxVal)
{
super(0, aMaxSteps);
addChangeListener(this);
myListener = aListener;
maxSteps = aMaxSteps;
minVal = aMinVal;
maxVal = aMaxVal;
rngVal = maxVal - minVal;
}
public GSlider(ActionListener aListener, double aMinVal, double aMaxVal)
{
this(aListener, 1000, aMinVal, aMaxVal);
}
/**
* Returns the model value for which this slider is currently set to.
* <P>
* Use this method over {@link JSlider#getValue()}
*/
public double getModelValue()
{
double retVal;
retVal = minVal + ((super.getValue() / (double)maxSteps) * rngVal);
return retVal;
}
/**
* Takes in the model's minVal and maxVal range. The current chosen model value will be adjusted to be in the middle
* of the range.
*/
public void setModelRange(double aMinVal, double aMaxVal)
{
minVal = aMinVal;
maxVal = aMaxVal;
rngVal = maxVal - minVal;
setModelValue(minVal + rngVal / 2);
}
/**
* Takes in a model value and will adjust the slider to display the value. Note this method will not trigger an
* ActionEvent.
* <P>
* Use this method over {@link JSlider#setValue}
*/
public void setModelValue(double aVal)
{
double guiVal;
guiVal = ((aVal - minVal) / rngVal) * maxSteps;
removeChangeListener(this);
setValue((int)guiVal);
addChangeListener(this);
}
@Override
public void stateChanged(ChangeEvent aEvent)
{
notifyLisener();
}
/**
* Helper method to notify our listener
*/
private void notifyLisener()
{
myListener.actionPerformed(new ActionEvent(this, 0, "update"));
}
}

View File

@@ -0,0 +1,114 @@
package glum.gui.component;
import java.awt.event.ActionListener;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
public class GTextField extends JTextField implements DocumentListener
{
/**
* Constructor
*
* @param aListener
* : Default ActionListener
*/
public GTextField(ActionListener aListener)
{
super("", 0);
if (aListener != null)
addActionListener(aListener);
// Form the appropriate Document
Document doc;
doc = new PlainDocument();
super.setDocument(doc);
// Register for events of interest
doc.addDocumentListener(this);
}
/**
* Returns whether the current input is valid
*/
public boolean isValidInput()
{
if (getText().isEmpty() == true)
return false;
return true;
}
/**
* Returns the currently stored model value
*/
public String getValue()
{
return getText();
}
/**
* Sets in the value for the GTextField. Note this method will not trigger an ActionEvent.
*/
public void setValue(String aText)
{
forceTF(aText);
}
@Override
public void setDocument(Document aDoc)
{
// throw new UnsupportedOperationException();
if (aDoc != null)
aDoc.addDocumentListener(this);
super.setDocument(aDoc);
}
@Override
public void changedUpdate(DocumentEvent aEvent)
{
fireActionPerformed();
}
@Override
public void insertUpdate(DocumentEvent aEvent)
{
fireActionPerformed();
}
@Override
public void removeUpdate(DocumentEvent aEvent)
{
fireActionPerformed();
}
/**
* Updates the internal model value and will update the display wrt tho active unit.
*/
protected void forceTF(String aStr)
{
Document aDocument;
// Update the GUI internals
aDocument = this.getDocument();
if (aDocument != null)
aDocument.removeDocumentListener(this);
setText(aStr);
setCaretPosition(0);
if (aDocument != null)
aDocument.addDocumentListener(this);
}
/**
* Keeps the "model" value conceptually linked to the GUI component. It will also trigger the actionEventListeners.
*/
protected void syncValue(DocumentEvent aEvent)
{
fireActionPerformed();
}
}

View File

@@ -0,0 +1,76 @@
package glum.gui.component;
import javax.swing.Icon;
import javax.swing.JToggleButton;
public class GToggle extends JToggleButton
{
// State vars
protected boolean isActive;
// Gui vars
protected Icon falseIcon, trueIcon;
public GToggle(Icon aFalseIcon, Icon aTrueIcon, boolean aIsActive)
{
super();
falseIcon = aFalseIcon;
trueIcon = aTrueIcon;
setSelected(aIsActive);
setModel(new GToggleButtonModel());
}
@Override
public void setSelected(boolean b)
{
super.setSelected(b);
updateGui();
}
@Override
public void doClick(int pressTime)
{
super.doClick(pressTime);
updateGui();
}
/**
* Utility method
*/
private void updateGui()
{
if (isSelected() == true)
setIcon(trueIcon);
else
setIcon(falseIcon);
}
/**
* The ToggleButton model
* <p>
* <strong>Warning:</strong> Serialized objects of this class will not be compatible with future Swing releases. The
* current serialization support is appropriate for short term storage or RMI between applications running the same
* version of Swing. As of 1.4, support for long term storage of all JavaBeans<sup><font size="-2">TM</font></sup>
* has been added to the <code>java.beans</code> package. Please see {@link java.beans.XMLEncoder}.
*/
public class GToggleButtonModel extends ToggleButtonModel
{
/**
* Creates a new ToggleButton Model
*/
public GToggleButtonModel()
{
}
@Override
public void setSelected(boolean b)
{
super.setSelected(b);
updateGui();
}
}
}

View File

@@ -0,0 +1,222 @@
package glum.gui.component.banner;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
import glum.gui.*;
import glum.io.token.MatchTokenizer;
import glum.io.token.Tokenizer;
public class Banner extends JComponent
{
// Our configuration
protected BannerConfig myConfig;
// Current message to display
protected String displayMsg;
/**
* Constructor
*/
public Banner()
{
super();
myConfig = new BannerConfig();
setConfig(myConfig);
}
@Override
public void paintComponent(Graphics g)
{
Graphics2D g2d;
Rectangle2D aRect;
double winW, winH, msgW, msgH;
double sX, sY, offSetY;
super.paintComponent(g);
g2d = (Graphics2D)g;
// Determine the window boundaries
winW = getWidth();
winH = getHeight();
// Compute the displayMsg boundaries
aRect = myConfig.font.getStringBounds(displayMsg, g2d.getFontRenderContext());
msgW = aRect.getWidth();
msgH = aRect.getHeight();
offSetY = msgH - aRect.getMaxY();
// Form our rectangle
aRect = new Rectangle2D.Double(0, 0, winW, winH);
// Draw the background
if (myConfig.bgColor != null)
{
g2d.setColor(myConfig.bgColor);
g2d.fill(aRect);
}
// Draw the border
if (myConfig.borderColor != null && myConfig.borderWidth > 0)
{
g2d.setColor(myConfig.borderColor);
g2d.setStroke(new BasicStroke(myConfig.borderWidth));
g2d.draw(aRect);
}
// Draw the displayMsg
sX = winW / 2.0 - msgW / 2.0;
sY = winH / 2.0 - msgH / 2.0;
g2d.setFont(myConfig.font);
g2d.setColor(myConfig.fgColor);
g2d.drawString(displayMsg, (int)sX, (int)(sY + offSetY));
}
/**
* setConfig - Sets in the new banner attributes
*/
public void setConfig(BannerConfig aConfig)
{
// Insanity check
if (aConfig == null)
return;
myConfig = aConfig;
// Build the actual displayMsg
if (myConfig.numRepeats > 1)
{
displayMsg = "";
for (int c1 = 0; c1 < myConfig.numRepeats; c1++)
displayMsg += myConfig.bannerMsg;
}
else
{
displayMsg = myConfig.bannerMsg;
}
// Time for a repaint
repaint();
}
/**
* Utility method that converts a string to a BannerConfig. Eventually this method class should be
* moved to a utility class of sorts.
*/
public static BannerConfig readBannerConfig(String strLine)
{
BannerConfig aConfig;
Collection<String> instrSet, parmSet;
Tokenizer instrTokenizer, parmTokenizer;
String parms[];
// Insanity check
if (strLine == null)
return null;
// Build our tokenizers
instrTokenizer = new MatchTokenizer("[^/]+");
parmTokenizer = new MatchTokenizer("[^\\,]+");
// Get the set of embedded instructions
instrSet = instrTokenizer.getTokens(strLine);
// Process the instruction
aConfig = new BannerConfig();
for (String aInstr : instrSet)
{
parmSet = parmTokenizer.getTokens(aInstr);
parms = parmSet.toArray(new String[] {""});
if (parms.length == 0)
{
; // Nothing to process
}
else if (parms[0].equalsIgnoreCase("refName") == true && parms.length == 2)
{
aConfig.refName = parms[1];
}
else if (parms[0].equalsIgnoreCase("label") == true && parms.length == 2)
{
aConfig.bannerMsg = parms[1];
}
else if (parms[0].equalsIgnoreCase("font") == true && parms.length >= 2)
{
String face;
int style, size;
boolean bold, italic;
face = parms[1];
size = 12;
if (parms.length > 2)
size = GuiUtil.readInt(parms[2], 12);
bold = false;
if (parms.length > 3)
bold = GuiUtil.readBoolean(parms[3], false);
italic = false;
if (parms.length > 4)
italic = GuiUtil.readBoolean(parms[4], false);
style = 0;
if (bold == false && italic == false)
style = Font.PLAIN;
if (bold == true)
style = Font.BOLD;
if (italic == true)
style |= Font.ITALIC;
aConfig.font = new Font(face, style, size);
}
else if (parms[0].equalsIgnoreCase("fgColor") == true && parms.length == 4)
{
int r, g, b;
r = GuiUtil.readRangeInt(parms[1], 0, 255, 255);
g = GuiUtil.readRangeInt(parms[2], 0, 255, 255);
b = GuiUtil.readRangeInt(parms[3], 0, 255, 255);
aConfig.fgColor = new Color(r, g, b);
}
else if (parms[0].equalsIgnoreCase("bgColor") == true && parms.length >= 4)
{
int r, g, b, a;
r = GuiUtil.readRangeInt(parms[1], 0, 255, 255);
g = GuiUtil.readRangeInt(parms[2], 0, 255, 255);
b = GuiUtil.readRangeInt(parms[3], 0, 255, 255);
a = 255;
if (parms.length > 4)
a = GuiUtil.readRangeInt(parms[4], 0, 255, 255);
aConfig.bgColor = new Color(r, g, b, a);
}
else if (parms[0].equalsIgnoreCase("border") == true && parms.length >= 4)
{
int r, g, b;
r = GuiUtil.readRangeInt(parms[1], 0, 255, 255);
g = GuiUtil.readRangeInt(parms[2], 0, 255, 255);
b = GuiUtil.readRangeInt(parms[3], 0, 255, 255);
aConfig.borderColor = new Color(r, g, b);
if (parms.length > 4)
aConfig.borderWidth = GuiUtil.readRangeInt(parms[4], 0, 10, 0);
if (parms.length > 5)
aConfig.borderPad = GuiUtil.readRangeInt(parms[5], -20, 20, 0);
}
else if (parms[0].equalsIgnoreCase("repeatMsg") == true && parms.length == 2)
{
aConfig.numRepeats = GuiUtil.readRangeInt(parms[1], -1, 100, 0);
}
}
return aConfig;
}
}

View File

@@ -0,0 +1,36 @@
package glum.gui.component.banner;
import java.awt.*;
public class BannerConfig
{
// State vars
public String refName;
public Color bgColor, fgColor;
public Color borderColor;
public int borderWidth, borderPad;
public Font font;
public int numRepeats;
public String bannerMsg;
/**
* Constructor
*/
public BannerConfig()
{
refName = "DEFAULT";
bgColor = Color.BLACK;
fgColor = Color.WHITE;
borderColor = null;
borderWidth = 0;
borderPad = 0;
font = new Font("Serif", Font.PLAIN, 12);
numRepeats = 1;
bannerMsg = "Banner";
}
}

View File

@@ -0,0 +1,60 @@
package glum.gui.component.model;
import java.util.Collection;
import javax.swing.ComboBoxModel;
import com.google.common.collect.Lists;
public class GComboBoxModel<G1> extends GListModel<G1> implements ComboBoxModel
{
protected G1 chosenItem;
public GComboBoxModel(G1... aItemArr)
{
this(Lists.newArrayList(aItemArr));
}
public GComboBoxModel(Collection<G1> aItemList)
{
super(aItemList);
chosenItem = null;
if (itemList.size() > 0)
chosenItem = itemList.get(0);
}
@Override
public void addItem(G1 aItem)
{
if (chosenItem == null)
chosenItem = aItem;
super.addItem(aItem);
}
@Override
public void removeItem(G1 aItem)
{
super.removeItem(aItem);
chosenItem = null;
if (itemList.size() > 0)
chosenItem = itemList.get(0);
}
/**
* Note aItem must be of the Generified type
*/
@Override @SuppressWarnings("unchecked")
public void setSelectedItem(Object aItem)
{
chosenItem = (G1)aItem;
}
@Override
public G1 getSelectedItem()
{
return chosenItem;
}
}

View File

@@ -0,0 +1,98 @@
package glum.gui.component.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.swing.AbstractListModel;
import com.google.common.collect.Lists;
/**
* Generified mutable ListModel
*/
public class GListModel<G1> extends AbstractListModel
{
protected List<G1> itemList;
public GListModel(Collection<G1> aItemList)
{
itemList = Lists.newArrayList(aItemList);
}
public GListModel(G1... aItemArr)
{
itemList = Lists.newArrayList(aItemArr);
}
/**
* Adds aItem to this model
*/
public void addItem(G1 aItem)
{
int index;
itemList.add(aItem);
index = itemList.size() - 1;
fireIntervalAdded(this, index, index);
}
/**
* Removes aItem from this model
*/
public void removeItem(G1 aItem)
{
int index;
index = itemList.indexOf(aItem);
itemList.remove(aItem);
fireIntervalRemoved(this, index, index);
}
/**
* Removes all the items from this model
*/
public void removeAllItems()
{
int lastIndex;
// Bail if the list is empty
if (itemList.isEmpty() == true)
return;
lastIndex = itemList.size() - 1;
itemList.clear();
fireIntervalRemoved(this, 0, lastIndex);
}
/**
* Returns a list of all the items
*/
public ArrayList<G1> getAllItems()
{
return Lists.newArrayList(itemList);
}
/**
* Returns the index of the item located in this model.
*/
public int indexOf(G1 aItem)
{
return itemList.indexOf(aItem);
}
@Override
public int getSize()
{
return itemList.size();
}
@Override
public G1 getElementAt(int index)
{
return itemList.get(index);
}
}

View File

@@ -0,0 +1,142 @@
package glum.gui.dialog;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import javax.swing.*;
import net.miginfocom.swing.MigLayout;
import glum.gui.FocusUtil;
import glum.gui.GuiUtil;
import glum.gui.action.ClickAction;
import glum.gui.component.GLabel;
import glum.unit.ConstUnitProvider;
import glum.unit.NumberUnit;
import glum.unit.UnitProvider;
public class MemoryUtilDialog extends JDialog implements ActionListener
{
// Gui components
private GLabel totalMemL, freeMemL, usedMemL;
private UnitProvider byteUP;
private JButton closeB, gcRunB, updateB;
/**
* Constructor
*/
public MemoryUtilDialog(JFrame parentFrame)
{
super(parentFrame);
setTitle("JVM Memory Usage");
setDefaultCloseOperation(HIDE_ON_CLOSE);
setModal(false);
DecimalFormat numFormat;
numFormat = new DecimalFormat();
numFormat.setGroupingUsed(true);
numFormat.setGroupingSize(3);
numFormat.setMaximumFractionDigits(0);
byteUP = new ConstUnitProvider(new NumberUnit("MB", "MB", 1.0 / (1024 * 1024), numFormat));
// Place the dialog in the center
buildGuiArea();
setLocationRelativeTo(parentFrame);
// Set up keyboard short cuts
FocusUtil.addWindowKeyBinding(getRootPane(), "ESCAPE", new ClickAction(closeB));
FocusUtil.addWindowKeyBinding(getRootPane(), "ENTER", new ClickAction(updateB));
}
@Override
public void actionPerformed(ActionEvent e)
{
Object source;
source = e.getSource();
if (source == gcRunB)
{
System.gc();
updateGui();
}
else if (source == updateB)
{
updateGui();
}
else if (source == closeB)
{
setVisible(false);
}
}
@Override
public void setVisible(boolean isVisible)
{
updateGui();
super.setVisible(isVisible);
}
/**
* Forms the actual dialog GUI
*/
private void buildGuiArea()
{
JPanel aPanel;
JLabel tmpL;
Font tmpFont;
// Form the panel
aPanel = new JPanel(new MigLayout("", "[right][left,grow]", "[][][]10[]10"));
tmpFont = new JTextField().getFont();
// Info area
tmpL = new JLabel("Total Memory:");
totalMemL = new GLabel(byteUP, tmpFont, true);
aPanel.add(tmpL, "");
aPanel.add(totalMemL, "growx,wrap");
tmpL = new JLabel("Free Memory:");
freeMemL = new GLabel(byteUP, tmpFont, true);
aPanel.add(tmpL, "");
aPanel.add(freeMemL, "growx,wrap");
tmpL = new JLabel("Used Memory:");
usedMemL = new GLabel(byteUP, tmpFont, true);
aPanel.add(tmpL, "");
aPanel.add(usedMemL, "growx,wrap");
// Control area
updateB = GuiUtil.createJButton("Update", this);
gcRunB = GuiUtil.createJButton("Run GC", this);
closeB = GuiUtil.createJButton("Close", this);
aPanel.add(updateB, "span 2,split 3");
aPanel.add(gcRunB, "");
aPanel.add(closeB, "");
// Add the main panel into the dialog
getContentPane().add(aPanel);
pack();
}
/**
* Utility method to update the Gui
*/
private void updateGui()
{
Runtime aRuntime;
long freeMem, usedMem, totalMem;
aRuntime = Runtime.getRuntime();
// Update the memory usage
freeMem = aRuntime.freeMemory();
totalMem = aRuntime.totalMemory();
usedMem = totalMem - freeMem;
freeMemL.setValue(freeMem);
totalMemL.setValue(totalMem);
usedMemL.setValue(usedMem);
}
}

View File

@@ -0,0 +1,52 @@
package glum.gui.dnd;
import java.awt.datatransfer.Transferable;
import javax.swing.JComponent;
import javax.swing.TransferHandler;
/**
* Generic TransferHandler that supports transferring an arbitrary Transferable. Note that before the DnD transfer
* mechanism is triggered via the method exportAsDrag(), the developer must set in the Transferable via the method
* setWorkTransferable().
*/
public class PlainTransferHandler extends TransferHandler
{
// State vars
protected Transferable workTransferable;
public PlainTransferHandler()
{
super();
workTransferable = null;
}
/**
* Method to set in the current "active" Trasferable for which this TrasferHandler will process.
*/
public void setWorkTransferable(Transferable aTransferable)
{
workTransferable = aTransferable;
}
@Override
public int getSourceActions(JComponent aComp)
{
return COPY_OR_MOVE;
}
@Override
public Transferable createTransferable(JComponent aComp)
{
// Just return the active Transferable
return workTransferable;
}
@Override
public void exportDone(JComponent c, Transferable t, int action)
{
workTransferable = null;
}
}

View File

@@ -0,0 +1,50 @@
package glum.gui.dock;
import javax.swing.Icon;
import javax.swing.JComponent;
import bibliothek.gui.DockStation;
import bibliothek.gui.dock.DefaultDockable;
public class BaseDockable extends DefaultDockable
{
// Tells whether this Dockable can be dragged and dropped to another station
private DockStation homeStation;
private boolean isTransferable;
public BaseDockable()
{
super();
isTransferable = true;
}
public BaseDockable(JComponent aComp, String aTitle, Icon aIcon)
{
super(aComp, aTitle, aIcon);
isTransferable = true;
}
public boolean isTransferable(DockStation aStation)
{
if (isTransferable == true)
return true;
// We can only be transfered to our homeStation when we are not transferable
return aStation == homeStation;
}
public void setTransferable(boolean aBool)
{
homeStation = null;
isTransferable = aBool;
// Record our parent when we become non transferable
if (isTransferable == false)
homeStation = getDockParent();
}
}

View File

@@ -0,0 +1,34 @@
package glum.gui.dock;
import glum.gui.dock.action.Closeable;
import javax.swing.Icon;
import javax.swing.JComponent;
import bibliothek.gui.DockFrontend;
/**
* Dockable class to wrap aComp into a DefaultDockable using the specified title and icon. This Dockable will be
* automatically installed into aFrontend and support proper closing via the {@link Closeable} interface.
*/
public class CloseableDockable extends BaseDockable implements Closeable
{
protected DockFrontend refFrontend;
public CloseableDockable(DockFrontend aFrontend, String idName, JComponent aComp, String aTitle, Icon aIcon)
{
super(aComp, aTitle, aIcon);
setTitleIcon(aIcon);
// Register ourselves with the refFrontend
refFrontend = aFrontend;
refFrontend.addDockable(idName, this);
}
@Override
public void close()
{
refFrontend.hide(this);
}
}

View File

@@ -0,0 +1,61 @@
package glum.gui.dock;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.gui.dock.station.support.PlaceholderStrategyListener;
import bibliothek.util.Path;
/*
* This is our very simple PlaceholderStrategy. It only recognizes our custom
* PrimDockable and returns the associated place holder.
*
* TODO: This class is incomplete. It might be deleted in the future.
*/
public class CustomPlaceholderStrategy implements PlaceholderStrategy
{
@Override
public void addListener(PlaceholderStrategyListener listener)
{
// ignore
}
@Override
public Path getPlaceholderFor(Dockable aDockable)
{
// We handle only PrimDockables
if (aDockable instanceof PrimDock == false)
return null;
//System.out.println("Attempting to add placeholder for dockable: " + aDockable.getTitleText());
// return new Path("Prim:" + ((PrimDockable)aDockable).getUniqueId());
return null;
}
@Override
public void install(DockStation station)
{
// ignore
}
@Override
public boolean isValidPlaceholder(Path placeholder)
{
System.out.println("isValidPlaceholder()-> Path:" + placeholder.toString());
return true;
}
@Override
public void removeListener(PlaceholderStrategyListener listener)
{
// ignore
}
@Override
public void uninstall(DockStation station)
{
// ignore
}
}

View File

@@ -0,0 +1,213 @@
package glum.gui.dock;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Set;
import javax.swing.Icon;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import bibliothek.gui.DockController;
import bibliothek.gui.DockFrontend;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.ScreenDockStation;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.action.DockAction;
import bibliothek.gui.dock.action.actions.SimpleButtonAction;
import bibliothek.gui.dock.station.screen.ScreenFullscreenAction;
import bibliothek.gui.dock.station.split.SplitFullScreenAction;
public class DockUtil
{
/**
* Utility method to construct a SimpleButtonAction.
*/
public static SimpleButtonAction createAction(String aText, Icon aIcon, ActionListener aListener)
{
SimpleButtonAction aAction;
aAction = new SimpleButtonAction();
aAction.setText(aText);
aAction.setIcon(aIcon);
aAction.addActionListener(aListener);
return aAction;
}
/**
* Utility method to return the appropriate DockAction associated with aStation which will cause a "full screen"
* action to be executed. Currently only object of type {@link SplitDockStation} and {@link ScreenDockStation} are
* supported.
*/
public static DockAction createFullScreenAction(DockStation aStation, DockController aController)
{
DockAction fullScreenAction;
if (aStation instanceof SplitDockStation)
{
fullScreenAction = new SplitFullScreenAction((SplitDockStation)aStation);
((SplitFullScreenAction)fullScreenAction).setController(aController);
}
else if (aStation instanceof ScreenDockStation)
{
fullScreenAction = new ScreenFullscreenAction((ScreenDockStation)aStation);
((ScreenFullscreenAction)fullScreenAction).setController(aController);
}
else
{
throw new RuntimeException("Unsupported Dockable type: " + aStation);
}
return fullScreenAction;
}
/**
* Method to locate the first Dockable (contained within aStation) which is of type aClass
*/
public static <G1 extends Dockable> G1 findDockable(DockStation aStation, Class<G1> aClass)
{
Dockable evalDockable;
for (int c1 = 0; c1 < aStation.getDockableCount(); c1++)
{
evalDockable = aStation.getDockable(c1);
if (evalDockable instanceof DockStation)
evalDockable = findDockable((DockStation)evalDockable, aClass);
if (evalDockable.getClass() == aClass)
return aClass.cast(evalDockable);
}
return null;
}
/**
* Method to locate the list of Dockables (contained within aFrontend) which is of one of the types in aClassArr. All
* child DockStations will be searched.
*/
public static List<Dockable> findDockableList(DockFrontend aFrontend, Class<?>... aClassArr)
{
Set<Class<?>> classSet;
List<Dockable> itemList;
// Transform the match class array to a set
classSet = Sets.newHashSet();
for (Class<?> aClass : aClassArr)
classSet.add(aClass);
itemList = Lists.newLinkedList();
for (DockStation aStation : aFrontend.getRoots())
findDockableList(aStation, classSet, itemList);
return itemList;
}
/**
* Method to locate the list of Dockables (contained within aFrontend) which is of type of aClass. All child
* DockStations will be searched.
*/
public static <G1 extends Dockable> List<G1> findDockableList(DockFrontend aFrontend, Class<G1> aClass)
{
Set<Class<?>> classSet;
List<G1> itemList;
// Transform the match class array to a set
classSet = Sets.newHashSet();
classSet.add(aClass);
itemList = Lists.newLinkedList();
for (DockStation aStation : aFrontend.getRoots())
findDockableList(aStation, classSet, itemList);
return itemList;
}
/**
* Method to locate the list of Dockables (contained within aStation) which is of one of the types in aClassArr. All
* child DockStations will be searched.
*/
public static List<Dockable> findDockableList(DockStation aStation, Class<?>... aClassArr)
{
Set<Class<?>> classSet;
List<Dockable> itemList;
// Transform the match class array to a set
classSet = Sets.newHashSet();
for (Class<?> aClass : aClassArr)
classSet.add(aClass);
itemList = Lists.newLinkedList();
findDockableList(aStation, classSet, itemList);
return itemList;
}
/**
* Method to locate the list of Dockables (contained within aStation) which is of type of aClass. All child
* DockStations will be searched.
*/
public static <G1 extends Dockable> List<G1> findDockableList(DockStation aStation, Class<G1> aClass)
{
Set<Class<?>> classSet;
List<G1> itemList;
// Transform the match class array to a set
classSet = Sets.newHashSet();
classSet.add(aClass);
itemList = Lists.newLinkedList();
findDockableList(aStation, classSet, itemList);
return itemList;
}
/**
* Helper method to remove all PlotGroupStations and ChartDockables
*/
public static <G1 extends Dockable> void removeAllDockablesOfType(DockFrontend aFrontend, Class<?>... dockTypeArr)
{
List<Dockable> dockList;
DockStation dockStation;
// Gather all of the Dockables of interest
dockList = DockUtil.findDockableList(aFrontend, dockTypeArr);
// Remove all of the Dockables
for (Dockable aDock : dockList)
{
dockStation = aDock.getDockParent();
if (dockStation != null)
dockStation.drag(aDock);
}
}
/**
* Recursive helper method to locate the list of Dockables (contained within aStation) which is of type aClass. The
* results will be stored in aItemList
*/
@SuppressWarnings("unchecked")
private static <G1 extends Dockable> void findDockableList(DockStation aStation, Set<Class<?>> aClassSet, List<G1> aItemList)
{
Dockable evalDockable;
for (int c1 = 0; c1 < aStation.getDockableCount(); c1++)
{
evalDockable = aStation.getDockable(c1);
for (Class<?> aClass : aClassSet)
{
if (aClass.isAssignableFrom(evalDockable.getClass()) == true)
{
aItemList.add((G1)evalDockable);
break;
}
}
if (evalDockable instanceof DockStation)
findDockableList((DockStation)evalDockable, aClassSet, aItemList);
}
}
}

View File

@@ -0,0 +1,86 @@
package glum.gui.dock;
import java.awt.*;
import java.awt.event.ActionEvent;
import bibliothek.gui.DockFrontend;
import glum.gui.panel.generic.TextInputPanel;
public class FrontendAddConfigPanel extends TextInputPanel
{
// Constants
public static final String DEFAULT_NAME = "Default";
// State vars
protected DockFrontend refFrontend;
/**
* Constructor
*/
public FrontendAddConfigPanel(Component aParent, DockFrontend aFrontend)
{
super(aParent);
refFrontend = aFrontend;
// Set in a more specific title and input label
titleL.setText("Save configuration as:");
inputL.setText("Name:");
}
@Override
public void actionPerformed(ActionEvent e)
{
Object source;
String configName;
// Save of the configuration
source = e.getSource();
if (source == acceptB)
{
configName = getInput();
refFrontend.save(configName);
}
// Call parent behavior
super.actionPerformed(e);
}
@Override
protected void updateGui()
{
String inputStr, infoMsg;
boolean isEnabled;
// Assume the GUI is invalid
isEnabled = false;
// Retrieve the name
inputStr = inputTF.getText();
// Determine the validity of specified name
if (inputStr.equals("") == true)
{
infoMsg = "Please enter a valid configuration name.";
}
else if (inputStr.startsWith(".") == true)
{
infoMsg = "Invalid name. Name can not start with the char '.'";
}
else if (refFrontend.getSetting(inputStr) != null)
{
infoMsg = "Name in use. Configuration will be overwritten.";
isEnabled = true;
}
else
{
infoMsg = "";
isEnabled = true;
}
infoL.setText(infoMsg);
acceptB.setEnabled(isEnabled);
}
}

View File

@@ -0,0 +1,252 @@
package glum.gui.dock;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
//import echo.gui.LookUp;
import com.google.common.collect.Lists;
import glum.database.QueryItem;
import glum.gui.FocusUtil;
import glum.gui.GuiUtil;
import glum.gui.action.ClickAction;
import glum.gui.panel.GlassPanel;
import glum.gui.panel.itemList.ItemListPanel;
import glum.gui.panel.itemList.StaticItemProcessor;
import glum.gui.panel.itemList.query.QueryComposer;
import glum.gui.panel.itemList.query.QueryItemHandler;
import bibliothek.gui.DockFrontend;
import net.miginfocom.swing.MigLayout;
public class FrontendManageConfigPanel extends GlassPanel implements ActionListener, ListSelectionListener
{
// GUI vars
protected ItemListPanel<ConfigItem> listPanel;
protected JButton deleteB, closeB;
// State vars
protected DockFrontend refFrontend;
protected StaticItemProcessor<ConfigItem> myItemProcessor;
/**
* Constructor
* @param aFrontend
*/
public FrontendManageConfigPanel(Component aParent, DockFrontend aFrontend)
{
super(aParent);
// State vars
refFrontend = aFrontend;
// Build the actual GUI
buildGuiArea();
setPreferredSize(new Dimension(250, 300));//TODO:getPreferredSize().height));
// Set up keyboard short cuts
FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(closeB));
FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(closeB));
FocusUtil.addFocusKeyBinding(listPanel.getTable(), "ENTER", new ClickAction(closeB));
}
@Override
public void setVisible(boolean aBool)
{
resetGui();
super.setVisible(aBool);
}
@Override
public void actionPerformed(ActionEvent e)
{
Object source;
ConfigItem chosenItem;
chosenItem = listPanel.getSelectedItem();
source = e.getSource();
if (source == deleteB)
{
refFrontend.delete(chosenItem.getName());
resetGui();
}
else if (source == closeB)
{
setVisible(false);
}
}
@Override
public void valueChanged(ListSelectionEvent aEvent)
{
if (aEvent.getValueIsAdjusting() == true)
return;
updateGui();
}
/**
* buildGuiArea - Forms the actual dialog GUI
*/
private void buildGuiArea()
{
JLabel tmpL;
QueryItemHandler<ConfigItem> aItemHandler;
QueryComposer<LookUp> aComposer;
setLayout(new MigLayout("", "[grow][][]", "[][grow][]"));
tmpL = new JLabel("Select configuration:", JLabel.CENTER);
add(tmpL, "growx,span 4,wrap");
// Construct the actual table panel
aComposer = new QueryComposer<LookUp>();
aComposer.addAttribute(LookUp.Name, String.class, "Configuration", null);
myItemProcessor = new StaticItemProcessor<ConfigItem>();
aItemHandler = new QueryItemHandler<ConfigItem>(aComposer);
listPanel = new ItemListPanel<ConfigItem>(aItemHandler, myItemProcessor, false, true);
listPanel.addListSelectionListener(this);
add(listPanel, "growx,growy,span 4,wrap");
// Control area
deleteB = GuiUtil.createJButton("Delete", this);
closeB = GuiUtil.createJButton("Close", this);
add(deleteB, "skip 1,span 1");
add(closeB, "span 1");
setBorder(new BevelBorder(BevelBorder.RAISED));
}
/**
* Utility method to update item list
*/
private void resetGui()
{
Collection<String> strList;
Collection<ConfigItem> itemList;
itemList = Lists.newLinkedList();
strList = refFrontend.getSettings();
for (String aStr : strList)
{
// Add only non reserved items
if (aStr.charAt(0) != '.')
itemList.add(new ConfigItem(aStr));
}
myItemProcessor.setItems(itemList);
// TODO: Ugly code: Should be able to just call updateGui but can not
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
updateGui();
}
});
}
/**
* Utility method to update the individual gui components
*/
private void updateGui()
{
ConfigItem chosenItem;
boolean isEnabled;
chosenItem = listPanel.getSelectedItem();
if (chosenItem != null)
refFrontend.load(chosenItem.getName());
isEnabled = chosenItem != null;
deleteB.setEnabled(isEnabled);
// Ensure we have the focus.
//listPanel.requestFocusInWindow();
repaint();
// TODO: Ugly code: Not sure why need to request focus multiple times to ensure we regrab
// the focus. This is particularly true when there are multiple DockStations located on different
// windows
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
listPanel.getTable().requestFocus();
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
listPanel.getTable().requestFocus();
}
});
// listPanel.getTable().getFocusCycleRootAncestor().setFocusCycleRoot(true);
listPanel.getTable().requestFocusInWindow();
repaint();
}
});
}
/**
* Internal class only used to wrap named (string) settings into
* a QueryItem
*/
class ConfigItem implements QueryItem<LookUp>
{
private String refName;
public ConfigItem(String aName)
{
refName = aName;
}
/**
* Accessor method
*/
public String getName()
{
return refName;
}
@Override
public Object getValue(LookUp aLookUp)
{
return refName;
}
@Override
public void setValue(LookUp aLookUp, Object aObj)
{
throw new UnsupportedOperationException();
}
}
}

View File

@@ -0,0 +1,7 @@
package glum.gui.dock;
public enum LookUp
{
Name
}

View File

@@ -0,0 +1,64 @@
package glum.gui.dock;
import java.util.Set;
import com.google.common.collect.Sets;
import bibliothek.gui.DockStation;
import bibliothek.gui.dock.DockElement;
import bibliothek.gui.dock.layout.DockSituationIgnore;
import bibliothek.gui.dock.perspective.PerspectiveElement;
import bibliothek.gui.dock.perspective.PerspectiveStation;
public class PlainDockSituationIgnore implements DockSituationIgnore
{
private Set<Class<?>> ignoreClassSet;
public PlainDockSituationIgnore()
{
ignoreClassSet = Sets.newLinkedHashSet();
}
/**
* Method to register a new type of Dockable/Station to ignore
*/
public void addIgnoreClass(Class<?> aClass)
{
ignoreClassSet.add(aClass);
}
@Override
public boolean ignoreElement(PerspectiveElement aElement)
{
// TODO Auto-generated method stub
return false;
}
@Override
public boolean ignoreElement(DockElement aElement)
{
// Check to see if aElement is of one of the types in ignoreClassSet
for (Class<?> aClass : ignoreClassSet)
{
if (aClass.isAssignableFrom(aElement.getClass()) == true)
return true;
}
return false;
}
@Override
public boolean ignoreChildren(PerspectiveStation station)
{
// TODO Auto-generated method stub
return false;
}
@Override
public boolean ignoreChildren(DockStation station)
{
// TODO Auto-generated method stub
return false;
}
}

View File

@@ -0,0 +1,322 @@
package glum.gui.dock;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.Maps;
import glum.io.IoUtil;
public class PrimConfig
{
// Constants
public static final byte CODE_BOOL = 1;
public static final byte CODE_INT = 10;
public static final byte CODE_LONG = 11;
public static final byte CODE_SHORT = 12;
public static final byte CODE_FLOAT = 13;
public static final byte CODE_DOUBLE = 14;
public static final byte CODE_STRING = 20;
// State vars
protected Map<String, Object> mySingletonMap;
/**
* Container which holds a mapping of keys (String) to a bunch
* of primitive objects.
*/
public PrimConfig()
{
mySingletonMap = Maps.newLinkedHashMap();
}
/**
* Returns the boolean associated with aKey
* If no such value is found then defaultVal is returned.
*/
public boolean getBoolean(String aKey, boolean defaultVal)
{
Object aValue;
// If no value associated with the key then return defaultVal
aValue = mySingletonMap.get(aKey);
if (aValue == null || aValue.getClass() != Boolean.class)
return defaultVal;
return (Boolean)aValue;
}
/**
* Returns the int associated with aKey
* If no such value is found then defaultVal is returned.
*/
public int getInt(String aKey, int defaultVal)
{
Object aValue;
// If no value associated with the key then return defaultVal
aValue = mySingletonMap.get(aKey);
if (aValue == null || aValue.getClass() != Integer.class)
return defaultVal;
return (Integer)aValue;
}
/**
* Returns the long associated with aKey
* If no such value is found then defaultVal is returned.
*/
public long getLong(String aKey, long defaultVal)
{
Object aValue;
// If no value associated with the key then return defaultVal
aValue = mySingletonMap.get(aKey);
if (aValue == null || aValue.getClass() != Long.class)
return defaultVal;
return (Long)aValue;
}
/**
* Returns the short associated with aKey
* If no such value is found then defaultVal is returned.
*/
public short getShort(String aKey, short defaultVal)
{
Object aValue;
// If no value associated with the key then return defaultVal
aValue = mySingletonMap.get(aKey);
if (aValue == null || aValue.getClass() != Short.class)
return defaultVal;
return (Short)aValue;
}
/**
* Returns the float associated with aKey
* If no such value is found then defaultVal is returned.
*/
public float getFloat(String aKey, float defaultVal)
{
Object aValue;
// If no value associated with the key then return defaultVal
aValue = mySingletonMap.get(aKey);
if (aValue == null || aValue.getClass() != Float.class)
return defaultVal;
return (Float)aValue;
}
/**
* Returns the double associated with aKey
* If no such value is found then defaultVal is returned.
*/
public double getDouble(String aKey, double defaultVal)
{
Object aValue;
// If no value associated with the key then return defaultVal
aValue = mySingletonMap.get(aKey);
if (aValue == null || aValue.getClass() != Double.class)
return defaultVal;
return (Double)aValue;
}
/**
* Returns the String associated with aKey
* If no such value is found then defaultVal is returned.
*/
public String getString(String aKey, String defaultVal)
{
Object aValue;
// If no value associated with the key then return defaultVal
aValue = mySingletonMap.get(aKey);
if (aValue == null || aValue.getClass() != String.class)
return defaultVal;
return (String)aValue;
}
/**
* Associates aVal with aKey. Note this will overwrite any
* previous association.
*/
public void setBoolean(String aKey, boolean aValue)
{
mySingletonMap.put(aKey, aValue);
}
/**
* Associates aVal with aKey. Note this will overwrite any
* previous association.
*/
public void setInt(String aKey, int aValue)
{
mySingletonMap.put(aKey, aValue);
}
/**
* Associates aVal with aKey. Note this will overwrite any
* previous association.
*/
public void setLong(String aKey, long aValue)
{
mySingletonMap.put(aKey, aValue);
}
/**
* Associates aVal with aKey. Note this will overwrite any
* previous association.
*/
public void setShort(String aKey, short aValue)
{
mySingletonMap.put(aKey, aValue);
}
/**
* Associates aVal with aKey. Note this will overwrite any
* previous association.
*/
public void setFloat(String aKey, float aValue)
{
mySingletonMap.put(aKey, aValue);
}
/**
* Associates aVal with aKey. Note this will overwrite any
* previous association.
*/
public void setDouble(String aKey, double aValue)
{
mySingletonMap.put(aKey, aValue);
}
/**
* Associates aVal with aKey. Note this will overwrite any
* previous association.
*/
public void setString(String aKey, String aValue)
{
mySingletonMap.put(aKey, aValue);
}
/**
* Utility method to read the configuration from a DataInputStream
*/
public void readBin(DataInputStream aStream) throws IOException
{
int numItems;
String aKey;
byte aType;
mySingletonMap.clear();
numItems = aStream.readInt();
for (int c1 = 0; c1 < numItems; c1++)
{
aKey = IoUtil.readString(aStream);
aType = aStream.readByte();
switch(aType)
{
case CODE_BOOL:
mySingletonMap.put(aKey, aStream.readBoolean());
break;
case CODE_INT:
mySingletonMap.put(aKey, aStream.readInt());
break;
case CODE_LONG:
mySingletonMap.put(aKey, aStream.readLong());
break;
case CODE_SHORT:
mySingletonMap.put(aKey, aStream.readShort());
break;
case CODE_FLOAT:
mySingletonMap.put(aKey, aStream.readFloat());
break;
case CODE_DOUBLE:
mySingletonMap.put(aKey, aStream.readDouble());
break;
case CODE_STRING:
mySingletonMap.put(aKey, IoUtil.readString(aStream));
break;
default:
throw new RuntimeException("Unreconnized type: " + aType);
}
}
}
/**
* Utility method to write the configuration to a DataOutputStream
*/
public void writeBin(DataOutputStream aStream) throws IOException
{
Set<String> keySet;
Object aVal;
keySet = mySingletonMap.keySet();
aStream.writeInt(keySet.size());
for (String aKey : keySet)
{
IoUtil.writeString(aStream, aKey);
aVal = mySingletonMap.get(aKey);
if (aVal instanceof Boolean)
{
aStream.writeByte(CODE_BOOL);
aStream.writeBoolean((Boolean)aVal);
}
else if (aVal instanceof Integer)
{
aStream.writeByte(CODE_INT);
aStream.writeInt((Integer)aVal);
}
else if (aVal instanceof Long)
{
aStream.writeByte(CODE_LONG);
aStream.writeLong((Long)aVal);
}
else if (aVal instanceof Short)
{
aStream.writeByte(CODE_SHORT);
aStream.writeLong((Short)aVal);
}
else if (aVal instanceof Float)
{
aStream.writeByte(CODE_FLOAT);
aStream.writeFloat((Float)aVal);
}
else if (aVal instanceof Double)
{
aStream.writeByte(CODE_DOUBLE);
aStream.writeDouble((Double)aVal);
}
else if (aVal instanceof String)
{
aStream.writeByte(CODE_STRING);
IoUtil.writeString(aStream, (String)aVal);
}
else
{
throw new RuntimeException("Unsupported Object: " + aVal);
}
}
}
}

View File

@@ -0,0 +1,35 @@
package glum.gui.dock;
import bibliothek.gui.dock.DockElement;
/**
* Base class for Dockables which would like to store their configuration via
* the PrimConfig mechanism. Note if the child class will be loaded with the
* PrimDockableFactory, then you should have a constructor with one of the
* following properties:.
* <LI> 1 arguments: Registry
* <LI> 0 arguments: Empty Constructor
* <BR>
* <BR>It is also important the method getFactoryID() is overridden so that it returns PrimDockableFactory.ID
*/
public interface PrimDock extends DockElement
{
/**
* Returns the PrimConfig associated with the Dockable
* @return
*/
public abstract PrimConfig getConfiguration();
/**
* Configures the Dockable with the aConfig
* @return
*/
public abstract void setConfiguration(PrimConfig aConfig);
// @Override
// public String getFactoryID()
// {
// return PrimDockableFactory.ID;
// }
}

View File

@@ -0,0 +1,197 @@
package glum.gui.dock;
import glum.reflect.ReflectUtil;
import glum.registry.Registry;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Map;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.DockFactory;
import bibliothek.gui.dock.dockable.DefaultDockablePerspective;
import bibliothek.gui.dock.layout.LocationEstimationMap;
import bibliothek.gui.dock.perspective.PerspectiveDockable;
import bibliothek.gui.dock.station.support.PlaceholderStrategy;
import bibliothek.util.xml.XElement;
/**
* Generic DockableFactory for creating PrimDocks.
* <P>
* Note that before this factory is used all PrimDockable class types must first be associated with a spawnName. This is
* used during serialization configuration associated with PrimDock. See method {@link PrimDockFactory#addSpawnMapping}
*/
public class PrimDockFactory implements DockFactory<PrimDock, DefaultDockablePerspective, PrimConfig>
{
// Constants
public static final String ID = "PrimDockFactory";
public static final String SpawnNameKey = "factory.spawnName";
// State var
protected Registry refRegistry;
protected BiMap<String, Class<? extends PrimDock>> spawnMap;
public PrimDockFactory(Registry aRegistry)
{
refRegistry = aRegistry;
spawnMap = HashBiMap.create();
}
/**
* Add a mapping for a PrimDockable to a the associated spawnName. It is mandatory
* that this mapping is always the same regardless of application executions, as
* this value will be serialized to the disk.
*/
public void addSpawnMapping(String spawnName, Class<? extends PrimDock> spawnClass)
{
// Ensure the spawnName is not already reserved
if (spawnMap.containsKey(spawnName) == true)
throw new RuntimeException("Previous mapping stored for spawnName:" + spawnName);
// Ensure the spawnClass is not already stored
if (spawnMap.inverse().containsKey(spawnClass) == true)
throw new RuntimeException("Previous mapping stored for spawnClass:" + spawnClass);
spawnMap.put(spawnName, spawnClass);
}
@Override
public String getID()
{
return ID;
}
@Override
public PrimConfig getLayout(PrimDock aDockable, Map<Dockable, Integer> children)
{
PrimConfig rConfig;
String spawnName;
rConfig = aDockable.getConfiguration();
// Store the associated spawnName used to instantiate the Dockable
spawnName = spawnMap.inverse().get(aDockable.getClass());
if (spawnName == null)
throw new RuntimeException("Factory is not configured properly. Failed to locate associated spawnName for class:" + aDockable.getClass());
// Ensure that the SpawnNameKey is not already reserved.
; // TODO
rConfig.setString(SpawnNameKey, spawnName);
return rConfig;
}
@Override
public PrimConfig getPerspectiveLayout(DefaultDockablePerspective element, Map<PerspectiveDockable, Integer> children)
{
// Perspectives are not supported
return null;
}
@Override
public void setLayout(PrimDock aDockable, PrimConfig aLayout, Map<Integer, Dockable> children, PlaceholderStrategy placeholders)
{
aDockable.setConfiguration(aLayout);
}
@Override
public void setLayout(PrimDock aDockable, PrimConfig aLayout, PlaceholderStrategy placeholders)
{
aDockable.setConfiguration(aLayout);
}
@Override
public void write(PrimConfig aLayout, DataOutputStream aStream) throws IOException
{
aLayout.writeBin(aStream);
}
@Override
public void write(PrimConfig layout, XElement element)
{
throw new UnsupportedOperationException();
}
@Override
public PrimConfig read(DataInputStream aStream, PlaceholderStrategy placeholders) throws IOException
{
PrimConfig rLayout;
rLayout = new PrimConfig();
rLayout.readBin(aStream);
return rLayout;
}
@Override
public PrimConfig read(XElement element, PlaceholderStrategy placeholders)
{
throw new UnsupportedOperationException();
}
@Override
public void estimateLocations(PrimConfig layout, LocationEstimationMap children)
{
; // Nothing to do
}
@Override
public PrimDock layout(PrimConfig layout, Map<Integer, Dockable> children, PlaceholderStrategy placeholders)
{
PrimDock aDockable;
aDockable = layout(layout, placeholders);
return aDockable;
}
@Override
public PrimDock layout(PrimConfig aLayout, PlaceholderStrategy placeholders)
{
PrimDock rDockable;
Class<? extends PrimDock> spawnClass;
Constructor<? extends PrimDock>spawnConstructor;
Class<?> parmTypes[] = {Registry.class};
Object parmValues[] = {refRegistry};
String spawnName;
spawnName = aLayout.getString(SpawnNameKey, null);
spawnClass = spawnMap.get(spawnName);
if (spawnClass == null)
throw new RuntimeException("Factory is not configured properly. Failed to locate associated class for spawn name:" + spawnName);
try
{
spawnConstructor = ReflectUtil.getConstructorSafe(spawnClass, parmTypes);
if (spawnConstructor != null)
rDockable = spawnConstructor.newInstance(parmValues);
else
rDockable = spawnClass.newInstance();
}
catch (Exception aExp)
{
throw new RuntimeException("Failed to instantite class.", aExp);
}
rDockable.setConfiguration(aLayout);
return rDockable;
}
@Override
public DefaultDockablePerspective layoutPerspective(PrimConfig layout, Map<Integer, PerspectiveDockable> children)
{
// Perspectives are not supported
return null;
}
@Override
public void layoutPerspective(DefaultDockablePerspective perspective, PrimConfig layout, Map<Integer, PerspectiveDockable> children)
{
; // Nothing to do
}
}

View File

@@ -0,0 +1,27 @@
package glum.gui.dock;
import bibliothek.gui.dock.DefaultDockable;
/**
* Base class for Dockables which would like to store their configuration via
* the PrimConfig mechanism. Note if the child class will be loaded with the
* PrimDockableFactory, then you should have a constructor with one of the
* following properties:.
* <LI> 1 arguments: Registry
* <LI> 0 arguments: Empty Constructor
*/
public abstract class PrimDockable extends DefaultDockable implements PrimDock
{
@Override
public abstract PrimConfig getConfiguration();
@Override
public abstract void setConfiguration(PrimConfig aConfig);
@Override
public String getFactoryID()
{
return PrimDockFactory.ID;
}
}

View File

@@ -0,0 +1,10 @@
package glum.gui.dock.action;
public interface Closeable
{
/**
* Notification method sent when this object should be closed
*/
public void close();
}

View File

@@ -0,0 +1,10 @@
package glum.gui.dock.action;
public interface Destroyable
{
/**
* Notification method sent when this object should be destroyed and all associated references should be released.
*/
public void destroy();
}

View File

@@ -0,0 +1,46 @@
package glum.gui.dock.action;
import javax.swing.Icon;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.action.actions.SimpleButtonAction;
/**
* A DockAction that will dismiss the targeted Dockable. If the targeted Dockable is a Destroyable, then is's destroy
* method will be called. If the targeted Dockable is a Closeable then it's close method is called. As a last resort the
* Dockable will just be hidden by removing it from it's parent. This should effectively hide the Dockable.
*/
public class DismissAction extends SimpleButtonAction
{
public DismissAction(String aText, Icon aIcon)
{
setText(aText);
setIcon(aIcon);
}
@Override
public void action(Dockable aDockable)
{
DockStation aStation;
super.action(aDockable);
// Destroy the Destroyable
if (aDockable instanceof Destroyable)
{
((Destroyable)aDockable).destroy();
}
// Close the Closable
else if (aDockable instanceof Closeable)
{
((Closeable)aDockable).close();
}
// Remove the Dockable from it's parent (last resort)
else
{
aStation = aDockable.getDockParent();
aStation.drag(aDockable);
}
}
}

View File

@@ -0,0 +1,33 @@
package glum.gui.dock.action;
import javax.swing.Icon;
import javax.swing.JComponent;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.action.actions.SimpleButtonAction;
/**
* A DockAction that will cause the target component to become visible. This action is useful to bring up a
* configuration panel related to the Dockable.
*/
public class MakeVisibleAction extends SimpleButtonAction
{
private JComponent targComp;
public MakeVisibleAction(JComponent aTargetComp, String aText, Icon aIcon)
{
targComp = aTargetComp;
setText(aText);
setIcon(aIcon);
}
@Override
public void action(Dockable aDockable)
{
super.action(aDockable);
// Make the component visible
targComp.setVisible(true);
}
}

View File

@@ -0,0 +1,33 @@
package glum.gui.dock.action;
import java.awt.event.ActionEvent;
import javax.swing.Action;
import javax.swing.Icon;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.action.actions.SimpleButtonAction;
/**
* An DockAction that will fire trigger an embedded java.awt Action
*/
public class SimpleDockAction extends SimpleButtonAction
{
protected Action refAction;
public SimpleDockAction(Action aAction, String aText, Icon aIcon)
{
refAction = aAction;
setText(aText);
setIcon(aIcon);
}
@Override
public void action(Dockable dockable)
{
super.action(dockable);
refAction.actionPerformed(new ActionEvent(this, 0, "SimpleDockAction"));
}
}

View File

@@ -0,0 +1,66 @@
package glum.gui.dock.action;
import javax.swing.Icon;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.action.actions.SimpleButtonAction;
/**
* An DockAction that will fire trigger an embedded java.awt Action
*/
public class ToggleAction extends SimpleButtonAction
{
// State vars
protected boolean isActive;
// Gui vars
protected Icon falseIcon, trueIcon;
public ToggleAction(String aText, Icon aFalseIcon, Icon aTrueIcon, boolean aIsActive)
{
super();
isActive = aIsActive;
falseIcon = aFalseIcon;
trueIcon = aTrueIcon;
setText(aText);
updateGui();
}
/**
* Accessor methods
*/
public boolean getIsActive()
{
return isActive;
}
public void setIsActive(boolean aBool)
{
isActive = aBool;
updateGui();
}
@Override
public void action(Dockable aDockable)
{
isActive = !isActive;
updateGui();
super.action(aDockable);
}
/**
* Utility method
*/
private void updateGui()
{
if (isActive == true)
setIcon(trueIcon);
else
setIcon(falseIcon);
}
}

View File

@@ -0,0 +1,105 @@
package glum.gui.dock.alt;
import glum.gui.dock.action.Destroyable;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JDialog;
import javax.swing.JFrame;
import bibliothek.gui.DockFrontend;
import bibliothek.gui.DockStation;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.ScreenDockStation;
import bibliothek.gui.dock.station.screen.ScreenDockFrame;
public class AltScreenDockFrame extends ScreenDockFrame implements WindowListener
{
private DockFrontend refFrontend;
private JFrame frame;
/**
* Creates a new ScroonDockWindow with an associated JFrame.
*
* @param aStation
* the station to which this dialog is responsible
* @param isUndecorated
* whether the dialog should suppress the default decorations
*/
public AltScreenDockFrame(DockFrontend aFrontend, ScreenDockStation aStation, boolean isUndecorated)
{
super(aStation, isUndecorated);
refFrontend = aFrontend;
// Set up the Jframe
frame = getFrame();
frame.addWindowListener(this);
frame.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
}
@Override
public void windowOpened(WindowEvent e)
{
; // Nothing to do
}
@Override
public void windowClosing(WindowEvent e)
{
Dockable dockable;
System.out.println("Window should be closed....");
dockable = getDockable();
// Destroy the associated Destroyable
if (dockable instanceof Destroyable)
{
((Destroyable)dockable).destroy();
}
// Attempt to hide the (if it is registered) Dockable
else if (refFrontend != null && refFrontend.listDockables().contains(dockable) == true)
{
refFrontend.hide(dockable);
}
// Just hide the dockable
else
{
DockStation aStation;
aStation = dockable.getDockParent();
aStation.drag(dockable);
}
}
@Override
public void windowClosed(WindowEvent e)
{
; // Nothing to do
}
@Override
public void windowIconified(WindowEvent e)
{
; // Nothing to do
}
@Override
public void windowDeiconified(WindowEvent e)
{
; // Nothing to do
}
@Override
public void windowActivated(WindowEvent e)
{
; // Nothing to do
}
@Override
public void windowDeactivated(WindowEvent e)
{
; // Nothing to do
}
}

View File

@@ -0,0 +1,117 @@
package glum.gui.dock.alt;
import glum.gui.dock.BaseDockable;
import java.awt.Window;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.ScreenDockStation;
import bibliothek.gui.dock.action.DefaultDockActionSource;
import bibliothek.gui.dock.action.DockAction;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.action.LocationHint;
/**
* Alternative ScreenDockStation which provides no default direct/indirect action offers.
*/
public class AltScreenDockStation extends ScreenDockStation
{
// Action vars
private List<DockAction> directDockActionList;
private List<DockAction> indirectDockActionList;
// Lock vars
private Set<Dockable> lockSet;
private boolean isLocked;
public AltScreenDockStation(Window owner)
{
super(owner);
directDockActionList = Lists.newArrayList();
indirectDockActionList = Lists.newArrayList();
lockSet = Sets.newHashSet();
isLocked = false;
}
/**
* Registers a DockAction to always be available for direct Dockables
*/
public void addDirectActionOffer(DockAction aDockAction)
{
directDockActionList.add(aDockAction);
}
/**
* Registers a DockAction to always be available for indirect Dockables
*/
public void addIndirectActionOffer(DockAction aDockAction)
{
indirectDockActionList.add(aDockAction);
}
/**
* Utility method to force this station to accept no further Dockables. Only dockables that are currently children
* will be accepted.
*/
public void lockStation(boolean aBool)
{
isLocked = aBool;
if (isLocked == false)
{
lockSet.clear();
return;
}
// Record all of the valid children when the lock is triggered
for (int c1 = 0; c1 < getDockableCount(); c1++)
{
lockSet.add(getDockable(c1));
}
}
@Override
public boolean accept(Dockable aChild)
{
// If we are locked then never accept any Dockable, which was not recorded as valid when the lock happened
if (isLocked == true && lockSet.contains(aChild) == false)
return false;
// Never accept any Dockable that has been marked as nontransferable
if (aChild instanceof BaseDockable)
return ((BaseDockable)aChild).isTransferable(this);
return super.accept(aChild);
}
@Override
public DefaultDockActionSource getDirectActionOffers(Dockable dockable)
{
DefaultDockActionSource source;
source = new DefaultDockActionSource(new LocationHint(LocationHint.DIRECT_ACTION, LocationHint.VERY_RIGHT));
source.add(directDockActionList.toArray(new DockAction[0]));
return source;
}
@Override
public DockActionSource getIndirectActionOffers(Dockable dockable)
{
DefaultDockActionSource source;
source = new DefaultDockActionSource(new LocationHint(LocationHint.INDIRECT_ACTION, LocationHint.VERY_RIGHT));
source.add(indirectDockActionList.toArray(new DockAction[0]));
return source;
}
}

View File

@@ -0,0 +1,69 @@
package glum.gui.dock.alt;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.Window;
import javax.swing.Icon;
import bibliothek.gui.DockFrontend;
import bibliothek.gui.dock.ScreenDockStation;
import bibliothek.gui.dock.station.screen.AbstractScreenDockWindow;
import bibliothek.gui.dock.station.screen.DefaultScreenDockWindowFactory;
import bibliothek.gui.dock.station.screen.ScreenDockDialog;
import bibliothek.gui.dock.station.screen.ScreenDockWindow;
/**
* Alternative ScreenDockWindowFactory that return AltScreenDockFrame instead of DefaultScreenDockFrame. Also the
* returned windows will by default have the typical OS decorations.
*/
public class AltScreenDockWindowFactory extends DefaultScreenDockWindowFactory
{
private DockFrontend refFrontend;
public AltScreenDockWindowFactory(DockFrontend aFrontend)
{
refFrontend = aFrontend;
setKind(Kind.FRAME);
setUndecorated(false);
}
@Override
public ScreenDockWindow createWindow(ScreenDockStation station)
{
Kind kind;
boolean undecorated;
boolean showDockTitle;
Icon titleIcon;
String titleText;
kind = getKind();
undecorated = isUndecorated();
showDockTitle = isShowDockTitle();
titleIcon = getTitleIcon();
titleText = getTitleText();
AbstractScreenDockWindow window;
if (kind == Kind.FRAME)
{
window = new AltScreenDockFrame(refFrontend, station, undecorated);
}
else
{
Window owner = station.getOwner();
if (owner instanceof Frame)
window = new ScreenDockDialog(station, (Frame)owner, undecorated);
else if (owner instanceof Dialog)
window = new ScreenDockDialog(station, (Dialog)owner, undecorated);
else
window = new ScreenDockDialog(station, undecorated);
}
window.setShowTitle(showDockTitle);
window.setTitleIcon(titleIcon);
window.setTitleText(titleText);
return window;
}
}

View File

@@ -0,0 +1,176 @@
package glum.gui.dock.alt;
import glum.gui.dock.BaseDockable;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import bibliothek.gui.Dockable;
import bibliothek.gui.dock.SplitDockStation;
import bibliothek.gui.dock.action.DefaultDockActionSource;
import bibliothek.gui.dock.action.DockAction;
import bibliothek.gui.dock.action.DockActionSource;
import bibliothek.gui.dock.action.LocationHint;
public class AltSplitDockStation extends SplitDockStation
{
// Action vars
private List<DockAction> directDockActionList;
private List<DockAction> localDockActionList;
private List<DockAction> indirectDockActionList;
// Lock vars
private Set<Dockable> lockSet;
private boolean isLocked;
public AltSplitDockStation()
{
super();
directDockActionList = Lists.newArrayList();
localDockActionList = Lists.newArrayList();
indirectDockActionList = Lists.newArrayList();
lockSet = Sets.newHashSet();
isLocked = false;
}
/**
* Registers a DockAction to always be available for direct Dockables
*/
public void addDirectActionOffer(DockAction aDockAction)
{
directDockActionList.add(aDockAction);
}
/**
* Registers a DockAction to always be available for local Dockables
*/
public void addLocalActionOffer(DockAction aDockAction)
{
localDockActionList.add(aDockAction);
}
/**
* Registers a DockAction to always be available for indirect Dockables
*/
public void addIndirectActionOffer(DockAction aDockAction)
{
indirectDockActionList.add(aDockAction);
}
/**
* Utility method to force this station to accept no further Dockables. Only dockables that are currently children
* will be accepted.
*/
public void lockStation(boolean aBool)
{
isLocked = aBool;
if (isLocked == false)
{
lockSet.clear();
return;
}
// Record all of the valid children when the lock is triggered
for (int c1 = 0; c1 < getDockableCount(); c1++)
{
lockSet.add(getDockable(c1));
}
}
@Override
public boolean accept(Dockable aChild)
{
// If we are locked then never accept any Dockable, which was not recorded as valid when the lock happened
if (isLocked == true && lockSet.contains(aChild) == false)
return false;
// Never accept any Dockable that has been marked as nontransferable
if (aChild instanceof BaseDockable)
return ((BaseDockable)aChild).isTransferable(this);
// Default behavior for non BaseDockables
return super.accept(aChild);
}
//
// @Override
// protected boolean acceptable(Dockable old, Dockable next)
// {
// return false;
//// // Never accept any Dockable that has been marked as nontransferable
//// if (old instanceof BaseDockable || next instanceof BaseDockable)
//// {
//// if (((BaseDockable)old).isTransferable() == false)
//// return false;
////
//// if (((BaseDockable)next).isTransferable() == false)
//// return false;
//// }
////
////
//// // TODO Auto-generated method stub
//// return super.acceptable(old, next);
// }
// @Override
// public boolean canDrag(Dockable aDockable)
// {
// if (lockSet.contains(aDockable) == true)
// return false;
//
// return super.canDrag(aDockable);
// }
//
//
// @Override
// public boolean canReplace(Dockable oldDockable, Dockable nextDockable)
// {
// if (lockSet.contains(oldDockable) == true)
// return false;
//
// if (lockSet.contains(nextDockable) == true)
// return false;
//
// return super.canReplace(oldDockable, nextDockable);
// }
@Override
public DefaultDockActionSource getDirectActionOffers(Dockable dockable)
{
DefaultDockActionSource source;
source = new DefaultDockActionSource(new LocationHint(LocationHint.DIRECT_ACTION, LocationHint.VERY_RIGHT));
source.add(directDockActionList.toArray(new DockAction[0]));
return source;
}
@Override
public DockActionSource getLocalActionOffers()
{
DefaultDockActionSource source;
source = new DefaultDockActionSource(new LocationHint(LocationHint.DOCKABLE, LocationHint.RIGHT));
source.add(localDockActionList.toArray(new DockAction[0]));
return source;
}
@Override
public DockActionSource getIndirectActionOffers(Dockable dockable)
{
DefaultDockActionSource source;
source = new DefaultDockActionSource(new LocationHint(LocationHint.INDIRECT_ACTION, LocationHint.VERY_RIGHT));
source.add(indirectDockActionList.toArray(new DockAction[0]));
return source;
}
}

View File

@@ -0,0 +1,50 @@
package glum.gui.document;
import java.awt.event.*;
import javax.swing.text.*;
import javax.swing.JTextField;
public abstract class BaseDocument extends PlainDocument implements ActionListener, FocusListener
{
// State vars
protected JTextField ownerTF;
public BaseDocument(JTextField aOwnerTF)
{
super();
ownerTF = aOwnerTF;
}
/**
* Get the owner of this Document model
* Todo: This method should no longer be needed.
*/
public JTextField getOwner()
{
return ownerTF;
}
@Override
public void actionPerformed(ActionEvent e)
{
formalizeInput();
}
@Override
public void focusGained(FocusEvent e)
{
}
@Override
public void focusLost(FocusEvent e)
{
formalizeInput();
}
/**
* Updates the text to reflect a formal output
*/
public abstract void formalizeInput();
}

View File

@@ -0,0 +1,61 @@
package glum.gui.document;
import javax.swing.JTextField;
public abstract class BaseNumberDocument extends BaseDocument
{
// State vars
protected double minVal, maxVal;
protected boolean formalizeDoc;
protected int numAvailCols;
public BaseNumberDocument(JTextField aOwner, double aMinVal, double aMaxVal)
{
super(aOwner);
setMinMaxValue(aMinVal, aMaxVal);
formalizeDoc = false;
numAvailCols = -1;
}
/**
* Updates the new range of valid numbers.
*/
public void setMinMaxValue(double aMinVal, double aMaxVal)
{
minVal = aMinVal;
maxVal = aMaxVal;
// Insanity check
if (minVal >= maxVal)
throw new RuntimeException("Illogical range. Range: [" + minVal + "," + maxVal + "]");
}
@Override
public void formalizeInput()
{
String currStr;
// Insanity check
if (ownerTF == null)
return;
// Is formalization required
if (formalizeDoc == false)
return;
// Disassociate ourselves from event handling
ownerTF.removeActionListener(this);
ownerTF.removeFocusListener(this);
currStr = ownerTF.getText();
ownerTF.setText(currStr);
// Reassociate ourselves from event handling
ownerTF.addActionListener(this);
ownerTF.addFocusListener(this);
}
}

View File

@@ -0,0 +1,65 @@
package glum.gui.document;
import java.util.Set;
import javax.swing.JTextField;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import com.google.common.collect.Sets;
/**
* Specialized Document designed to accept only the specified input chars
*/
public class CharDocument extends BaseDocument
{
private Set<Character> validSet;
public CharDocument(JTextField aOwner, String validCharStr)
{
this(aOwner, validCharStr, true);
}
public CharDocument(JTextField aOwner, String validCharStr, boolean isCaseSensitive)
{
super(aOwner);
validSet = Sets.newHashSet();
for (int c1 = 0; c1 < validCharStr.length(); c1++)
{
validSet.add(validCharStr.charAt(c1));
if (isCaseSensitive == false)
{
validSet.add(Character.toLowerCase(validCharStr.charAt(c1)));
validSet.add(Character.toUpperCase(validCharStr.charAt(c1)));
}
}
}
@Override
public void formalizeInput()
{
; // Nothing to do
}
@Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException
{
char aChar;
// Insanity check
if (str == null)
return;
// Ensure all of the characters in str are in the valid set
for (int c1 = 0; c1 < str.length(); c1++)
{
aChar = str.charAt(c1);
if (validSet.contains(aChar) == false)
throw new BadLocationException("Invalid character: " + aChar, offs);
}
super.insertString(offs, str, a);
}
}

View File

@@ -0,0 +1,109 @@
package glum.gui.document;
import javax.swing.text.*;
import javax.swing.JTextField;
import glum.gui.GuiUtil;
public class NumberDocument extends BaseNumberDocument
{
protected boolean allowFloats;
// protected NumberUnit myUnit;
public NumberDocument(JTextField aOwnerTF, boolean aFormalizeDoc)
{
super(aOwnerTF, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
allowFloats = true;
formalizeDoc = aFormalizeDoc;
}
/**
* Sets in whether floating point numbers are allowed
*/
public void setAllowFloats(boolean aBool)
{
allowFloats = aBool;
}
@Override
public void insertString(int offs, String str, AttributeSet a) throws BadLocationException
{
String bStr, eStr, resultStr;
double aVal;
char aChar;
// Insanity check
if (str == null)
return;
// Special cases
aChar = str.charAt(0);
if (offs == 0)
{
// Reject if we need a positive number
if (aChar == '-' && minVal >= 0)
{
throw new BadLocationException("Negative values are not allowed.", offs);
}
// Potential valid string that starts off an int
if ((aChar == '-') && str.length() == 1)
{
super.insertString(offs, str, a);
return;
}
// Potential valid string that starts off a double
if ((aChar == '.') && str.length() == 1 && allowFloats == true)
{
super.insertString(offs, str, a);
return;
}
}
else if (offs == 1 && str.length() == 1 && aChar == '.')
{
// Potential valid string that starts off a double
if ("-".equals(getText(0, offs)) == true && allowFloats == true)
{
super.insertString(offs, str, a);
return;
}
}
// Reject if we detect a floating point, but it is not allowed
if (str.contains(".") == true && allowFloats == false)
throw new BadLocationException("Only integers are allowed.", offs);
// Ensure we do not exceed number of columns
if (numAvailCols > 0)
{
if (offs + str.length() >= numAvailCols)
throw new BadLocationException("Too many characters to insert.", offs);
}
// Form the resultant string
bStr = "";
eStr = "";
if (offs > 0)
bStr = getText(0, offs);
eStr = getText(offs, getLength() - offs);
resultStr = bStr + str + eStr;
// Ensure the resultant is sensical
aVal = GuiUtil.readDouble(resultStr, Double.NaN);
if (Double.isNaN(aVal) == true)
throw new BadLocationException("Nonsensical number.", offs);
// Ensure that the number is in range
if (aVal > maxVal)
throw new BadLocationException("Out of numerical range.", offs);
else if (aVal < minVal)
throw new BadLocationException("Out of numerical range.", offs);
// Insert the string
super.insertString(offs, str, a);
}
}

View File

@@ -0,0 +1,56 @@
package glum.gui.icon;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Polygon;
import javax.swing.plaf.metal.MetalLookAndFeel;
public class ArrowNorthIcon extends BaseIcon
{
/**
* Constructor
*/
public ArrowNorthIcon(int aDim)
{
super(aDim);
}
@Override
public void paintIcon(Component aComp, Graphics g, int x, int y)
{
Polygon aPolygon;
int w, h;
int hW, hH;
w = getIconWidth();
h = getIconHeight();
hW = w / 2;
hH = h / 2;
g.setColor(Color.BLACK);
if (aComp.isEnabled() == false)
g.setColor(MetalLookAndFeel.getControlDisabled());
if (IconUtil.isPressed(aComp) == true)
y += 2;
g.translate(x + hW, y + hH);
aPolygon = new Polygon();
aPolygon.addPoint(0, -hH);
aPolygon.addPoint(-hW, 0);
aPolygon.addPoint(-hW/4, 0);
aPolygon.addPoint(-hW/4, hH);
aPolygon.addPoint(+hW/4, hH);
aPolygon.addPoint(+hW/4, 0);
aPolygon.addPoint(+hW, 0);
g.fillPolygon(aPolygon);
g.translate(-(x + hW), -(y + hH));
}
}

View File

@@ -0,0 +1,56 @@
package glum.gui.icon;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Polygon;
import javax.swing.plaf.metal.MetalLookAndFeel;
public class ArrowSouthIcon extends BaseIcon
{
/**
* Constructor
*/
public ArrowSouthIcon(int aDim)
{
super(aDim);
}
@Override
public void paintIcon(Component aComp, Graphics g, int x, int y)
{
Polygon aPolygon;
int w, h;
int hW, hH;
w = getIconWidth();
h = getIconHeight();
hW = w / 2;
hH = h / 2;
g.setColor(Color.BLACK);
if (aComp.isEnabled() == false)
g.setColor(MetalLookAndFeel.getControlDisabled());
if (IconUtil.isPressed(aComp) == true)
y += 2;
g.translate(x + hW, y + hH);
aPolygon = new Polygon();
aPolygon.addPoint(0, +hH);
aPolygon.addPoint(+hW, 0);
aPolygon.addPoint(+hW/4, 0);
aPolygon.addPoint(+hW/4, -hH);
aPolygon.addPoint(-hW/4, -hH);
aPolygon.addPoint(-hW/4, 0);
aPolygon.addPoint(-hW, 0);
g.fillPolygon(aPolygon);
g.translate(-(x + hW), -(y + hH));
}
}

View File

@@ -0,0 +1,29 @@
package glum.gui.icon;
import javax.swing.Icon;
public abstract class BaseIcon implements Icon
{
protected int mDim;
/**
* Constructor
*/
public BaseIcon(int aDim)
{
mDim = aDim;
}
@Override
public int getIconWidth()
{
return mDim;
}
@Override
public int getIconHeight()
{
return mDim;
}
}

View File

@@ -0,0 +1,58 @@
package glum.gui.icon;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.plaf.metal.MetalLookAndFeel;
public class DeleteIcon extends BaseIcon
{
public DeleteIcon(int aDim)
{
super(aDim);
}
@Override
public void paintIcon(Component aComp, Graphics g, int x, int y)
{
Graphics2D g2d;
BasicStroke aStroke;
int w, h;
int hW, hH, dX, dY;
int shrinkSize;
float strokeW;
w = getIconWidth();
h = getIconHeight();
hW = w / 2;
hH = h / 2;
g2d = (Graphics2D)g;
g2d.setColor(Color.RED.darker());
if (aComp.isEnabled() == false)
g2d.setColor(MetalLookAndFeel.getControlDisabled());
shrinkSize = 2;
if (IconUtil.isPressed(aComp) == true)
shrinkSize += 2;
g2d.translate(x + hW, y + hH);
strokeW = getIconWidth() / 7.0f;
aStroke = new BasicStroke(strokeW, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
g2d.setStroke(aStroke);
dX = hW - shrinkSize;
dY = hH - shrinkSize;
g2d.drawLine(-dX,-dY, +dX,+dY);
g2d.drawLine(-dX,+dY, +dX,-dY);
g2d.translate(-(x + hW), -(y + hH));
}
}

View File

@@ -0,0 +1,36 @@
package glum.gui.icon;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.Icon;
public class EmptyIcon implements Icon
{
protected int width, height;
EmptyIcon(int aWidth, int aHeight)
{
width = aWidth;
height = aHeight;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y)
{
; // Nothing to do
}
@Override
public int getIconWidth()
{
return width;
}
@Override
public int getIconHeight()
{
return height;
}
}

View File

@@ -0,0 +1,35 @@
package glum.gui.icon;
import java.awt.Component;
import javax.swing.ImageIcon;
import javax.swing.JButton;
public class IconUtil
{
/**
* Utility method to load the Icon specified by the resource path.
*/
public static ImageIcon loadIcon(String iconPath)
{
// URL aURL;
//
// aURL = IconUtil.class.getClassLoader().getResource(iconPath);
// if (aURL == null)
// throw new RuntimeException("Failed to load icon for path: " + iconPath);
//
// return new ImageIcon(aURL);
return new ImageIcon(ClassLoader.getSystemResource(iconPath));
}
/**
* Utility method to test if aComp is pressed down.
*/
public static boolean isPressed(Component aComp)
{
if (aComp instanceof JButton == false)
return false;
return ((JButton)aComp).getModel().isPressed();
}
}

View File

@@ -0,0 +1,40 @@
package glum.gui.info;
import glum.zio.ZinStream;
import glum.zio.ZoutStream;
import glum.zio.raw.ZioRaw;
import java.io.IOException;
public class FilePathInfo implements ZioRaw
{
// State vars
protected String filePath;
public FilePathInfo()
{
filePath = null;
}
public FilePathInfo(String aFilePath)
{
filePath = aFilePath;
}
@Override
public void zioReadRaw(ZinStream aStream) throws IOException
{
aStream.readVersion(0);
filePath = aStream.readString();
}
@Override
public void zioWriteRaw(ZoutStream aStream) throws IOException
{
aStream.writeVersion(0);
aStream.writeString(filePath);
}
}

View File

@@ -0,0 +1,79 @@
package glum.gui.info;
import glum.zio.ZinStream;
import glum.zio.ZoutStream;
import glum.zio.raw.ZioRaw;
import glum.zio.util.ZioUtil;
import java.awt.*;
import java.io.*;
public class WindowInfo implements ZioRaw
{
// Raw vars
protected Point position;
protected Dimension size;
protected boolean isVisible;
/**
* Constructor
*/
public WindowInfo()
{
position = null;
size = null;
isVisible = false;
}
public WindowInfo(Component aComponent)
{
this();
if (aComponent == null)
return;
position = aComponent.getLocation();
size = aComponent.getSize();
isVisible = aComponent.isVisible();
}
/**
* configure - Syncs aComponent with parmaters of this WindowInfo
*/
public void configure(Component aComponent)
{
if (position != null)
aComponent.setLocation(position);
if (size != null)
{
aComponent.setPreferredSize(size);
aComponent.setSize(size);
}
}
@Override
public void zioReadRaw(ZinStream aStream) throws IOException
{
aStream.readVersion(0);
isVisible = aStream.readBool();
position = ZioUtil.readPoint(aStream);
size = ZioUtil.readDimension(aStream);
}
@Override
public void zioWriteRaw(ZoutStream aStream) throws IOException
{
aStream.writeVersion(0);
aStream.writeBool(isVisible);
ZioUtil.writePoint(aStream, position);
ZioUtil.writeDimension(aStream, size);
}
}

View File

@@ -0,0 +1,75 @@
package glum.gui.misc;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.LinkedList;
import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
public class BooleanCellEditor extends AbstractCellEditor implements ActionListener, TableCellEditor
{
// State vars
protected Collection<ActionListener> myListeners;
protected JCheckBox refCheckBox;
/**
* Constructor
*/
public BooleanCellEditor()
{
this(null);
}
public BooleanCellEditor(ActionListener aListener)
{
myListeners = new LinkedList<ActionListener>();
if (aListener != null)
myListeners.add(aListener);
refCheckBox = new JCheckBox("", false);
refCheckBox.addActionListener(this);
}
public void addActionListener(ActionListener aListener)
{
myListeners.add(aListener);
}
public void removeActionListener(ActionListener aListener)
{
myListeners.remove(aListener);
}
@Override
public void actionPerformed(ActionEvent aEvent)
{
fireEditingStopped();
aEvent = new ActionEvent(this, aEvent.getID(), "BooleanCell edited.");
for (ActionListener aListener : myListeners)
aListener.actionPerformed(aEvent);
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
// Update our checkbox with the appropriate state
refCheckBox.removeActionListener(this);
if (value instanceof Boolean)
refCheckBox.setSelected((Boolean)value);
refCheckBox.addActionListener(this);
return refCheckBox;
}
@Override
public Object getCellEditorValue()
{
return refCheckBox.isSelected();
}
}

View File

@@ -0,0 +1,28 @@
package glum.gui.misc;
import java.awt.Component;
import javax.swing.JCheckBox;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
public class BooleanCellRenderer extends JCheckBox implements TableCellRenderer
{
public BooleanCellRenderer()
{
super("", false);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
if (value instanceof Boolean)
{
setSelected((Boolean)value);
return this;
}
return null;
}
}

View File

@@ -0,0 +1,60 @@
package glum.gui.misc;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
public class ColorCellRenderer extends JPanel implements TableCellRenderer
{
// State vars
protected Color activeColor;
/**
* Constructor
*/
public ColorCellRenderer()
{
super();
activeColor = null;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
activeColor = null;
if (value instanceof Color)
activeColor = (Color)value;
if (activeColor != null)
setBackground(activeColor);
else
setBackground(Color.LIGHT_GRAY);
return this;
}
@Override
public void paint(Graphics g)
{
Graphics2D g2d;
super.paint(g);
g2d = (Graphics2D)g;
// Bail if we have a valid color
if (activeColor != null)
return;
// Draw a red x if no valid color
g2d.setColor(Color.RED);
g2d.drawLine(0, 0, getWidth(), getHeight());
g2d.drawLine(getWidth(), 0, 0, getHeight());
}
}

View File

@@ -0,0 +1,89 @@
package glum.gui.misc;
import java.awt.*;
public enum MultiState
{
Checked("Checked", "MultiState.Checked")
{
@Override
public void render(Graphics g, int x, int y, int iconSize)
{
g.fillRect(x + 3, y + 5, 2, iconSize - 8);
g.drawLine(x + (iconSize - 4), y + 3, x + 5, y + (iconSize - 6));
g.drawLine(x + (iconSize - 4), y + 4, x + 5, y + (iconSize - 5));
}
},
Crossed("Crossed", "MultiState.Crossed")
{
@Override
public void render(Graphics g, int x, int y, int iconSize)
{
g.drawLine(x + (iconSize - 4), y + 2, x + 3, y + (iconSize - 5));
g.drawLine(x + (iconSize - 4), y + 3, x + 3, y + (iconSize - 4));
g.drawLine(x + 3, y + 2, x + (iconSize - 4), y + (iconSize - 5));
g.drawLine(x + 3, y + 3, x + (iconSize - 4), y + (iconSize - 4));
}
},
Mixed("Mixed", "MultiState.Mixed")
{
@Override
public void render(Graphics g, int x, int y, int iconSize)
{
int cX, cY;
cX = x + iconSize / 2;
cY = y + iconSize / 2;
g.drawOval(cX - 2, cY - 2, 4, 4);
g.drawOval(cX - 3, cY - 3, 6, 6);
}
},
None("None", "MultiState.None")
{
@Override
public void render(Graphics g, int x, int y, int iconSize)
{
; // Nothing to render
}
};
// Vars
private final String userStr;
private final String referenceName;
/**
* Constructor
*/
MultiState(String aUserStr, String aReferenceName)
{
userStr = aUserStr;
referenceName = aReferenceName;
}
/**
* getReferenceName
*/
public String getReferenceName()
{
return referenceName;
}
/**
* Method to draw a representative icon of the state
*/
public void render(Graphics g, int x, int y, int iconSize)
{
this.render(g, x, y, iconSize);
}
@Override
public String toString()
{
return userStr;
}
}

View File

@@ -0,0 +1,197 @@
package glum.gui.misc;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.plaf.*;
public class MultiStateCheckBox extends JCheckBox implements MouseListener
{
// State vars
protected MultiStateModel model;
protected HashMap<Object, MultiState> nextStateMap;
/**
* Constructor
*/
public MultiStateCheckBox(String text, boolean is3StateCycle)
{
super(text);
// Set up our replacement icon
super.setIcon(new MultiStateIcon());
// Construct the cycle order of the MultiState checkbox
nextStateMap = new HashMap<Object, MultiState>();
nextStateMap.put(false, MultiState.Checked);
nextStateMap.put(true, MultiState.None);
if (is3StateCycle == false)
{
nextStateMap.put(MultiState.Checked, MultiState.None);
nextStateMap.put(MultiState.None, MultiState.Checked);
nextStateMap.put(MultiState.Mixed, MultiState.None);
}
else
{
nextStateMap.put(MultiState.None, MultiState.Checked);
nextStateMap.put(MultiState.Checked, MultiState.Mixed);
nextStateMap.put(MultiState.Mixed, MultiState.None);
// nextStateMap.put(MultiState.Mixed, MultiState.Crossed);
}
// Register for mouse events
super.addMouseListener(this);
// Reset the keyboard action map
rebuildKeyboardMap();
// set the model to the adapted model
model = new MultiStateModel(getModel(), nextStateMap);
setModel(model);
setState(MultiState.None);
}
public MultiStateCheckBox(String text, HashMap<Object, MultiState> aNextStateMap)
{
super(text, false);
// Insanity check
if (aNextStateMap == null)
throw new NullPointerException();
// Save off the custom cycle order of the MultiState checkbox
nextStateMap = aNextStateMap;
}
public MultiStateCheckBox(String text)
{
this(text, false);
}
public MultiStateCheckBox()
{
this(null);
}
@Override
public void doClick()
{
MouseEvent aEvent;
aEvent = new MouseEvent(this, MouseEvent.MOUSE_PRESSED, 0, 0, 0, 0, 0, false);
handleMouseEvent(aEvent);
model.advanceToNextState();
}
/** No one may add mouse listeners, not even Swing! */
@Override
public void addMouseListener(MouseListener l)
{
}
/** No one may set a new icon */
@Override
public void setIcon(Icon icon)
{
}
// @formatter:off
/**
* MouseListener interface methods
*/
@Override public void mouseClicked(MouseEvent e) { handleMouseEvent(e); }
@Override public void mouseEntered(MouseEvent e) { handleMouseEvent(e); }
@Override public void mouseExited(MouseEvent e) { handleMouseEvent(e); }
@Override public void mousePressed(MouseEvent e) { handleMouseEvent(e); }
@Override public void mouseReleased(MouseEvent e) { handleMouseEvent(e); }
// @formatter:on
/**
* Return the current state of the Checkbox
*/
public MultiState getState()
{
return model.getState();
}
@Override
public void setSelected(boolean selected)
{
if (selected)
setState(MultiState.Checked);
else
setState(MultiState.None);
}
/**
* setState
*/
public void setState(MultiState state)
{
model.setState(state);
repaint();
}
/**
* Utility method for handling various MouseEvents
*/
protected void handleMouseEvent(MouseEvent e)
{
int aID;
aID = e.getID();
if (aID == MouseEvent.MOUSE_ENTERED)
{
model.setArmed(true);
}
else if (aID == MouseEvent.MOUSE_EXITED)
{
model.setArmed(false);
}
else if (aID == MouseEvent.MOUSE_RELEASED)
{
if (model.isArmed() == true)
model.advanceToNextState();
model.setPressed(false);
}
else if (aID == MouseEvent.MOUSE_PRESSED)
{
grabFocus();
model.setPressed(true);
}
}
/**
* Utility method to set up a custom keyboard map
*/
protected void rebuildKeyboardMap()
{
ActionMap map;
map = new ActionMapUIResource();
map.put("pressed", new AbstractAction()
{
@Override
public void actionPerformed(ActionEvent e)
{
model.setPressed(true);
}
});
map.put("released", new AbstractAction()
{
@Override
public void actionPerformed(ActionEvent e)
{
model.setPressed(false);
model.advanceToNextState();
}
});
SwingUtilities.replaceUIActionMap(this, map);
}
}

View File

@@ -0,0 +1,68 @@
package glum.gui.misc;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collection;
import java.util.LinkedList;
import javax.swing.AbstractCellEditor;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;
public class MultiStateCheckBoxCellEditor extends AbstractCellEditor implements ActionListener, TableCellEditor
{
// State vars
protected Collection<ActionListener> myListeners;
protected MultiStateCheckBox refMultiStateCheckBox;
/**
* Constructor
*/
public MultiStateCheckBoxCellEditor()
{
myListeners = new LinkedList<ActionListener>();
refMultiStateCheckBox = new MultiStateCheckBox("", false);
refMultiStateCheckBox.addActionListener(this);
}
public void addActionListener(ActionListener aListener)
{
myListeners.add(aListener);
}
public void removeActionListener(ActionListener aListener)
{
myListeners.remove(aListener);
}
@Override
public void actionPerformed(ActionEvent aEvent)
{
fireEditingStopped();
aEvent = new ActionEvent(this, aEvent.getID(), "MultiStateCheckBoxCell edited.");
for (ActionListener aListener : myListeners)
aListener.actionPerformed(aEvent);
}
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
{
// Update our checkbox with the appropriate state
refMultiStateCheckBox.removeActionListener(this);
if (value instanceof MultiState)
refMultiStateCheckBox.setState((MultiState)value);
else if (value instanceof Boolean)
refMultiStateCheckBox.setSelected((Boolean)value);
refMultiStateCheckBox.addActionListener(this);
return refMultiStateCheckBox;
}
@Override
public Object getCellEditorValue()
{
return refMultiStateCheckBox.getState();
}
}

View File

@@ -0,0 +1,40 @@
package glum.gui.misc;
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
public class MultiStateCheckBoxCellRenderer extends MultiStateCheckBox implements TableCellRenderer
{
// State vars
protected MultiStateCheckBox refMultiStateCheckBox;
/**
* Constructor
*/
public MultiStateCheckBoxCellRenderer()
{
refMultiStateCheckBox = new MultiStateCheckBox("", false);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
if (value instanceof MultiState)
{
refMultiStateCheckBox.setState((MultiState)value);
return refMultiStateCheckBox;
}
else if (value instanceof Boolean)
{
if ((Boolean)value == true)
refMultiStateCheckBox.setState(MultiState.Checked);
else
refMultiStateCheckBox.setState(MultiState.None);
return refMultiStateCheckBox;
}
return null;
}
}

View File

@@ -0,0 +1,174 @@
package glum.gui.misc;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
public class MultiStateCheckBoxHeader extends MultiStateCheckBox implements TableCellRenderer, MouseListener, MouseMotionListener
{
// State vars
protected JTableHeader refHeader;
protected int column;
/**
* Constructor
*/
public MultiStateCheckBoxHeader(JTable aTable, boolean is3StateCycle)
{
super("", is3StateCycle);
// Register for mouse events on the table header
refHeader = aTable.getTableHeader();
if (refHeader != null)
{
refHeader.addMouseListener(this);
refHeader.addMouseMotionListener(this);
}
else
{
System.out.println("Failed to register a mouse listener onto the table header.");
}
}
/**
* Returns the column associated with the mouse event
*/
public int getAssociatedColumn(MouseEvent aEvent)
{
JTableHeader aHeader;
JTable aTable;
TableColumnModel aColumnModel;
int viewCol, refCol;
if (aEvent.getSource() instanceof JTableHeader == false)
return -1;
aHeader = (JTableHeader)aEvent.getSource();
aTable = aHeader.getTable();
aColumnModel = aTable.getColumnModel();
viewCol = aColumnModel.getColumnIndexAtX(aEvent.getX());
refCol = aTable.convertColumnIndexToModel(viewCol);
return viewCol;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int aColumn)
{
if (table != null)
{
JTableHeader header = table.getTableHeader();
if (header != null)
{
setForeground(header.getForeground());
setBackground(header.getBackground());
setFont(header.getFont());
// Perhaps we should deregister our listener and register in case we get a new header
;
}
}
column = aColumn;
setText((value == null) ? "" : value.toString());
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
return this;
}
@Override
public void mouseClicked(MouseEvent e)
{
// Go back to the deactivated state
model.setPressed(false);
((Component)e.getSource()).repaint();
}
@Override
public void mousePressed(MouseEvent e)
{
// Bail if this mouse event does not correspond to our column
if (getAssociatedColumn(e) != column)
return;
// Always activate on mouse press
model.setPressed(true);
((Component)e.getSource()).repaint();
}
@Override
public void mouseReleased(MouseEvent e)
{
// Bail if this mouse event does not correspond to our column
if (getAssociatedColumn(e) != column)
return;
// Advance to the next state (if armed)
if (model.isArmed() == true)
model.advanceToNextState();
else
model.setArmed(true);
// Always deactivate on mouse release
model.setPressed(false);
((Component)e.getSource()).repaint();
}
@Override
public void mouseEntered(MouseEvent e)
{
// Bail if this mouse event does not correspond to our column
if (getAssociatedColumn(e) != column)
return;
// Activate our model
model.setArmed(true);
((Component)e.getSource()).repaint();
}
@Override
public void mouseExited(MouseEvent e)
{
model.setArmed(false);
((Component)e.getSource()).repaint();
}
@Override
public void mouseMoved(MouseEvent e)
{
// Deactivate if not in our column
if (getAssociatedColumn(e) != column)
{
model.setArmed(false);
model.setPressed(false);
}
// Activate if we are in our column
else
{
model.setArmed(true);
}
((Component)e.getSource()).repaint();
}
@Override
public void mouseDragged(MouseEvent e)
{
// Always deactivate whenever dragging starts to occur
model.setArmed(false);
model.setPressed(false);
((Component)e.getSource()).repaint();
}
@Override
public void setState(MultiState aState)
{
super.setState(aState);
if (refHeader != null)
refHeader.repaint();
}
}

View File

@@ -0,0 +1,83 @@
package glum.gui.misc;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
public class MultiStateCheckBoxHeaderTest extends JFrame implements ActionListener
{
public MultiStateCheckBoxHeaderTest()
{
super("MultiStateCheckBoxHeaderTest");
buildGui();
}
/**
* Test application entry point
*/
public static void main(String[] argv)
{
MultiStateCheckBoxHeaderTest mainClass;
mainClass = new MultiStateCheckBoxHeaderTest();
mainClass.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainClass.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e)
{
Object source;
String text;
source = e.getSource();
text = "";
if (source instanceof AbstractButton)
text = ((AbstractButton)source).getText();
System.out.println("HeaderListener::actionPerformed() " + text + ", state:" + ((MultiStateCheckBoxHeader)source).getState());
}
/**
* Utility method to build the main GUI
*/
private void buildGui()
{
JTable table;
DefaultTableModel dataModel;
JScrollPane scrollpane;
MultiStateCheckBoxHeader aHeader;
// Create sample content for the JTable, don't care
String[][] data = new String[7][5];
String[] headers = new String[5];
for (int col = 0; col < data[0].length; col++)
{
headers[col] = "- " + col + " -";
for (int row = 0; row < data.length; row++)
data[row][col] = "(" + row + "," + col + ")";
}
dataModel = new DefaultTableModel(data, headers);
table = new JTable(dataModel);
// Create a HeaderCellRenderer for each column
Enumeration<TableColumn> enumeration = table.getColumnModel().getColumns();
while (enumeration.hasMoreElements())
{
TableColumn aColumn = enumeration.nextElement();
aHeader = new MultiStateCheckBoxHeader(table, true);
aHeader.addActionListener(this);
aColumn.setHeaderRenderer(aHeader);
}
scrollpane = new JScrollPane(table);
getContentPane().add(scrollpane);
pack();
}
}

View File

@@ -0,0 +1,34 @@
package glum.gui.misc;
import java.awt.*;
import javax.swing.*;
public class MultiStateCheckBoxTest
{
public static void main(String args[]) throws Exception
{
JFrame frame;
JCheckBox multi1CB, multi2CB;
JCheckBox standardCB;
frame = new JFrame("MultiStateCheckBoxTest");
frame.getContentPane().setLayout(new GridLayout(0, 1, 5, 5));
standardCB = new JCheckBox("Standard checkbox");
standardCB.setMnemonic('S');
frame.getContentPane().add(standardCB);
multi1CB = new MultiStateCheckBox("Multistate-1 checkbox", false);
multi1CB.setMnemonic('1');
frame.getContentPane().add(multi1CB);
multi2CB = new MultiStateCheckBox("Multistate-2 checkbox", true);
multi2CB.setMnemonic('2');
frame.getContentPane().add(multi2CB);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}

View File

@@ -0,0 +1,114 @@
package glum.gui.misc;
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.metal.*;
public class MultiStateIcon implements Icon
{
@Override
public int getIconWidth()
{
return getIconSize();
}
@Override
public int getIconHeight()
{
return getIconSize();
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y)
{
MultiStateModel model;
MultiState aState;
int iconSize;
// Insanity check
if (c instanceof MultiStateCheckBox == false)
return;
model = (MultiStateModel)((JCheckBox)c).getModel();
iconSize = getIconSize();
if (model.isEnabled())
{
if (model.isPressed() && model.isArmed())
{
g.setColor(MetalLookAndFeel.getControlShadow());
g.fillRect(x, y, iconSize - 1, iconSize - 1);
drawPressed3DBorder(g, x, y, iconSize, iconSize);
}
else
{
if (model.isArmed() == true)
{
g.setColor(MetalLookAndFeel.getControlShadow());
drawPressed3DBorder(g, x, y, iconSize, iconSize);
}
else
{
drawFlush3DBorder(g, x, y, iconSize, iconSize);
}
}
g.setColor(MetalLookAndFeel.getControlInfo());
}
else
{
g.setColor(MetalLookAndFeel.getControlShadow());
g.drawRect(x, y, iconSize - 1, iconSize - 1);
}
// Render the appropriate symbol
aState = model.getState();
aState.render(g, x, y, iconSize);
}
/**
* Utility method
*/
protected void drawFlush3DBorder(Graphics g, int x, int y, int w, int h)
{
g.translate(x, y);
g.setColor(MetalLookAndFeel.getControlDarkShadow());
g.drawRect(0, 0, w - 2, h - 2);
g.setColor(MetalLookAndFeel.getControlHighlight());
g.drawRect(1, 1, w - 2, h - 2);
g.setColor(MetalLookAndFeel.getControl());
g.drawLine(0, h - 1, 1, h - 2);
g.drawLine(w - 1, 0, w - 2, 1);
g.translate(-x, -y);
}
/**
* Utility method
*/
protected void drawPressed3DBorder(Graphics g, int x, int y, int w, int h)
{
g.translate(x, y);
drawFlush3DBorder(g, 0, 0, w, h);
g.setColor(MetalLookAndFeel.getControlShadow());
g.drawLine(1, 1, 1, h - 2);
g.drawLine(1, 1, w - 2, 1);
g.drawLine(2, 1, 2, h - 2);
g.drawLine(1, 2, w - 2, 2);
g.drawLine(w - 2, 1, w - 2, h - 2);
g.drawLine(1, h - 2, w - 2, h - 2);
g.drawLine(w - 3, 1, w - 3, h - 2);
g.drawLine(1, h - 3, w - 2, h - 3);
g.translate(-x, -y);
}
/**
* Returns the square dimensions of this GUI
*/
protected int getIconSize()
{
return 13;
}
}

View File

@@ -0,0 +1,181 @@
package glum.gui.misc;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
public class MultiStateModel implements ButtonModel
{
protected final ButtonModel refModel;
protected final HashMap<Object, MultiState> nextStateMap;
protected MultiState myState;
public MultiStateModel(ButtonModel aRefModel, HashMap<Object, MultiState> aNextStateMap)
{
refModel = aRefModel;
nextStateMap = aNextStateMap;
myState = MultiState.None;
}
public void advanceToNextState()
{
MultiState aState;
aState = nextStateMap.get(myState);
if (aState == null)
aState = MultiState.None;
setState(aState);
}
public MultiState getState()
{
return myState;
}
public void setState(MultiState state)
{
this.myState = state;
}
@Override
public boolean isSelected()
{
if (myState != MultiState.None)
return true;
return false;
}
/**
* All these methods simply delegate to the "refModel" model that is being decorated.
*/
@Override
public boolean isArmed()
{
return refModel.isArmed();
}
@Override
public boolean isEnabled()
{
return refModel.isEnabled();
}
@Override
public boolean isPressed()
{
return refModel.isPressed();
}
@Override
public boolean isRollover()
{
return refModel.isRollover();
}
@Override
public void setArmed(boolean b)
{
refModel.setArmed(b);
}
@Override
public void setEnabled(boolean b)
{
refModel.setEnabled(b);
}
@Override
public void setSelected(boolean b)
{
refModel.setSelected(b);
}
@Override
public void setPressed(boolean b)
{
refModel.setPressed(b);
}
@Override
public void setRollover(boolean b)
{
refModel.setRollover(b);
}
@Override
public void setMnemonic(int key)
{
refModel.setMnemonic(key);
}
@Override
public int getMnemonic()
{
return refModel.getMnemonic();
}
@Override
public void setActionCommand(String s)
{
refModel.setActionCommand(s);
}
@Override
public String getActionCommand()
{
return refModel.getActionCommand();
}
@Override
public void setGroup(ButtonGroup group)
{
refModel.setGroup(group);
}
@Override
public void addActionListener(ActionListener l)
{
refModel.addActionListener(l);
}
@Override
public void removeActionListener(ActionListener l)
{
refModel.removeActionListener(l);
}
@Override
public void addItemListener(ItemListener l)
{
refModel.addItemListener(l);
}
@Override
public void removeItemListener(ItemListener l)
{
refModel.removeItemListener(l);
}
@Override
public void addChangeListener(ChangeListener l)
{
refModel.addChangeListener(l);
}
@Override
public void removeChangeListener(ChangeListener l)
{
refModel.removeChangeListener(l);
}
@Override
public Object[] getSelectedObjects()
{
return refModel.getSelectedObjects();
}
}

View File

@@ -0,0 +1,60 @@
package glum.gui.misc;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.border.*;
public class SimpleTitledBorder extends TitledBorder
{
// Class var used to strip the disabled color
private static JTextComponent testComponent = null;
/**
* Constuctor
*/
public SimpleTitledBorder(Border border)
{
super(border);
}
public SimpleTitledBorder(Border border, String title)
{
super(border, title);
}
public SimpleTitledBorder(Border border, String title, int titleJustification, int titlePosition)
{
super(border, title, titleJustification, titlePosition);
}
public SimpleTitledBorder(Border border, String title, int titleJustification, int titlePosition, Font titleFont)
{
super(border, title, titleJustification, titlePosition, titleFont);
}
public SimpleTitledBorder(Border border, String title, int titleJustification, int titlePosition, Font titleFont, Color titleColor)
{
super(border, title, titleJustification, titlePosition, titleFont, titleColor);
}
public SimpleTitledBorder(String title)
{
super(title);
}
/**
* setEnabled
*/
public void setEnabled(boolean aBool)
{
if (testComponent == null)
testComponent = new JTextField();
// ! TODO: Color should be based on system settings
setTitleColor(Color.BLACK);
if (aBool == false)
setTitleColor(testComponent.getDisabledTextColor());
}
}

View File

@@ -0,0 +1,89 @@
package glum.gui.panel;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import com.google.common.collect.*;
public class CardPanel<G1> extends JPanel
{
protected BiMap<String, G1> myMap;
protected CardLayout myLayout;
protected G1 activeCard;
public CardPanel()
{
super();
myLayout = new CardLayout();
setLayout(myLayout);
myMap = HashBiMap.create();
activeCard = null;
}
@Override
public Component add(String name, Component comp)
{
throw new RuntimeException("Improper method call. Use addCard() instead of add()");
}
public void addCard(String aTitle, G1 aComponent)
{
// aComponent must be of type Component
if ((aComponent instanceof Component) == false)
throw new IllegalArgumentException("aComponent must be of type Component. Found class: " + aComponent.getClass().getName());
// Add the component if no component associated with the key
if (myMap.get(aTitle) == null)
{
myMap.put(aTitle, aComponent);
add((Component)aComponent, aTitle);
}
// If the key is associated, then ensure it is matched to aComponent
else if (myMap.get(aTitle) != aComponent)
{
throw new RuntimeException("Attempting to add new card with an already inserted key: " + aTitle);
}
switchToCard(aTitle);
}
public G1 getActiveCard()
{
return activeCard;
}
public Collection<G1> getAllCards()
{
Collection<G1> itemList;
itemList = new ArrayList<G1>(myMap.values());
return itemList;
}
public Set<String> getCardNames()
{
return new HashSet<String>(myMap.keySet());
}
public void switchToCard(String aTitle)
{
activeCard = myMap.get(aTitle);
if (activeCard == null)
throw new RuntimeException("No mapping found when switching to card: " + aTitle);
myLayout.show(this, aTitle);
}
public void switchToCard(G1 aCard)
{
if (myMap.values().contains(aCard) == false)
throw new RuntimeException("No mapping found when switching to card: " + aCard);
activeCard = aCard;
myLayout.show(this, myMap.inverse().get(aCard));
}
}

View File

@@ -0,0 +1,367 @@
package glum.gui.panel;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import net.miginfocom.swing.MigLayout;
import glum.gui.component.GNumberField;
import glum.unit.ConstUnitProvider;
import glum.unit.NumberUnit;
import glum.unit.UnitProvider;
public class ColorInputPanel extends JPanel implements ActionListener, ChangeListener
{
// Constants
private static final Font miniFont = new Font("Serif", Font.PLAIN, 10);
// Gui components
private ColorPanel colorP;
private JLabel redL, greenL, blueL;
private JSlider redS, greenS, blueS;
private GNumberField redNF, greenNF, blueNF;
// State vars
private Collection<ActionListener> myActionListeners;
/**
* Constructor
*/
public ColorInputPanel(boolean isHorizontal, boolean showTF)
{
super();
// Init internal vars
myActionListeners = new LinkedHashSet<ActionListener>();
// Build the gui areas
buildGuiArea(isHorizontal, showTF);
// Set in the default color
setColor(Color.BLACK);
}
/**
* addActionListener
*/
public synchronized void addActionListener(ActionListener aActionListener)
{
// Insanity check
if (aActionListener == null)
return;
myActionListeners.add(aActionListener);
}
/**
* removeActionListener
*/
public synchronized void removeActionListener(ActionListener aActionListener)
{
// Insanity check
if (aActionListener == null)
return;
myActionListeners.remove(aActionListener);
}
/**
* Returns the selected color
*/
public Color getColor()
{
int redVal, greenVal, blueVal;
redVal = redS.getValue();
greenVal = greenS.getValue();
blueVal = blueS.getValue();
return new Color(redVal, greenVal, blueVal);
}
/**
* Sets in the current selected color
*/
public void setColor(Color aColor)
{
// Insanity check
if (aColor == null)
return;
synchronizeGui(aColor);
}
@Override
public void actionPerformed(ActionEvent aEvent)
{
Object source;
// Perform GUI updates
source = aEvent.getSource();
updateGui(source);
// Notify the listeners
fireActionEvent(false);
}
@Override
public void setEnabled(boolean aBool)
{
redL.setEnabled(aBool);
redS.setEnabled(aBool);
redNF.setEnabled(aBool);
greenL.setEnabled(aBool);
greenS.setEnabled(aBool);
greenNF.setEnabled(aBool);
blueL.setEnabled(aBool);
blueS.setEnabled(aBool);
blueNF.setEnabled(aBool);
colorP.setEnabled(aBool);
}
@Override
public void stateChanged(ChangeEvent aEvent)
{
Object source;
JSlider aSlider;
// Perform GUI updates
source = aEvent.getSource();
updateGui(source);
// Notify the listeners
if (source instanceof JSlider)
{
// Fire off an event only if not being updated
aSlider = (JSlider)source;
if (aSlider.getValueIsAdjusting() == false)
fireActionEvent(false);
else
fireActionEvent(true);
}
}
/**
* Forms the actual gui
*/
private void buildGuiArea(boolean isHorizontal, boolean showTF)
{
JPanel rPanel, gPanel, bPanel;
UnitProvider countUP;
int sliderStyle;
sliderStyle = JSlider.HORIZONTAL;
if (isHorizontal == false)
sliderStyle = JSlider.VERTICAL;
countUP = new ConstUnitProvider(new NumberUnit("", "", 1.0, 0));
// RGB sliders
redL = new JLabel("R", JLabel.CENTER);
redS = new JSlider(sliderStyle, 0, 255, 0);
redNF = new GNumberField(this, countUP, 0, 255);
rPanel = formColorControl(redS, redL, redNF, isHorizontal, showTF);
greenL = new JLabel("G", JLabel.CENTER);
greenS = new JSlider(sliderStyle, 0, 255, 0);
greenNF = new GNumberField(this, countUP, 0, 255);
gPanel = formColorControl(greenS, greenL, greenNF, isHorizontal, showTF);
blueL = new JLabel("B", JLabel.CENTER);
blueS = new JSlider(sliderStyle, 0, 255, 0);
blueNF = new GNumberField(this, countUP, 0, 255);
bPanel = formColorControl(blueS, blueL, blueNF, isHorizontal, showTF);
// The color area
colorP = new ColorPanel(40, 40);
if (isHorizontal == true)
{
setLayout(new MigLayout("", "0[grow,75::][]0", "0[][][]0"));
add(rPanel, "growx,span 1,wrap");
add(gPanel, "growx,span 1,wrap");
add(bPanel, "growx,span 1,wrap");
add(colorP, "cell 1 0,growy,spanx 1,spany 3");
}
else
{
setLayout(new MigLayout("", "0[][][]0", "0[grow,75::][]0"));
add(rPanel, "growy,span 1");
add(gPanel, "growy,span 1");
add(bPanel, "growy,span 1,wrap");
add(colorP, "growx,span 3");
}
}
/**
* builds a JSlider with a label
*/
private JPanel formColorControl(JSlider aS, JLabel aL, GNumberField aNF, boolean isHorizontal, boolean showTF)
{
JPanel aPanel;
aPanel = new JPanel();
if (isHorizontal == true)
{
aPanel.setLayout(new BoxLayout(aPanel, BoxLayout.X_AXIS));
aL.setAlignmentY(0.5f);
aS.setAlignmentY(0.5f);
aNF.setAlignmentY(0.5f);
}
else
{
aPanel.setLayout(new BoxLayout(aPanel, BoxLayout.Y_AXIS));
aL.setAlignmentX(0.5f);
aS.setAlignmentX(0.5f);
aNF.setAlignmentX(0.5f);
}
aS.addChangeListener(this);
aNF.setHorizontalAlignment(JTextField.CENTER);
aNF.setColumns(3);
aNF.setValue(0);
aNF.setFont(miniFont);
aNF.setMinimumSize(aNF.getPreferredSize());
aNF.setMaximumSize(aNF.getPreferredSize());
aPanel.add(aL);
aPanel.add(aS);
if (isHorizontal == true)
aPanel.add(Box.createHorizontalStrut(2));
else
aPanel.add(Box.createVerticalStrut(2));
if (showTF == true)
aPanel.add(aNF);
return aPanel;
}
/**
* Notifies all listeners of color change
*/
private void fireActionEvent(boolean isChanging)
{
Collection<ActionListener> currListeners;
ActionEvent aEvent;
// Get a copy of the current set of listeners
synchronized(this)
{
currListeners = new LinkedHashSet<ActionListener>(myActionListeners);
}
// Construct the event
if (isChanging == false)
aEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Color changed.");
else
aEvent = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "Color changing.");
// Notify our listeners
for (ActionListener aListener : currListeners)
{
aListener.actionPerformed(aEvent);
}
}
/**
* Syncs the GUI to match aColor
*/
private void synchronizeGui(Color aColor)
{
int redVal, greenVal, blueVal;
// Get the rgb values
redVal = aColor.getRed();
greenVal = aColor.getGreen();
blueVal = aColor.getBlue();
// Stop listening to events while updating
redS.removeChangeListener(this);
greenS.removeChangeListener(this);
blueS.removeChangeListener(this);
// Update the gui components
if (redVal != redNF.getValue())
redNF.setValue(redVal);
if (greenVal != greenNF.getValue())
greenNF.setValue(greenVal);
if (blueVal != blueNF.getValue())
blueNF.setValue(blueVal);
redS.setValue(redVal);
greenS.setValue(greenVal);
blueS.setValue(blueVal);
colorP.setColor(new Color(redVal, greenVal, blueVal));
// Proceed with listening to events
redS.addChangeListener(this);
greenS.addChangeListener(this);
blueS.addChangeListener(this);
}
/**
* Updates the gui to reflect the source that has changed
*/
private void updateGui(Object source)
{
int redVal, greenVal, blueVal;
// Determine what values to retrieve based on the source
if (source instanceof GNumberField)
{
redVal = redNF.getValueAsInt(0);
greenVal = greenNF.getValueAsInt(0);
blueVal = blueNF.getValueAsInt(0);
}
else
{
// Get the slider values
redVal = redS.getValue();
greenVal = greenS.getValue();
blueVal = blueS.getValue();
}
// Update the appropriate component
if (source == redS)
{
redNF.setValue(redVal);
}
else if (source == greenS)
{
greenNF.setValue(greenVal);
}
else if (source == blueS)
{
blueNF.setValue(blueVal);
}
else if (source == redNF)
{
redS.removeChangeListener(this);
redS.setValue(redVal);
redS.addChangeListener(this);
}
else if (source == greenNF)
{
greenS.removeChangeListener(this);
greenS.setValue(greenVal);
greenS.addChangeListener(this);
}
else if (source == blueNF)
{
blueS.removeChangeListener(this);
blueS.setValue(blueVal);
blueS.addChangeListener(this);
}
// Update the preview color
colorP.setColor(new Color(redVal, greenVal, blueVal));
}
}

View File

@@ -0,0 +1,66 @@
package glum.gui.panel;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JPanel;
import javax.swing.border.BevelBorder;
import javax.swing.border.LineBorder;
public class ColorPanel extends JPanel
{
// State vars
protected Color dispColor;
public ColorPanel()
{
super();
dispColor = Color.BLACK;
updateGui();
}
public ColorPanel(int sizeX, int sizeY)
{
this();
setMinimumSize(new Dimension(sizeX, sizeY));
setPreferredSize(new Dimension(sizeX, sizeY));
}
/**
* Sets in the color that is displayed by this component
*/
public void setColor(Color aColor)
{
dispColor = aColor;
updateGui();
}
@Override
public void setEnabled(boolean aBool)
{
super.setEnabled(aBool);
updateGui();
}
/**
* Updates the GUI to reflect the chosen color
*/
protected void updateGui()
{
boolean isEnabled;
isEnabled = isEnabled();
if (isEnabled == false)
{
setBackground(Color.LIGHT_GRAY);
setBorder(new LineBorder(Color.GRAY));
return;
}
setBackground(dispColor);
setBorder(new BevelBorder(BevelBorder.RAISED));
}
}

View File

@@ -0,0 +1,156 @@
package glum.gui.panel;
import java.awt.Component;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.List;
import com.google.common.collect.Lists;
/**
* Utility class to allow a component to track other Component so it can keep it's
* properties synchronized with the tracked Components.
*/
public class ComponentTracker implements ComponentListener
{
protected Component targComp;
protected Component trkHiddenComp;
protected Component trkMovedComp;
protected Component trkResizedComp;
protected Component trkShownComp;
public ComponentTracker(Component aTargComp)
{
targComp = aTargComp;
trkHiddenComp = null;
trkMovedComp = null;
trkResizedComp = null;
trkShownComp = null;
}
/**
* Track aComp so that if it is hidden, then the reference
* targetComponent will be hidden.
*/
public void setHiddenTracker(Component aComp)
{
// Deregister from the old trkShownComp
if (trkHiddenComp != null)
trkHiddenComp.removeComponentListener(this);
trkHiddenComp = aComp;
updateListenersForTrackedComponents();
}
/**
* Track aComp so that if it is moved, then the reference
* targetComponent will be moved.
*/
public void setMovedTracker(Component aComp)
{
// Deregister from the old trkShownComp
if (trkMovedComp != null)
trkMovedComp.removeComponentListener(this);
trkMovedComp = aComp;
updateListenersForTrackedComponents();
}
/**
* Track aComp so that if it is resized, then the reference
* targetComponent will be resized.
*/
public void setResizedTracker(Component aComp)
{
// Deregister from the old trkShownComp
if (trkResizedComp != null)
trkResizedComp.removeComponentListener(this);
trkResizedComp = aComp;
updateListenersForTrackedComponents();
}
/**
* Track aComp so that if it is shown, then the reference
* targetComponent will be shown.
*/
public void setShownTracker(Component aComp)
{
// Deregister from the old trkShownComp
if (trkShownComp != null)
trkShownComp.removeComponentListener(this);
trkShownComp = aComp;
updateListenersForTrackedComponents();
}
@Override
public void componentHidden(ComponentEvent aEvent)
{
if (aEvent.getComponent() == trkHiddenComp)
targComp.setVisible(false);
}
@Override
public void componentMoved(ComponentEvent aEvent)
{
if (aEvent.getComponent() == trkMovedComp)
targComp.setLocation(trkMovedComp.getLocation());
}
@Override
public void componentResized(ComponentEvent aEvent)
{
if (aEvent.getComponent() == trkResizedComp)
{
targComp.setSize(trkResizedComp.getSize());
targComp.validate();
}
}
@Override
public void componentShown(ComponentEvent aEvent)
{
if (aEvent.getComponent() == trkShownComp)
targComp.setVisible(true);
}
/**
* Utility method to ensure that we are registered (Component events) for
* all component which are being tracked. Note that at most we will register
* only only once for each unique Component.
*/
protected void updateListenersForTrackedComponents()
{
List<ComponentListener> listenerList;
if (trkHiddenComp != null)
{
listenerList = Lists.newArrayList(trkHiddenComp.getComponentListeners());
if (listenerList.contains(this) == false)
trkHiddenComp.addComponentListener(this);
}
if (trkMovedComp != null)
{
listenerList = Lists.newArrayList(trkMovedComp.getComponentListeners());
if (listenerList.contains(this) == false)
trkMovedComp.addComponentListener(this);
}
if (trkResizedComp != null)
{
listenerList = Lists.newArrayList(trkResizedComp.getComponentListeners());
if (listenerList.contains(this) == false)
trkResizedComp.addComponentListener(this);
}
if (trkShownComp != null)
{
listenerList = Lists.newArrayList(trkShownComp.getComponentListeners());
if (listenerList.contains(this) == false)
trkShownComp.addComponentListener(this);
}
}
}

View File

@@ -0,0 +1,289 @@
package glum.gui.panel;
import glum.net.Credential;
import glum.net.NetUtil;
import glum.net.Result;
import glum.gui.FocusUtil;
import glum.gui.GuiUtil;
import glum.gui.action.ClickAction;
import glum.gui.component.GPasswordField;
import glum.gui.component.GTextField;
import glum.gui.panel.GlassPanel;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.border.BevelBorder;
import net.miginfocom.swing.MigLayout;
public class CredentialPanel extends GlassPanel implements ActionListener
{
// Constants
private static final Color warnColor = new Color(128, 0, 0);
// GUI vars
protected JLabel titleL;
protected JButton ignoreB, acceptB;
protected JPasswordField passTF;
protected GTextField userTF;
protected JTextField sourceTF, warnTA;
// State vars
protected Credential myCredential;
protected Result eResult;
protected Boolean isReset;
/**
* Constructor
*/
public CredentialPanel(Component aParent)
{
super(aParent);
// State vars
myCredential = null;
eResult = null;
isReset = true;
// Build the actual GUI
buildGuiArea();
updateGui();
// Set up some keyboard shortcuts
FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(ignoreB));
}
public Credential getCredential()
{
return myCredential;
}
public void setCredential(Credential aCredential, String aSourceUri)
{
// Construct the default (empty) credential
if (aCredential == null)
aCredential = new Credential("", null);
userTF.removeActionListener(this);
userTF.setText(aCredential.getUsername());
userTF.addActionListener(this);
passTF.removeActionListener(this);
passTF.setText(aCredential.getPasswordAsString());
passTF.addActionListener(this);
sourceTF.setText(aSourceUri);
sourceTF.setCaretPosition(0);
// Reset the dialog
isReset = true;
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
updateGui();
}
});
}
public void setTitle(String aTitle)
{
titleL.setText(aTitle);
}
@Override
public void actionPerformed(ActionEvent e)
{
Object source;
source = e.getSource();
if (source == ignoreB)
{
// Hide the dialog
myCredential = null;
setVisible(false);
// Notify our listeners
notifyListeners(this, 0, "Ignore");
}
else if (source == acceptB || source == passTF)
{
// Set the GUI into the waiting mode
isReset = null;
updateGui();
// Validate the settings
final Credential aCredential = new Credential(userTF.getText(), passTF.getPassword());
final String uriRoot = sourceTF.getText();
new Thread()
{
@Override
public void run()
{
final Result aResult;
// Pause for 0.5 sec to let the user register the change
try
{
Thread.sleep(500);
}
catch (InterruptedException aExp)
{
aExp.printStackTrace();
}
// Test the credentials
aResult = NetUtil.checkCredentials(uriRoot, aCredential);
// Update the Gui
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
// Bail if the user decided to quit before our results came
// back.
if (isVisible() == false)
return;
// If the credentials are valid then save them and bail
if (aResult == Result.Success)
{
myCredential = aCredential;
setVisible(false);
// Notify our listeners
notifyListeners(CredentialPanel.this, 0, "Accept");
}
// Try again
else
{
eResult = aResult;
isReset = false;
updateGui();
}
}
});
}
}.start();
}
else
{
updateGui();
}
}
/**
* Construct the GUI
*/
private void buildGuiArea()
{
Dimension aDimension;
JLabel tmpL;
String aStr;
// Form the grid bag constraints
setLayout(new MigLayout("", "[right][grow][][]"));
// Title Area
titleL = new JLabel("Product Name", JLabel.CENTER);
add(titleL, "growx,span 4,wrap");
// Source area
tmpL = new JLabel("Source:");
add(tmpL);
sourceTF = GuiUtil.createUneditableTextField("http://www.google.edu");
add(sourceTF, "growx,span 3,wrap");
// Username area
tmpL = new JLabel("Username:");
userTF = new GTextField(this);
add(tmpL);
add(userTF, "growx,span 3,wrap");
// Password area
tmpL = new JLabel("Password:");
passTF = new GPasswordField(this);
add(tmpL);
add(passTF, "growx,span 3,wrap");
// Warn area
aStr = "Please enter the credentials for accessing the data.";
warnTA = GuiUtil.createUneditableTextField(aStr);
warnTA.setForeground(warnColor);
add(warnTA, "growx,span 4,wrap");
// Action area
aDimension = GuiUtil.computePreferredJButtonSize("Ignore", "Accept");
ignoreB = GuiUtil.createJButton("Ignore", this, aDimension);
acceptB = GuiUtil.createJButton("Accept", this, aDimension);
add(ignoreB, "skip 2,span 1");
add(acceptB, "span 1");
setBorder(new BevelBorder(BevelBorder.RAISED));
}
/**
* Utility method to update the GUI
*/
private void updateGui()
{
boolean isEnabled;
if (isReset == null)
{
warnTA.setText("Checking the credentials...");
isEnabled = false;
}
else
{
if (userTF.getText().isEmpty() == true && passTF.getPassword().length < 2)
isReset = true;
isEnabled = true;
if (isReset == true)
{
warnTA.setText("Please enter the credentials for accessing the data.");
}
else
{
switch (eResult)
{
case BadCredentials:
warnTA.setText("Credentials are invalid.");
break;
case ConnectFailure:
warnTA.setText("Failed to connect to resource.");
break;
case UnreachableHost:
warnTA.setText("Unreachable host.");
break;
default:
warnTA.setText("Unreconzied error. Error: " + eResult);
break;
}
}
}
// Update the UI elements
userTF.setEnabled(isEnabled);
passTF.setEnabled(isEnabled);
acceptB.setEnabled(isEnabled);
}
}

Some files were not shown because too many files have changed in this diff Show More