JAL-3026 all tabbed panes use JavaScript option
authorhansonr <hansonr@Geoff_Surface>
Fri, 6 Jul 2018 07:58:29 +0000 (08:58 +0100)
committerhansonr <hansonr@Geoff_Surface>
Fri, 6 Jul 2018 07:58:29 +0000 (08:58 +0100)
jalview.jbui.swing.JTabbedPane

libjs/MiGLayout-site.zip
src/jalview/fts/core/GFTSPanel.java
src/jalview/gui/WebserviceInfo.java
src/jalview/jbgui/GAlignFrame.java
src/jalview/jbgui/GPreferences.java
src/jalview/jbgui/GRestServiceEditorPane.java
src/jalview/jbgui/GStructureChooser.java
src/jalview/jbgui/swing/JTabbedPane.java [new file with mode: 0644]
swingjs/SwingJS-site.zip

index 181733e..0c3f63b 100644 (file)
Binary files a/libjs/MiGLayout-site.zip and b/libjs/MiGLayout-site.zip differ
index 86710e1..57bad5d 100644 (file)
@@ -89,7 +89,8 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI
   protected JInternalFrame mainFrame = new JInternalFrame(
           getFTSFrameTitle());
 
-  protected JTabbedPane tabs = new JTabbedPane();
+  protected JTabbedPane tabs = jalview.jbgui.swing.JTabbedPane.createTabbedPane();
   protected IProgressIndicator progressIndicator;
 
   protected JComboBox<FTSDataColumnI> cmb_searchTarget = new JComboBox<>();
@@ -131,7 +132,7 @@ public abstract class GFTSPanel extends JPanel implements GFTSPanelI
 
   protected JLabel lbl_blank = new JLabel(balnkPlaceholderImage);
 
-  private JTabbedPane tabbedPane = new JTabbedPane();
+  private JTabbedPane tabbedPane = jalview.jbgui.swing.JTabbedPane.createTabbedPane();
 
   private JPanel pnl_actions = new JPanel();
 
index 2fc08e1..598c4af 100644 (file)
@@ -216,7 +216,7 @@ public class WebserviceInfo extends GWebserviceInfo
         // revert to a tabbed pane.
         JScrollPane firstpane;
         this.remove(firstpane = (JScrollPane) jobPanes.get(0));
-        subjobs = new JTabbedPane();
+        subjobs = jalview.jbgui.swing.JTabbedPane.createTabbedPane();
         this.add(subjobs, BorderLayout.CENTER);
         subjobs.add(firstpane);
         subjobs.setTitleAt(0, firstpane.getName());
index 7a85a4a..a5c2a36 100755 (executable)
@@ -169,7 +169,7 @@ public class GAlignFrame extends JInternalFrame
 
   protected JCheckBoxMenuItem hiddenMarkers = new JCheckBoxMenuItem();
 
-  protected JTabbedPane tabbedPane = new JTabbedPane();
+  protected JTabbedPane tabbedPane = jalview.jbgui.swing.JTabbedPane.createTabbedPane();
 
   protected JMenuItem reload = new JMenuItem();
 
index 6807382..327cdc5 100755 (executable)
@@ -291,7 +291,7 @@ public class GPreferences extends JPanel
    */
   private void jbInit() throws Exception
   {
-    final JTabbedPane tabbedPane = new JTabbedPane();
+    final JTabbedPane tabbedPane = jalview.jbgui.swing.JTabbedPane.createTabbedPane();
     this.setLayout(new BorderLayout());
     JPanel okCancelPanel = initOkCancelPanel();
     this.add(tabbedPane, BorderLayout.CENTER);
index a4dca4b..0df8192 100644 (file)
@@ -98,7 +98,7 @@ public class GRestServiceEditorPane extends JPanel
     paste.setLayout(
             new MigLayout("", "[grow 100, fill]", "[][grow 100,fill]"));
 
-    panels = new JTabbedPane();
+    panels = jalview.jbgui.swing.JTabbedPane.createTabbedPane();
     panels.addTab(details.getName(), details);
     panels.addTab(inputs.getName(), inputs);
     panels.addTab(paste.getName(), paste);
index 240e1fd..489dc01 100644 (file)
@@ -157,7 +157,7 @@ public abstract class GStructureChooser extends JPanel
 
   protected JTable tbl_local_pdb = new JTable();
 
-  protected JTabbedPane pnl_filter = new JTabbedPane();
+  protected JTabbedPane pnl_filter = jalview.jbgui.swing.JTabbedPane.createTabbedPane();
 
   protected FTSDataColumnPreferences pdbDocFieldPrefs = new FTSDataColumnPreferences(
           PreferenceSource.STRUCTURE_CHOOSER,
diff --git a/src/jalview/jbgui/swing/JTabbedPane.java b/src/jalview/jbgui/swing/JTabbedPane.java
new file mode 100644 (file)
index 0000000..b1c0bd8
--- /dev/null
@@ -0,0 +1,474 @@
+package jalview.jbgui.swing;
+
+import jalview.fts.core.GFTSPanel;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.swing.BoxLayout;
+import javax.swing.Icon;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.ToolTipManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+import javajs.util.Lst;
+
+@SuppressWarnings("serial")
+public class JTabbedPane extends JPanel
+{
+
+  @SuppressWarnings("unused")
+  public static javax.swing.JTabbedPane createTabbedPane()
+  {
+    // BH 2018 coercing jalview.jbgui.swing.JTabbedPane() for now
+    if (/** @j2sNative false && */
+    true)
+    {
+      // Java
+      return new javax.swing.JTabbedPane();
+    }
+    // JavaScript
+    return (javax.swing.JTabbedPane) (Object) new jalview.jbgui.swing.JTabbedPane();
+  }
+
+  private JPanel pagePanel;
+
+  private JComboBox<String> tabs;
+
+  private Component visComp;
+  
+  /**
+   * Only one <code>ChangeEvent</code> is needed per <code>TabPane</code>
+   * instance since the
+   * event's only (read-only) state is the source property.  The source
+   * of events generated here is always "this".
+   */
+  protected transient ChangeEvent changeEvent = null;
+
+  
+  /** just faking a JTabbedPane here using a drop-down combo box and a JPanel
+  
+    This will take considerably more work to make it right in general.
+  */
+  
+  private JTabbedPane()
+  {
+    pagePanel = new JPanel();
+    pagePanel.setLayout(new BoxLayout(pagePanel, BoxLayout.PAGE_AXIS));
+    tabs = new JComboBox<String>();
+    tabs.setMaximumSize(new Dimension(150, 15));
+    tabs.addItemListener(new ItemListener()
+    {
+
+      @Override
+      public void itemStateChanged(ItemEvent e)
+      {
+        update(false);
+      }
+
+    });
+    setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
+    add(tabs);
+    add(pagePanel);
+  }
+
+  public void setTitleAt(int index, String title)
+  {
+    Page page = pages.get(index);
+    String oldTitle = page.title;
+    page.title = title;
+
+    if (oldTitle != title)
+    {
+      firePropertyChange("indexForTitle", -1, index);
+    }
+    page.updateDisplayedMnemonicIndex();
+    // if ((oldTitle != title) && (accessibleContext != null)) {
+    // accessibleContext.firePropertyChange(
+    // AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+    // oldTitle, title);
+    // }
+    if (title == null || oldTitle == null || !title.equals(oldTitle))
+    {
+      revalidate();
+      repaint();
+    }
+  }
+
+  public String getTitleAt(int index)
+  {
+    return pages.get(index).title;
+  }
+
+  /**
+   * We pass <code>ModelChanged</code> events along to the listeners with
+   * the tabbedpane (instead of the model itself) as the event source.
+   */
+  protected class ModelListener implements ChangeListener {
+      @Override
+                              public void stateChanged(ChangeEvent e) {
+          fireStateChanged();
+      }
+  }
+
+  /**
+   * Subclasses that want to handle <code>ChangeEvents</code> differently
+   * can override this to return a subclass of <code>ModelListener</code> or
+   * another <code>ChangeListener</code> implementation.
+   *
+   * @see #fireStateChanged
+   */
+  protected ChangeListener createChangeListener() {
+      return new ModelListener();
+  }
+  
+  /**
+   * Adds a <code>ChangeListener</code> to this tabbedpane.
+   *
+   * @param l the <code>ChangeListener</code> to add
+   * @see #fireStateChanged
+   * @see #removeChangeListener
+   */
+  public void addChangeListener(ChangeListener l) {
+      listenerList.add(ChangeListener.class, l);
+  }
+
+  /**
+   * Removes a <code>ChangeListener</code> from this tabbedpane.
+   *
+   * @param l the <code>ChangeListener</code> to remove
+   * @see #fireStateChanged
+   * @see #addChangeListener
+   */
+  public void removeChangeListener(ChangeListener l) {
+      listenerList.remove(ChangeListener.class, l);
+  }
+
+  public void setSelectedComponent(JPanel structureTab)
+  {
+    // TODO Auto-generated method stub
+
+  }
+
+  /**
+   * Returns an array of all the <code>ChangeListener</code>s added
+   * to this <code>JTabbedPane</code> with <code>addChangeListener</code>.
+   *
+   * @return all of the <code>ChangeListener</code>s added or an empty
+   *         array if no listeners have been added
+   * @since 1.4
+   */
+  public ChangeListener[] getChangeListeners() {
+      return (ChangeListener[])listenerList.getListeners(
+              ChangeListener.class);
+  }
+
+  /**
+   * Sends a {@code ChangeEvent}, with this {@code JTabbedPane} as the source,
+   * to each registered listener. This method is called each time there is
+   * a change to either the selected index or the selected tab in the
+   * {@code JTabbedPane}. Usually, the selected index and selected tab change
+   * together. However, there are some cases, such as tab addition, where the
+   * selected index changes and the same tab remains selected. There are other
+   * cases, such as deleting the selected tab, where the index remains the
+   * same, but a new tab moves to that index. Events are fired for all of
+   * these cases.
+   *
+   * @see #addChangeListener
+   * @see EventListenerList
+   */
+  protected void fireStateChanged() {
+      /* --- Begin code to deal with visibility --- */
+
+      /* This code deals with changing the visibility of components to
+       * hide and show the contents for the selected tab. It duplicates
+       * logic already present in BasicTabbedPaneUI, logic that is
+       * processed during the layout pass. This code exists to allow
+       * developers to do things that are quite difficult to accomplish
+       * with the previous model of waiting for the layout pass to process
+       * visibility changes; such as requesting focus on the new visible
+       * component.
+       *
+       * For the average code, using the typical JTabbedPane methods,
+       * all visibility changes will now be processed here. However,
+       * the code in BasicTabbedPaneUI still exists, for the purposes
+       * of backward compatibility. Therefore, when making changes to
+       * this code, ensure that the BasicTabbedPaneUI code is kept in
+       * synch.
+       */
+
+      int selIndex = getSelectedIndex();
+
+      /* if the selection is now nothing */
+      if (selIndex < 0) {
+          /* if there was a previous visible component */
+          if (visComp != null && visComp.isVisible()) {
+              /* make it invisible */
+              visComp.setVisible(false);
+          }
+
+          /* now there's no visible component */
+          visComp = null;
+
+      /* else - the selection is now something */
+      } else {
+          /* Fetch the component for the new selection */
+          Component newComp = getComponentAt(selIndex);
+
+          /* if the new component is non-null and different */
+          if (newComp != null && newComp != visComp) {
+//SwingJS X: Key Focus
+//              boolean shouldChangeFocus = false;
+
+              /* Note: the following (clearing of the old visible component)
+               * is inside this if-statement for good reason: Tabbed pane
+               * should continue to show the previously visible component
+               * if there is no component for the chosen tab.
+               */
+
+              /* if there was a previous visible component */
+              if (visComp != null) {
+//SwingJS X: Key Focus
+//                  shouldChangeFocus =
+//                      (SwingUtilities.findFocusOwner(visComp) != null);
+
+                  /* if it's still visible */
+                  if (visComp.isVisible()) {
+                      /* make it invisible */
+                      visComp.setVisible(false);
+                  }
+              }
+
+              if (!newComp.isVisible()) {
+                  newComp.setVisible(true);
+              }
+
+//SwingJS X: Key Focus
+//              if (shouldChangeFocus) {
+//                  SwingUtilities2.tabbedPaneChangeFocusTo(newComp);
+//              }
+
+              visComp = newComp;
+          } /* else - the visible component shouldn't changed */
+      }
+
+      /* --- End code to deal with visibility --- */
+
+      // 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]==ChangeListener.class) {
+              // Lazily create the event:
+              if (changeEvent == null)
+                  changeEvent = new ChangeEvent(this);
+              ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
+          }
+      }
+  }
+
+  private class Page
+  {
+    public String title;
+
+    String tab;
+
+    JComponent component;
+
+    public void updateDisplayedMnemonicIndex()
+    {
+      // TODO Auto-generated method stub
+      
+    }
+  }
+
+  public Component getSelectedComponent()
+  {
+    int i = getSelectedIndex();
+    return (i < 0 ? null : getComponent(i));
+  }
+
+  
+  /**
+   * Returns the component at <code>index</code>.
+   *
+   * @param index  the index of the item being queried
+   * @return the <code>Component</code> at <code>index</code>
+   * @exception IndexOutOfBoundsException if index is out of range
+   *            (index < 0 || index >= tab count)
+   *
+   * @see #setComponentAt
+   */
+  public Component getComponentAt(int index) {
+      return pages.get(index).component;
+  }
+  
+  Lst<Page> pages = new Lst<Page>();
+
+  public Component add(String title, Component panel)
+  {
+    add(panel, title, pages.size());
+    return panel;
+  }
+
+  public void addTab(String name, Component panel)
+  {
+    add(panel, name, Integer.MAX_VALUE);
+  }
+  
+  public void add(Component panel, String title, int index)
+  {
+    addIndex((JComponent) panel, title, index);
+  }
+
+
+  private void addIndex(JComponent panel, String title, int index)
+  {
+    // improperly allowing for multiple instances of a panel
+    Page page = new Page();
+    page.component = panel;
+    page.tab = title;
+    panel.setVisible(true);
+    if (index < pages.size())
+    {
+      pages.get(index).component = panel;
+    }
+    else
+    {
+      pages.addLast(page);
+    }
+    update(true);
+ }
+
+  public void insertTab(String title, Icon icon, Component component,
+          String tip, int index)
+  {
+    // int newIndex = index;
+
+    addIndex((JComponent) component, title, index);
+    //
+    // // If component already exists, remove corresponding
+    // // tab so that new tab gets added correctly
+    // // Note: we are allowing component=null because of compatibility,
+    // // but we really should throw an exception because much of the
+    // // rest of the JTabbedPane implementation isn't designed to deal
+    // // with null components for tabs.
+    // int removeIndex = indexOfComponent(component);
+    // if (component != null && removeIndex != -1) {
+    // removeTabAt(removeIndex);
+    // if (newIndex > removeIndex) {
+    // newIndex--;
+    // }
+    // }
+    //
+    // int selectedIndex = getSelectedIndex();
+    //
+    // pages.add(
+    // newIndex,
+    // new Page(this, title != null? title : "", icon, null, component, tip));
+    //
+    //
+    // if (component != null) {
+    // addImpl(component, null, -1);
+    // component.setVisible(false);
+    // } else {
+    // firePropertyChange("indexForNullComponent", -1, index);
+    // }
+    //
+    // if (pages.size() == 1) {
+    // setSelectedIndex(0);
+    // }
+    //
+    // if (selectedIndex >= newIndex) {
+    // setSelectedIndexImpl(selectedIndex + 1, false);
+    // }
+    //
+    // if (!haveRegistered && tip != null) {
+    // ToolTipManager.sharedInstance().registerComponent(this);
+    // haveRegistered = true;
+    // }
+
+    // if (accessibleContext != null) {
+    // accessibleContext.firePropertyChange(
+    // AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+    // null, component);
+    // }
+  }
+
+  private void update(boolean combo)
+  {
+    if (combo)
+    {
+      int i0 = tabs.getSelectedIndex();
+      tabs.removeAllItems();
+      for (int i = 0; i < pages.size(); i++)
+      {
+        tabs.addItem(pages.get(i).tab);
+      }
+      tabs.setSelectedIndex(i0 < 0 ? 0 : i0);
+    }
+    pagePanel.removeAll();
+    int selected = getSelectedIndex();
+    if (selected >= 0)
+    {// && bsEnabled.get(selected)) {
+      pagePanel.add(pages.get(selected).component);
+    }
+    revalidate();
+    repaint();
+  }
+
+  public void setComponentAt(int index, JPanel panel)
+  {
+    if (index < 0 || index >= pages.size())
+      throw new IndexOutOfBoundsException();
+    pages.get(index).component = panel;
+    update(false);
+  }
+
+  public int getTabCount()
+  {
+    return tabs.getItemCount();
+  }
+
+  public void setSelectedIndex(int index)
+  {
+    tabs.setSelectedIndex(index);
+    update(false);
+  }
+
+  public int getSelectedIndex()
+  {
+    return tabs.getSelectedIndex();
+  }
+
+  public void setEnabledAt(int index, boolean bEnable)
+  {
+    // bsEnabled.setBitTo(index, bEnable);
+    // if (!bEnable && index == getSelectedIndex()) {
+    // for (int i = index - 1; --i >= 0;) {
+    // if (bsEnabled.get(i)) {
+    // setSelectedIndex(i);
+    // return;
+    // }
+    // }
+    // }
+    // update(false, true);
+  }
+
+  public void remove(int n)
+  {
+    pages.removeItemAt(n);
+    tabs.removeItemAt(n);
+    update(false);
+    // bsEnabled.clearAll();
+    // bsEnabled.set(lastSelected);
+  }
+
+}
index 8754b6b..447678e 100644 (file)
Binary files a/swingjs/SwingJS-site.zip and b/swingjs/SwingJS-site.zip differ