1 19 20 package org.openide.actions; 21 22 import java.awt.AWTEvent ; 23 import java.awt.AlphaComposite ; 24 import java.awt.Color ; 25 import java.awt.Composite ; 26 import java.awt.Dimension ; 27 import java.awt.Font ; 28 import java.awt.FontMetrics ; 29 import java.awt.GradientPaint ; 30 import java.awt.Graphics ; 31 import java.awt.Graphics2D ; 32 import java.awt.Image ; 33 import java.awt.event.ActionEvent ; 34 import java.awt.event.ActionListener ; 35 import java.awt.event.MouseEvent ; 36 import java.awt.image.BufferedImage ; 37 import java.awt.image.ConvolveOp ; 38 import java.awt.image.Kernel ; 39 import java.text.MessageFormat ; 40 import javax.swing.JCheckBoxMenuItem ; 41 import javax.swing.JComponent ; 42 import javax.swing.JLabel ; 43 import javax.swing.JPopupMenu ; 44 import javax.swing.SwingUtilities ; 45 import javax.swing.Timer ; 46 import org.openide.util.NbBundle; 47 48 52 class HeapView extends JComponent { 53 private static final boolean AUTOMATIC_REFRESH = System.getProperty("org.netbeans.log.startup") == null; 54 55 58 private static final int STYLE_DEFAULT = 0; 59 60 63 private static final int STYLE_OVERLAY = 1; 64 65 68 private static final int TICK = 1500; 69 70 73 private static final int HEAP_GROW_ANIMATE_TIME = 1000; 74 75 78 private static final int BORDER_W = 2; 79 80 83 private static final int BORDER_H = 4; 84 85 88 private static final Color [] GRID_COLORS = new Color [] { new Color (0xE3DFCF), new Color (0xE7E4D3), 90 new Color (0xDAD7C6), new Color (0xDFDCCB), 91 new Color (0xD3CFBF), new Color (0xD7D3C3), 92 new Color (0xCECABA), new Color (0xD0CCBC) 93 }; 94 95 98 private static final Color BORDER1_COLOR = new Color (0xA6A295); 99 100 103 private static final Color BORDER2_COLOR = new Color (0xC0BCAD); 104 105 108 private static final Color MIN_TICK_COLOR = new Color (0xC7D6AD); 109 110 113 private static final Color MAX_TICK_COLOR = new Color (0x615d0f); 114 115 118 private static final Color TEXT_BLUR_COLOR = Color.WHITE; 119 120 123 private static final Color TEXT_COLOR = Color.WHITE; 124 125 128 private static final Color BACKGROUND1_COLOR = new Color (0xD0CCBC); 129 130 133 private static final Color BACKGROUND2_COLOR = new Color (0xEAE7D7); 134 135 138 private static final int KERNEL_SIZE = 3; 139 140 143 private static final float BLUR_FACTOR = 0.1f; 144 145 148 private static final int SHIFT_X = 0; 149 150 153 private static final int SHIFT_Y = 1; 154 155 158 private final ConvolveOp blur; 159 160 163 private final MessageFormat format; 164 165 168 private boolean showDropShadow; 169 170 173 private int tickStyle; 174 175 178 private boolean showText; 179 180 183 private float[] graph; 184 185 188 private int graphIndex; 189 190 194 private boolean graphFilled; 195 196 199 private long lastTotal; 200 201 204 private Timer updateTimer; 205 206 209 private Image bgImage; 210 211 214 private int cachedWidth; 215 216 219 private int cachedHeight; 220 221 224 private BufferedImage textImage; 225 226 229 private BufferedImage dropShadowImage; 230 231 234 private HeapGrowTimer heapGrowTimer; 235 236 239 private int maxTextWidth; 240 241 244 private String heapSizeText; 245 246 249 private Image tickGradientImage; 250 251 254 private BufferedImage gridOverlayImage; 255 256 257 258 public HeapView() { 259 int kw = KERNEL_SIZE, kh = KERNEL_SIZE; 261 float blurFactor = BLUR_FACTOR; 262 float[] kernelData = new float[kw * kh]; 263 for (int i = 0; i < kernelData.length; i++) { 264 kernelData[i] = blurFactor; 265 } 266 blur = new ConvolveOp (new Kernel (kw, kh, kernelData)); 267 format = new MessageFormat ("{0,number,0.0}/{1,number,0.0}MB"); 268 heapSizeText = ""; 269 showDropShadow = true; 270 showText = true; 271 tickStyle = STYLE_OVERLAY; 272 enableEvents(AWTEvent.MOUSE_EVENT_MASK); 275 setToolTipText(NbBundle.getMessage(GarbageCollectAction.class, "CTL_GC")); 276 updateUI(); 277 } 278 279 283 public boolean isOpaque() { 284 return true; 285 } 286 287 290 public void updateUI() { 291 Font f = new JLabel ().getFont(); 292 f = new Font (f.getName(), Font.BOLD, f.getSize()); 293 setFont(f); 294 revalidate(); 295 repaint(); 296 } 297 298 305 public void setTickStyle(int style) { 306 tickStyle = style; 307 repaint(); 308 } 309 310 316 public int getTickStyle() { 317 return tickStyle; 318 } 319 320 327 public void setShowText(boolean showText) { 328 this.showText = showText; 329 repaint(); 330 } 331 332 337 public boolean getShowText() { 338 return showText; 339 } 340 341 347 public void setShowDropShadow(boolean show) { 348 showDropShadow = show; 349 repaint(); 350 } 351 352 355 public boolean getShowDropShadow() { 356 return showDropShadow; 357 } 358 359 364 public void setFont(Font font) { 365 super.setFont(font); 366 updateTextWidth(); 367 } 368 369 Dimension heapViewPreferredSize() { 370 Dimension size = new Dimension (maxTextWidth + 8, getFontMetrics( 371 getFont()).getHeight() + 8); 372 return size; 373 } 374 375 378 private void updateTextWidth() { 379 String maxString = format.format(new Object [] { 380 new Float (999.9f), new Float (999.9f) }); 381 maxTextWidth = getFontMetrics(getFont()).stringWidth(maxString) + 4; 382 } 383 384 389 protected void processMouseEvent(MouseEvent e) { 390 super.processMouseEvent(e); 391 if (!e.isConsumed()) { 392 if (e.isPopupTrigger()) { 393 showPopup(e.getX(), e.getY()); 395 } else if (e.getID() == e.MOUSE_ENTERED) { 396 containsMouse = true; 397 cachedBorderVaild = false; 398 repaint(); 399 } else if (e.getID() == e.MOUSE_EXITED) { 400 containsMouse = false; 401 cachedBorderVaild = false; 402 repaint(); 403 } 404 405 } 406 407 if (e.getID() == MouseEvent.MOUSE_CLICKED && 408 SwingUtilities.isLeftMouseButton(e) && 409 e.getClickCount() == 1) { 410 ((GarbageCollectAction)GarbageCollectAction.get(GarbageCollectAction.class)).performAction();; 412 } 413 } 414 415 419 private void showPopup(int x, int y) { 420 JPopupMenu popup = new JPopupMenu (); 421 JCheckBoxMenuItem cbmi = new JCheckBoxMenuItem (NbBundle.getMessage(HeapView.class, "LBL_ShowText")); 422 cbmi.setSelected(getShowText()); 423 cbmi.addActionListener(new ActionListener () { 424 public void actionPerformed(ActionEvent e) { 425 setShowText(((JCheckBoxMenuItem )e.getSource()). 426 isSelected()); 427 } 428 }); 429 popup.add(cbmi); 430 cbmi = new JCheckBoxMenuItem (NbBundle.getMessage(HeapView.class, "LBL_DropShadow")); 431 cbmi.setSelected(getShowDropShadow()); 432 cbmi.addActionListener(new ActionListener () { 433 public void actionPerformed(ActionEvent e) { 434 setShowDropShadow(((JCheckBoxMenuItem )e.getSource()). 435 isSelected()); 436 } 437 }); 438 popup.add(cbmi); 439 cbmi = new JCheckBoxMenuItem (NbBundle.getMessage(HeapView.class, "LBL_OverlayGrid")); 440 cbmi.setSelected(getTickStyle() == STYLE_OVERLAY); 441 cbmi.addActionListener(new ActionListener () { 442 public void actionPerformed(ActionEvent e) { 443 int style = ((JCheckBoxMenuItem )e.getSource()). 444 isSelected() ? STYLE_OVERLAY : STYLE_DEFAULT; 445 setTickStyle(style); 446 } 447 }); 448 popup.add(cbmi); 449 popup.show(this, x, y); 450 } 451 452 455 private int getGraphStartIndex() { 456 if (graphFilled) { 457 return graphIndex; 458 } else { 459 return 0; 460 } 461 } 462 463 466 protected void paintComponent(Graphics g) { 467 Graphics2D g2 = (Graphics2D )g; 468 int width = getWidth(); 469 int height = getHeight(); 470 if (width - BORDER_W > 0 && height - BORDER_H > 0) { 471 startTimerIfNecessary(); 472 updateCacheIfNecessary(width, height); 473 paintCachedBackground(g2, width, height); 474 g.translate(1, 2); 475 if (containsMouse) { 476 g.clipRect(1, 0, width - 4, height - 4); 477 } 478 else { 479 g.clipRect(0, 0, width - 2, height - 4); 480 } 481 int innerW = width - BORDER_W; 482 int innerH = height - BORDER_H; 483 if (heapGrowTimer != null) { 484 Composite lastComposite = ((Graphics2D )g).getComposite(); 486 float percent = 1f - heapGrowTimer.getPercent(); 487 ((Graphics2D )g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, percent)); 488 g.drawImage(heapGrowTimer.image, 0, 0, null); 489 ((Graphics2D )g).setComposite(lastComposite); 490 } 491 paintTicks(g2, innerW, innerH); 492 if (getTickStyle() == STYLE_OVERLAY) { 493 g2.drawImage(getGridOverlayImage(), 0, 0, null); 494 } 495 if (getShowText()) { 496 if (getShowDropShadow()) { 497 paintDropShadowText(g, innerW, innerH); 498 } else { 499 g.setColor(Color.WHITE); 500 paintText(g, innerW, innerH); 501 } 502 } 503 g.translate(-1, -2); 504 } else { 505 stopTimerIfNecessary(); 506 g.setColor(getBackground()); 508 g.fillRect(0, 0, width, height); 509 } 510 } 511 512 private void paintTicks(Graphics2D g, int width, int height) { 513 if (graphIndex > 0 || graphFilled) { 514 int index = getGraphStartIndex(); 515 int x = 0; 516 if (!graphFilled) { 517 x = width - graphIndex; 518 } 519 float min = graph[index]; 520 index = (index + 1) % graph.length; 521 while (index != graphIndex) { 522 min = Math.min(min, graph[index]); 523 index = (index + 1) % graph.length; 524 } 525 int minHeight = (int)(min * (float)height); 526 if (minHeight > 0) { 527 g.drawImage(tickGradientImage, x, height - minHeight, width, height, 528 x, height - minHeight, width, height, null); 529 } 530 index = getGraphStartIndex(); 531 do { 532 int tickHeight = (int)(graph[index] * (float)height); 533 if (tickHeight > minHeight) { 534 g.drawImage(tickGradientImage, x, height - tickHeight, x + 1, height - minHeight, 535 x, height - tickHeight, x + 1, height - minHeight, null); 536 } 537 index = (index + 1) % graph.length; 538 x++; 539 } while (index != graphIndex); 540 } 541 } 542 543 546 private void paintText(Graphics g, int w, int h) { 547 g.setFont(getFont()); 548 String text = getHeapSizeText(); 549 FontMetrics fm = g.getFontMetrics(); 550 int textWidth = fm.stringWidth(text); 551 g.drawString(text, (w - maxTextWidth) / 2 + (maxTextWidth - textWidth), 552 h / 2 + fm.getAscent() / 2); 553 } 554 555 558 private void paintDropShadowText(Graphics g, int w, int h) { 559 if (textImage == null) { 560 textImage = new BufferedImage (w, h, BufferedImage.TYPE_INT_ARGB); 561 dropShadowImage = new BufferedImage (w, h, BufferedImage.TYPE_INT_ARGB); 562 } 563 Graphics2D textImageG = textImage.createGraphics(); 565 textImageG.setComposite(AlphaComposite.Clear); 566 textImageG.fillRect(0, 0, w, h); 567 textImageG.setComposite(AlphaComposite.SrcOver); 568 textImageG.setColor(TEXT_BLUR_COLOR); 569 paintText(textImageG, w, h); 570 textImageG.dispose(); 571 572 Graphics2D blurryImageG = dropShadowImage.createGraphics(); 575 blurryImageG.setComposite(AlphaComposite.Clear); 576 blurryImageG.fillRect(0, 0, w, h); 577 blurryImageG.setComposite(AlphaComposite.SrcOver); 578 blurryImageG.drawImage(textImage, blur, SHIFT_X, SHIFT_Y); 579 blurryImageG.setColor(TEXT_COLOR); 580 blurryImageG.setFont(getFont()); 581 582 paintText(blurryImageG, w, h); 584 blurryImageG.dispose(); 585 586 g.drawImage(dropShadowImage, 0, 0, null); 588 } 589 590 private String getHeapSizeText() { 591 return heapSizeText; 592 } 593 594 597 private void paintGridOverlay(Graphics2D g, int w, int h) { 598 int numCells = GRID_COLORS.length / 2; 599 int cellSize = (h - numCells - 1) / numCells; 600 int c1 = 0xD0CCBC; 601 int c2 = 0xEAE7D7; 602 g.setPaint(new GradientPaint ( 603 0, 0, new Color ((c1 >> 16) & 0xFF, (c1 >> 8) & 0xFF, c1 & 0xFF, 0x30), 604 0, h, new Color ((c2 >> 16) & 0xFF, (c2 >> 8) & 0xFF, c2 & 0xFF, 0x40))); 605 for (int x = 0; x < w; x += cellSize + 1) { 606 g.fillRect(x, 0, 1, h); 607 } 608 for (int y = h - cellSize - 1; y >= 0; y -= (cellSize + 1)) { 609 g.fillRect(0, y, w, 1); 610 } 611 } 612 613 private void paintCachedBackground(Graphics2D g, int w, int h) { 614 if (bgImage != null) { 615 g.drawImage(bgImage, 0, 0, null); 616 } 617 } 618 619 private void paintBackgroundTiles(Graphics2D g, int w, int h) { 620 g.translate(1, 2); 621 w -= BORDER_W; 622 h -= BORDER_H; 623 int numCells = GRID_COLORS.length / 2; 624 int cellSize = (h - numCells - 1) / numCells; 625 for (int i = 0; i < numCells; i++) { 626 int colorIndex = i; 627 int y = h - cellSize * (i + 1) - i; 628 int x = 1; 629 g.setPaint(new GradientPaint (0, y, GRID_COLORS[colorIndex * 2], 630 0, y + cellSize - 1, GRID_COLORS[colorIndex * 2 + 1])); 631 while (x < w) { 632 int endX = Math.min(w, x + cellSize); 633 g.fillRect(x, y, endX - x, cellSize); 634 x = endX + 1; 635 } 636 y += cellSize + 1; 637 } 638 g.translate(-1, -2); 639 } 640 641 private void paintBackground(Graphics2D g, int w, int h) { 642 g.setPaint(new GradientPaint (0, 0, BACKGROUND1_COLOR, 643 0, h, BACKGROUND2_COLOR)); 644 g.fillRect(0, 0, w, h); 645 } 646 647 private void paintBorder(Graphics g, int w, int h) { 648 if (containsMouse) { 650 g.setColor(Color.WHITE); 651 g.drawRect(0, 0, w - 1, h - 1); 652 g.drawRect(1, 1, w - 3, h - 3); 653 } 654 else { 655 g.setColor(BORDER1_COLOR); 656 g.drawRect(0, 0, w - 1, h - 2); 657 g.setColor(BORDER2_COLOR); 658 g.fillRect(1, 1, w - 2, 1); 659 g.setColor(Color.WHITE); 660 g.fillRect(0, h - 1, w, 1); 661 } 662 } 663 664 private void updateCacheIfNecessary(int w, int h) { 665 if (cachedWidth != w || cachedHeight != h || !cachedBorderVaild) { 666 cachedWidth = w; 667 cachedHeight = h; 668 cachedBorderVaild = true; 669 updateCache(w, h); 670 } 671 } 672 673 private Image getGridOverlayImage() { 674 if (gridOverlayImage == null) { 675 gridOverlayImage = new BufferedImage ( 676 getInnerWidth(), getInnerHeight(), 677 BufferedImage.TYPE_INT_ARGB); 678 Graphics2D g = gridOverlayImage.createGraphics(); 679 paintGridOverlay(g, getInnerWidth(), getInnerHeight()); 680 g.dispose(); 681 } 682 return gridOverlayImage; 683 } 684 685 688 private void updateCache(int w, int h) { 689 disposeImages(); 690 textImage = new BufferedImage (w, h, BufferedImage.TYPE_INT_ARGB); 691 dropShadowImage = new BufferedImage (w, h, BufferedImage.TYPE_INT_ARGB); 692 bgImage = createImage(w, h); 693 Graphics2D imageG = (Graphics2D )bgImage.getGraphics(); 694 paintBackground(imageG, w, h); 695 paintBackgroundTiles(imageG, w, h); 696 paintBorder(imageG, w, h); 697 imageG.dispose(); 698 w -= BORDER_W; 699 h -= BORDER_H; 700 if (graph == null || graph.length != w) { 701 graph = new float[w]; 702 graphFilled = false; 703 graphIndex = 0; 704 } 705 GradientPaint tickGradient = new GradientPaint (0, h, MIN_TICK_COLOR, 706 w, 0, MAX_TICK_COLOR); 707 tickGradientImage = createImage(w, h); 708 imageG = (Graphics2D )tickGradientImage.getGraphics(); 709 imageG.setPaint(tickGradient); 710 imageG.fillRect(0, 0, w, h); 711 imageG.dispose(); 712 if (gridOverlayImage != null) { 713 gridOverlayImage.flush(); 714 gridOverlayImage = null; 715 } 716 } 717 718 722 public void removeNotify() { 723 super.removeNotify(); 724 stopTimerIfNecessary(); 725 } 726 727 730 private void startTimerIfNecessary() { 731 if (!AUTOMATIC_REFRESH) 732 return; 733 734 if (updateTimer == null) { 735 updateTimer = new Timer (TICK, new ActionHandler()); 736 updateTimer.setRepeats(true); 737 updateTimer.start(); 738 } 739 } 740 741 744 private void stopTimerIfNecessary() { 745 if (updateTimer != null) { 746 graph = null; 747 graphFilled = false; 748 updateTimer.stop(); 749 updateTimer = null; 750 lastTotal = 0; 751 disposeImages(); 752 cachedHeight = cachedHeight = -1; 753 if (heapGrowTimer != null) { 754 heapGrowTimer.stop(); 755 heapGrowTimer = null; 756 } 757 } 758 } 759 760 private void disposeImages() { 761 if (bgImage != null) { 762 bgImage.flush(); 763 bgImage = null; 764 } 765 if (textImage != null) { 766 textImage.flush(); 767 textImage = null; 768 } 769 if (dropShadowImage != null) { 770 dropShadowImage.flush(); 771 dropShadowImage = null; 772 } 773 if (tickGradientImage != null) { 774 tickGradientImage.flush(); 775 tickGradientImage = null; 776 } 777 if (gridOverlayImage != null) { 778 gridOverlayImage.flush(); 779 gridOverlayImage = null; 780 } 781 } 782 783 787 private void update() { 788 if (!isShowing()) { 789 stopTimerIfNecessary(); 793 return; 794 } 795 Runtime r = Runtime.getRuntime(); 796 long total = r.totalMemory(); 797 if (total != lastTotal) { 798 if (lastTotal != 0) { 799 startHeapAnimate(); 801 int index = getGraphStartIndex(); 803 do { 804 graph[index] = (float)(((double)graph[index] * 805 (double)lastTotal) / (double)total); 806 index = (index + 1) % graph.length; 807 } while (index != graphIndex); 808 } 809 lastTotal = total; 810 } 811 if (heapGrowTimer == null) { 812 long used = total - r.freeMemory(); 814 graph[graphIndex] = (float)((double)used / (double)total); 815 graphIndex = (graphIndex + 1) % graph.length; 816 if (graphIndex == 0) { 817 graphFilled = true; 818 } 819 heapSizeText = format.format( 820 new Object [] { new Double ((double)used / 1024 / 1024), 821 new Double ((double)total / 1024 / 1024) }); 822 } 823 repaint(); 824 } 825 826 private void startHeapAnimate() { 827 if (heapGrowTimer == null) { 828 heapGrowTimer = new HeapGrowTimer(); 829 heapGrowTimer.start(); 830 } 831 } 832 833 private void stopHeapAnimate() { 834 if (heapGrowTimer != null) { 835 heapGrowTimer.stop(); 836 heapGrowTimer = null; 837 } 838 } 839 840 private int getInnerWidth() { 841 return getWidth() - BORDER_W; 842 } 843 844 private int getInnerHeight() { 845 return getHeight() - BORDER_H; 846 } 847 848 849 private final class ActionHandler implements ActionListener { 850 public void actionPerformed(ActionEvent e) { 851 update(); 852 } 853 } 854 855 856 private final class HeapGrowTimer extends Timer { 857 private final long startTime; 858 private float percent; 859 BufferedImage image; 860 861 HeapGrowTimer() { 862 super(30, null); 863 setRepeats(true); 864 startTime = System.currentTimeMillis(); 865 percent = 0f; 866 int w = getWidth() - BORDER_W; 867 int h = getHeight() - BORDER_H; 868 image = new BufferedImage (w, h, BufferedImage.TYPE_INT_ARGB); 869 Graphics2D g = image.createGraphics(); 870 paintTicks(g, w, h); 871 g.dispose(); 872 } 873 874 public float getPercent() { 875 return percent; 876 } 877 878 protected void fireActionPerformed(ActionEvent e) { 879 long time = System.currentTimeMillis(); 880 long delta = time - startTime; 881 if (delta > HEAP_GROW_ANIMATE_TIME) { 882 stopHeapAnimate(); 883 } else { 884 percent = (float)delta / (float)HEAP_GROW_ANIMATE_TIME; 885 repaint(); 886 } 887 } 888 } 889 private boolean containsMouse; 890 private boolean cachedBorderVaild; 891 } 892 | Popular Tags |