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