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