diff --git a/JShim/JShim.csproj b/JShim/JShim.csproj index 9da9518..9e6ff7f 100644 --- a/JShim/JShim.csproj +++ b/JShim/JShim.csproj @@ -46,6 +46,11 @@ + + + + + @@ -83,6 +88,7 @@ + diff --git a/JShim/Javax/Swing/BoundedRangeModel.cs b/JShim/Javax/Swing/BoundedRangeModel.cs new file mode 100644 index 0000000..4eb2fdb --- /dev/null +++ b/JShim/Javax/Swing/BoundedRangeModel.cs @@ -0,0 +1,148 @@ + +using System; +using Javax.Swing.Event; + +namespace Javax.Swing +{ + /// + /// Description of BoundedRangeModel. + /// + public interface BoundedRangeModel + { + /// + /// Adds a ChangeListener to the model's listener list. + /// + /// the ChangeListener to add + void AddChangeListener(ChangeListener x); + + /// + /// Returns the model's extent, the length of the inner range that + /// begins at the model's value. + /// + /// the value of the model's extent property + int GetExtent(); + + /// + /// Returns the model's maximum. Note that the upper limit on the + /// model's value is (maximum - extent). + /// + /// the value of the maximum property. + int GetMaximum(); + + /// + /// Returns the minimum acceptable value. + /// + /// the value of the minimum property + int GetMinimum(); + + /// + /// Returns the model's current value. Note that the upper limit on the + /// model's value is maximum - extent and the lower limit is minimum. + /// + /// the model's value + int GetValue(); + + /// + /// Returns true if the current changes to the value property are part + /// of a series of changes. + /// + /// the valueIsAdjustingProperty. + bool GetValueIsAdjusting(); + + /// + /// Removes a ChangeListener from the model's listener list. + /// + /// the ChangeListener to remove + void RemoveChangeListener(ChangeListener x); + + /// + /// Sets the model's extent. The newExtent is forced to be greater than + /// or equal to zero and less than or equal to maximum - value. + /// + /// When a BoundedRange model is used with a scrollbar the extent + /// defines the length of the scrollbar knob (aka the "thumb" or + /// "elevator"). The extent usually represents how much of the object + /// being scrolled is visible. When used with a slider, the extent + /// determines how much the value can "jump", for example when the user + /// presses PgUp or PgDn. + /// + /// Notifies any listeners if the model changes. + /// + /// + /// the model's new extent + void SetExtent(int newExtent); + + /// + /// Sets the model's maximum to newMaximum. The other three properties + /// may be changed as well, to ensure that + /// minimum <= value <= value+extent <= maximum + /// + /// Notifies any listeners if the model changes. + /// + /// + /// the model's new maximum + void SetMaximum(int newMaximum); + + /// + /// Sets the model's minimum to newMinimum. The other three properties + /// may be changed as well, to ensure that: + /// minimum <= value <= value+extent <= maximum + /// + /// Notifies any listeners if the model changes. + /// + /// the model's new minimum + void SetMinimum(int newMinimum); + + /// + /// This method sets all of the model's data with a single method call. + /// The method results in a single change event being generated. This is + /// convenient when you need to adjust all the model data simultaneously + /// and do not want individual change events to occur. + /// + /// an int giving the current value + /// an int giving the amount by which the value can "jump" + /// an int giving the minimum value + /// an int giving the maximum value + /// a boolean, true if a series of changes are in progress + void SetRangeProperties(int value, int extent, int min, int max, bool adjusting); + + /// + /// Sets the model's current value to newValue if newValue satisfies the + /// model's constraints. Those constraints are: + /// minimum <= value <= value+extent <= maximum + /// + /// Otherwise, if newValue is less than minimum it's set to minimum, if + /// its greater than maximum then it's set to maximum, and if it's + /// greater than value+extent then it's set to value+extent. + /// + /// When a BoundedRange model is used with a scrollbar the value + /// specifies the origin of the scrollbar knob (aka the "thumb" or + /// "elevator"). The value usually represents the origin of the visible + /// part of the object being scrolled. + /// + /// Notifies any listeners if the model changes. + /// + /// + /// the model's new value + void SetValue(int newValue); + + /// + /// This attribute indicates that any upcoming changes to the value of + /// the model should be considered a single event. This attribute will + /// be set to true at the start of a series of changes to the value, and + /// will be set to false when the value has finished changing. Normally + /// this allows a listener to only take action when the final value + /// change in committed, instead of having to do updates for all + /// intermediate values. + /// + /// Sliders and scrollbars use this property when a drag is underway. + /// + /// + /// + /// true if the upcoming changes to the value property + /// are part of a series + /// + void SetValueIsAdjusting(bool b); + + } +} diff --git a/JShim/Javax/Swing/DefaultBoundedRangeModel.cs b/JShim/Javax/Swing/DefaultBoundedRangeModel.cs new file mode 100644 index 0000000..e049722 --- /dev/null +++ b/JShim/Javax/Swing/DefaultBoundedRangeModel.cs @@ -0,0 +1,367 @@ + +using System; +using Javax.Swing.Event; + +namespace Javax.Swing +{ + /// + /// A generic implementation of BoundedRangeModel. + /// + public class DefaultBoundedRangeModel : BoundedRangeModel + { + protected ChangeEvent changeEvent = null; + + /** The listeners waiting for model changes. */ + protected EventListenerList listenerList = new EventListenerList(); + + private int val = 0; + private int extent = 0; + private int min = 0; + private int max = 100; + private bool isAdjusting = false; + + /// + /// Initializes all of the properties with default values. + /// Those values are: + /// + /// + /// property + /// value + /// + /// + /// value + /// 0 + /// + /// + /// extent + /// 0 + /// + /// + /// minimum + /// 0 + /// + /// + /// maximum + /// 100 + /// + /// + /// adjusting + /// false + /// + /// + /// + public DefaultBoundedRangeModel() + { + } + + /// + /// Initializes value, extent, minimum and maximum. Adjusting is false. + /// Throws an IllegalArgumentException if the following + /// constraints aren't satisfied: + /// + /// min <= value <= value+extent <= max + /// + /// + /// + /// + /// + /// + public DefaultBoundedRangeModel(int val, int extent, int min, int max) + { + if ((max >= min) && + (val >= min) && + ((val + extent) >= val) && + ((val + extent) <= max)) + { + this.val = val; + this.extent = extent; + this.min = min; + this.max = max; + } + else + { + throw new ArgumentException("invalid range properties"); + } + } + + /// + /// Adds a ChangeListener. The change listeners are run each + /// time any one of the Bounded Range model properties changes. + /// + /// the ChangeListener to add + /// + /// + public void AddChangeListener(ChangeListener l) + { + listenerList.Add(typeof(ChangeListener), l); + } + + /// + /// Returns the model's extent. + /// + /// the model's extent + public int GetExtent() + { + return extent; + } + + /// + /// Returns the model's maximum. + /// + /// the model's maximum + public int GetMaximum() + { + return max; + } + + /// + /// Returns the model's minimum. + /// + /// the model's minimum + public int GetMinimum() + { + return min; + } + + /// + /// Returns the model's current value. + /// + /// the model's current value + /// #SetValue + /// BoundedRangeModel#GetValue + public int GetValue() + { + return val; + } + + /// + /// Returns true if the value is in the process of changing + /// as a result of actions being taken by the user. + /// + /// + /// the value of the valueIsAdjusting property + /// + /// + public bool GetValueIsAdjusting() + { + return isAdjusting; + } + + /// + /// Removes a ChangeListener. + /// + /// the ChangeListener to remove + /// + /// + public void RemoveChangeListener(ChangeListener l) + { + listenerList.Remove(typeof(ChangeListener), l); + } + + /// + /// Sets the extent to after ensuring that + /// is greater than or equal to zero and falls within the model's + /// constraints: + /// + /// minimum <= value <= value+extent <= maximum + /// + /// + /// + /// + public void SetExtent(int n) + { + int newExtent = Math.Max(0, n); + if(val + newExtent > max) + { + newExtent = max - val; + } + SetRangeProperties(val, newExtent, min, max, isAdjusting); + } + + /// + /// Sets the maximum to after ensuring that + /// that the other three properties obey the model's constraints: + /// + /// minimum <= value <= value+extent <= maximum + /// + /// + /// + /// + public void SetMaximum(int n) + { + int newMin = Math.Min(n, min); + int newExtent = Math.Min(n - newMin, extent); + int newValue = Math.Min(n - newExtent, val); + SetRangeProperties(newValue, newExtent, newMin, n, isAdjusting); + } + + /// + /// Sets the minimum to after ensuring that + /// that the other three properties obey the model's constraints: + /// + /// minimum <= value <= value+extent <= maximum + /// + /// + /// + /// + /// + public void SetMinimum(int n) + { + int newMax = Math.Max(n, max); + int newValue = Math.Max(n, val); + int newExtent = Math.Min(newMax - newValue, extent); + SetRangeProperties(newValue, newExtent, n, newMax, isAdjusting); + } + + /// + /// Sets all of the BoundedRangeModel properties after forcing + /// the arguments to obey the usual constraints: + /// + /// + /// minimum <= value <= value+extent <= maximum + /// + /// At most, one ChangeEvent is generated. + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void SetRangeProperties(int newValue, int newExtent, int newMin, int newMax, bool adjusting) + { + if (newMin > newMax) + { + newMin = newMax; + } + if (newValue > newMax) + { + newMax = newValue; + } + if (newValue < newMin) + { + newMin = newValue; + } + + /* Convert the addends to long so that extent can be + * Integer.MAX_VALUE without rolling over the sum. + * A JCK test covers this, see bug 4097718. + */ + if (((long)newExtent + (long)newValue) > newMax) + { + newExtent = newMax - newValue; + } + + if (newExtent < 0) + { + newExtent = 0; + } + + bool isChange = + (newValue != val) || + (newExtent != extent) || + (newMin != min) || + (newMax != max) || + (adjusting != isAdjusting); + + if (isChange) + { + val = newValue; + extent = newExtent; + min = newMin; + max = newMax; + isAdjusting = adjusting; + + FireStateChanged(); + } + } + + /// + /// Sets the current value of the model. For a slider, that + /// determines where the knob appears. Ensures that the new + /// value, falls within the model's constraints: + /// + /// minimum <= value <= value+extent <= maximum + /// + /// + /// + /// + public void SetValue(int n) + { + n = Math.Min(n, int.MaxValue - extent); + + int newValue = Math.Max(n, min); + if (newValue + extent > max) + { + newValue = max - extent; + } + SetRangeProperties(newValue, extent, min, max, isAdjusting); + } + + /// + /// Sets the valueIsAdjusting property. + /// + /// + /// + /// + /// + public void SetValueIsAdjusting(bool b) + { + SetRangeProperties(val, extent, min, max, b); + } + + /// + /// Returns an array of all the change listeners + /// registered on this DefaultBoundedRangeModel. + /// + /// + /// all of this model's ChangeListeners + /// or an empty + /// array if no change listeners are currently registered + /// + /// + /// + public ChangeListener[] GetChangeListeners() + { + return listenerList.GetListeners(typeof(ChangeListener)); + } + + /// + /// Runs each ChangeListener's StateChanged method. + /// + /// + /// + protected void FireStateChanged() + { + object[] listeners = listenerList.GetListenerList(); + for (int i = listeners.Length - 2; i >= 0; i -=2 ) + { + if (listeners[i] == typeof(ChangeListener)) + { + if (changeEvent == null) + { + changeEvent = new ChangeEvent(this); + } + ((ChangeListener)listeners[i+1]).StateChanged(changeEvent); + } + } + } + + public override string ToString() + { + string modelString = + "value=" + GetValue() + ", " + + "extent=" + GetExtent() + ", " + + "min=" + GetMinimum() + ", " + + "max=" + GetMaximum() + ", " + + "adj=" + GetValueIsAdjusting(); + + return GetType().FullName + "[" + modelString + "]"; + } + } +} diff --git a/JShim/Javax/Swing/Event/ChangeEvent.cs b/JShim/Javax/Swing/Event/ChangeEvent.cs new file mode 100644 index 0000000..f70e891 --- /dev/null +++ b/JShim/Javax/Swing/Event/ChangeEvent.cs @@ -0,0 +1,18 @@ + +using System; +using Java.Util; + +namespace Javax.Swing.Event +{ + /// + /// ChangeEvent is used to notify interested parties that state has changed + /// in the event source. + /// + public class ChangeEvent : EventObject + { + public ChangeEvent(object source) : base(source) + { + + } + } +} diff --git a/JShim/Javax/Swing/Event/ChangeListener.cs b/JShim/Javax/Swing/Event/ChangeListener.cs new file mode 100644 index 0000000..edf3fcb --- /dev/null +++ b/JShim/Javax/Swing/Event/ChangeListener.cs @@ -0,0 +1,17 @@ + +using System; + +namespace Javax.Swing.Event +{ + /// + /// Defines an object which listens for ChangeEvents. + /// + public interface ChangeListener + { + /// + /// Invoked when the target of the listener has changed its state. + /// + /// a ChangeEvent object + void StateChanged(ChangeEvent e); + } +} diff --git a/JShim/Javax/Swing/Event/EventListenerList.cs b/JShim/Javax/Swing/Event/EventListenerList.cs new file mode 100644 index 0000000..c7e77d5 --- /dev/null +++ b/JShim/Javax/Swing/Event/EventListenerList.cs @@ -0,0 +1,301 @@ + +using System; + +namespace Javax.Swing.Event +{ + /// + /// A class that holds a list of EventListeners. A single instance + /// can be used to hold all listeners (of all types) for the instance + /// using the list. It is the responsiblity of the class using the + /// EventListenerList to provide type-safe API (preferably conforming + /// to the JavaBeans spec) and methods which dispatch event notification + /// methods to appropriate Event Listeners on the list. + /// + /// The main benefits that this class provides are that it is relatively + /// cheap in the case of no listeners, and it provides serialization for + /// event-listener lists in a single place, as well as a degree of MT safety + /// (when used correctly). + /// + /// Usage example: + /// Say one is defining a class that sends out FooEvents, and one wants + /// to allow users of the class to register FooListeners and receive + /// notification when FooEvents occur. The following should be added + /// to the class definition: + /// + /// EventListenerList listenerList = new EventListenerList(); + /// FooEvent fooEvent = null; + /// + /// public void AddFooListener(FooListener l) + /// { + /// listenerList.Add(typeof(FooListener), l); + /// } + /// + /// public void RemoveFooListener(FooListener l) + /// { + /// listenerList.Remove(typeof(FooListener), l); + /// } + /// + /// // Notify all listeners that have registered interest for + /// // notification on this event type. The event instance + /// // is lazily created using the parameters passed into + /// // the fire method. + /// + /// protected void fireFooXXX() { + /// // Guaranteed to return a non-null array + /// Object[] listeners = listenerList.getListenerList(); + /// // Process the listeners last to first, notifying + /// // those that are interested in this event + /// for (int i = listeners.length-2; i>=0; i-=2) { + /// if (listeners[i]==FooListener.class) { + /// // Lazily create the event: + /// if (fooEvent == null) + /// fooEvent = new FooEvent(this); + /// ((FooListener)listeners[i+1]).fooXXX(fooEvent); + /// } + /// } + /// } + /// + /// foo should be changed to the appropriate name, and fireFooXxx to the + /// appropriate method name. One fire method should exist for each + /// notification method in the FooListener interface. + /// + /// + /// Warning: + /// 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™ + /// has been added to the Java.Beans namespace. + /// Please see {@link java.beans.XMLEncoder}. + /// + public class EventListenerList + { + /* A null array to be shared by all empty listener lists*/ + private readonly static object[] NULL_ARRAY = new object[0]; + /* The list of ListenerType - Listener pairs */ + protected object[] listenerList = NULL_ARRAY; + + /// + /// Passes back the event listener list as an array + /// of ListenerType-listener pairs. Note that for + /// performance reasons, this implementation passes back + /// the actual data structure in which the listener data + /// is stored internally! + /// + /// This method is guaranteed to pass back a non-null + /// array, so that no null-checking is required in + /// fire methods. A zero-length array of Object should + /// be returned if there are currently no listeners. + /// + /// WARNING!!! Absolutely NO modification of + /// the data contained in this array should be made -- if + /// any such manipulation is necessary, it should be done + /// on a copy of the array returned rather than the array + /// itself. + /// + /// + public object[] GetListenerList() + { + return listenerList; + } + + /// + /// Return an array of all the listeners of the given type. + /// + /// + /// all of the listeners of the specified type. + public T[] GetListeners(Class t) where T : EventListener + { + object[] lList = listenerList; + int n = GetListenerCount(lList, t); + T[] result = (T[])Array.CreateInstance(t, n); + int j = 0; + for (int i = lList.length-2; i>=0; i-=2) + { + if (lList[i] == t) + { + result[j++] = (T)lList[i+1]; + } + } + return result; + } + + /// + /// Returns the total number of listeners for this listener list. + /// + /// + public int GetListenerCount() + { + return listenerList.length/2; + } + + /** + * Returns the total number of listeners of the supplied type + * for this listener list. + */ + + /// + /// Returns the total number of listeners of the supplied type + /// for this listener list. + /// + /// + /// + public int GetListenerCount(Type t) + { + object[] lList = listenerList; + return GetListenerCount(lList, t); + } + + private int GetListenerCount(object[] list, Type t) + { + int count = 0; + for (int i = 0; i < list.Length; i+=2) + { + if (t == (Type)list[i]) + count++; + } + return count; + } + + /// + /// Adds the listener as a listener of the specified type. + /// + /// the type of the listener to be added + /// the listener to be added + public void Add(Type t, T l) where T : EventListener + { + if (l==null) + { + // In an ideal world, we would do an assertion here + // to help developers know they are probably doing + // something wrong + return; + } + if (!(l is t)) + { + throw new ArgumentException("Listener " + l + + " is not of type " + t); + } + if (listenerList == NULL_ARRAY) + { + // if this is the first listener added, + // initialize the lists + listenerList = new object[] { t, l }; + } + else + { + // Otherwise copy the array and add the new listener + int i = listenerList.length; + object[] tmp = new object[i+2]; + Array.Copy(listenerList, 0, tmp, 0, i); + + tmp[i] = t; + tmp[i+1] = l; + + listenerList = tmp; + } + } + + /// + /// Removes the listener as a listener of the specified type. + /// + /// the type of the listener to be removed + /// the listener to be removed + public void Remove(Type t, T l) where T : EventListener + { + if (l ==null) + { + // In an ideal world, we would do an assertion here + // to help developers know they are probably doing + // something wrong + return; + } + if (!(l is t)) + { + throw new ArgumentException("Listener " + l + + " is not of type " + t); + } + // Is l on the list? + int index = -1; + for (int i = listenerList.Length-2; i>=0; i-=2) + { + if ((listenerList[i]==t) && (listenerList[i+1].Equals(l) == true)) + { + index = i; + break; + } + } + + // If so, remove it + if (index != -1) + { + object[] tmp = new object[listenerList.length-2]; + // Copy the list up to index + Array.Copy(listenerList, 0, tmp, 0, index); + // Copy from two past the index, up to + // the end of tmp (which is two elements + // shorter than the old list) + if (index < tmp.length) + Array.Copy(listenerList, index+2, tmp, index, + tmp.length - index); + // set the listener array to the new array or null + listenerList = (tmp.length == 0) ? NULL_ARRAY : tmp; + } + } + + // Serialization support. + private void WriteObject(ObjectOutputStream s) + { + object[] lList = listenerList; + s.DefaultWriteObject(); + + // Save the non-null event listeners: + for (int i = 0; i < lList.length; i+=2) + { + Class t = (Class)lList[i]; + EventListener l = (EventListener)lList[i+1]; + if ((l!=null) && (l instanceof Serializable)) + { + s.WriteObject(t.GetName()); + s.WriteObject(l); + } + } + + s.WriteObject(null); + } + + private void ReadObject(ObjectInputStream s) + { + listenerList = NULL_ARRAY; + s.DefaultReadObject(); + object listenerTypeOrNull; + + while (null != (listenerTypeOrNull = s.ReadObject())) + { + ClassLoader cl = Thread.CurrentThread().GetContextClassLoader(); + EventListener l = (EventListener)s.ReadObject(); + string name = (string) listenerTypeOrNull; + ReflectUtil.CheckPackageAccess(name); + Add((Class)Class.forName(name, true, cl), l); + } + } + + /// + /// Returns a string representation of the EventListenerList. + /// + /// + public string ToString() + { + object[] lList = listenerList; + string s = "EventListenerList: "; + s += lList.length/2 + " listeners: "; + for (int i = 0 ; i <= lList.Length-2 ; i+=2) + { + s += " type " + ((Class)lList[i]).GetName(); + s += " listener " + lList[i+1]; + } + return s; + } + } +} diff --git a/WreckGui/Model/GuiModel.cs b/WreckGui/Model/GuiModel.cs index e0a125c..944d0f3 100644 --- a/WreckGui/Model/GuiModel.cs +++ b/WreckGui/Model/GuiModel.cs @@ -1,5 +1,6 @@  using System; +using Javax.Swing; using Wreck.Entity; using WreckGui.Model; @@ -33,7 +34,7 @@ public class GuiModel : IModel // private readonly DefaultListModel aboutModel; -// private readonly BoundedRangeModel scanningProgressModel; + private readonly BoundedRangeModel scanningProgressModel; public GuiModel() { @@ -42,6 +43,8 @@ public GuiModel() this.fileStatisticsTableModel = new SampleTableModel(typeof(FileStatisticsBean)); this.metadataStatisticsTableModel = new SampleTableModel(typeof(MetadataStatisticsBean)); this.extensionStatisticsTableModel = new SampleTableModel(typeof(ExtensionStatisticsBean)); + + this.scanningProgressModel = new DefaultBoundedRangeModel(); } public SampleTableModel TableModel @@ -63,5 +66,10 @@ public SampleTableModel ExtensionStatisticsTableModel { get { return extensionStatisticsTableModel; } } + + public BoundedRangeModel GetScanningProgressModel() + { + return scanningProgressModel; + } } } diff --git a/WreckGui/Model/IModel.cs b/WreckGui/Model/IModel.cs index 1db8896..cbb6fd3 100644 --- a/WreckGui/Model/IModel.cs +++ b/WreckGui/Model/IModel.cs @@ -1,5 +1,6 @@  using System; +using Javax.Swing; namespace WreckGui.Model { @@ -8,6 +9,6 @@ namespace WreckGui.Model /// public interface IModel { - + BoundedRangeModel GetScanningProgressModel(); } }