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.Color; 032 import java.awt.Component; 033 import java.awt.Cursor; 034 import java.awt.FontMetrics; 035 import java.awt.Graphics; 036 import java.awt.Image; 037 import java.awt.Point; 038 import java.awt.Rectangle; 039 import java.awt.datatransfer.DataFlavor; 040 import java.awt.datatransfer.Transferable; 041 import java.awt.dnd.DnDConstants; 042 import java.awt.dnd.DragGestureEvent; 043 import java.awt.dnd.DragGestureListener; 044 import java.awt.dnd.DragSource; 045 import java.awt.dnd.DragSourceDragEvent; 046 import java.awt.dnd.DragSourceDropEvent; 047 import java.awt.dnd.DragSourceEvent; 048 import java.awt.dnd.DragSourceListener; 049 import java.awt.dnd.DropTarget; 050 import java.awt.dnd.DropTargetDragEvent; 051 import java.awt.dnd.DropTargetDropEvent; 052 import java.awt.dnd.DropTargetEvent; 053 import java.awt.dnd.DropTargetListener; 054 import java.awt.event.InputEvent; 055 import java.awt.event.MouseEvent; 056 import java.awt.event.MouseListener; 057 import java.awt.event.MouseMotionListener; 058 import java.util.HashMap; 059 import java.util.List; 060 import java.util.Map; 061 062 import javax.swing.Icon; 063 import javax.swing.ImageIcon; 064 import javax.swing.JComponent; 065 import javax.swing.JTabbedPane; 066 import javax.swing.SwingUtilities; 067 import javax.swing.plaf.basic.BasicTabbedPaneUI; 068 import javax.swing.plaf.metal.MetalTabbedPaneUI; 069 070 import org.w3c.dom.Element; 071 072 import ucar.unidata.idv.IntegratedDataViewer; 073 import ucar.unidata.idv.ui.IdvWindow; 074 import ucar.unidata.ui.ComponentGroup; 075 import ucar.unidata.ui.ComponentHolder; 076 import ucar.unidata.util.GuiUtils; 077 import ucar.unidata.xml.XmlUtil; 078 import edu.wisc.ssec.mcidasv.Constants; 079 import edu.wisc.ssec.mcidasv.ui.DraggableTabbedPane.TabButton.ButtonState; 080 081 /** 082 * This is a rather simplistic drag and drop enabled JTabbedPane. It allows 083 * users to use drag and drop to move tabs between windows and reorder tabs. 084 */ 085 public class DraggableTabbedPane extends JTabbedPane implements 086 DragGestureListener, DragSourceListener, DropTargetListener, MouseListener, 087 MouseMotionListener 088 { 089 private static final long serialVersionUID = -5710302260509445686L; 090 091 /** Local shorthand for the actions we're accepting. */ 092 private static final int VALID_ACTION = DnDConstants.ACTION_COPY_OR_MOVE; 093 094 /** Path to the icon we'll use as an index indicator. */ 095 private static final String IDX_ICON = 096 "/edu/wisc/ssec/mcidasv/resources/icons/tabmenu/go-down.png"; 097 098 /** 099 * Used to signal across all DraggableTabbedPanes that the component 100 * currently being dragged originated in another window. This'll let McV 101 * determine if it has to do a quiet ComponentHolder transfer. 102 */ 103 protected static boolean outsideDrag = false; 104 105 /** The actual image that we'll use to display the index indications. */ 106 private final Image INDICATOR = 107 (new ImageIcon(getClass().getResource(IDX_ICON))).getImage(); 108 109 /** The tab index where the drag started. */ 110 private int sourceIndex = -1; 111 112 /** The tab index that the user is currently over. */ 113 private int overIndex = -1; 114 115 /** Used for starting the dragging process. */ 116 private DragSource dragSource; 117 118 /** Used for signaling that we'll accept drops (registers listeners). */ 119 private DropTarget dropTarget; 120 121 /** The component group holding our components. */ 122 private McvComponentGroup group; 123 124 /** The IDV window that contains this tabbed pane. */ 125 private IdvWindow window; 126 127 /** Keep around this reference so that we can access the UI Manager. */ 128 private IntegratedDataViewer idv; 129 130 /** 131 * Mostly just registers that this component should listen for drag and 132 * drop operations. 133 * 134 * @param win The IDV window containing this tabbed pane. 135 * @param idv The main IDV instance. 136 * @param group The {@link McvComponentGroup} that holds this component's tabs. 137 */ 138 public DraggableTabbedPane(IdvWindow win, IntegratedDataViewer idv, McvComponentGroup group) { 139 dropTarget = new DropTarget(this, this); 140 dragSource = new DragSource(); 141 dragSource.createDefaultDragGestureRecognizer(this, VALID_ACTION, this); 142 143 this.group = group; 144 this.idv = idv; 145 window = win; 146 147 addMouseListener(this); 148 addMouseMotionListener(this); 149 150 if (getUI() instanceof MetalTabbedPaneUI) { 151 setUI(new CloseableMetalTabbedPaneUI(SwingUtilities.LEFT)); 152 currentTabColor = indexColorMetal; 153 } else { 154 setUI(new CloseableTabbedPaneUI(SwingUtilities.LEFT)); 155 currentTabColor = indexColorUglyTabs; 156 } 157 } 158 159 /** 160 * Triggered when the user does a (platform-dependent) drag initiating 161 * gesture. Used to populate the things that the user is attempting to 162 * drag. 163 */ 164 public void dragGestureRecognized(DragGestureEvent e) { 165 sourceIndex = getSelectedIndex(); 166 167 // transferable allows us to store the current DraggableTabbedPane and 168 // the source index of the drag inside the various drag and drop event 169 // listeners. 170 Transferable transferable = new TransferableIndex(this, sourceIndex); 171 172 Cursor cursor = DragSource.DefaultMoveDrop; 173 if (e.getDragAction() != DnDConstants.ACTION_MOVE) 174 cursor = DragSource.DefaultCopyDrop; 175 176 dragSource.startDrag(e, cursor, transferable, this); 177 } 178 179 /** 180 * Triggered when the user drags into <tt>dropTarget</tt>. 181 */ 182 public void dragEnter(DropTargetDragEvent e) { 183 DataFlavor[] flave = e.getCurrentDataFlavors(); 184 if ((flave.length == 0) || !(flave[0] instanceof DraggableTabFlavor)) 185 return; 186 187 //System.out.print("entered window outsideDrag=" + outsideDrag + " sourceIndex=" + sourceIndex); 188 189 // if the DraggableTabbedPane associated with this drag isn't the 190 // "current" DraggableTabbedPane we're dealing with a drag from another 191 // window and we need to make this DraggableTabbedPane aware of that. 192 if (((DraggableTabFlavor)flave[0]).getDragTab() != this) { 193 //System.out.println(" coming from outside!"); 194 outsideDrag = true; 195 } else { 196 //System.out.println(" re-entered parent window"); 197 outsideDrag = false; 198 } 199 } 200 201 /** 202 * Triggered when the user drags out of {@code dropTarget}. 203 */ 204 public void dragExit(DropTargetEvent e) { 205 // System.out.println("drag left a window outsideDrag=" + outsideDrag + " sourceIndex=" + sourceIndex); 206 overIndex = -1; 207 208 //outsideDrag = true; 209 repaint(); 210 } 211 212 /** 213 * Triggered continually while the user is dragging over 214 * {@code dropTarget}. McIDAS-V uses this to draw the index indicator. 215 * 216 * @param e Information about the current state of the drag. 217 */ 218 public void dragOver(DropTargetDragEvent e) { 219 // System.out.println("dragOver outsideDrag=" + outsideDrag + " sourceIndex=" + sourceIndex); 220 if ((!outsideDrag) && (sourceIndex == -1)) 221 return; 222 223 Point dropPoint = e.getLocation(); 224 overIndex = indexAtLocation(dropPoint.x, dropPoint.y); 225 226 repaint(); 227 } 228 229 /** 230 * Triggered when a drop has happened over {@code dropTarget}. 231 * 232 * @param e State that we'll need in order to handle the drop. 233 */ 234 public void drop(DropTargetDropEvent e) { 235 // if the dragged ComponentHolder was dragged from another window we 236 // must do a behind-the-scenes transfer from its old ComponentGroup to 237 // the end of the new ComponentGroup. 238 if (outsideDrag) { 239 DataFlavor[] flave = e.getCurrentDataFlavors(); 240 DraggableTabbedPane other = ((DraggableTabFlavor)flave[0]).getDragTab(); 241 242 ComponentHolder target = other.removeDragged(); 243 sourceIndex = group.quietAddComponent(target); 244 outsideDrag = false; 245 } 246 247 // check to see if we've actually dropped something McV understands. 248 if (sourceIndex >= 0) { 249 e.acceptDrop(VALID_ACTION); 250 Point dropPoint = e.getLocation(); 251 int dropIndex = indexAtLocation(dropPoint.x, dropPoint.y); 252 253 // make sure the user chose to drop over a valid area/thing first 254 // then do the actual drop. 255 if ((dropIndex != -1) && (getComponentAt(dropIndex) != null)) 256 doDrop(sourceIndex, dropIndex); 257 258 // clean up anything associated with the current drag and drop 259 e.getDropTargetContext().dropComplete(true); 260 sourceIndex = -1; 261 overIndex = -1; 262 263 repaint(); 264 } 265 } 266 267 /** 268 * {@literal "Quietly"} removes the dragged component from its group. If the 269 * last component in a group has been dragged out of the group, the 270 * associated window will be killed. 271 * 272 * @return The removed component. 273 */ 274 private ComponentHolder removeDragged() { 275 ComponentHolder removed = group.quietRemoveComponentAt(sourceIndex); 276 277 // no point in keeping an empty window around... but killing the 278 // window here doesn't properly terminate the drag and drop (as this 279 // method is typically called from *another* window). 280 return removed; 281 } 282 283 /** 284 * Moves a component to its new index within the component group. 285 * 286 * @param srcIdx The old index of the component. 287 * @param dstIdx The new index of the component. 288 */ 289 public void doDrop(int srcIdx, int dstIdx) { 290 List<ComponentHolder> comps = group.getDisplayComponents(); 291 ComponentHolder src = comps.get(srcIdx); 292 293 group.removeComponent(src); 294 group.addComponent(src, dstIdx); 295 } 296 297 /** 298 * Overridden so that McIDAS-V can draw an indicator of a dragged tab's 299 * possible 300 */ 301 @Override public void paint(Graphics g) { 302 super.paint(g); 303 304 if (overIndex == -1) 305 return; 306 307 Rectangle bounds = getBoundsAt(overIndex); 308 309 if (bounds != null) 310 g.drawImage(INDICATOR, bounds.x-7, bounds.y, null); 311 } 312 313 /** 314 * Overriden so that McIDAS-V can change the window title upon changing 315 * tabs. 316 */ 317 @Override public void setSelectedIndex(int index) { 318 super.setSelectedIndex(index); 319 320 // there are only ever component holders in the display comps. 321 @SuppressWarnings("unchecked") 322 List<ComponentHolder> comps = group.getDisplayComponents(); 323 324 ComponentHolder h = comps.get(index); 325 String newTitle = 326 UIManager.makeTitle(idv.getStateManager().getTitle(), h.getName()); 327 if (window != null) 328 window.setTitle(newTitle); 329 } 330 331 /** 332 * Used to simply provide a reference to the originating 333 * DraggableTabbedPane while we're dragging and dropping. 334 */ 335 private static class TransferableIndex implements Transferable { 336 private DraggableTabbedPane tabbedPane; 337 338 private int index; 339 340 public TransferableIndex(DraggableTabbedPane dt, int i) { 341 tabbedPane = dt; 342 index = i; 343 } 344 345 // whatever is returned here needs to be serializable. so we can't just 346 // return the tabbedPane. :( 347 public Object getTransferData(DataFlavor flavor) { 348 return index; 349 } 350 351 public DataFlavor[] getTransferDataFlavors() { 352 return new DataFlavor[] { new DraggableTabFlavor(tabbedPane) }; 353 } 354 355 public boolean isDataFlavorSupported(DataFlavor flavor) { 356 return true; 357 } 358 } 359 360 /** 361 * To be perfectly honest I'm still a bit fuzzy about DataFlavors. As far 362 * as I can tell they're used like so: if a user dragged an image file on 363 * to a toolbar, the toolbar might be smart enough to add the image. If the 364 * user dragged the same image file into a text document, the text editor 365 * might be smart enough to insert the path to the image or something. 366 * 367 * I'm thinking that would require two data flavors: some sort of toolbar 368 * flavor and then some sort of text flavor? 369 */ 370 private static class DraggableTabFlavor extends DataFlavor { 371 private DraggableTabbedPane tabbedPane; 372 373 public DraggableTabFlavor(DraggableTabbedPane dt) { 374 super(DraggableTabbedPane.class, "DraggableTabbedPane"); 375 tabbedPane = dt; 376 } 377 378 public DraggableTabbedPane getDragTab() { 379 return tabbedPane; 380 } 381 } 382 383 /** 384 * Handle the user dropping a tab outside of a McV window. This will create 385 * a new window and add the dragged tab to the ComponentGroup within the 386 * newly created window. The new window is the same size as the origin 387 * window, with the top centered over the location where the user released 388 * the mouse. 389 * 390 * @param dragged The ComponentHolder that's being dragged around. 391 * @param drop The x- and y-coordinates where the user dropped the tab. 392 */ 393 private void newWindowDrag(ComponentHolder dragged, Point drop) { 394 // if ((dragged == null) || (window == null)) 395 if (dragged == null) 396 return; 397 398 UIManager ui = (UIManager)idv.getIdvUIManager(); 399 400 try { 401 Element skinRoot = XmlUtil.getRoot(Constants.BLANK_COMP_GROUP, getClass()); 402 403 // create the new window with visibility off, so we can position 404 // the window in a sensible way before the user has to see it. 405 IdvWindow w = ui.createNewWindow(null, false, "McIDAS-V", 406 Constants.BLANK_COMP_GROUP, 407 skinRoot, false, null); 408 409 // make the new window the same size as the old and center the 410 // *top* of the window over the drop point. 411 int height = window.getBounds().height; 412 int width = window.getBounds().width; 413 int startX = drop.x - (width / 2); 414 415 w.setBounds(new Rectangle(startX, drop.y, width, height)); 416 417 // be sure to add the dragged component holder to the new window. 418 ComponentGroup newGroup = 419 (ComponentGroup)w.getComponentGroups().get(0); 420 421 newGroup.addComponent(dragged); 422 423 // let there be a window 424 w.setVisible(true); 425 } catch (Throwable e) { 426 e.printStackTrace(); 427 } 428 } 429 430 /** 431 * Handles what happens at the very end of a drag and drop. Since I could 432 * not find a better method for it, tabs that are dropped outside of a McV 433 * window are handled with this method. 434 */ 435 public void dragDropEnd(DragSourceDropEvent e) { 436 if (!e.getDropSuccess() && e.getDropAction() == 0) { 437 newWindowDrag(removeDragged(), e.getLocation()); 438 } 439 440 // this should probably be the last thing to happen in this method. 441 // checks to see if we've got a blank window after a drag and drop; 442 // if so, dispose! 443 List<ComponentHolder> comps = group.getDisplayComponents(); 444 if (comps == null || comps.isEmpty()) { 445 window.dispose(); 446 } 447 } 448 449 // required methods that we don't need to implement yet. 450 public void dragEnter(DragSourceDragEvent e) { } 451 public void dragExit(DragSourceEvent e) { } 452 public void dragOver(DragSourceDragEvent e) { } 453 public void dropActionChanged(DragSourceDragEvent e) { } 454 public void dropActionChanged(DropTargetDragEvent e) { } 455 456 public void mouseClicked(final MouseEvent e) { 457 processMouseEvents(e); 458 } 459 460 public void mouseExited(final MouseEvent e) { 461 processMouseEvents(e); 462 } 463 464 public void mousePressed(final MouseEvent e) { 465 processMouseEvents(e); 466 } 467 468 public void mouseEntered(final MouseEvent e) { 469 processMouseEvents(e); 470 } 471 472 public void mouseMoved(final MouseEvent e) { 473 processMouseEvents(e); 474 } 475 476 public void mouseDragged(final MouseEvent e) { 477 processMouseEvents(e); 478 } 479 480 public void mouseReleased(final MouseEvent e) { 481 processMouseEvents(e); 482 } 483 484 private void processMouseEvents(final MouseEvent e) { 485 int eventX = e.getX(); 486 int eventY = e.getY(); 487 488 int tabIndex = getUI().tabForCoordinate(this, eventX, eventY); 489 if (tabIndex < 0) 490 return; 491 492 TabButton icon = (TabButton)getIconAt(tabIndex); 493 if (icon == null) 494 return; 495 496 int id = e.getID(); 497 Rectangle iconBounds = icon.getBounds(); 498 if (!iconBounds.contains(eventX, eventY) || id == MouseEvent.MOUSE_EXITED) { 499 if (icon.getState() == ButtonState.ROLLOVER || icon.getState() == ButtonState.PRESSED) 500 icon.setState(ButtonState.DEFAULT); 501 502 if (e.getClickCount() >= 2 && !e.isPopupTrigger() && id == MouseEvent.MOUSE_CLICKED) 503 group.renameDisplay(tabIndex); 504 505 repaint(iconBounds); 506 return; 507 } 508 509 if (id == MouseEvent.MOUSE_PRESSED && (e.getModifiersEx() & InputEvent.BUTTON1_DOWN_MASK) != 0) { 510 icon.setState(ButtonState.PRESSED); 511 } else if (id == MouseEvent.MOUSE_CLICKED) { 512 icon.setState(ButtonState.DEFAULT); 513 group.destroyDisplay(tabIndex); 514 } else { 515 icon.setState(ButtonState.ROLLOVER); 516 } 517 repaint(iconBounds); 518 } 519 520 @Override public void addTab(String title, Component component) { 521 addTab(title, component, null); 522 } 523 524 public void addTab(String title, Component component, Icon extraIcon) { 525 if (getTabCount() < 9) 526 title = "<html><font color=\""+currentTabColor+"\">"+(getTabCount()+1)+"</font> "+title+"</html>"; 527 else if (getTabCount() == 9) 528 title = "<html><font color=\""+currentTabColor+"\">0</font> "+title+"</html>"; 529 super.addTab(title, new TabButton(), component); 530 } 531 532 private static final Color unselected = new Color(165, 165, 165); 533 private static final Color selected = new Color(225, 225, 225); 534 535 private static final String indexColorMetal = "#AAAAAA"; 536 private static final String indexColorUglyTabs = "#708090"; 537 private String currentTabColor = indexColorMetal; 538 539 class CloseableTabbedPaneUI extends BasicTabbedPaneUI { 540 private int horizontalTextPosition = SwingUtilities.LEFT; 541 542 public CloseableTabbedPaneUI() { } 543 544 public CloseableTabbedPaneUI(int horizontalTextPosition) { 545 this.horizontalTextPosition = horizontalTextPosition; 546 } 547 548 @Override protected void layoutLabel(int tabPlacement, 549 FontMetrics metrics, int tabIndex, String title, Icon icon, 550 Rectangle tabRect, Rectangle iconRect, Rectangle textRect, 551 boolean isSelected) 552 { 553 if (tabPane.getTabCount() == 0) 554 return; 555 556 textRect.x = textRect.y = iconRect.x = iconRect.y = 0; 557 javax.swing.text.View v = getTextViewForTab(tabIndex); 558 if (v != null) 559 tabPane.putClientProperty("html", v); 560 561 SwingUtilities.layoutCompoundLabel((JComponent)tabPane, 562 metrics, title, icon, 563 SwingUtilities.CENTER, 564 SwingUtilities.CENTER, 565 SwingUtilities.CENTER, 566 horizontalTextPosition, 567 tabRect, 568 iconRect, 569 textRect, 570 textIconGap + 2); 571 572 int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); 573 int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); 574 iconRect.x += xNudge; 575 iconRect.y += yNudge; 576 textRect.x += xNudge; 577 textRect.y += yNudge; 578 } 579 580 @Override protected void paintTabBackground(Graphics g, int tabPlacement, int tabIndex, int x, int y, int w, int h, boolean isSelected) { 581 if (!isSelected) { 582 g.setColor(unselected); 583 } else { 584 g.setColor(selected); 585 } 586 587 g.fillRect(x, y, w, h); 588 g.setColor(selected); 589 g.drawLine(x, y, x, y+h); 590 } 591 } 592 593 class CloseableMetalTabbedPaneUI extends MetalTabbedPaneUI { 594 595 private int horizontalTextPosition = SwingUtilities.LEFT; 596 597 public CloseableMetalTabbedPaneUI() { } 598 599 public CloseableMetalTabbedPaneUI(int horizontalTextPosition) { 600 this.horizontalTextPosition = horizontalTextPosition; 601 } 602 603 @Override protected void layoutLabel(int tabPlacement, 604 FontMetrics metrics, int tabIndex, String title, Icon icon, 605 Rectangle tabRect, Rectangle iconRect, Rectangle textRect, 606 boolean isSelected) 607 { 608 if (tabPane.getTabCount() == 0) 609 return; 610 611 textRect.x = 0; 612 textRect.y = 0; 613 iconRect.x = 0; 614 iconRect.y = 0; 615 616 javax.swing.text.View v = getTextViewForTab(tabIndex); 617 if (v != null) 618 tabPane.putClientProperty("html", v); 619 620 SwingUtilities.layoutCompoundLabel((JComponent)tabPane, 621 metrics, title, icon, 622 SwingUtilities.CENTER, 623 SwingUtilities.CENTER, 624 SwingUtilities.CENTER, 625 horizontalTextPosition, 626 tabRect, 627 iconRect, 628 textRect, 629 textIconGap + 2); 630 631 int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected); 632 int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected); 633 iconRect.x += xNudge; 634 iconRect.y += yNudge; 635 textRect.x += xNudge; 636 textRect.y += yNudge; 637 } 638 } 639 640 public static class TabButton implements Icon { 641 public enum ButtonState { DEFAULT, PRESSED, DISABLED, ROLLOVER }; 642 private static final Map<ButtonState, String> iconPaths = new HashMap<ButtonState, String>(); 643 644 private ButtonState currentState = ButtonState.DEFAULT; 645 private int iconWidth = 0; 646 private int iconHeight = 0; 647 648 private int posX = 0; 649 private int posY = 0; 650 651 public TabButton() { 652 setStateIcon(ButtonState.DEFAULT, "/edu/wisc/ssec/mcidasv/resources/icons/closetab/metal_close_enabled.png"); 653 setStateIcon(ButtonState.PRESSED, "/edu/wisc/ssec/mcidasv/resources/icons/closetab/metal_close_pressed.png"); 654 setStateIcon(ButtonState.ROLLOVER, "/edu/wisc/ssec/mcidasv/resources/icons/closetab/metal_close_rollover.png"); 655 setState(ButtonState.DEFAULT); 656 } 657 658 public static Icon getStateIcon(final ButtonState state) { 659 String path = iconPaths.get(state); 660 if (path == null) 661 path = iconPaths.get(ButtonState.DEFAULT); 662 return GuiUtils.getImageIcon(path); 663 } 664 665 public static void setStateIcon(final ButtonState state, final String path) { 666 iconPaths.put(state, path); 667 } 668 669 public static String getStateIconPath(final ButtonState state) { 670 if (!iconPaths.containsKey(state)) 671 return iconPaths.get(ButtonState.DEFAULT); 672 return iconPaths.get(state); 673 } 674 675 public void setState(final ButtonState state) { 676 currentState = state; 677 Icon currentIcon = getStateIcon(state); 678 if (currentIcon == null) 679 return; 680 681 iconWidth = currentIcon.getIconWidth(); 682 iconHeight = currentIcon.getIconHeight(); 683 } 684 685 public ButtonState getState() { 686 return currentState; 687 } 688 689 public Icon getIcon() { 690 return getStateIcon(currentState); 691 } 692 693 public void paintIcon(Component c, Graphics g, int x, int y) { 694 Icon current = getIcon(); 695 if (current == null) 696 return; 697 698 posX = x; 699 posY = y; 700 current.paintIcon(c, g, x, y); 701 } 702 703 public int getIconWidth() { 704 return iconWidth; 705 } 706 707 public int getIconHeight() { 708 return iconHeight; 709 } 710 711 public Rectangle getBounds() { 712 return new Rectangle(posX, posY, iconWidth, iconHeight); 713 } 714 } 715 }