001/*
002 * $Id: McIDASVViewPanel.java,v 1.14 2011/03/24 18:13:11 davep Exp $
003 *
004 * This file is part of McIDAS-V
005 *
006 * Copyright 2007-2011
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
031package edu.wisc.ssec.mcidasv.ui;
032
033import java.awt.BorderLayout;
034import java.awt.Color;
035import java.awt.Component;
036import java.awt.Container;
037import java.awt.Cursor;
038import java.awt.Dimension;
039import java.awt.Font;
040import java.awt.FontMetrics;
041import java.awt.Graphics;
042import java.awt.Image;
043import java.awt.Insets;
044import java.awt.Rectangle;
045import java.awt.event.ActionEvent;
046import java.awt.event.ActionListener;
047import java.awt.event.KeyAdapter;
048import java.awt.event.KeyEvent;
049import java.awt.event.KeyListener;
050import java.awt.event.MouseAdapter;
051import java.awt.event.MouseEvent;
052import java.awt.image.ImageObserver;
053import java.util.ArrayList;
054import java.util.Hashtable;
055import java.util.List;
056
057import javax.swing.BorderFactory;
058import javax.swing.BoxLayout;
059import javax.swing.ButtonGroup;
060import javax.swing.ImageIcon;
061import javax.swing.JButton;
062import javax.swing.JComponent;
063import javax.swing.JLabel;
064import javax.swing.JMenuItem;
065import javax.swing.JPanel;
066import javax.swing.JPopupMenu;
067import javax.swing.JScrollPane;
068import javax.swing.JToggleButton;
069import javax.swing.SwingConstants;
070import javax.swing.border.Border;
071import javax.swing.border.EtchedBorder;
072
073import edu.wisc.ssec.mcidasv.Constants;
074
075import ucar.unidata.idv.DisplayControl;
076import ucar.unidata.idv.IdvConstants;
077import ucar.unidata.idv.IdvManager;
078import ucar.unidata.idv.IntegratedDataViewer;
079import ucar.unidata.idv.MapViewManager;
080import ucar.unidata.idv.TransectViewManager;
081import ucar.unidata.idv.ViewManager;
082import ucar.unidata.idv.control.DisplayControlImpl;
083import ucar.unidata.idv.control.MapDisplayControl;
084import ucar.unidata.idv.ui.IdvComponentGroup;
085import ucar.unidata.idv.ui.IdvWindow;
086import ucar.unidata.idv.ui.ViewPanel;
087import ucar.unidata.ui.ComponentHolder;
088import ucar.unidata.ui.DndImageButton;
089import ucar.unidata.util.GuiUtils;
090import ucar.unidata.util.Misc;
091import ucar.unidata.util.Msg;
092import 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 */
103public 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}