Merge commit '8b2627ce767579eb616e262b3f45f810a88ec200'
authorNiki Roo <niki@nikiroo.be>
Fri, 1 May 2020 09:10:54 +0000 (11:10 +0200)
committerNiki Roo <niki@nikiroo.be>
Fri, 1 May 2020 09:10:54 +0000 (11:10 +0200)
1  2 
src/be/nikiroo/utils/Progress.java
src/be/nikiroo/utils/ui/BreadCrumbsBar.java
src/be/nikiroo/utils/ui/DataNode.java
src/be/nikiroo/utils/ui/DataTree.java
src/be/nikiroo/utils/ui/DelayWorker.java
src/be/nikiroo/utils/ui/ListModel.java
src/be/nikiroo/utils/ui/ListSnapshot.java
src/be/nikiroo/utils/ui/ListenerItem.java
src/be/nikiroo/utils/ui/ListenerPanel.java
src/be/nikiroo/utils/ui/UIUtils.java

index dea6be3fa011c351e4990b39e396368a50174e9d,748d4a666c377123ff2eadd29c840ec4baf402e5..748d4a666c377123ff2eadd29c840ec4baf402e5
@@@ -9,6 -9,16 +9,16 @@@ import java.util.Map.Entry
  
  /**
   * Progress reporting system, possibly nested.
+  * <p>
+  * A {@link Progress} can have a name, and that name will be reported through
+  * the event system (it will report the first non-null name in the stack from
+  * the {@link Progress} from which the event originated to the parent the event
+  * is listened on).
+  * <p>
+  * The {@link Progress} also has a table of keys/values shared amongst all the
+  * hierarchy (note that when adding a {@link Progress} to others, its values
+  * will be prioritized if some with the same keys were already present in the
+  * hierarchy).
   * 
   * @author niki
   */
@@@ -35,6 -45,7 +45,7 @@@ public class Progress 
                public void progress(Progress progress, String name);
        }
  
+       private Map<Object, Object> map = new HashMap<Object, Object>();
        private Progress parent = null;
        private Object lock = new Object();
        private String name;
                };
  
                synchronized (lock) {
+                       // Should not happen but just in case
+                       if (this.map != progress.map) {
+                               this.map.putAll(progress.map);
+                       }
+                       progress.map = this.map;
                        progress.parent = this;
                        this.children.put(progress, weight);
                        progress.addProgressListener(progressListener);
                }
        }
+       /**
+        * Set the given value for the given key on this {@link Progress} and it's
+        * children.
+        * 
+        * @param key
+        *            the key
+        * @param value
+        *            the value
+        */
+       public void put(Object key, Object value) {
+               map.put(key, value);
+       }
+       /**
+        * Return the value associated with this key as a {@link String} if any,
+        * NULL if not.
+        * <p>
+        * If the value is not NULL but not a {@link String}, it will be converted
+        * via {@link Object#toString()}.
+        * 
+        * @param key
+        *            the key to check
+        * 
+        * @return the value or NULL
+        */
+       public String getString(Object key) {
+               Object value = map.get(key);
+               if (value == null) {
+                       return null;
+               }
+               return value.toString();
+       }
+       /**
+        * Return the value associated with this key if any, NULL if not.
+        * 
+        * @param key
+        *            the key to check
+        * 
+        * @return the value or NULL
+        */
+       public Object get(Object key) {
+               return map.get(key);
+       }
  }
index 8b993e64b58706c4bf1b567d482ff1618deae43d,a0e205c9d2152c6c8d0404a42217bdb42e420748..a0e205c9d2152c6c8d0404a42217bdb42e420748
@@@ -3,7 -3,6 +3,6 @@@ package be.nikiroo.utils.ui
  
  import java.awt.BorderLayout;
  import java.awt.Dimension;
- import java.awt.FlowLayout;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.ComponentAdapter;
index 2d3ac267842325333484b68886998471981d8135,b4dbe7bb04130bf69e4e67ca12ea330b812544ef..b4dbe7bb04130bf69e4e67ca12ea330b812544ef
@@@ -78,8 -78,14 +78,14 @@@ public class DataNode<T> 
                return userData;
        }
  
-       protected int count() {
-               int s = 0;
+       /**
+        * The total number of nodes present in this {@link DataNode} (including
+        * itself and descendants).
+        * 
+        * @return the number
+        */
+       public int count() {
+               int s = 1;
                for (DataNode<T> child : children) {
                        s += child.count();
                }
index e941a71d2ac00a238c0c6fd6a67932b7f08ca080,6b3657da7f0f2dbc3a73506ea26aa70b8581b4fc..6b3657da7f0f2dbc3a73506ea26aa70b8581b4fc
@@@ -10,7 -10,7 +10,7 @@@ import javax.swing.tree.DefaultMutableT
  import javax.swing.tree.MutableTreeNode;
  
  public abstract class DataTree<E> {
-       private DataNode<E> data;
+       protected DataNode<E> data;
  
        public DataNode<E> loadData() throws IOException {
                return this.data = extractData();
  
                return new DataNode<E>(children, source.getUserData());
        }
-       // TODO: not in this class:
-       public void loadInto(DefaultMutableTreeNode root, String filter) {
-               DataNode<E> filtered = getRoot(filter);
-               for (DataNode<E> child : filtered.getChildren()) {
-                       root.add(nodeToNode(child));
-               }
-       }
-       private MutableTreeNode nodeToNode(DataNode<E> node) {
-               // TODO: node.toString
-               DefaultMutableTreeNode otherNode = new DefaultMutableTreeNode(
-                               node.toString());
-               for (DataNode<E> child : node.getChildren()) {
-                       otherNode.add(nodeToNode(child));
-               }
-               return otherNode;
-       }
  }
index 3618b89e51e25e28c6a549181065e89b59d67468,2a16c983b21ef54ee1b09dcc884d68e4ba6f0ee9..2a16c983b21ef54ee1b09dcc884d68e4ba6f0ee9
@@@ -201,7 -201,7 +201,7 @@@ public class DelayWorker 
         * @param worker
         *            the process to delay
         */
-       public void delay(final String id, final SwingWorker worker) {
+       public void delay(String id, SwingWorker worker) {
                synchronized (lazyEnCoursLock) {
                        lazyEnCours.put(id, worker);
                }
index 06e7914c1a80a5dbcc6287d8d636f96c8de000e2,cf16d5f0a111e660171102b3efad3bdefe1d6d48..cf16d5f0a111e660171102b3efad3bdefe1d6d48
@@@ -2,6 -2,7 +2,7 @@@ package be.nikiroo.utils.ui
  
  import java.awt.Component;
  import java.awt.Point;
+ import java.awt.Window;
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
  import java.util.ArrayList;
@@@ -11,6 -12,7 +12,7 @@@ import java.util.List
  import javax.swing.JList;
  import javax.swing.JPopupMenu;
  import javax.swing.ListCellRenderer;
+ import javax.swing.SwingWorker;
  
  import be.nikiroo.utils.compat.DefaultListModel6;
  import be.nikiroo.utils.compat.JList6;
@@@ -78,32 -80,66 +80,66 @@@ public class ListModel<T> extends Defau
                public void setHovered(boolean hovered);
        }
  
+       /**
+        * An interface required to support tooltips on this {@link ListModel}.
+        * 
+        * @author niki
+        *
+        * @param <T>
+        *            the type of elements and items (the same type)
+        */
+       public interface TooltipCreator<T> {
+               /**
+                * Generate a tooltip {@link Window} for this element.
+                * <p>
+                * Note that the tooltip can be of two modes: undecorated or standalone.
+                * An undecorated tooltip will be taken care of by this
+                * {@link ListModel}, but a standalone one is supposed to be its own
+                * Dialog or Frame (it won't be automatically closed).
+                * 
+                * @param t
+                *            the element to generate a tooltip for
+                * @param undecorated
+                *            TRUE for undecorated tooltip, FALSE for standalone
+                *            tooltips
+                * 
+                * @return the generated tooltip or NULL for none
+                */
+               public Window generateTooltip(T t, boolean undecorated);
+       }
        private int hoveredIndex;
        private List<T> items = new ArrayList<T>();
-       private JList6<T> list;
+       private boolean keepSelection = true;
+       private TooltipCreator<T> tooltipCreator;
+       private Window tooltip;
+       @SuppressWarnings("rawtypes") // JList<?> not compatible Java 1.6
+       private JList list;
  
        /**
         * Create a new {@link ListModel}.
         * 
         * @param list
-        *            the {@link JList} we will handle the data of (cannot be NULL)
+        *            the {@link JList6} we will handle the data of (cannot be NULL)
         */
-       @SuppressWarnings({ "unchecked", "rawtypes" }) // not compatible Java 1.6
-       public ListModel(JList list) {
-               this((JList6<T>) list);
+       @SuppressWarnings("rawtypes") // JList<?> not compatible Java 1.6
+       public ListModel(JList6<T> list) {
+               this((JList) list);
        }
  
        /**
         * Create a new {@link ListModel}.
         * 
         * @param list
-        *            the {@link JList} we will handle the data of (cannot be NULL)
+        *            the {@link JList6} we will handle the data of (cannot be NULL)
         * @param popup
         *            the popup to use and keep track of (can be NULL)
         */
-       @SuppressWarnings({ "unchecked", "rawtypes" }) // not compatible Java 1.6
-       public ListModel(final JList list, final JPopupMenu popup) {
-               this((JList6<T>) list, popup);
+       @SuppressWarnings("rawtypes") // JList<?> not compatible Java 1.6
+       public ListModel(JList6<T> list, JPopupMenu popup) {
+               this((JList) list, popup);
        }
  
        /**
         * 
         * @param list
         *            the {@link JList6} we will handle the data of (cannot be NULL)
+        * @param tooltipCreator
+        *            use this if you want the list to display tooltips on hover
+        *            (can be NULL)
         */
-       public ListModel(JList6<T> list) {
-               this(list, null);
+       @SuppressWarnings("rawtypes") // JList<?> not compatible Java 1.6
+       public ListModel(JList6<T> list, TooltipCreator<T> tooltipCreator) {
+               this((JList) list, null, tooltipCreator);
        }
  
        /**
         *            the {@link JList6} we will handle the data of (cannot be NULL)
         * @param popup
         *            the popup to use and keep track of (can be NULL)
+        * @param tooltipCreator
+        *            use this if you want the list to display tooltips on hover
+        *            (can be NULL)
+        */
+       @SuppressWarnings("rawtypes") // JList<?> not compatible Java 1.6
+       public ListModel(JList6<T> list, JPopupMenu popup,
+                       TooltipCreator<T> tooltipCreator) {
+               this((JList) list, popup, tooltipCreator);
+       }
+       /**
+        * Create a new {@link ListModel}.
+        * <p>
+        * Note that you must take care of passing a {@link JList} that only handles
+        * elements of the type of this {@link ListModel} -- you can also use
+        * {@link ListModel#ListModel(JList6)} instead.
+        * 
+        * @param list
+        *            the {@link JList} we will handle the data of (cannot be NULL,
+        *            must only contain elements of the type of this
+        *            {@link ListModel})
+        */
+       @SuppressWarnings("rawtypes") // JList<?> not compatible Java 1.6
+       public ListModel(JList list) {
+               this(list, null, null);
+       }
+       /**
+        * Create a new {@link ListModel}.
+        * <p>
+        * Note that you must take care of passing a {@link JList} that only handles
+        * elements of the type of this {@link ListModel} -- you can also use
+        * {@link ListModel#ListModel(JList6, JPopupMenu)} instead.
+        * 
+        * @param list
+        *            the {@link JList} we will handle the data of (cannot be NULL,
+        *            must only contain elements of the type of this
+        *            {@link ListModel})
+        * @param popup
+        *            the popup to use and keep track of (can be NULL)
         */
-       public ListModel(final JList6<T> list, final JPopupMenu popup) {
+       @SuppressWarnings("rawtypes") // JList<?> not in Java 1.6
+       public ListModel(JList list, JPopupMenu popup) {
+               this(list, popup, null);
+       }
+       /**
+        * Create a new {@link ListModel}.
+        * <p>
+        * Note that you must take care of passing a {@link JList} that only handles
+        * elements of the type of this {@link ListModel} -- you can also use
+        * {@link ListModel#ListModel(JList6, JPopupMenu)} instead.
+        * 
+        * @param list
+        *            the {@link JList} we will handle the data of (cannot be NULL,
+        *            must only contain elements of the type of this
+        *            {@link ListModel})
+        * @param tooltipCreator
+        *            use this if you want the list to display tooltips on hover
+        *            (can be NULL)
+        */
+       @SuppressWarnings("rawtypes") // JList<?> not in Java 1.6
+       public ListModel(JList list, TooltipCreator<T> tooltipCreator) {
+               this(list, null, tooltipCreator);
+       }
+       /**
+        * Create a new {@link ListModel}.
+        * <p>
+        * Note that you must take care of passing a {@link JList} that only handles
+        * elements of the type of this {@link ListModel} -- you can also use
+        * {@link ListModel#ListModel(JList6, JPopupMenu)} instead.
+        * 
+        * @param list
+        *            the {@link JList} we will handle the data of (cannot be NULL,
+        *            must only contain elements of the type of this
+        *            {@link ListModel})
+        * @param popup
+        *            the popup to use and keep track of (can be NULL)
+        * @param tooltipCreator
+        *            use this if you want the list to display tooltips on hover
+        *            (can be NULL)
+        */
+       @SuppressWarnings({ "unchecked", "rawtypes" }) // JList<?> not in Java 1.6
+       public ListModel(final JList list, final JPopupMenu popup,
+                       TooltipCreator<T> tooltipCreator) {
                this.list = list;
+               this.tooltipCreator = tooltipCreator;
                list.setModel(this);
  
+               final DelayWorker tooltipWatcher = new DelayWorker(500);
+               if (tooltipCreator != null) {
+                       tooltipWatcher.start();
+               }
                list.addMouseMotionListener(new MouseAdapter() {
                        @Override
-                       public void mouseMoved(MouseEvent me) {
+                       public void mouseMoved(final MouseEvent me) {
                                if (popup != null && popup.isShowing())
                                        return;
  
                                Point p = new Point(me.getX(), me.getY());
-                               int index = list.locationToIndex(p);
+                               final int index = list.locationToIndex(p);
                                if (index != hoveredIndex) {
                                        int oldIndex = hoveredIndex;
                                        hoveredIndex = index;
                                        fireElementChanged(oldIndex);
                                        fireElementChanged(index);
+                                       if (ListModel.this.tooltipCreator != null) {
+                                               tooltipWatcher.delay("tooltip",
+                                                               new SwingWorker<Void, Void>() {
+                                                                       @Override
+                                                                       protected Void doInBackground()
+                                                                                       throws Exception {
+                                                                               return null;
+                                                                       }
+                                                                       @Override
+                                                                       protected void done() {
+                                                                               Window oldTooltip = tooltip;
+                                                                               tooltip = null;
+                                                                               if (oldTooltip != null) {
+                                                                                       oldTooltip.setVisible(false);
+                                                                               }
+                                                                               if (index < 0
+                                                                                               || index != hoveredIndex) {
+                                                                                       return;
+                                                                               }
+                                                                               tooltip = newTooltip(index, me);
+                                                                       }
+                                                               });
+                                       }
                                }
                        }
                });
                                        popup.show(list, e.getX(), e.getY());
                                }
                        }
                });
        }
  
+       /**
+        * (Try and) keep the elements that were selected when filtering.
+        * <p>
+        * This will use toString on the elements to identify them, and can be a bit
+        * resource intensive.
+        * 
+        * @return TRUE if we do
+        */
+       public boolean isKeepSelection() {
+               return keepSelection;
+       }
+       /**
+        * (Try and) keep the elements that were selected when filtering.
+        * <p>
+        * This will use toString on the elements to identify them, and can be a bit
+        * resource intensive.
+        * 
+        * @param keepSelection
+        *            TRUE to try and keep them selected
+        */
+       public void setKeepSelection(boolean keepSelection) {
+               this.keepSelection = keepSelection;
+       }
        /**
         * Check if this element is currently under the mouse.
         * 
         *            the filter will be copied as an element (can be NULL, in that
         *            case all items will be copied as elements)
         */
-       @SuppressWarnings("unchecked") // ListModel<T> and JList<T> are not java 1.6
+       @SuppressWarnings("unchecked") // JList<?> not compatible Java 1.6
        public void filter(Predicate<T> filter) {
+               ListSnapshot snapshot = null;
+               if (keepSelection)
+                       snapshot = new ListSnapshot(list);
                clear();
                for (T item : items) {
                        if (filter == null || filter.test(item)) {
                        }
                }
  
+               if (keepSelection)
+                       snapshot.apply();
                list.repaint();
        }
  
                }
        }
  
-       @SuppressWarnings("unchecked") // ListModel<T> and JList<T> are not java 1.6
+       @SuppressWarnings("unchecked") // JList<?> not compatible Java 1.6
        @Override
        public T get(int index) {
                return (T) super.get(index);
        }
  
+       private Window newTooltip(final int index, final MouseEvent me) {
+               final T value = ListModel.this.get(index);
+               final Window newTooltip = tooltipCreator.generateTooltip(value, true);
+               if (newTooltip != null) {
+                       newTooltip.addMouseListener(new MouseAdapter() {
+                               @Override
+                               public void mouseClicked(MouseEvent e) {
+                                       Window promotedTooltip = tooltipCreator
+                                                       .generateTooltip(value, false);
+                                       promotedTooltip.setLocation(newTooltip.getLocation());
+                                       newTooltip.setVisible(false);
+                                       promotedTooltip.setVisible(true);
+                               }
+                       });
+                       newTooltip.setLocation(me.getXOnScreen(), me.getYOnScreen());
+                       newTooltip.setVisible(true);
+               }
+               return newTooltip;
+       }
        /**
         * Generate a {@link ListCellRenderer} that supports {@link Hoverable}
         * elements.
index 0000000000000000000000000000000000000000,d2e89c86884491996cf75a40056079d5bb03038e..d2e89c86884491996cf75a40056079d5bb03038e
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,62 +1,62 @@@
+ package be.nikiroo.utils.ui;
+ import java.util.ArrayList;
+ import java.util.List;
+ import javax.swing.JList;
+ public class ListSnapshot {
+       private JList list;
+       private List<Object> elements = new ArrayList<Object>();
+       public ListSnapshot(JList list) {
+               this.list = list;
+               for (int index : list.getSelectedIndices()) {
+                       elements.add(list.getModel().getElementAt(index));
+               }
+       }
+       public void apply() {
+               applyTo(list);
+       }
+       public void applyTo(JList list) {
+               List<Integer> indices = new ArrayList<Integer>();
+               for (int i = 0; i < list.getModel().getSize(); i++) {
+                       Object newObject = list.getModel().getElementAt(i);
+                       for (Object oldObject : elements) {
+                               if (isSameElement(oldObject, newObject)) {
+                                       indices.add(i);
+                                       break;
+                               }
+                       }
+               }
+               int a[] = new int[indices.size()];
+               for (int i = 0; i < indices.size(); i++) {
+                       a[i] = indices.get(i);
+               }
+               list.setSelectedIndices(a);
+       }
+       // You can override this
+       protected boolean isSameElement(Object oldElement, Object newElement) {
+               if (oldElement == null || newElement == null)
+                       return oldElement == null && newElement == null;
+               return oldElement.toString().equals(newElement.toString());
+       }
+       @Override
+       public String toString() {
+               StringBuilder builder = new StringBuilder();
+               builder.append("List Snapshot of: ").append(list).append("\n");
+               builder.append("Selected elements:\n");
+               for (Object element : elements) {
+                       builder.append("\t").append(element).append("\n");
+               }
+               return builder.toString();
+       }
+ }
index 0000000000000000000000000000000000000000,3fa41c83cb51e3c5e7ef9ecad6f7e07204e2b203..3fa41c83cb51e3c5e7ef9ecad6f7e07204e2b203
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,53 +1,53 @@@
+ package be.nikiroo.utils.ui;
+ import java.awt.event.ActionListener;
+ /**
+  * The default {@link ActionListener} add/remove/fire methods.
+  * 
+  * @author niki
+  */
+ public interface ListenerItem {
+       /**
+        * Check that this {@link ListenerItem} currently has
+        * {@link ActionListener}s that listen on it.
+        * 
+        * @return TRUE if it has
+        */
+       public boolean hasListeners();
+       /**
+        * Check how many events are currently waiting for an
+        * {@link ActionListener}.
+        * 
+        * @return the number of waiting events (can be 0)
+        */
+       public int getWaitingEventCount();
+       /**
+        * Adds the specified action listener to receive action events from this
+        * {@link ListenerItem}.
+        *
+        * @param listener
+        *            the action listener to be added
+        */
+       public void addActionListener(ActionListener listener);
+       /**
+        * Removes the specified action listener so that it no longer receives
+        * action events from this {@link ListenerItem}.
+        *
+        * @param listener
+        *            the action listener to be removed
+        */
+       public void removeActionListener(ActionListener listener);
+       /**
+        * Notify the listeners of an action.
+        * 
+        * @param listenerCommand
+        *            A string that may specify a command (possibly one of several)
+        *            associated with the event
+        */
+       public void fireActionPerformed(String listenerCommand);
+ }
index 144cdd249a81be951a082618f2a9f4371adaf0a9,ada07960dbe5806f3750b54f83c3f4d413aeb423..ada07960dbe5806f3750b54f83c3f4d413aeb423
@@@ -17,7 -17,7 +17,7 @@@ import javax.swing.JPanel
   * 
   * @author niki
   */
- public class ListenerPanel extends JPanel {
+ public class ListenerPanel extends JPanel implements ListenerItem {
        private static final long serialVersionUID = 1L;
  
        /** Waiting queue until at least one listener is here to get the events. */
                waitingQueue = new LinkedList<ActionEvent>();
        }
  
-       /**
-        * Check that this {@link ListenerPanel} currently has
-        * {@link ActionListener}s that listen on it.
-        * 
-        * @return TRUE if it has
-        */
+       @Override
        public synchronized boolean hasListeners() {
                return listenerList.getListenerList().length > 1;
        }
  
-       /**
-        * Check how many events are currently waiting for an
-        * {@link ActionListener}.
-        * 
-        * @return the number of waiting events (can be 0)
-        */
+       @Override
        public synchronized int getWaitingEventCount() {
                return waitingQueue.size();
        }
  
-       /**
-        * Adds the specified action listener to receive action events from this
-        * {@link ListenerPanel}.
-        *
-        * @param listener
-        *            the action listener to be added
-        */
+       @Override
        public synchronized void addActionListener(ActionListener listener) {
                if (!hasListeners()) {
                        while (!waitingQueue.isEmpty()) {
                listenerList.add(ActionListener.class, listener);
        }
  
-       /**
-        * Removes the specified action listener so that it no longer receives
-        * action events from this {@link ListenerPanel}.
-        *
-        * @param listener
-        *            the action listener to be removed
-        */
+       @Override
        public synchronized void removeActionListener(ActionListener listener) {
                listenerList.remove(ActionListener.class, listener);
        }
  
-       /**
-        * Notify the listeners of an action.
-        * 
-        * @param listenerCommand
-        *            A string that may specify a command (possibly one of several)
-        *            associated with the event
-        */
-       protected synchronized void fireActionPerformed(String listenerCommand) {
+       @Override
+       public synchronized void fireActionPerformed(String listenerCommand) {
                ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
                                listenerCommand);
  
index 9f16aabe79c3ba8dc80e8221bace21b2ec5f0d4a,5861d00fc3a6e3a254230c8bd5d43a17d26299db..5861d00fc3a6e3a254230c8bd5d43a17d26299db
@@@ -178,12 -178,39 +178,39 @@@ public class UIUtils 
         * @return the {@link JScrollPane}
         */
        static public JScrollPane scroll(JComponent pane, boolean allowHorizontal) {
+               return scroll(pane, allowHorizontal, true);
+       }
+       /**
+        * Add a {@link JScrollPane} around the given panel and use a sensible (for
+        * me) increment for the mouse wheel.
+        * 
+        * @param pane
+        *            the panel to wrap in a {@link JScrollPane}
+        * @param allowHorizontal
+        *            allow horizontal scrolling (not always desired)
+        * @param allowVertical
+        *            allow vertical scrolling (usually yes, but sometimes you only
+        *            want horizontal)
+        * 
+        * @return the {@link JScrollPane}
+        */
+       static public JScrollPane scroll(JComponent pane, boolean allowHorizontal,
+                       boolean allowVertical) {
                JScrollPane scroll = new JScrollPane(pane);
                scroll.getVerticalScrollBar().setUnitIncrement(16);
+               scroll.getHorizontalScrollBar().setUnitIncrement(16);
                if (!allowHorizontal) {
                        scroll.setHorizontalScrollBarPolicy(
                                        JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
                }
+               if (!allowVertical) {
+                       scroll.setVerticalScrollBarPolicy(
+                                       JScrollPane.VERTICAL_SCROLLBAR_NEVER);
+               }
                return scroll;
        }
  }