From: hansonr Date: Fri, 6 Jul 2018 07:58:29 +0000 (+0100) Subject: JAL-3026 all tabbed panes use JavaScript option X-Git-Tag: Develop-2_11_2_0-d20201215~24^2~68^2~574^2~1 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=65a4ec4dee935394e57a2af5a9daa2f6ecc4aef3;hp=98638423b556672dbb6c3a815d99ff1cab884388;p=jalview.git JAL-3026 all tabbed panes use JavaScript option jalview.jbui.swing.JTabbedPane --- diff --git a/libjs/MiGLayout-site.zip b/libjs/MiGLayout-site.zip index 181733e..0c3f63b 100644 Binary files a/libjs/MiGLayout-site.zip and b/libjs/MiGLayout-site.zip differ diff --git a/src/jalview/fts/core/GFTSPanel.java b/src/jalview/fts/core/GFTSPanel.java index 86710e1..57bad5d 100644 --- a/src/jalview/fts/core/GFTSPanel.java +++ b/src/jalview/fts/core/GFTSPanel.java @@ -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 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(); diff --git a/src/jalview/gui/WebserviceInfo.java b/src/jalview/gui/WebserviceInfo.java index 2fc08e1..598c4af 100644 --- a/src/jalview/gui/WebserviceInfo.java +++ b/src/jalview/gui/WebserviceInfo.java @@ -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()); diff --git a/src/jalview/jbgui/GAlignFrame.java b/src/jalview/jbgui/GAlignFrame.java index 7a85a4a..a5c2a36 100755 --- a/src/jalview/jbgui/GAlignFrame.java +++ b/src/jalview/jbgui/GAlignFrame.java @@ -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(); diff --git a/src/jalview/jbgui/GPreferences.java b/src/jalview/jbgui/GPreferences.java index 6807382..327cdc5 100755 --- a/src/jalview/jbgui/GPreferences.java +++ b/src/jalview/jbgui/GPreferences.java @@ -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); diff --git a/src/jalview/jbgui/GRestServiceEditorPane.java b/src/jalview/jbgui/GRestServiceEditorPane.java index a4dca4b..0df8192 100644 --- a/src/jalview/jbgui/GRestServiceEditorPane.java +++ b/src/jalview/jbgui/GRestServiceEditorPane.java @@ -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); diff --git a/src/jalview/jbgui/GStructureChooser.java b/src/jalview/jbgui/GStructureChooser.java index 240e1fd..489dc01 100644 --- a/src/jalview/jbgui/GStructureChooser.java +++ b/src/jalview/jbgui/GStructureChooser.java @@ -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 index 0000000..b1c0bd8 --- /dev/null +++ b/src/jalview/jbgui/swing/JTabbedPane.java @@ -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 tabs; + + private Component visComp; + + /** + * Only one ChangeEvent is needed per TabPane + * 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(); + 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 ModelChanged 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 ChangeEvents differently + * can override this to return a subclass of ModelListener or + * another ChangeListener implementation. + * + * @see #fireStateChanged + */ + protected ChangeListener createChangeListener() { + return new ModelListener(); + } + + /** + * Adds a ChangeListener to this tabbedpane. + * + * @param l the ChangeListener to add + * @see #fireStateChanged + * @see #removeChangeListener + */ + public void addChangeListener(ChangeListener l) { + listenerList.add(ChangeListener.class, l); + } + + /** + * Removes a ChangeListener from this tabbedpane. + * + * @param l the ChangeListener 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 ChangeListeners added + * to this JTabbedPane with addChangeListener. + * + * @return all of the ChangeListeners 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 index. + * + * @param index the index of the item being queried + * @return the Component at index + * @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 pages = new Lst(); + + 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); + } + +} diff --git a/swingjs/SwingJS-site.zip b/swingjs/SwingJS-site.zip index 8754b6b..447678e 100644 Binary files a/swingjs/SwingJS-site.zip and b/swingjs/SwingJS-site.zip differ