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