From 423c617bac4e8bcd964e831e8e4bca51210df3bc Mon Sep 17 00:00:00 2001 From: Doublestrike Date: Sat, 27 Aug 2011 12:23:17 +0000 Subject: [PATCH] View utils: Progress bar interface Progress bar base class Progress bar embedded class (extends base) --- .gitattributes | 4 + .../forge/view/util/ProgressBar_Base.java | 575 ++++++++++++++++++ .../forge/view/util/ProgressBar_Embedded.java | 97 +++ .../view/util/ProgressBar_Interface.java | 152 +++++ .../java/forge/view/util/package-info.java | 2 + 5 files changed, 830 insertions(+) create mode 100644 src/main/java/forge/view/util/ProgressBar_Base.java create mode 100644 src/main/java/forge/view/util/ProgressBar_Embedded.java create mode 100644 src/main/java/forge/view/util/ProgressBar_Interface.java create mode 100644 src/main/java/forge/view/util/package-info.java diff --git a/.gitattributes b/.gitattributes index a28ed63d61e..8f54b1e1898 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9797,6 +9797,10 @@ src/main/java/forge/view/swing/Main.java svneol=native#text/plain src/main/java/forge/view/swing/OldGuiNewGame.java svneol=native#text/plain src/main/java/forge/view/swing/SplashFrame.java -text src/main/java/forge/view/swing/package-info.java svneol=native#text/plain +src/main/java/forge/view/util/ProgressBar_Base.java -text +src/main/java/forge/view/util/ProgressBar_Embedded.java -text +src/main/java/forge/view/util/ProgressBar_Interface.java -text +src/main/java/forge/view/util/package-info.java -text src/main/java/net/slightlymagic/braids/LICENSE.txt svneol=native#text/plain src/main/java/net/slightlymagic/braids/util/ClumsyRunnable.java svneol=native#text/plain src/main/java/net/slightlymagic/braids/util/ImmutableIterableFrom.java svneol=native#text/plain diff --git a/src/main/java/forge/view/util/ProgressBar_Base.java b/src/main/java/forge/view/util/ProgressBar_Base.java new file mode 100644 index 00000000000..6d490335fca --- /dev/null +++ b/src/main/java/forge/view/util/ProgressBar_Base.java @@ -0,0 +1,575 @@ +package forge.view.util; + +import java.util.Date; +import java.util.Hashtable; +import javax.swing.JProgressBar; +import com.esotericsoftware.minlog.Log; + +/** + * This base class also acts as a "null" progress monitor; it doesn't display + * anything when updated. + * + * Absolute times are measured in seconds, in congruence with ProgressMonitor. + * + * @see forge.view.util.ProgressBar_Interface + */ +@SuppressWarnings("serial") +public class ProgressBar_Base extends JProgressBar implements ProgressBar_Interface { + private int numPhases; + private int currentPhase; + private long totalUnitsThisPhase; + private long unitsCompletedSoFarThisPhase; + private float minUIUpdateIntervalSec; + private long lastUIUpdateTime; + private long phaseOneStartTime; + private long currentPhaseStartTime; + private float currentPhaseExponent; + private long[] phaseDurationHistorySecList; + private float[] phaseWeights; + private Hashtable phaseNames; + + public final int SECONDS_PER_MINUTE = 60; + public final int SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE; + public final int SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR; + + + /** + * Convenience for + * ProgressBar_Base(numPhases, totalUnitsFirstPhase, 2.0f, null). + * + * @see #ProgressBar_Base(int,long,float,float[]) + */ + public ProgressBar_Base(int numPhases, long totalUnitsFirstPhase) { + this(numPhases, totalUnitsFirstPhase, 2.0f, null); + } + + /** + * Convenience for + * ProgressBar_Base(numPhases, totalUnitsFirstPhase, + * minUIUpdateIntervalSec, null). + * + * @see #ProgressBar_Base(int,long,float,float[]) + */ + public ProgressBar_Base(int numPhases, long totalUnitsFirstPhase, + float minUIUpdateIntervalSec) + { + this(numPhases, totalUnitsFirstPhase, minUIUpdateIntervalSec, null); + } + + /** + * Initializes fields and starts the timers. + * + * @param numPhases the total number of phases we will monitor + * + * @param totalUnitsFirstPhase how many units to expect in phase 1 + * + * @param minUIUpdateIntervalSec the approximate interval at which we + * update the user interface, in seconds + * + * @param phaseWeights may be null; if not null, this indicates the + * relative weight of each phase in terms of time to complete all phases. + * Index 0 of this array indicates phase 1's weight, index 1 indicates + * the weight of phase 2, and so on. If null, all phases are considered to + * take an equal amount of time to complete, which is equivalent to setting + * all phase weights to 1.0f. For example, if there are two phases, and + * the phase weights are set to {2.0f, 1.0f}, then the methods that compute + * the final ETA (Estimated Time of Arrival or completion) will assume that + * phase 2 takes half as long as phase 1. In other words, the operation + * will spend 67% of its time in phase 1, and 33% of its time in phase 2. + */ + public ProgressBar_Base(int numPhases, long totalUnitsFirstPhase, + float minUIUpdateIntervalSec, float[] phaseWeights) + { + super(); + + this.numPhases = numPhases; + this.currentPhase = 1; + this.unitsCompletedSoFarThisPhase = 0L; + this.minUIUpdateIntervalSec = minUIUpdateIntervalSec; + this.lastUIUpdateTime = 0L; + this.phaseOneStartTime = new Date().getTime()/1000; + this.currentPhaseStartTime = this.phaseOneStartTime; + this.currentPhaseExponent = 1; + this.phaseDurationHistorySecList = new long[numPhases]; + + if (phaseWeights == null) { + this.phaseWeights = new float[numPhases]; + for (int ix = 0; ix < numPhases; ix++) { + this.phaseWeights[ix] = 1.0f; + } + } + else { + this.phaseWeights = phaseWeights; + } + + if (phaseNames == null) { + this.phaseNames = new Hashtable(); + for(int i=1;i<=numPhases;i++) { + this.phaseNames.put(i, "Phase "+i); + } + } + + setTotalUnitsThisPhase(totalUnitsFirstPhase); + } + + /** + * Does nothing. + */ + public void dispose() { + ; + } + + /** + * @see forge.view.util.ProgressBar_Interface#getNumPhases() + */ + public int getNumPhases() { + return this.numPhases; + } + + /** + * @see forge.view.util.ProgressBar_Interface#getMinUpdateIntervalSec() + */ + public float getMinUpdateIntervalSec() { + return this.minUIUpdateIntervalSec; + } + + /** + * @see forge.view.util.ProgressBar_Interface#getCurrentPhase() + */ + public int getCurrentPhase() { + return this.currentPhase; + } + + /** + * @see forge.view.util.ProgressBar_Interface#getUnitsCompletedSoFarThisPhase() + */ + public long getUnitsCompletedSoFarThisPhase() { + return this.unitsCompletedSoFarThisPhase; + } + + /** + * @see forge.view.util.ProgressBar_Interface#getTotalUnitsThisPhase() + */ + public long getTotalUnitsThisPhase() { + return this.totalUnitsThisPhase; + } + + /** + * @see forge.view.util.ProgressBar_Interface#getLastUIUpdateTime() + */ + public long getLastUIUpdateTime() { + return this.lastUIUpdateTime; + } + + /** + * @see forge.view.util.ProgressBar_Interface#getPhaseOneStartTime() + */ + public long getPhaseOneStartTime() { + return this.phaseOneStartTime; + } + + /** + * @see forge.view.util.ProgressBar_Interface#getCurrentPhaseStartTime() + */ + public long getCurrentPhaseStartTime() { + return this.currentPhaseStartTime; + } + + + /** + * @see forge.view.util.ProgressBar_Interface#setMinUpdateIntervalSec(float) + */ + public void setMinUpdateIntervalSec(float value) { + this.minUIUpdateIntervalSec = value; + } + + + /** + * @see forge.view.util.ProgressBar_Interface#setTotalUnitsThisPhase(long) + */ + public void setTotalUnitsThisPhase(long value) { + // DS - why is this called twice? + if (value > Integer.MAX_VALUE) { + throw new IllegalArgumentException("numUnits must be <= " + Integer.MAX_VALUE); + } + else { + this.totalUnitsThisPhase = value; + + // (Temporary solution until I know a better way) + this.setProgressRange(0,(int)value); + } + } + + /** + *

setProgressRange.

+ * + * @param min an int. + * @param max an int. + */ + public void setProgressRange(int min, int max) { + this.setMinimum(min); + this.setMaximum(max); + } + + /** + * @see forge.view.util.ProgressBar_Interface#getPercentCompleteOfThisPhaseAsString() + */ + public String getPercentCompleteOfThisPhaseAsString() { + + Float percent = getPercentCompleteOfThisPhaseAsFloat(); + + if (percent != null) { + return Integer.toString((int) (float) percent); + } + else { + return "??"; + } + } + + + /** + * @see forge.view.util.ProgressBar_Interface#getTotalPercentCompleteAsString() + */ + public String getTotalPercentCompleteAsString() { + Float percent = getTotalPercentCompleteAsFloat(); + + if (percent == null) { + return "??"; + } + else { + return Integer.toString((int) (float) percent); + } + } + + + /** + * Convenience for getRelativeETAAsString(false), meaning to compute the + * value for the end of the last phase. + * + * @see #getRelativeETAAsString(boolean) + */ + public String getRelativeETAAsString() { + return getRelativeETAAsString(false); + } + + /** + * @see forge.view.util.ProgressBar_Interface#getRelativeETAAsString(boolean) + */ + public String getRelativeETAAsString(boolean thisPhaseOnly) { + + Integer etaSec = getRelativeETASec(thisPhaseOnly); + + if (etaSec == null) { + return "unknown"; + } + + String result = ""; + if (etaSec > SECONDS_PER_DAY) { + result += Integer.toString(etaSec / SECONDS_PER_DAY); + result += " da, "; + etaSec %= SECONDS_PER_DAY; // Shave off the portion recorded. + } + if (result.length() > 0 || etaSec > SECONDS_PER_HOUR) { + result += Integer.toString(etaSec / SECONDS_PER_HOUR); + result += " hr, "; + etaSec %= SECONDS_PER_HOUR; // Shave off the portion recorded. + } + if (result.length() > 0 || etaSec > SECONDS_PER_MINUTE) { + result += Integer.toString(etaSec / SECONDS_PER_MINUTE); + result += " min, "; + etaSec %= SECONDS_PER_MINUTE; // Shave off the portion recorded. + } + + result += Integer.toString(etaSec); + result += " sec"; + + return result; + } + + /** + * Convenience for getAbsoluteETAAsLocalTimeString(false), meaning to + * compute the value for the end of the last phase. + * + * @see #getAbsoluteETAAsLocalTimeString(boolean) + */ + public String getAbsoluteETAAsLocalTimeString() { + return getAbsoluteETAAsLocalTimeString(false); + } + + /** + * @see forge.view.util.ProgressBar_Interface#getAbsoluteETAAsLocalTimeString(boolean) + */ + public String getAbsoluteETAAsLocalTimeString(boolean thisPhaseOnly) { + Long etaTime = getAbsoluteETATime(thisPhaseOnly); + + if (etaTime == null) { + return "unknown"; + } + + return (new Date(etaTime*1000).toString()); + } + + + /** + * @see forge.view.util.ProgressBar_Interface#incrementUnitsCompletedThisPhase(long) + */ + public void incrementUnitsCompletedThisPhase(long numUnits) { + this.unitsCompletedSoFarThisPhase += numUnits; + } + + public void increment() { + setValue(getValue() + 1); + if (getValue() % 10 == 0) { repaint(); } + } + + /** + * Subclasses must call this immediately after updating the UI, to + * preserve the integrity of the shouldUpdateUI method. + */ + protected void justUpdatedUI() { + this.lastUIUpdateTime = new Date().getTime()/1000; + } + + /** + * @see forge.view.util.ProgressBar_Interface#shouldUpdateUI() + */ + public boolean shouldUpdateUI() { + + doctorStartTimes(); + long nowTime = (new Date().getTime()/1000); + + if (nowTime - this.lastUIUpdateTime >= this.minUIUpdateIntervalSec || + (this.getUnitsCompletedSoFarThisPhase() == + this.getTotalUnitsThisPhase())) + { + return true; + } + else { + return false; + } + } + + + /** + * @see forge.view.util.ProgressBar_Interface#markCurrentPhaseAsComplete(long) + */ + public void markCurrentPhaseAsComplete(long totalUnitsNextPhase) { + + if ((this.currentPhase > this.numPhases)) { + String message = "The phase just completed ("; + message += this.currentPhase; + message += ") is greater than the total number "; + message += "of anticipated phases ("; + message += this.numPhases; + message += "); the latter is probably incorrect."; + + Log.warn(message); + } + + this.currentPhase += 1; + this.unitsCompletedSoFarThisPhase = 0; + setTotalUnitsThisPhase(totalUnitsNextPhase); + this.currentPhaseExponent = 1; + + long nowTime = (new Date().getTime()/1000); + long durationOfThisPhaseSec = nowTime - this.currentPhaseStartTime; + if (durationOfThisPhaseSec < 0) { + durationOfThisPhaseSec = 0; + } + + if (0 <= currentPhase-2 && currentPhase-2 < phaseDurationHistorySecList.length) { + this.phaseDurationHistorySecList[currentPhase-2] = durationOfThisPhaseSec; + } + this.currentPhaseStartTime = nowTime; + + if (this.currentPhase >= this.numPhases) { + String message = "Actual individual phase durations: ["; + for (int ix = 0 ; ix < phaseDurationHistorySecList.length ; ix++) { + message += phaseDurationHistorySecList[ix] + ", "; + } + + Log.info(message + ']'); + } + } + + + /** + * @see forge.view.util.ProgressBar_Interface#sendMessage(java.lang.String) + */ + public void sendMessage(String message) { + ; + } + + + /** + * @see forge.view.util.ProgressBar_Interface#setCurrentPhaseAsExponential(float) + */ + public void setCurrentPhaseAsExponential(float value) { + this.currentPhaseExponent = value; + } + + + /** + * @see forge.view.util.ProgressBar_Interface#getCurrentPhaseExponent() + */ + public float getCurrentPhaseExponent() { + return this.currentPhaseExponent; + } + + /** + * Sets the name of a phase in the process (e.g. "Phase 1" becomes "Loading XML") + */ + public void setPhaseName(int i, String name) { + + } + + public String getPhaseName(int i) { + return phaseNames.get(i); + } + + /** + * @return number in range [0.0, 100.0] or null. + */ + protected Float getPercentCompleteOfThisPhaseAsFloat() { + if (this.totalUnitsThisPhase < 1 || + this.unitsCompletedSoFarThisPhase > this.totalUnitsThisPhase) { + return null; + } + else { + float ratio = ((float) (this.unitsCompletedSoFarThisPhase)) / + ((float) this.totalUnitsThisPhase); + + ratio = (float) Math.pow(ratio, this.getCurrentPhaseExponent()); + + return (ratio * 100.0f); + } + } + + + /** + * Returns number in range [0.0, 100.0] or null. + */ + protected Float getTotalPercentCompleteAsFloat() { + long totalPoints = 0; + for (float weight : this.phaseWeights) { + totalPoints += weight * 100; + } + + Float percentThisPhase = getPercentCompleteOfThisPhaseAsFloat(); + + if (percentThisPhase == null) { + // If we can't know the percentage for this phase, use a + // conservative estimate. + percentThisPhase = 0.0f; + } + + long pointsSoFar = 0; + for (int ix = 0; ix < this.currentPhase-1; ix++) { + // We get full points for (all the phases completed prior to this one. + pointsSoFar += phaseWeights[ix] * 100; + } + + pointsSoFar += percentThisPhase * this.phaseWeights[this.currentPhase-1]; + + if (totalPoints <= 0.0 || pointsSoFar > totalPoints) { + return null; + } + else { + return (100.0f * pointsSoFar) / totalPoints; + } + } + + + /** + * Convenience for getRelativeETASec(false), meaning to compute the value + * for the end of the last phase. + * + * @see #getRelativeETASec(boolean) + */ + protected Integer getRelativeETASec() { + return getRelativeETASec(false); + } + + /** + * @return estimated seconds until completion for either thisPhaseOnly + * or for the entire operation. May return null if unknown. + */ + protected Integer getRelativeETASec(boolean thisPhaseOnly) { + + Long absoluteETATime = getAbsoluteETATime(thisPhaseOnly); + if (absoluteETATime == null) { + return null; + } + return (int) (absoluteETATime - (new Date().getTime()/1000)); + } + + + /** + * Convenience for getAbsoluteETATime(false), meaning to compute the value + * for the end of all phases. + * + * @see #getAbsoluteETATime(boolean) + */ + protected Long getAbsoluteETATime() { + return getAbsoluteETATime(false); + } + + /** + * @return the estimated time (in absolute seconds) at which thisPhaseOnly + * or the entire operation will be completed. May return null if (unknown. + */ + protected Long getAbsoluteETATime(boolean thisPhaseOnly) { + doctorStartTimes(); + + // If we're in the last phase, the overall ETA is the same as the ETA + // for (this particular phase. + if (this.getCurrentPhase() >= this.getNumPhases()) { + thisPhaseOnly = true; + } + + Float percentDone = null; + long startTime = 0L; + + if (thisPhaseOnly) { + percentDone = getPercentCompleteOfThisPhaseAsFloat(); + startTime = this.currentPhaseStartTime; + } + else { + percentDone = getTotalPercentCompleteAsFloat(); + startTime = this.phaseOneStartTime; + } + + if (percentDone == null || percentDone <= 0.001) { + return null; + } + + // Elapsed time is to percent done as total time is to total done => + // elapsed/percentDone == totalTime/100.0 => + long totalTime = (long) (100.0f * ((new Date().getTime()/1000) - startTime) / percentDone); + + return totalTime + startTime; + } + + + /** + * Repair the start times in case the system clock has been moved + * backwards. + */ + protected void doctorStartTimes() { + + long nowTime = (new Date().getTime()/1000); + + if (this.lastUIUpdateTime > nowTime) { + this.lastUIUpdateTime = 0; + } + + if (this.phaseOneStartTime > nowTime) { + this.phaseOneStartTime = nowTime; + } + + if (this.currentPhaseStartTime > nowTime) { + this.currentPhaseStartTime = nowTime; + } + } + +} diff --git a/src/main/java/forge/view/util/ProgressBar_Embedded.java b/src/main/java/forge/view/util/ProgressBar_Embedded.java new file mode 100644 index 00000000000..e99f931874b --- /dev/null +++ b/src/main/java/forge/view/util/ProgressBar_Embedded.java @@ -0,0 +1,97 @@ +package forge.view.util; + +import javax.swing.SwingUtilities; + +import forge.view.util.ProgressBar_Base; + +/** + * GUI Progress Monitor that displays the ETA (Estimated Time of Arrival or + * completion) on some platforms and supports one or multiple phases of + * progress. + * + * In this implementation, the progress bar must be embedded in a parent component. + */ +@SuppressWarnings("serial") +public class ProgressBar_Embedded extends ProgressBar_Base { + + public ProgressBar_Embedded(int numPhases, long totalUnitsFirstPhase) { + super(numPhases, totalUnitsFirstPhase); + + setIndeterminate(false); + setString(""); + setStringPainted(true); + setValue(0); + } + + // public void increment() { + // setValue(getValue() + 1); + // System.out.println("x"); + // //if (getValue() % 10 == 0) { repaint(); } + // } + + @Override + /** + * @see forge.view.util.ProgressBar_Base#incrementUnitsCompletedThisPhase(long) + */ + public final void incrementUnitsCompletedThisPhase(final long numUnits) { + super.incrementUnitsCompletedThisPhase(numUnits); + SwingUtilities.invokeLater(new Runnable() { // NOPMD by Braids on 8/18/11 11:18 PM + public void run() { + for (int i = 0; i < numUnits; i++) { + increment(); + } + } + }); + + if (shouldUpdateUI()) { + + if ((getNumPhases() > 1)) { + displayUpdate( + "Phase " + getCurrentPhase() + ". " + //+ getUnitsCompletedSoFarThisPhase() + " units processed. " + //+ "Overall: " + getTotalPercentCompleteAsString() + "% complete, " + + "Overall ETA in " + getRelativeETAAsString() + "." + ); + } + else { + displayUpdate( + //"Overall: " + + getUnitsCompletedSoFarThisPhase() + " units processed; " + //+ "(" + getTotalPercentCompleteAsString() + "%); " + + "ETA in " + getRelativeETAAsString() + "." + ); + } + } + + if (getCurrentPhase() == getNumPhases() + && getUnitsCompletedSoFarThisPhase() >= getTotalUnitsThisPhase()) + { + displayUpdate("Done!"); + } + } + + /** + * Shows the message inside the progress dialog; does not always work on + * all platforms. + * + * @param message the message to display + */ + public final void displayUpdate(final String message) { + + final Runnable proc = new Runnable() { // NOPMD by Braids on 8/18/11 11:18 PM + public void run() { + setString(message); + justUpdatedUI(); + } + }; + + if (SwingUtilities.isEventDispatchThread()) { + proc.run(); + } + else { + SwingUtilities.invokeLater(proc); + } + } + + +} diff --git a/src/main/java/forge/view/util/ProgressBar_Interface.java b/src/main/java/forge/view/util/ProgressBar_Interface.java new file mode 100644 index 00000000000..e1c72019ec6 --- /dev/null +++ b/src/main/java/forge/view/util/ProgressBar_Interface.java @@ -0,0 +1,152 @@ +package forge.view.util; + +/** + * Interface for a progress monitor that can have multiple phases + * and periodically update its UI. + * + * All times must be in seconds; absolute times are measured in seconds since + * 01 Jan 1970 00:00:00 UTC (GMT) a la (new Date().getTime()/1000). + */ +public interface ProgressBar_Interface { + + /** + * Destroy this progress monitor, making it no longer usable and/or + * visible. + */ + public void dispose(); + + /** + * @return the total number of phases monitored by this object. + */ + public abstract int getNumPhases(); + + /** + * @return the approximate minimum interval in seconds at which the UI + * should be updated. + */ + public abstract float getMinUpdateIntervalSec(); + + /** + * @return the current phase number; this is never less than 1 (one). + */ + public abstract int getCurrentPhase(); + + /** + * @return the number of units (an intentionally vague amount) completed + * so far in the current phase. + */ + public abstract long getUnitsCompletedSoFarThisPhase(); + + /** + * @return the total units we expect to process in this phase + */ + public abstract long getTotalUnitsThisPhase(); + + /** + * @return the time in absolute seconds since the UI was last updated + */ + public abstract long getLastUIUpdateTime(); + + /** + * @return the time in absolute seconds at which the first phase started + */ + public abstract long getPhaseOneStartTime(); + + /** + * @return the time in absolute seconds at which the current phase started + */ + public abstract long getCurrentPhaseStartTime(); + + /** + * @param value + * the approximate time in relative seconds at which the UI + * should be updated periodically + */ + public abstract void setMinUpdateIntervalSec(float value); + + /** + * @param value the total number of units expected to processed in this + * phase + */ + public abstract void setTotalUnitsThisPhase(long value); + + /** + * Resulting string does not contain a percent sign. + * + * @return the percentage completion of this phase as a String with no + * percent sign. + */ + public abstract String getPercentCompleteOfThisPhaseAsString(); + + /** + * Resulting string does not contain a percent sign. + * + * @return the percentage completion at this point, taking into account all + * phases and phase-weights, as a String with no percent sign. + */ + public abstract String getTotalPercentCompleteAsString(); + + /** + * May return "unknown" + */ + public abstract String getRelativeETAAsString(boolean thisPhaseOnly); + + /** + * May return "unknown" + */ + public abstract String getAbsoluteETAAsLocalTimeString(boolean thisPhaseOnly); + + /** + * Note this will NOT advance the phase. + * To do that, use markCurrentPhaseAsComplete(). + */ + public abstract void incrementUnitsCompletedThisPhase(long numUnits); + + /** + * Returns a boolean, whether or not to display the updated information. + * This throttles the update so it doesn't refresh so fast that it is + * unreadable. Implementers should call this method from their own + * incrementUnitsCompletedThisPhase method. + * + * If we have just reached 100% for (the current phase, we return true, + * even if it would otherwise be too soon to update the UI. + */ + public abstract boolean shouldUpdateUI(); + + /** + * This is the only way to advance the phase number. + * It automatically "starts the clock" for the next phase. + * + * @param totalUnitsNextPhase if unknown, use zero (0), and be sure to call + * setTotalUnitsThisPhase() soon after. + */ + public abstract void markCurrentPhaseAsComplete(long totalUnitsNextPhase); + + /** + * Attempt to display a message to the user; not all implementations + * support this. + * + * If they do not, they may silently ignore this call. + * + * @param message the message to display + */ + public abstract void sendMessage(String message); + + + /** + * Mark the current phase as having an exponential rate; such phases + * reach their totalUnits slower and slower as they process more units. + * + * By default, a phase is considered to be linear, meaning this value is + * 1.0f. + * + * @param value usually less than 1.0f; often determined empirically. + */ + public abstract void setCurrentPhaseAsExponential(float value); + + /** + * @return the exponent for this phase + */ + public abstract float getCurrentPhaseExponent(); + +} diff --git a/src/main/java/forge/view/util/package-info.java b/src/main/java/forge/view/util/package-info.java new file mode 100644 index 00000000000..62e9fbefa63 --- /dev/null +++ b/src/main/java/forge/view/util/package-info.java @@ -0,0 +1,2 @@ +/** Forge Card Game */ +package forge.view.util;