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