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.Component; 033import java.awt.event.ActionEvent; 034import java.awt.event.ActionListener; 035import java.awt.event.MouseAdapter; 036import java.awt.event.MouseEvent; 037import java.lang.reflect.InvocationTargetException; 038import java.net.URL; 039import java.util.ArrayList; 040import java.util.List; 041 042import javax.swing.ImageIcon; 043import javax.swing.JComponent; 044import javax.swing.JMenuItem; 045import javax.swing.JOptionPane; 046import javax.swing.JPanel; 047import javax.swing.JPopupMenu; 048import javax.swing.SwingUtilities; 049import javax.swing.border.BevelBorder; 050 051import org.w3c.dom.Document; 052import org.w3c.dom.Element; 053 054import ucar.unidata.idv.IdvResourceManager; 055import ucar.unidata.idv.IntegratedDataViewer; 056import ucar.unidata.idv.MapViewManager; 057import ucar.unidata.idv.TransectViewManager; 058import ucar.unidata.idv.ViewDescriptor; 059import ucar.unidata.idv.ViewManager; 060import ucar.unidata.idv.control.DisplayControlImpl; 061import ucar.unidata.idv.ui.IdvComponentGroup; 062import ucar.unidata.idv.ui.IdvComponentHolder; 063import ucar.unidata.idv.ui.IdvUIManager; 064import ucar.unidata.idv.ui.IdvWindow; 065import ucar.unidata.ui.ComponentHolder; 066import ucar.unidata.util.GuiUtils; 067import ucar.unidata.util.LayoutUtil; 068import ucar.unidata.util.LogUtil; 069import ucar.unidata.util.Msg; 070import ucar.unidata.xml.XmlResourceCollection; 071import ucar.unidata.xml.XmlUtil; 072 073import edu.wisc.ssec.mcidasv.PersistenceManager; 074 075/** 076 * Extends the IDV component groups so that we can intercept clicks for Bruce's 077 * tab popup menu and handle drag and drop. It also intercepts ViewManager 078 * creation in order to wrap components in McIDASVComponentHolders rather than 079 * IdvComponentHolders. Doing this allows us to associate ViewManagers back to 080 * their ComponentHolders, and this functionality is taken advantage of to form 081 * the hierarchical names seen in the McIDASVViewPanel. 082 */ 083public class McvComponentGroup extends IdvComponentGroup { 084 085 /** Path to the "close tab" icon in the popup menu. */ 086 protected static final String ICO_CLOSE = 087 "/edu/wisc/ssec/mcidasv/resources/icons/tabmenu/stop-loads16.png"; 088 089 /** Path to the "rename" icon in the popup menu. */ 090 protected static final String ICO_RENAME = 091 "/edu/wisc/ssec/mcidasv/resources/icons/tabmenu/accessories-text-editor16.png"; 092 093 /** Path to the eject icon in the popup menu. */ 094 protected static final String ICO_UNDOCK = 095 "/edu/wisc/ssec/mcidasv/resources/icons/tabmenu/media-eject16.png"; 096 097 /** Action command for destroying a display. */ 098 private static final String CMD_DISPLAY_DESTROY = "DESTROY_DISPLAY_TAB"; 099 100 /** Action command for ejecting a display from a tab. */ 101 private static final String CMD_DISPLAY_EJECT = "EJECT_TAB"; 102 103 /** Action command for renaming a display. */ 104 private static final String CMD_DISPLAY_RENAME = "RENAME_DISPLAY"; 105 106 /** The popup menu for the McV tabbed display interface. */ 107 private final JPopupMenu popup = doMakeTabMenu(); 108 109 /** Number of tabs that have been stored in this group. */ 110 @SuppressWarnings("unused") 111 private int tabCount = 0; 112 113 /** Whether or not {@code init} has been called. */ 114 private boolean initDone = false; 115 116 /** 117 * Holders that McV knows are held by this component group. Used to avoid 118 * any needless work in {@code redoLayout}. 119 */ 120 private List<ComponentHolder> knownHolders = new ArrayList<>(); 121 122 /** Keep a reference to avoid extraneous calls to {@code getIdv()}. */ 123 private IntegratedDataViewer idv; 124 125 /** Reference to the window associated with this group. */ 126 private IdvWindow window = IdvWindow.getActiveWindow(); 127 128 /** 129 * Whether or not {@link #redoLayout()} needs to worry about a renamed 130 * tab. 131 */ 132 private boolean tabRenamed = false; 133 134 /** 135 * Default constructor for serialization. 136 */ 137 public McvComponentGroup() {} 138 139 /** 140 * A pretty typical constructor. 141 * 142 * @param idv The main IDV instance. 143 * @param name Presumably the name of this component group? 144 */ 145 public McvComponentGroup(final IntegratedDataViewer idv, 146 final String name) 147 { 148 super(idv, name); 149 this.idv = idv; 150 init(); 151 } 152 153 /** 154 * This constructor catches the window that will be contain this group. 155 * 156 * @param idv The main IDV instance. 157 * @param name Presumably the name of this component group? 158 * @param window The window holding this component group. 159 */ 160 public McvComponentGroup(final IntegratedDataViewer idv, 161 final String name, final IdvWindow window) 162 { 163 super(idv, name); 164 this.window = window; 165 this.idv = idv; 166 init(); 167 } 168 169 /** 170 * Initializes the various UI components. 171 */ 172 private void init() { 173 if (initDone) { 174 return; 175 } 176 tabbedPane = new DraggableTabbedPane(window, idv, this); 177// tabbedPane.addMouseListener(new TabPopupListener()); 178 179 container = new JPanel(new BorderLayout()); 180 container.add(tabbedPane); 181// container.addComponentListener(new ComponentListener() { 182// @Override public void componentHidden(ComponentEvent e) { 183// 184// } 185// @Override public void componentShown(ComponentEvent e) { 186// 187// } 188// @Override public void componentMoved(ComponentEvent e) {} 189// @Override public void componentResized(ComponentEvent e) {} 190// }); 191 GuiUtils.handleHeavyWeightComponentsInTabs(tabbedPane); 192 initDone = true; 193 } 194 195 /** 196 * Create and return the GUI contents. Overridden so that McV can implement 197 * the right click tab menu and draggable tabs. 198 * 199 * @return GUI contents 200 */ 201 @Override public JComponent doMakeContents() { 202 redoLayout(); 203 outerContainer = LayoutUtil.center(container); 204 outerContainer.validate(); 205 return outerContainer; 206 } 207 208 /** 209 * Importing a display control entails adding the control to the component 210 * group and informing the UI that the control is no longer in its own 211 * window. 212 * 213 * <p> 214 * Overridden in McV so that the display control is wrapped in a 215 * McIDASVComponentHolder rather than a IdvComponentHolder. 216 * </p> 217 * 218 * @param dc The display control to import. 219 */ 220 @Override public void importDisplayControl(final DisplayControlImpl dc) { 221 if (dc.getComponentHolder() != null) { 222 dc.getComponentHolder().removeDisplayControl(dc); 223 } 224 idv.getIdvUIManager().getViewPanel().removeDisplayControl(dc); 225 dc.guiImported(); 226 addComponent(new McvComponentHolder(idv, dc)); 227 } 228 229 /** 230 * Basically just creates a McVCompHolder for holding a dynamic skin and 231 * sets the name of the component holder. 232 * 233 * @param root The XML skin that we'll use. 234 */ 235 public void makeDynamicSkin(final Element root) { 236 IdvComponentHolder comp = 237 new McvComponentHolder(idv, XmlUtil.toString(root)); 238 239 comp.setType(McvComponentHolder.TYPE_DYNAMIC_SKIN); 240 comp.setName("Dynamic Skin Test"); 241 addComponent(comp); 242 comp.doMakeContents(); 243 } 244 245 /** 246 * Doesn't do anything for the time being... 247 * 248 * @param doc 249 * 250 * @return XML representation of the contents of this component group. 251 */ 252 @Override public Element createXmlNode(final Document doc) { 253 // System.err.println("caught createXmlNode"); 254 Element e = super.createXmlNode(doc); 255 // System.err.println(XmlUtil.toString(e)); 256 // System.err.println("exit createXmlNode"); 257 return e; 258 } 259 260 /** 261 * Handles creation of the component represented by the XML skin at the 262 * given index. 263 * 264 * <p> 265 * Overridden so that McV can wrap the component in a 266 * McIDASVComponentHolder. 267 * </p> 268 * 269 * @param index The index of the skin within the skin resource. 270 */ 271 @Override public void makeSkin(final int index) { 272// final XmlResourceCollection skins = idv.getResourceManager().getXmlResources( 273// IdvResourceManager.RSC_SKIN); 274// 275//// String id = skins.getProperty("skinid", index); 276//// if (id == null) 277//// id = skins.get(index).toString(); 278// 279//// SwingUtilities.invokeLater(new Runnable() { 280//// public void run() { 281// String id = skins.getProperty("skinid", index); 282// if (id == null) 283// id = skins.get(index).toString(); 284// IdvComponentHolder comp = new McvComponentHolder(idv, id); 285// comp.setType(IdvComponentHolder.TYPE_SKIN); 286// comp.setName("untitled"); 287// 288// addComponent(comp); 289//// } 290//// }); 291 makeSkinAtIndex(index); 292 } 293 294 public IdvComponentHolder makeSkinAtIndex(final int index) { 295 final XmlResourceCollection skins = idv.getResourceManager().getXmlResources( 296 IdvResourceManager.RSC_SKIN); 297 String id = skins.getProperty("skinid", index); 298 if (id == null) { 299 id = skins.get(index).toString(); 300 } 301 IdvComponentHolder comp = new McvComponentHolder(idv, id); 302 comp.setType(IdvComponentHolder.TYPE_SKIN); 303 comp.setName("untitled"); 304 305 addComponent(comp); 306 return comp; 307 } 308 309 /** 310 * Create a new component whose type will be determined by the contents of 311 * {@code what}. 312 * 313 * <p> 314 * Overridden so that McV can wrap up the components in 315 * McVComponentHolders, which allow McV to map ViewManagers to 316 * ComponentHolders. 317 * </p> 318 * 319 * @param what String that determines what sort of component we create. 320 */ 321 @Override public void makeNew(final String what) { 322 try { 323 ViewManager vm = null; 324 ComponentHolder comp = null; 325 String property = "showControlLegend=false"; 326 ViewDescriptor desc = new ViewDescriptor(); 327 328 // we're only really interested in map, globe, or transect views. 329 if (what.equals(IdvUIManager.COMP_MAPVIEW)) { 330 vm = new MapViewManager(idv, desc, property); 331 } else if (what.equals(IdvUIManager.COMP_TRANSECTVIEW)) { 332 vm = new TransectViewManager(idv, desc, property); 333 } else if (what.equals(IdvUIManager.COMP_GLOBEVIEW)) { 334 vm = new MapViewManager(idv, desc, property); 335 ((MapViewManager)vm).setUseGlobeDisplay(true); 336 } else { 337 // hand off uninteresting things to the IDV 338 super.makeNew(what); 339 return; 340 } 341 342 // make sure we get the component into a mcv component holder, 343 // otherwise we won't be able to easily map ViewManagers to 344 // ComponentHolders for the hierarchical names in the ViewPanel. 345 idv.getVMManager().addViewManager(vm); 346 comp = new McvComponentHolder(idv, vm); 347 348 if (comp != null) { 349 addComponent(comp); 350// GuiUtils.showComponentInTabs(comp.getContents()); 351 } 352 353 } catch (Exception exc) { 354 LogUtil.logException("Error making new " + what, exc); 355 } 356 } 357 358 /** 359 * Forces this group to layout its components. Extended because the IDV was 360 * doing extra work that McIDAS-V doesn't need, such as dealing with 361 * layouts other than LAYOUT_TABS and needlessly reinitializing the group's 362 * container. 363 * 364 * @see ucar.unidata.ui.ComponentGroup#redoLayout() 365 */ 366 @SuppressWarnings("unchecked") 367 @Override public void redoLayout() { 368 final List<ComponentHolder> currentHolders = getDisplayComponents(); 369 if (!tabRenamed && knownHolders.equals(currentHolders)) { 370 return; 371 } 372 373 if (tabbedPane == null) { 374 return; 375 } 376 377 Runnable updateGui = new Runnable() { 378 public void run() { 379 int selectedIndex = tabbedPane.getSelectedIndex(); 380 381 tabbedPane.setVisible(false); 382 tabbedPane.removeAll(); 383 384 knownHolders = new ArrayList<>(currentHolders); 385 for (ComponentHolder holder : knownHolders) { 386 tabbedPane.addTab(holder.getName(), holder.getContents()); 387 } 388 389 if (tabRenamed) { 390 tabbedPane.setSelectedIndex(selectedIndex); 391 } 392 393 tabbedPane.setVisible(true); 394 tabRenamed = false; 395 } 396 }; 397 398 if (SwingUtilities.isEventDispatchThread()) { 399 SwingUtilities.invokeLater(updateGui); 400 } else { 401 try { 402 SwingUtilities.invokeAndWait(updateGui); 403 } catch (InterruptedException e) { 404 // TODO Auto-generated catch block 405 e.printStackTrace(); 406 } catch (InvocationTargetException e) { 407 // TODO Auto-generated catch block 408 e.printStackTrace(); 409 } 410 } 411 } 412 413 // TODO(jon): remove this method if Unidata implements your fix. 414 @Override public void getViewManagers(@SuppressWarnings("rawtypes") final List viewManagers) { 415 if ((viewManagers == null) || (getDisplayComponents() == null)) { 416// logger.debug("McvComponentGroup.getViewManagers(): bailing out early!"); 417 return; 418 } 419 420 super.getViewManagers(viewManagers); 421 } 422 423 /** 424 * Adds a component holder to this group. Extended so that the added holder 425 * becomes the active tab, and the component is explicitly set to visible 426 * in an effort to fix that heavyweight/lightweight component problem. 427 * 428 * @param holder 429 * @param index 430 * 431 * @see ucar.unidata.ui.ComponentGroup#addComponent(ComponentHolder, int) 432 */ 433 @Override public void addComponent(final ComponentHolder holder, 434 final int index) 435 { 436 if (shouldGenerateName(holder, index)) { 437 holder.setName("untitled"); 438 } 439 440 if (holder.getName().trim().isEmpty()) { 441 holder.setName("untitled"); 442 } 443 444 super.addComponent(holder, index); 445 setActiveComponentHolder(holder); 446 holder.getContents().setVisible(true); 447 448 if (window != null) { 449 window.setTitle(makeWindowTitle(holder.getName())); 450 } 451 } 452 453 private boolean shouldGenerateName(final ComponentHolder h, final int i) { 454 if ((h.getName() != null) && !h.getName().startsWith("untitled")) { 455 return false; 456 } 457 458 boolean invalidIndex = i >= 0; 459 boolean withoutName = ((h.getName() == null) || (h.getName().length() == 0)); 460 boolean loadingBundle = ((PersistenceManager)getIdv().getPersistenceManager()).isBundleLoading(); 461 462 return invalidIndex || withoutName || !loadingBundle; 463 } 464 465 /** 466 * Used to set the tab associated with {@code holder} as the active tab 467 * in our {@link javax.swing.JTabbedPane JTabbedPane}. 468 * 469 * @param holder The active component holder. 470 */ 471 public void setActiveComponentHolder(final ComponentHolder holder) { 472 if (getDisplayComponentCount() > 1) { 473 final int newIdx = getDisplayComponents().indexOf(holder); 474 SwingUtilities.invokeLater(new Runnable() { 475 public void run() { 476 setActiveIndex(newIdx); 477 } 478 }); 479 480 } 481 482 // TODO: this doesn't work quite right... 483 if (window == null) { 484 window = IdvWindow.getActiveWindow(); 485 } 486 if (window != null) { 487// SwingUtilities.invokeLater(new Runnable() { 488// public void run() { 489 window.toFront(); 490// window.setTitle(holder.getName()); 491 window.setTitle(makeWindowTitle(holder.getName())); 492// } 493// }); 494 } 495 } 496 497 /** 498 * @return The index of the active component holder within this group. 499 */ 500 public int getActiveIndex() { 501 if (tabbedPane == null) { 502 return -1; 503 } else { 504 return tabbedPane.getSelectedIndex(); 505 } 506 } 507 508 /** 509 * Make the component holder at {@code index} active. 510 * 511 * @param index The index of the desired component holder. 512 * 513 * @return True if the active component holder was set, false otherwise. 514 */ 515 public boolean setActiveIndex(final int index) { 516 int size = getDisplayComponentCount(); 517 if ((index < 0) || (index >= size)) { 518 return false; 519 } 520 521// SwingUtilities.invokeLater(new Runnable() { 522// public void run() { 523 tabbedPane.setSelectedIndex(index); 524 if (window != null) { 525 ComponentHolder h = (ComponentHolder)getDisplayComponents().get(index); 526 if (h != null) { 527 window.setTitle(makeWindowTitle(h.getName())); 528 } 529 } 530// } 531// }); 532 return true; 533 } 534 535 /** 536 * Returns the index of {@code holder} within this component group. 537 * 538 * @return Either the index of {@code holder}, or {@code -1} 539 * if {@link #getDisplayComponents()} returns a {@code null} {@link List}. 540 * 541 * @see List#indexOf(Object) 542 */ 543 @Override public int indexOf(final ComponentHolder holder) { 544 @SuppressWarnings("rawtypes") 545 List dispComps = getDisplayComponents(); 546 if (dispComps == null) { 547 return -1; 548 } else { 549 return getDisplayComponents().indexOf(holder); 550 } 551 } 552 553 /** 554 * Returns the {@link ComponentHolder} at the given position within this 555 * component group. 556 * 557 * @param index Index of the {@code ComponentHolder} to return. 558 * 559 * @return {@code ComponentHolder} at {@code index}. 560 * 561 * @see List#get(int) 562 */ 563 protected ComponentHolder getHolderAt(final int index) { 564 @SuppressWarnings("unchecked") 565 List<ComponentHolder> dispComps = getDisplayComponents(); 566 return dispComps.get(index); 567 } 568 569 /** 570 * @return Component holder that corresponds to the selected tab. 571 */ 572 public ComponentHolder getActiveComponentHolder() { 573 int idx = 0; 574 575 if (getDisplayComponentCount() > 1) { 576// idx = tabbedPane.getSelectedIndex(); 577 idx = getActiveIndex(); 578 } 579 580// return (ComponentHolder)getDisplayComponents().get(idx); 581 return getHolderAt(idx); 582 } 583 584 /** 585 * Overridden so that McV can also update its copy of the IDV reference. 586 */ 587 @Override public void setIdv(final IntegratedDataViewer newIdv) { 588 super.setIdv(newIdv); 589 idv = newIdv; 590 } 591 592 /** 593 * Create a window title suitable for an application window. 594 * 595 * @param title Window title 596 * 597 * @return Application title plus the window title. 598 */ 599 private String makeWindowTitle(final String title) { 600 String defaultApplicationName = "McIDAS-V"; 601 if (idv != null) { 602 defaultApplicationName = idv.getStateManager().getTitle(); 603 } 604 return UIManager.makeTitle(defaultApplicationName, title); 605 } 606 607 /** 608 * Returns the number of display components {@literal "in"} this group. 609 * 610 * @return Either the {@code size()} of the {@link List} returned by 611 * {@link #getDisplayComponents()} or {@code -1} if 612 * {@code getDisplayComponents()} returns a {@code null} {@code List}. 613 */ 614 protected int getDisplayComponentCount() { 615 @SuppressWarnings("rawtypes") 616 List dispComps = getDisplayComponents(); 617 if (dispComps == null) { 618 return -1; 619 } else { 620 return dispComps.size(); 621 } 622 } 623 624 /** 625 * Create the {@code JPopupMenu} that will be displayed for a tab. 626 * 627 * @return Menu initialized with tab options 628 */ 629 protected JPopupMenu doMakeTabMenu() { 630 ActionListener menuListener = new ActionListener() { 631 public void actionPerformed(ActionEvent evt) { 632 final String cmd = evt.getActionCommand(); 633 if (CMD_DISPLAY_EJECT.equals(cmd)) { 634 ejectDisplay(tabbedPane.getSelectedIndex()); 635 } else if (CMD_DISPLAY_RENAME.equals(cmd)) { 636 renameDisplay(tabbedPane.getSelectedIndex()); 637 } else if (CMD_DISPLAY_DESTROY.equals(cmd)) { 638 destroyDisplay(tabbedPane.getSelectedIndex()); 639 } 640 } 641 }; 642 643 final JPopupMenu popup = new JPopupMenu(); 644 JMenuItem item; 645 646 // URL img = getClass().getResource(ICO_UNDOCK); 647 // item = new JMenuItem("Undock", new ImageIcon(img)); 648 // item.setActionCommand(CMD_DISPLAY_EJECT); 649 // item.addActionListener(menuListener); 650 // popup.add(item); 651 652 URL img = getClass().getResource(ICO_RENAME); 653 item = new JMenuItem("Rename", new ImageIcon(img)); 654 item.setActionCommand(CMD_DISPLAY_RENAME); 655 item.addActionListener(menuListener); 656 popup.add(item); 657 658 // popup.addSeparator(); 659 660 img = getClass().getResource(ICO_CLOSE); 661 item = new JMenuItem("Close", new ImageIcon(img)); 662 item.setActionCommand(CMD_DISPLAY_DESTROY); 663 item.addActionListener(menuListener); 664 popup.add(item); 665 666 popup.setBorder(new BevelBorder(BevelBorder.RAISED)); 667 668 Msg.translateTree(popup); 669 return popup; 670 } 671 672 /** 673 * Remove the component holder at index {@code idx}. This method does 674 * not destroy the component holder. 675 * 676 * @param idx Index of the ejected component holder. 677 * 678 * @return Component holder that was ejected. 679 */ 680 private ComponentHolder ejectDisplay(final int idx) { 681 return null; 682 } 683 684 /** 685 * Prompt the user to change the name of the component holder at index 686 * {@code idx}. Nothing happens if the user doesn't enter anything. 687 * 688 * @param idx Index of the component holder. 689 */ 690 protected void renameDisplay(final int idx) { 691 final String title = 692 JOptionPane.showInputDialog( 693 IdvWindow.getActiveWindow().getFrame(), "Enter new name", 694 makeWindowTitle("Rename Tab"), JOptionPane.PLAIN_MESSAGE); 695 696 if (title == null) { 697 return; 698 } 699 700// final List<ComponentHolder> comps = getDisplayComponents(); 701// comps.get(idx).setName(title); 702 getHolderAt(idx).setName(title); 703 tabRenamed = true; 704 if (window != null) { 705 window.setTitle(makeWindowTitle(title)); 706 } 707 redoLayout(); 708 } 709 710 /** 711 * Prompts the user to confirm removal of the component holder at index 712 * {@code idx}. Nothing happens if the user declines. 713 * 714 * @param idx Index of the component holder. 715 * 716 * @return Either {@code true} if the user elected to remove, 717 * {@code false} otherwise. 718 */ 719 protected boolean destroyDisplay(final int idx) { 720// final List<IdvComponentHolder> comps = getDisplayComponents(); 721// IdvComponentHolder comp = comps.get(idx); 722 return ((IdvComponentHolder)getHolderAt(idx)).removeDisplayComponent(); 723// return comp.removeDisplayComponent(); 724 } 725 726 /** 727 * Remove the component at {@code index} without forcing the IDV-land 728 * component group to redraw. 729 * 730 * @param index The index of the component to be removed. 731 * 732 * @return The removed component. 733 */ 734 @SuppressWarnings("unchecked") 735 public ComponentHolder quietRemoveComponentAt(final int index) { 736 List<ComponentHolder> comps = getDisplayComponents(); 737 if (comps == null || comps.size() == 0) { 738 return null; 739 } 740 ComponentHolder removed = comps.remove(index); 741 removed.setParent(null); 742 return removed; 743 } 744 745 /** 746 * Adds a component to the end of the list of display components without 747 * forcing the IDV-land code to redraw. 748 * 749 * @param component The component to add. 750 * 751 * @return The index of the newly added component, or {@code -1} if 752 * {@link #getDisplayComponents()} returned a null {@code List}. 753 */ 754 @SuppressWarnings("unchecked") 755 public int quietAddComponent(final ComponentHolder component) { 756 List<ComponentHolder> comps = getDisplayComponents(); 757 if (comps == null) { 758 return -1; 759 } 760 if (comps.contains(component)) { 761 comps.remove(component); 762 } 763 comps.add(component); 764 component.setParent(this); 765 return comps.indexOf(component); 766 } 767 768 /** 769 * Handle pop-up events for tabs. 770 */ 771 @SuppressWarnings("unused") 772 private class TabPopupListener extends MouseAdapter { 773 774 @Override public void mouseClicked(final MouseEvent evt) { 775 checkPopup(evt); 776 } 777 778 @Override public void mousePressed(final MouseEvent evt) { 779 checkPopup(evt); 780 } 781 782 @Override public void mouseReleased(final MouseEvent evt) { 783 checkPopup(evt); 784 } 785 786 /** 787 * <p> 788 * Determines whether or not the tab popup menu should be shown, and 789 * if so, which parts of it should be enabled or disabled. 790 * </p> 791 * 792 * @param evt Allows us to determine the type of event. 793 */ 794 private void checkPopup(final MouseEvent evt) { 795 if (evt.isPopupTrigger()) { 796 // can't close or eject last tab 797 // TODO: re-evaluate this 798 Component[] comps = popup.getComponents(); 799 for (Component comp : comps) { 800 if (comp instanceof JMenuItem) { 801 String cmd = ((JMenuItem)comp).getActionCommand(); 802 if ((CMD_DISPLAY_DESTROY.equals(cmd) || CMD_DISPLAY_EJECT.equals(cmd)) 803 && tabbedPane.getTabCount() == 1) { 804 comp.setEnabled(false); 805 } else { 806 comp.setEnabled(true); 807 } 808 } 809 } 810 popup.show(tabbedPane, evt.getX(), evt.getY()); 811 } 812 } 813 } 814}