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