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