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