JAL-3026 Java 8 upgrade and JTabbedPane
[jalview.git] / src / jalview / jbgui / swing / JTabbedPane.java
1 package jalview.jbgui.swing;
2
3 import jalview.fts.core.GFTSPanel;
4
5 import java.awt.Component;
6 import java.awt.Dimension;
7 import java.awt.event.ItemEvent;
8 import java.awt.event.ItemListener;
9
10 import javax.swing.BoxLayout;
11 import javax.swing.Icon;
12 import javax.swing.JComboBox;
13 import javax.swing.JComponent;
14 import javax.swing.JPanel;
15 import javax.swing.ToolTipManager;
16 import javax.swing.event.ChangeEvent;
17 import javax.swing.event.ChangeListener;
18 import javax.swing.event.EventListenerList;
19
20 import javajs.util.Lst;
21
22 @SuppressWarnings("serial")
23 public class JTabbedPane extends JPanel
24 {
25
26   // no longer necessary
27   
28   private JPanel pagePanel;
29
30   private JComboBox<String> tabs;
31
32   private Component visComp;
33   
34   /**
35    * Only one <code>ChangeEvent</code> is needed per <code>TabPane</code>
36    * instance since the
37    * event's only (read-only) state is the source property.  The source
38    * of events generated here is always "this".
39    */
40   protected transient ChangeEvent changeEvent = null;
41
42   
43   /** just faking a JTabbedPane here using a drop-down combo box and a JPanel
44   
45     This will take considerably more work to make it right in general.
46   */
47   
48   private JTabbedPane()
49   {
50     pagePanel = new JPanel();
51     pagePanel.setLayout(new BoxLayout(pagePanel, BoxLayout.PAGE_AXIS));
52     tabs = new JComboBox<String>();
53     tabs.setMaximumSize(new Dimension(150, 15));
54     tabs.addItemListener(new ItemListener()
55     {
56
57       @Override
58       public void itemStateChanged(ItemEvent e)
59       {
60         update(false);
61       }
62
63     });
64     setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
65     add(tabs);
66     add(pagePanel);
67   }
68
69   public void setTitleAt(int index, String title)
70   {
71     Page page = pages.get(index);
72     String oldTitle = page.title;
73     page.title = title;
74
75     if (oldTitle != title)
76     {
77       firePropertyChange("indexForTitle", -1, index);
78     }
79     page.updateDisplayedMnemonicIndex();
80     // if ((oldTitle != title) && (accessibleContext != null)) {
81     // accessibleContext.firePropertyChange(
82     // AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
83     // oldTitle, title);
84     // }
85     if (title == null || oldTitle == null || !title.equals(oldTitle))
86     {
87       revalidate();
88       repaint();
89     }
90   }
91
92   public String getTitleAt(int index)
93   {
94     return pages.get(index).title;
95   }
96
97   /**
98    * We pass <code>ModelChanged</code> events along to the listeners with
99    * the tabbedpane (instead of the model itself) as the event source.
100    */
101   protected class ModelListener implements ChangeListener {
102       @Override
103                               public void stateChanged(ChangeEvent e) {
104           fireStateChanged();
105       }
106   }
107
108   /**
109    * Subclasses that want to handle <code>ChangeEvents</code> differently
110    * can override this to return a subclass of <code>ModelListener</code> or
111    * another <code>ChangeListener</code> implementation.
112    *
113    * @see #fireStateChanged
114    */
115   protected ChangeListener createChangeListener() {
116       return new ModelListener();
117   }
118   
119   /**
120    * Adds a <code>ChangeListener</code> to this tabbedpane.
121    *
122    * @param l the <code>ChangeListener</code> to add
123    * @see #fireStateChanged
124    * @see #removeChangeListener
125    */
126   public void addChangeListener(ChangeListener l) {
127       listenerList.add(ChangeListener.class, l);
128   }
129
130   /**
131    * Removes a <code>ChangeListener</code> from this tabbedpane.
132    *
133    * @param l the <code>ChangeListener</code> to remove
134    * @see #fireStateChanged
135    * @see #addChangeListener
136    */
137   public void removeChangeListener(ChangeListener l) {
138       listenerList.remove(ChangeListener.class, l);
139   }
140
141   public void setSelectedComponent(JPanel structureTab)
142   {
143     // TODO Auto-generated method stub
144
145   }
146
147   /**
148    * Returns an array of all the <code>ChangeListener</code>s added
149    * to this <code>JTabbedPane</code> with <code>addChangeListener</code>.
150    *
151    * @return all of the <code>ChangeListener</code>s added or an empty
152    *         array if no listeners have been added
153    * @since 1.4
154    */
155   public ChangeListener[] getChangeListeners() {
156       return (ChangeListener[])listenerList.getListeners(
157               ChangeListener.class);
158   }
159
160   /**
161    * Sends a {@code ChangeEvent}, with this {@code JTabbedPane} as the source,
162    * to each registered listener. This method is called each time there is
163    * a change to either the selected index or the selected tab in the
164    * {@code JTabbedPane}. Usually, the selected index and selected tab change
165    * together. However, there are some cases, such as tab addition, where the
166    * selected index changes and the same tab remains selected. There are other
167    * cases, such as deleting the selected tab, where the index remains the
168    * same, but a new tab moves to that index. Events are fired for all of
169    * these cases.
170    *
171    * @see #addChangeListener
172    * @see EventListenerList
173    */
174   protected void fireStateChanged() {
175       /* --- Begin code to deal with visibility --- */
176
177       /* This code deals with changing the visibility of components to
178        * hide and show the contents for the selected tab. It duplicates
179        * logic already present in BasicTabbedPaneUI, logic that is
180        * processed during the layout pass. This code exists to allow
181        * developers to do things that are quite difficult to accomplish
182        * with the previous model of waiting for the layout pass to process
183        * visibility changes; such as requesting focus on the new visible
184        * component.
185        *
186        * For the average code, using the typical JTabbedPane methods,
187        * all visibility changes will now be processed here. However,
188        * the code in BasicTabbedPaneUI still exists, for the purposes
189        * of backward compatibility. Therefore, when making changes to
190        * this code, ensure that the BasicTabbedPaneUI code is kept in
191        * synch.
192        */
193
194       int selIndex = getSelectedIndex();
195
196       /* if the selection is now nothing */
197       if (selIndex < 0) {
198           /* if there was a previous visible component */
199           if (visComp != null && visComp.isVisible()) {
200               /* make it invisible */
201               visComp.setVisible(false);
202           }
203
204           /* now there's no visible component */
205           visComp = null;
206
207       /* else - the selection is now something */
208       } else {
209           /* Fetch the component for the new selection */
210           Component newComp = getComponentAt(selIndex);
211
212           /* if the new component is non-null and different */
213           if (newComp != null && newComp != visComp) {
214 //SwingJS X: Key Focus
215 //              boolean shouldChangeFocus = false;
216
217               /* Note: the following (clearing of the old visible component)
218                * is inside this if-statement for good reason: Tabbed pane
219                * should continue to show the previously visible component
220                * if there is no component for the chosen tab.
221                */
222
223               /* if there was a previous visible component */
224               if (visComp != null) {
225 //SwingJS X: Key Focus
226 //                  shouldChangeFocus =
227 //                      (SwingUtilities.findFocusOwner(visComp) != null);
228
229                   /* if it's still visible */
230                   if (visComp.isVisible()) {
231                       /* make it invisible */
232                       visComp.setVisible(false);
233                   }
234               }
235
236               if (!newComp.isVisible()) {
237                   newComp.setVisible(true);
238               }
239
240 //SwingJS X: Key Focus
241 //              if (shouldChangeFocus) {
242 //                  SwingUtilities2.tabbedPaneChangeFocusTo(newComp);
243 //              }
244
245               visComp = newComp;
246           } /* else - the visible component shouldn't changed */
247       }
248
249       /* --- End code to deal with visibility --- */
250
251       // Guaranteed to return a non-null array
252       Object[] listeners = listenerList.getListenerList();
253       // Process the listeners last to first, notifying
254       // those that are interested in this event
255       for (int i = listeners.length-2; i>=0; i-=2) {
256           if (listeners[i]==ChangeListener.class) {
257               // Lazily create the event:
258               if (changeEvent == null)
259                   changeEvent = new ChangeEvent(this);
260               ((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
261           }
262       }
263   }
264
265   private class Page
266   {
267     public String title;
268
269     String tab;
270
271     JComponent component;
272
273     public void updateDisplayedMnemonicIndex()
274     {
275       // TODO Auto-generated method stub
276       
277     }
278   }
279
280   public Component getSelectedComponent()
281   {
282     int i = getSelectedIndex();
283     return (i < 0 ? null : getComponent(i));
284   }
285
286   
287   /**
288    * Returns the component at <code>index</code>.
289    *
290    * @param index  the index of the item being queried
291    * @return the <code>Component</code> at <code>index</code>
292    * @exception IndexOutOfBoundsException if index is out of range
293    *            (index < 0 || index >= tab count)
294    *
295    * @see #setComponentAt
296    */
297   public Component getComponentAt(int index) {
298       return pages.get(index).component;
299   }
300   
301   Lst<Page> pages = new Lst<Page>();
302
303   public Component add(String title, Component panel)
304   {
305     add(panel, title, pages.size());
306     return panel;
307   }
308
309   public void addTab(String name, Component panel)
310   {
311     add(panel, name, Integer.MAX_VALUE);
312   }
313   
314   public void add(Component panel, String title, int index)
315   {
316     addIndex((JComponent) panel, title, index);
317   }
318
319
320   private void addIndex(JComponent panel, String title, int index)
321   {
322     // improperly allowing for multiple instances of a panel
323     Page page = new Page();
324     page.component = panel;
325     page.tab = title;
326     panel.setVisible(true);
327     if (index < pages.size())
328     {
329       pages.get(index).component = panel;
330     }
331     else
332     {
333       pages.addLast(page);
334     }
335     update(true);
336  }
337
338   public void insertTab(String title, Icon icon, Component component,
339           String tip, int index)
340   {
341     // int newIndex = index;
342
343     addIndex((JComponent) component, title, index);
344     //
345     // // If component already exists, remove corresponding
346     // // tab so that new tab gets added correctly
347     // // Note: we are allowing component=null because of compatibility,
348     // // but we really should throw an exception because much of the
349     // // rest of the JTabbedPane implementation isn't designed to deal
350     // // with null components for tabs.
351     // int removeIndex = indexOfComponent(component);
352     // if (component != null && removeIndex != -1) {
353     // removeTabAt(removeIndex);
354     // if (newIndex > removeIndex) {
355     // newIndex--;
356     // }
357     // }
358     //
359     // int selectedIndex = getSelectedIndex();
360     //
361     // pages.add(
362     // newIndex,
363     // new Page(this, title != null? title : "", icon, null, component, tip));
364     //
365     //
366     // if (component != null) {
367     // addImpl(component, null, -1);
368     // component.setVisible(false);
369     // } else {
370     // firePropertyChange("indexForNullComponent", -1, index);
371     // }
372     //
373     // if (pages.size() == 1) {
374     // setSelectedIndex(0);
375     // }
376     //
377     // if (selectedIndex >= newIndex) {
378     // setSelectedIndexImpl(selectedIndex + 1, false);
379     // }
380     //
381     // if (!haveRegistered && tip != null) {
382     // ToolTipManager.sharedInstance().registerComponent(this);
383     // haveRegistered = true;
384     // }
385
386     // if (accessibleContext != null) {
387     // accessibleContext.firePropertyChange(
388     // AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
389     // null, component);
390     // }
391   }
392
393   private void update(boolean combo)
394   {
395     if (combo)
396     {
397       int i0 = tabs.getSelectedIndex();
398       tabs.removeAllItems();
399       for (int i = 0; i < pages.size(); i++)
400       {
401         tabs.addItem(pages.get(i).tab);
402       }
403       tabs.setSelectedIndex(i0 < 0 ? 0 : i0);
404     }
405     pagePanel.removeAll();
406     int selected = getSelectedIndex();
407     if (selected >= 0)
408     {// && bsEnabled.get(selected)) {
409       pagePanel.add(pages.get(selected).component);
410     }
411     revalidate();
412     repaint();
413   }
414
415   public void setComponentAt(int index, JPanel panel)
416   {
417     if (index < 0 || index >= pages.size())
418       throw new IndexOutOfBoundsException();
419     pages.get(index).component = panel;
420     update(false);
421   }
422
423   public int getTabCount()
424   {
425     return tabs.getItemCount();
426   }
427
428   public void setSelectedIndex(int index)
429   {
430     tabs.setSelectedIndex(index);
431     update(false);
432   }
433
434   public int getSelectedIndex()
435   {
436     return tabs.getSelectedIndex();
437   }
438
439   public void setEnabledAt(int index, boolean bEnable)
440   {
441     // bsEnabled.setBitTo(index, bEnable);
442     // if (!bEnable && index == getSelectedIndex()) {
443     // for (int i = index - 1; --i >= 0;) {
444     // if (bsEnabled.get(i)) {
445     // setSelectedIndex(i);
446     // return;
447     // }
448     // }
449     // }
450     // update(false, true);
451   }
452
453   public void remove(int n)
454   {
455     pages.removeItemAt(n);
456     tabs.removeItemAt(n);
457     update(false);
458     // bsEnabled.clearAll();
459     // bsEnabled.set(lastSelected);
460   }
461
462 }