001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
005     * Space Science and Engineering Center (SSEC)
006     * University of Wisconsin - Madison
007     * 1225 W. Dayton Street, Madison, WI 53706, USA
008     * https://www.ssec.wisc.edu/mcidas
009     * 
010     * All Rights Reserved
011     * 
012     * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013     * some McIDAS-V source code is based on IDV and VisAD source code.  
014     * 
015     * McIDAS-V is free software; you can redistribute it and/or modify
016     * it under the terms of the GNU Lesser Public License as published by
017     * the Free Software Foundation; either version 3 of the License, or
018     * (at your option) any later version.
019     * 
020     * McIDAS-V is distributed in the hope that it will be useful,
021     * but WITHOUT ANY WARRANTY; without even the implied warranty of
022     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023     * GNU Lesser Public License for more details.
024     * 
025     * You should have received a copy of the GNU Lesser Public License
026     * along with this program.  If not, see http://www.gnu.org/licenses.
027     */
028    
029    package edu.wisc.ssec.mcidasv.ui;
030    
031    import java.awt.BorderLayout;
032    import java.awt.Color;
033    import java.awt.Component;
034    import java.awt.Container;
035    import java.awt.Cursor;
036    import java.awt.Dimension;
037    import java.awt.Font;
038    import java.awt.FontMetrics;
039    import java.awt.Graphics;
040    import java.awt.Image;
041    import java.awt.Insets;
042    import java.awt.Rectangle;
043    import java.awt.event.ActionEvent;
044    import java.awt.event.ActionListener;
045    import java.awt.event.KeyAdapter;
046    import java.awt.event.KeyEvent;
047    import java.awt.event.KeyListener;
048    import java.awt.event.MouseAdapter;
049    import java.awt.event.MouseEvent;
050    import java.awt.image.ImageObserver;
051    import java.util.ArrayList;
052    import java.util.Hashtable;
053    import java.util.List;
054    
055    import javax.swing.BorderFactory;
056    import javax.swing.BoxLayout;
057    import javax.swing.ButtonGroup;
058    import javax.swing.ImageIcon;
059    import javax.swing.JButton;
060    import javax.swing.JComponent;
061    import javax.swing.JLabel;
062    import javax.swing.JMenuItem;
063    import javax.swing.JPanel;
064    import javax.swing.JPopupMenu;
065    import javax.swing.JScrollPane;
066    import javax.swing.JToggleButton;
067    import javax.swing.SwingConstants;
068    import javax.swing.border.Border;
069    import javax.swing.border.EtchedBorder;
070    
071    import edu.wisc.ssec.mcidasv.Constants;
072    
073    import ucar.unidata.idv.DisplayControl;
074    import ucar.unidata.idv.IdvConstants;
075    import ucar.unidata.idv.IdvManager;
076    import ucar.unidata.idv.IntegratedDataViewer;
077    import ucar.unidata.idv.MapViewManager;
078    import ucar.unidata.idv.TransectViewManager;
079    import ucar.unidata.idv.ViewManager;
080    import ucar.unidata.idv.control.DisplayControlImpl;
081    import ucar.unidata.idv.control.MapDisplayControl;
082    import ucar.unidata.idv.ui.IdvComponentGroup;
083    import ucar.unidata.idv.ui.IdvWindow;
084    import ucar.unidata.idv.ui.ViewPanel;
085    import ucar.unidata.ui.ComponentHolder;
086    import ucar.unidata.ui.DndImageButton;
087    import ucar.unidata.util.GuiUtils;
088    import ucar.unidata.util.Misc;
089    import ucar.unidata.util.Msg;
090    import ucar.unidata.util.StringUtil;
091    
092    /**
093     * <p>This class has largely been copied over wholesale from the IDV code. 
094     * Merely extending was proving to be as much as a hassle as just copying it, 
095     * though now we still maintain complete control over the ViewPanel, and we have 
096     * an obvious point of departure for whenever the JTree is started.</p>
097     * 
098     * <p>That said, I personally recommend avoiding this class until the JTree 
099     * stuff is ready to go.</p>
100     */
101    public class McIDASVViewPanel extends IdvManager implements ViewPanel {
102    
103            private static final Image BUTTON_ICON =
104                    GuiUtils.getImage("/auxdata/ui/icons/Selected.gif");
105    
106            private static final ImageIcon CATEGORY_OPEN_ICON = 
107                    GuiUtils.getImageIcon("/auxdata/ui/icons/CategoryOpen.gif");
108    
109            private static final ImageIcon CATEGORY_CLOSED_ICON = 
110                    GuiUtils.getImageIcon("/auxdata/ui/icons/CategoryClosed.gif");
111    
112            private static final Border BUTTON_BORDER = 
113                    BorderFactory.createEmptyBorder(2, 6, 2, 0);
114    
115            private static final Font BUTTON_FONT = new Font("Dialog", Font.PLAIN, 11);
116            private static final Color LINE_COLOR = Color.gray;
117            private static final Font CAT_FONT = new Font("Dialog", Font.BOLD, 11);
118            
119            /** The border for the header panel */
120            public static Border headerNormal = 
121                    BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,
122                            0, 0, 0), BorderFactory.createMatteBorder(0, 0, 2, 0,
123                                    Color.black));
124    
125            /** highlight border for view infos */
126            public static Border headerHighlight = 
127                    BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(3,
128                            0, 0, 0), BorderFactory.createMatteBorder(0, 0, 2, 0,
129                                    ViewManager.borderHighlightColor));
130    
131            private static Color fgColor = Color.black;
132            private static Color onColor = null;
133            private static boolean showPopup = true;
134            private static boolean showCategories = false;
135            
136            private JComponent contents;
137            
138            private JPanel leftPanel;
139            
140            private JPanel viewContainer;
141            
142            private ButtonGroup buttonGroup = new ButtonGroup();
143            
144            private GuiUtils.CardLayoutPanel rightPanel;
145            
146            private IntegratedDataViewer idv;
147            
148            private enum ViewManagers { DEFAULT, GLOBE, MAP, TRANSECT };
149            
150            private Hashtable<DisplayControl, ControlInfo> controlToInfo = 
151                    new Hashtable<DisplayControl, ControlInfo>();
152            
153            
154            private List<VMInfo> vmInfos = new ArrayList<VMInfo>();
155            
156            public McIDASVViewPanel(IntegratedDataViewer idv) {
157                    super(idv);
158                    
159                    this.idv = idv;
160    
161            }
162    
163            public void createUI() {
164                    
165                    leftPanel = new JPanel(new BorderLayout());
166                    leftPanel.setBorder(
167                            BorderFactory.createCompoundBorder(
168                                    BorderFactory.createEtchedBorder(EtchedBorder.LOWERED),
169                                    BorderFactory.createEmptyBorder(0, 0, 0, 0)));
170                    
171                    leftPanel.add(BorderLayout.NORTH, GuiUtils.filler(150, 1));
172    
173                    viewContainer = new JPanel();
174                    viewContainer.setLayout(new BoxLayout(viewContainer, BoxLayout.Y_AXIS));
175    
176                    JScrollPane viewScroll = new JScrollPane(GuiUtils.top(viewContainer));
177                    viewScroll.setBorder(null);
178                    
179                    leftPanel.add(BorderLayout.CENTER, viewScroll);
180                    
181                    rightPanel = new GuiUtils.CardLayoutPanel() {
182                            public void show(Component comp) {
183                                    super.show(comp);
184                                    for (VMInfo vinfo : vmInfos) {
185                                            for (ControlInfo cinfo : vinfo.controlInfos) {
186                                                    if (cinfo.outer == comp) {
187                                                            cinfo.button.setSelected(true);
188                                                            break;
189                                                    }
190                                            }
191                                    }
192                            }
193                    };
194                    
195                    contents = GuiUtils.leftCenter(leftPanel, rightPanel);
196                    Msg.translateTree(contents);
197            }
198    
199            public void selectNext(boolean up) {
200                    boolean gotit  = false;
201                    VMInfo  select = null;
202                    int index  = 0;
203                    for (int vmIdx = 0; !gotit && (vmIdx < vmInfos.size()); vmIdx++) {
204                            
205                            VMInfo vmInfo = vmInfos.get(vmIdx);
206                            
207                            List<ControlInfo> ctrlInfos = vmInfo.controlInfos;
208                            
209                            for (int i = 0; i < ctrlInfos.size(); i++) {
210                                    ControlInfo ci = ctrlInfos.get(i);
211                            
212                                    if ( !ci.button.isSelected())
213                                            continue;
214    
215                                    if (up) {
216                                            if (vmInfo.getCatOpen() && (i > 0)) {
217                                                    select = vmInfo;
218                                                    index  = i - 1;
219                                            } else {
220                                                    vmIdx--;
221                                                    while (vmIdx >= 0) {
222                                                            VMInfo prev = vmInfos.get(vmIdx--);
223                                                            if (prev.getCatOpen() && (prev.controlInfos.size() > 0)) {
224                                                                    select = prev;
225                                                                    index  = select.controlInfos.size() - 1;
226                                                                    break;
227                                                            }
228                                                    }
229                                            }
230                                    } else {
231                                            if (vmInfo.getCatOpen() && (i < ctrlInfos.size() - 1)) {
232                                                    select = vmInfo;
233                                                    index  = i + 1;
234                                            } else {
235                                                    vmIdx++;
236                                                    while (vmIdx < vmInfos.size()) {
237                                                            VMInfo next = vmInfos.get(vmIdx++);
238                                                            if (next.getCatOpen() && (next.controlInfos.size() > 0)) {
239                                                                    select = next;
240                                                                    index  = 0;
241                                                                    break;
242                                                            }
243                                                    }
244                                            }
245                                    }
246                                    gotit = true;
247                                    break;
248                            }
249                    }
250    
251                    if ((select != null) && (index >= 0) && (index < select.controlInfos.size()))
252                            select.controlInfos.get(index).button.doClick();
253            }
254            
255            public void addControlTab(DisplayControl control, boolean forceShow) {
256                    if (!control.canBeDocked() || !control.shouldBeDocked())
257                            return;
258    
259                    //Check if there are any groups that have autoimport set
260                    ViewManager viewManager = control.getViewManager();
261                    if (viewManager != null) {
262                            IdvWindow window = viewManager.getDisplayWindow();
263                            if (window != null) {
264                                    List groups = window.getComponentGroups();
265                                    for (int i = 0; i < groups.size(); i++) {
266                                            Object obj = groups.get(i);
267                                            if (obj instanceof IdvComponentGroup) {
268                                                    if (((IdvComponentGroup) obj)
269                                                                    .tryToImportDisplayControl(
270                                                                            (DisplayControlImpl)control)) {
271                                                            return;
272                                                    }
273                                            }
274                                    }
275                            }
276                    }
277    
278                    ControlInfo info = controlToInfo.get(control);
279                    if (info != null)
280                            return;
281    
282                    //For now cheat a little with the cast
283                    ((DisplayControlImpl)control).setMakeWindow(false);
284    
285                    JButton removeBtn =
286                            GuiUtils.makeImageButton("/auxdata/ui/icons/Remove16.gif",
287                                    control, "doRemove");
288                    removeBtn.setToolTipText("Remove Display Control");
289    
290                    JButton expandBtn =
291                            GuiUtils.makeImageButton("/auxdata/ui/icons/DownDown.gif", this,
292                                    "expandControl", control);
293                    expandBtn.setToolTipText("Expand in the tabs");
294    
295                    JButton exportBtn =
296                            GuiUtils.makeImageButton("/auxdata/ui/icons/Export16.gif", this,
297                                    "undockControl", control);
298                    exportBtn.setToolTipText("Undock control window");
299    
300                    JButton propBtn =
301                            GuiUtils.makeImageButton("/auxdata/ui/icons/Information16.gif",
302                                    control, "showProperties");
303                    propBtn.setToolTipText("Show Display Control Properties");
304    
305                    DndImageButton dnd = new DndImageButton(control, "idv/display");
306                    dnd.setToolTipText("Drag and drop to a window component");
307    //              JPanel buttonPanel =
308    //                      GuiUtils.left(GuiUtils.hbox(Misc.newList(expandBtn, exportBtn,
309    //                              propBtn, removeBtn, dnd), 4));
310                    JPanel buttonPanel =
311                            GuiUtils.left(GuiUtils.hbox(Misc.newList(exportBtn,
312                                    propBtn, removeBtn, dnd), 4));
313    
314                    buttonPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 0,
315                            Color.lightGray.darker()));
316    
317                    JComponent inner =
318                            (JComponent) ((DisplayControlImpl)control).getOuterContents();
319                    inner = GuiUtils.centerBottom(inner, buttonPanel);
320                    JComponent outer = GuiUtils.top(inner);
321                    outer.setBorder(BorderFactory.createEmptyBorder(2, 1, 0, 0));
322                    
323                    info = new ControlInfo(control, expandBtn, outer, inner,
324                            getVMInfo(control.getDefaultViewManager()));
325                    
326                    controlToInfo.put(control, info);
327                    
328                    GuiUtils.toggleHeavyWeightComponents(outer, false);
329                    if (!getStateManager().getProperty(IdvConstants.PROP_LOADINGXML, false)) {
330    
331                            //A hack for now
332                            if (!(control instanceof MapDisplayControl)) {
333                                    GuiUtils.toggleHeavyWeightComponents(outer, true);
334                                    GuiUtils.showComponentInTabs(outer);
335                            }
336    
337                    }
338            }
339            
340            public void expandControl(DisplayControl control) {
341                    ControlInfo info = controlToInfo.get(control);
342                    if (info != null)
343                            info.expand();
344            }
345            
346            public void dockControl(DisplayControl control) {
347                    control.setShowInTabs(true);
348                    ((DisplayControlImpl)control).guiImported();
349                    addControlTab(control, true);
350            }
351            
352            public void undockControl(DisplayControl control) {
353                    removeControlTab(control);
354                    control.setShowInTabs(false);
355                    ((DisplayControlImpl)control).setMakeWindow(true);
356                    ((DisplayControlImpl)control).popup(null);
357            }
358            
359            public void controlMoved(DisplayControl control) {
360                    removeControlTab(control);
361                    addControlTab(control, true);
362            }
363            
364            public void removeControlTab(DisplayControl control) {
365                    ControlInfo info = controlToInfo.remove(control);
366                    if (info != null)
367                            info.removeDisplayControl();
368            }
369            
370            public JComponent getContents() {
371                    if (contents == null)
372                            createUI();
373                    
374                    return contents;
375            }
376            
377            public void addDisplayControl(DisplayControl control) {
378                    addControlTab(control, false);
379            }
380            
381            public void displayControlChanged(DisplayControl control) {
382                    ControlInfo info = controlToInfo.get(control);
383                    if (info != null)
384                            info.displayControlChanged();
385            }
386    
387            public void removeDisplayControl(DisplayControl control) {
388                    removeControlTab(control);
389            }
390            
391            public void addViewMenuItems(DisplayControl control, List items) {
392                    if (!control.canBeDocked())
393                            return;
394                    
395                    items.add(GuiUtils.MENU_SEPARATOR);
396                    
397                    if (!control.shouldBeDocked())
398                            items.add(GuiUtils.makeMenuItem("Dock in Data Explorer", this, "dockControl", control));
399                    else
400                            items.add(GuiUtils.makeMenuItem("Undock from Data Explorer", this, "undockControl", control));
401                    
402                    List groups = getIdvUIManager().getComponentGroups();
403                    List<JMenuItem> subItems = new ArrayList<JMenuItem>();
404                    for (int i = 0; i < groups.size(); i++) {
405                            IdvComponentGroup group = (IdvComponentGroup)groups.get(i);
406                            subItems.add(GuiUtils.makeMenuItem(group.getHierachicalName(), group, "importDisplayControl", control));
407                    }
408                    
409                    if (subItems.size() > 0)
410                            items.add(GuiUtils.makeMenu("Export to component", subItems));
411            }
412            
413            public void viewManagerAdded(ViewManager vm) {
414                    // this forces the addition of the ViewManager
415                    getVMInfo(vm);
416            }
417            
418            public void viewManagerDestroyed(ViewManager vm) {
419                    VMInfo info = findVMInfo(vm);
420                    if (info != null) {
421                            vmInfos.remove(info);
422                            info.viewManagerDestroyed();
423    //                      System.err.println("destroying "+info+" for "+vm);
424                    }
425            }
426            
427            /**
428             * Triggered upon a change in the given ViewManager. Just used so that our
429             * ControlInfo object can update its internal state.
430             * 
431             * @param vm The ViewManager that's changed.
432             */
433            public void viewManagerChanged(ViewManager vm) {
434                    VMInfo info = findVMInfo(vm);
435                    if (info != null)
436                            info.viewManagerChanged();
437            }
438    
439            /**
440             * Initialize the button state
441             *
442             * @param idv the idv
443             */
444            protected void initButtonState() {
445                    if (fgColor != null)
446                            return;
447    
448                    fgColor = Color.black;
449    
450                    showPopup = idv.getProperty(Constants.PROP_VP_SHOWPOPUP, false);
451    
452                    showCategories = idv.getProperty(Constants.PROP_VP_SHOWCATS, false);
453            }
454            
455            public VMInfo getVMInfo(ViewManager vm) {
456                    VMInfo info = findVMInfo(vm);
457                    if (info == null) {
458    
459                            // oh no :(
460                            if (vm instanceof MapViewManager)
461                                    if (((MapViewManager)vm).getUseGlobeDisplay())
462                                            info = new VMInfo(vm, ViewManagers.GLOBE);
463                                    else
464                                            info = new VMInfo(vm, ViewManagers.MAP);
465                            else if (vm instanceof TransectViewManager)
466                                    info = new VMInfo(vm, ViewManagers.TRANSECT);
467                            else
468                                    info = new VMInfo(vm, ViewManagers.DEFAULT);
469    
470                            vmInfos.add(info);
471                    }
472                    return info;
473            }
474    
475            public VMInfo findVMInfo(ViewManager vm) {
476                    for (VMInfo info : vmInfos)
477                            if (info.holds(vm))
478                                    return info;
479    
480                    return null;
481            }
482    
483            public class VMInfo implements ImageObserver {
484                    private ViewManager viewManager;
485                    
486                    private JButton popupButton;
487                    
488                    private JComponent tabContents = new JPanel(new BorderLayout());
489                    
490                    private JPanel headerPanel;
491                    
492                    private boolean ignore = false;
493                    
494                    // private list of controlinfos?
495                    private List<ControlInfo> controlInfos = new ArrayList<ControlInfo>();
496                    private List<JToggleButton> buttons = new ArrayList<JToggleButton>();
497                    
498                    //private JComponent buttonPanel;
499                    
500                    private JComponent contents;
501                    
502                    private JLabel viewLabel;
503                    
504                    private JButton catToggle;
505                    
506                    private boolean catOpen = true;
507                    
508                    private KeyListener listener;
509                    
510                    private List<String> categories = new ArrayList<String>();
511                    
512                    private ViewManagers myType = ViewManagers.DEFAULT;
513                    
514                    public VMInfo(ViewManager vm, ViewManagers type) {
515    
516                            listener = new KeyAdapter() {
517                                    public void keyPressed(KeyEvent e) {
518                                            if (e.getKeyCode() == KeyEvent.VK_UP)
519                                                    selectNext(true);
520                                            else if (e.getKeyCode() == KeyEvent.VK_DOWN)
521                                                    selectNext(false);
522                                    }
523                            };
524    
525                            initButtonState();
526                            BUTTON_ICON.getWidth(this);
527    
528                            viewManager = vm;
529    
530                            ImageIcon icon = ICON_DEFAULT;
531                            if (type == ViewManagers.GLOBE)
532                                    icon = ICON_GLOBE;
533                            else if (type == ViewManagers.MAP)
534                                    icon = ICON_MAP;
535                            else if (type == ViewManagers.TRANSECT)
536                                    icon = ICON_TRANSECT;
537                            
538                            viewLabel = new JLabel(" " + getLabel());
539                            viewLabel.addMouseListener(new MouseAdapter() {
540                                    public void mousePressed(MouseEvent e) {
541                                            if (viewManager == null)
542                                                    return;
543                                            
544                                            getVMManager().setLastActiveViewManager(viewManager);
545                                            if (e.getClickCount() == 2)
546                                                    viewManager.toFront();
547                                    }
548                            });
549    
550                            catToggle = GuiUtils.getImageButton(getCatOpen() ? CATEGORY_OPEN_ICON : CATEGORY_CLOSED_ICON);
551                            catToggle.addKeyListener(listener);
552                            catToggle.addActionListener(new ActionListener() {
553                                    public void actionPerformed(ActionEvent e) {
554                                            setCatOpen(!getCatOpen());
555                                    }
556                            });
557    
558                            popupButton = new JButton(icon);
559                            popupButton.addKeyListener(listener);
560                            popupButton.setContentAreaFilled(false);
561                            popupButton.addActionListener(GuiUtils.makeActionListener(VMInfo.this, "showPopupMenu", null));
562                            popupButton.setToolTipText("Show View Menu");
563                            popupButton.setBorder(BorderFactory.createEmptyBorder());
564    
565                            headerPanel = GuiUtils.leftCenter(GuiUtils.hbox(
566                                            GuiUtils.inset(catToggle, 1),
567                                            popupButton), viewLabel);
568    
569                            if (viewManager != null)
570                                    headerPanel = viewManager.makeDropPanel(headerPanel, true);
571    
572                            JComponent headerWrapper = GuiUtils.center(headerPanel);
573                            headerPanel.setBorder(headerNormal);
574                            contents = GuiUtils.topCenter(headerWrapper, tabContents);
575                            viewContainer.add(contents);
576                            popupButton.setHorizontalAlignment(SwingConstants.LEFT);
577                            buttonsChanged();
578    
579                            setCatOpen(getCatOpen());
580    
581                            if (viewManager != null)
582                                    viewManagerChanged();
583                    }
584    
585                    public boolean getCatOpen() {
586                            if (viewManager != null) {
587                                    Boolean b = 
588                                            (Boolean)viewManager.getProperty(Constants.PROP_VP_CATOPEN);
589                                    if (b != null)
590                                            return b;
591                            }
592    
593                            return catOpen;
594                    }
595    
596                    public void setCatOpen(boolean v) {
597                            if (viewManager != null)
598                                    viewManager.putProperty(Constants.PROP_VP_CATOPEN, v);
599    
600                            catOpen = v;
601                            catToggle.setIcon(v ? CATEGORY_OPEN_ICON : CATEGORY_CLOSED_ICON);
602                            tabContents.setVisible(v);
603                    }
604    
605                    public void showPopupMenu() {
606                            if (viewManager == null)
607                                    return;
608    
609                            List items = new ArrayList();
610                            viewManager.addContextMenuItems(items);
611                            JPopupMenu popup = GuiUtils.makePopupMenu(items);
612                            popup.show(popupButton, 0, popupButton.getHeight());
613                    }
614    
615                    /**
616                     * Determine if this VMInfo contains a given ViewManager.
617                     * 
618                     * @param vm The ViewManager you wish to test.
619                     * 
620                     * @return True if this VMInfo contains <tt>vm</tt>, false otherwise.
621                     */
622                    public boolean holds(ViewManager vm) {
623                            return viewManager == vm;
624                    }
625    
626                    public void removeControlInfo(ControlInfo info) {
627                            int idx = controlInfos.indexOf(info);
628                            if (idx == -1)
629                                    return;
630                            
631                            int btnIdx = buttons.indexOf(info.button);
632                            controlInfos.remove(info);
633                            rightPanel.remove(info.outer);
634    
635                            if (info.button.isSelected() && (buttons.size() > 0)) {
636                                    while ((btnIdx >= buttons.size()) && (btnIdx >= 0))
637                                            btnIdx--;
638    
639                                    if (btnIdx >= 0)
640                                            buttons.get(btnIdx).doClick();
641                            }
642    
643                            GuiUtils.toggleHeavyWeightComponents(info.outer, true);
644    
645                            buttonsChanged();
646    
647                            // hmm -- this must be for synchronization?
648                            ignore = true;
649                            buttonGroup.remove(info.button);
650                            ignore = false;
651    
652                            // if there are still control infos left then we'll use those?
653                            if (controlInfos.size() > 0)
654                                    return;
655    
656                            // otherwise we need to click the buttons of each remaining viewmanager?
657                            for (VMInfo vm : vmInfos)
658                                    if (vm.controlInfos.size() > 0)
659                                            vm.controlInfos.get(0).button.doClick();
660                    }
661    
662                    public void changeControlInfo(ControlInfo info) {
663                            if (!Misc.equals(info.lastCategory, info.control.getDisplayCategory()))
664                                    buttonsChanged();
665                    }
666    
667                    public void paintButton(Graphics g, ControlInfo info) {
668                            g.setFont(BUTTON_FONT);
669                            FontMetrics fm = g.getFontMetrics(g.getFont());
670    
671                            JToggleButton btn = info.button;
672                            Rectangle b = btn.getBounds();
673                            String text = info.getLabel();
674                            int y = (btn.getHeight() + fm.getHeight()) / 2 - 2;
675                            int buttonWidth = BUTTON_ICON.getWidth(null);
676                            int offset = 2 + buttonWidth + 4;
677                            g.setColor(btn.getBackground());
678                            g.fillRect(0, 0, b.width, b.height);
679    
680                            if (btn.isSelected()) {
681    
682                                    if (onColor == null) {
683                                            Color c = btn.getBackground();
684                                            //Just go a little bit darker than the normal background
685                                            onColor = new Color((int) Math.max(0,
686                                                            c.getRed() - 20), (int) Math.max(0,
687                                                                    c.getGreen() - 20), (int) Math.max(0,
688                                                                            c.getBlue() - 20));
689                                    }
690    
691                                    g.setColor(onColor);
692                                    g.fillRect(offset - 1, 0, b.width, b.height);
693                            }
694                            g.setColor(LINE_COLOR);
695    
696                            g.drawLine(offset - 1, b.height - 1, b.width, b.height - 1);
697    
698                            g.setColor(fgColor);
699                            int rightSide = b.width;
700                            if (btn.isSelected())
701                                    rightSide = b.width - buttonWidth - 2;
702    
703                            int textPos = offset;
704                            int textRight = textPos + fm.stringWidth(text);
705                            if (textRight >= rightSide) {
706                                    while ((text.length() > 5) && (textRight >= rightSide)) {
707                                            text = text.substring(0, text.length() - 2);
708                                            textRight = textPos + fm.stringWidth(text + ".");
709                                    }
710                                    text = text + ".";
711                            }
712                            g.drawString(text, offset, y);
713    
714                            if (!btn.isSelected())
715                                    return;
716                            
717                            int height = BUTTON_ICON.getHeight(null);
718                            g.drawImage(BUTTON_ICON, b.width - 2 - buttonWidth, b.height / 2 - height / 2, null);
719                    }
720    
721                    public void addControlInfo(final ControlInfo info) {
722                            // ugly :(
723                            // why even have b?
724                            //JToggleButton b = info.button = new JToggleButton(StringUtil.padRight("", 20), true) {
725                            info.button = new JToggleButton(StringUtil.padRight("", 20), true) {
726                                    public void paint(Graphics g) {
727                                            paintButton(g, info);
728                                    }
729                            };
730                            
731                            info.button.addKeyListener(listener);
732                            info.button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
733                            info.button.setToolTipText(info.getLabel());
734                            info.button.setFont(BUTTON_FONT);
735                            info.button.setForeground(fgColor);
736                            info.button.setBorder(BUTTON_BORDER);
737    
738                            info.button.setHorizontalAlignment(SwingConstants.LEFT);
739                            info.button.addActionListener(new ActionListener() {
740                                    public void actionPerformed(ActionEvent e) {
741                                            if (ignore)
742                                                    return;
743    
744                                            GuiUtils.toggleHeavyWeightComponents(info.outer, true);
745                                            rightPanel.show(info.outer);
746                                    }
747                            });
748                            buttons.add(info.button);
749                            controlInfos.add(info);
750                            rightPanel.addCard(info.outer);
751                            GuiUtils.toggleHeavyWeightComponents(info.outer, false);
752                            info.displayControlChanged();
753    
754                            if (info.control.getExpandedInTabs())
755                                    info.expand();
756    
757                            buttonGroup.add(info.button);
758                            buttonsChanged();
759                            setCatOpen(getCatOpen());
760                    }
761    
762                    /**
763                     * Redo the buttons
764                     */
765                    private void buttonsChanged() {
766                            List<JComponent> comps  = new ArrayList<JComponent>();
767    
768                            Hashtable<String, List<JComponent>> catMap = 
769                                    new Hashtable<String, List<JComponent>>();
770    
771                            for (ControlInfo info : controlInfos) {
772                                    String cat = info.control.getDisplayCategory();
773                                    if (cat == null)
774                                            cat = "Displays";
775    
776                                    info.lastCategory = cat;
777    
778                                    if (!showCategories) {
779                                            comps.add(info.button);
780                                            continue;
781                                    }
782    
783                                    List<JComponent> catList = catMap.get(cat);
784                                    if (catList == null) {
785                                            if (!categories.contains(cat))
786                                                    categories.add(cat);
787    
788                                            catList = new ArrayList<JComponent>();
789    
790                                            catMap.put(cat, catList);
791    
792                                            JLabel catLabel = new JLabel(" " + cat);
793    
794                                            catLabel.setFont(CAT_FONT);
795    
796                                            catList.add(catLabel);
797                                    }
798                                    catList.add(info.button);
799                            }
800    
801                            if (showCategories) {
802                                    for (String category : categories) {
803                                            List<JComponent> catList = catMap.get(category);
804                                            if (catList != null)
805                                                    comps.addAll(catList);
806                                    }
807                            }
808    
809                            if (comps.size() == 0) {
810                                if (myType == ViewManagers.TRANSECT) {
811                                    JLabel noLbl = new JLabel("No Displays");
812                                    noLbl.setFont(BUTTON_FONT);
813                                    JPanel inset = GuiUtils.inset(noLbl, new Insets(0, 10, 0, 0));
814                                    inset.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0,
815                                            Color.gray));
816                                    comps.add(inset);
817                                } else {
818                                    headerPanel.setVisible(false);
819                                }
820                            } else {
821                                headerPanel.setVisible(true);
822                            }
823    
824                            comps.add(GuiUtils.filler(10, 2));
825                            JComponent buttonPanel = GuiUtils.vbox(comps);
826    
827                            tabContents.removeAll();
828                            tabContents.add(BorderLayout.NORTH, buttonPanel);
829                            tabContents.repaint();
830                    }
831    
832                    /**
833                     * Handles ViewManager removal.
834                     */
835                    public void viewManagerDestroyed() {
836                            viewContainer.remove(contents);
837                    }
838    
839                    /**
840                     * my viewmanager has changed. Update the gui.
841                     */
842                    public void viewManagerChanged() {
843                            viewLabel.setText(getLabel());
844    
845                            if (viewManager.showHighlight()) {
846    
847                                    headerHighlight =
848                                            BorderFactory.createCompoundBorder(
849                                                    BorderFactory.createEmptyBorder(3, 0, 0, 0),
850                                                    BorderFactory.createMatteBorder(
851                                                            0, 0, 2, 0,
852                                                            getStore().get(
853                                                                    ViewManager.PREF_BORDERCOLOR, Color.blue)));
854    
855                                    headerPanel.setBorder(headerHighlight);
856                            } else {
857                                    headerPanel.setBorder(headerNormal);
858                            }
859                            
860                            if (contents != null)
861                                    contents.repaint();
862                    }
863    
864                    /**
865                     * Get the ViewManager label. If the ViewManager does not already have
866                     * a valid name, a default name is created, based on the number of 
867                     * existing ViewManagers.
868                     *
869                     * @return label The ViewManager's name.
870                     */
871                    public String getLabel() {
872                            // nothing to query for a name?
873                            if (viewManager == null)
874                                    return "No Display";
875    
876                            // do we already have a valid name?
877                            String name = viewManager.getName();
878                            if ((name != null) && (name.trim().length() > 0)) {
879                                    UIManager uiManager = (UIManager)idv.getIdvUIManager();
880                                    ComponentHolder holder = uiManager.getViewManagerHolder(viewManager);
881                                    if (holder != null)
882                                            return holder.getName() + ">" + name;
883                                    else
884                                            return name;
885                            }
886    
887                            // if our name was invalid, build a default one.
888                            int idx = vmInfos.indexOf(this);
889                            return "Default " + Constants.PANEL_NAME + " " + 
890                                    ((idx == -1) ? vmInfos.size() : idx);
891                    }
892    
893                    public boolean imageUpdate(Image img, int flags, int x, int y, int width, int height) {
894                            if ((flags & ImageObserver.ALLBITS) == 0)
895                                    return true;
896    
897                            leftPanel.repaint();
898                            return false;
899                    }
900            }
901    
902            public class ControlInfo {
903                    DisplayControl control;
904                    JButton expandButton;
905                    JComponent outer;
906                    JComponent inner;
907                    boolean expanded = false;
908                    Dimension innerSize = new Dimension();
909                    VMInfo info;
910                    JToggleButton button;
911                    String lastCategory = "";
912                    String label = null;
913    
914                    /**
915                     * ctor
916                     *
917                     * @param control control
918                     * @param expandButton expand button
919                     * @param outer outer comp
920                     * @param inner inner comp
921                     * @param vmInfo my vminfo
922                     */
923                    public ControlInfo(DisplayControl control, JButton expandButton,
924                                                            JComponent outer, JComponent inner,
925                                                            VMInfo vmInfo) {
926                            this.info  = vmInfo;
927                            this.control = control;
928                            if (control.getExpandedInTabs())
929                                    expanded = false;
930    
931                            this.expandButton = expandButton;
932                            this.outer = outer;
933                            this.inner = inner;
934                            inner.getSize(innerSize);
935                            info.addControlInfo(this);
936                            this.expand();
937                    }
938    
939                    /**
940                     * get the label for the display control
941                     *
942                     * @return display control label
943                     */
944                    public String getLabel() {
945                            if (label == null)
946                                    label = control.getMenuLabel();
947    
948                            return label;
949                    }
950    
951                    /**
952                     * display control changed
953                     */
954                    public void displayControlChanged() {
955                            String tmp = label;
956                            label = null;
957                            getLabel();
958                            if (!Misc.equals(tmp, label) && (button != null)) {
959                                    button.setToolTipText(label);
960                                    button.repaint();
961                            }
962                            info.changeControlInfo(this);
963                    }
964    
965                    /**
966                     * display control is removed
967                     */
968                    public void removeDisplayControl() {
969                            info.removeControlInfo(this);
970                    }
971    
972                    /**
973                     * Expand the contents
974                     */
975                    public void expand() {
976                            outer.removeAll();
977                            outer.setLayout(new BorderLayout());
978    
979                            if (!expanded) {
980                                    outer.add(BorderLayout.CENTER, inner);
981                                    expandButton.setIcon(
982                                            GuiUtils.getImageIcon("/auxdata/ui/icons/UpUp.gif"));
983                                    inner.getSize(innerSize);
984    //                              System.err.println("ControlInfo.expand: innerSize=" + innerSize);
985                            } else {
986                                    outer.add(BorderLayout.NORTH, inner);
987                                    expandButton.setIcon(
988                                            GuiUtils.getImageIcon("/auxdata/ui/icons/DownDown.gif"));
989                                    inner.setSize(innerSize);
990                            }
991    
992                            expanded = !expanded;
993                            control.setExpandedInTabs(expanded);
994    
995                            final Container parent = outer.getParent();
996                            outer.invalidate();
997                            parent.validate();
998                            parent.doLayout();
999                    }
1000            }
1001    }