001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.gui.lut; 020 021import icy.action.IcyAbstractAction; 022import icy.gui.component.BorderedPanel; 023import icy.image.colormap.IcyColorMap; 024import icy.image.colormap.IcyColorMap.IcyColorMapType; 025import icy.image.colormap.IcyColorMapComponent; 026import icy.image.colormap.IcyColorMapComponent.ControlPoint; 027import icy.image.lut.LUT.LUTChannel; 028import icy.image.lut.LUT.LUTChannelEvent; 029import icy.image.lut.LUT.LUTChannelEvent.LUTChannelEventType; 030import icy.image.lut.LUT.LUTChannelListener; 031import icy.resource.icon.IcyIcon; 032import icy.util.ColorUtil; 033import icy.util.EventUtil; 034 035import java.awt.BasicStroke; 036import java.awt.Color; 037import java.awt.Cursor; 038import java.awt.Dimension; 039import java.awt.Graphics; 040import java.awt.Graphics2D; 041import java.awt.Point; 042import java.awt.RenderingHints; 043import java.awt.event.ActionEvent; 044import java.awt.event.ActionListener; 045import java.awt.event.KeyEvent; 046import java.awt.event.MouseEvent; 047import java.awt.event.MouseListener; 048import java.awt.event.MouseMotionListener; 049import java.awt.geom.GeneralPath; 050import java.awt.geom.Path2D; 051import java.awt.geom.Point2D; 052import java.util.ArrayList; 053import java.util.EventListener; 054import java.util.List; 055 056import javax.swing.ActionMap; 057import javax.swing.BorderFactory; 058import javax.swing.InputMap; 059import javax.swing.JComponent; 060import javax.swing.JMenuItem; 061import javax.swing.JPopupMenu; 062import javax.swing.KeyStroke; 063import javax.swing.event.EventListenerList; 064 065/** 066 * @author stephane 067 */ 068public class ColormapViewer extends BorderedPanel implements MouseListener, MouseMotionListener, LUTChannelListener 069{ 070 private enum ActionType 071 { 072 NULL, MODIFY_CONTROLPOINT 073 } 074 075 public interface ColormapPositionListener extends EventListener 076 { 077 public void positionChanged(int index, int value); 078 } 079 080 /** 081 * 082 */ 083 private static final long serialVersionUID = -8338817004756013113L; 084 085 private static final int POINT_SIZE = 10; 086 private static final int LINE_SIZE = 3; 087 private static final int BORDER_WIDTH = 4; 088 private static final int BORDER_HEIGHT = 4; 089 090 private static final int MIN_INDEX = 0; 091 private static final int MAX_INDEX = IcyColorMap.MAX_INDEX; 092 private static final int MIN_VALUE = 0; 093 private static final int MAX_VALUE = IcyColorMap.MAX_LEVEL; 094 095 /** 096 * associated {@link LUTChannel} 097 */ 098 private final LUTChannel lutChannel; 099 100 /** 101 * alpha enabled 102 */ 103 private boolean alphaEnabled; 104 105 /** 106 * gui 107 */ 108 private final JPopupMenu menu; 109 110 /** 111 * listeners 112 */ 113 private final EventListenerList colorMapPositionListeners; 114 /** 115 * cached 116 */ 117 final IcyColorMap colormap; 118 /** 119 * internals 120 */ 121 private float pixToIndexRatio; 122 private float indexToPixRatio; 123 private float pixToValueRatio; 124 private float valueToPixRatio; 125 private ActionType action; 126 private IcyColorMapComponent currentComponent; 127 ControlPoint currentControlPoint; 128 129 public ColormapViewer(LUTChannel lutChannel) 130 { 131 super(); 132 133 // dimension (don't change or you will regret !) 134 setMinimumSize(new Dimension(100, 100)); 135 setPreferredSize(new Dimension(240, 100)); 136 setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE)); 137 // faster draw 138 setOpaque(true); 139 // set border 140 setBorder(BorderFactory.createEmptyBorder(BORDER_HEIGHT, BORDER_WIDTH, BORDER_HEIGHT, BORDER_WIDTH)); 141 // for the input map 142 setFocusable(true); 143 144 this.lutChannel = lutChannel; 145 colormap = lutChannel.getColorMap(); 146 147 colorMapPositionListeners = new EventListenerList(); 148 149 // gui 150 menu = new JPopupMenu(); 151 152 alphaEnabled = true; 153 154 action = ActionType.NULL; 155 currentComponent = null; 156 currentControlPoint = null; 157 158 // calculate ratios 159 updateRatios(); 160 161 // we can't get key events without focus and having focus here is a problem 162 // as we can have externalized windows... 163 // addKeyListener(this); 164 165 // add listeners 166 addMouseListener(this); 167 addMouseMotionListener(this); 168 169 buildActionMap(); 170 } 171 172 @Override 173 public void addNotify() 174 { 175 super.addNotify(); 176 177 // add listeners 178 lutChannel.addListener(this); 179 } 180 181 @Override 182 public void removeNotify() 183 { 184 super.removeNotify(); 185 186 // remove listeners 187 lutChannel.removeListener(this); 188 } 189 190 void buildActionMap() 191 { 192 final InputMap imap = getInputMap(JComponent.WHEN_FOCUSED); 193 final ActionMap amap = getActionMap(); 194 195 imap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete"); 196 197 amap.put("delete", new IcyAbstractAction("delete", (IcyIcon) null, "Delete control point", KeyEvent.VK_DELETE, 198 0) 199 { 200 /** 201 * 202 */ 203 private static final long serialVersionUID = 2375566345981567387L; 204 205 @Override 206 protected boolean doAction(ActionEvent e) 207 { 208 // remove current control point 209 if (currentControlPoint != null) 210 { 211 currentControlPoint.remove(); 212 return true; 213 } 214 215 return false; 216 } 217 }); 218 } 219 220 /** 221 * @return the colormap 222 */ 223 public IcyColorMap getColormap() 224 { 225 return colormap; 226 } 227 228 /** 229 * Translate index to pixel 230 * 231 * @param index 232 */ 233 public int indexToPix(int index) 234 { 235 final int clientX = getClientX(); 236 final int pix = (int) (index * indexToPixRatio) + clientX; 237 return Math.max(Math.min(pix, getClientWidth() + clientX), clientX); 238 } 239 240 /** 241 * Translate pixel to index 242 * 243 * @param pixel 244 */ 245 public int pixToIndex(int pixel) 246 { 247 final int ind = (int) ((pixel - getClientX()) * pixToIndexRatio); 248 return Math.max(Math.min(ind, MAX_INDEX), MIN_INDEX); 249 } 250 251 /** 252 * Translate value to pixel 253 * 254 * @param value 255 * @return pixel for specified value 256 */ 257 public int valueToPix(int value) 258 { 259 final int clientY = getClientY(); 260 final int hl = (getClientHeight() - 1) + clientY; 261 final int pix = hl - (int) (value * valueToPixRatio); 262 return Math.max(Math.min(pix, hl), clientY); 263 } 264 265 /** 266 * Translate pixel to value 267 * 268 * @param pixel 269 * @return value for specified pixel 270 */ 271 public int pixToValue(int pixel) 272 { 273 final int hl = (getClientHeight() - 1) + getClientY(); 274 final int value = (int) ((hl - pixel) * pixToValueRatio); 275 return Math.max(Math.min(value, MAX_VALUE), MIN_VALUE); 276 } 277 278 @Override 279 protected void paintComponent(Graphics g) 280 { 281 super.paintComponent(g); 282 283 // we do it here as componentResized event occurs after paint (and it is not time consuming) 284 updateRatios(); 285 286 final Graphics2D g2 = (Graphics2D) g.create(); 287 final int w = getWidth(); 288 final int h = getHeight(); 289 290 // draw colored background mesh 291 for (int i = 0; i < w; i++) 292 { 293 // get current color from pixel position 294 final Color curColor = getColorFromPixel(i); 295 final Color grayMixed = ColorUtil.mixOver(Color.gray, curColor); 296 final Color whiteMixed = ColorUtil.mixOver(Color.white, curColor); 297 298 for (int j = 0; j < h; j += 16) 299 { 300 // set graphics color 301 if (((i ^ j) & 16) != 0) 302 g2.setColor(grayMixed); 303 else 304 g2.setColor(whiteMixed); 305 306 g2.drawLine(i, j, i, j + 15); 307 } 308 } 309 310 if (alphaEnabled) 311 drawColormapBand(g2, colormap.alpha); 312 313 switch (colormap.getType()) 314 { 315 case RGB: 316 drawColormapBand(g2, colormap.blue); 317 drawColormapBand(g2, colormap.green); 318 drawColormapBand(g2, colormap.red); 319 break; 320 321 case GRAY: 322 drawColormapBand(g2, colormap.gray); 323 break; 324 } 325 326 g2.setColor(Color.black); 327 g2.drawRect(0, 0, w - 1, h - 1); 328 329 g2.dispose(); 330 } 331 332 private void drawColormapBand(Graphics2D g, IcyColorMapComponent band) 333 { 334 drawColormap(g, band); 335 drawControlPoints(g, band); 336 } 337 338 private void drawColormap(Graphics2D g, IcyColorMapComponent cmc) 339 { 340 final Graphics2D g2 = (Graphics2D) g.create(); 341 342 // enable anti alias for better rendering 343 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 344 345 GeneralPath polyline = null; 346 347 // the LUT is defined directly, without control points 348 if (cmc.isRawData()) 349 { 350 final int x = getClientX(); 351 final int w = getClientWidth(); 352 353 polyline = new GeneralPath(Path2D.WIND_EVEN_ODD, w); 354 355 int intensity = valueToPix(cmc.map[pixToIndex(0)]); 356 polyline.moveTo(x, intensity); 357 358 for (int i = x; i < (w + x); i++) 359 { 360 intensity = valueToPix(cmc.map[pixToIndex(i)]); 361 polyline.lineTo(i, intensity); 362 } 363 } 364 else 365 // the LUT is defined through control points, use them. 366 { 367 polyline = new GeneralPath(Path2D.WIND_EVEN_ODD, cmc.getControlPointCount()); 368 369 ArrayList<ControlPoint> controlPoints = cmc.getControlPoints(); 370 int x = getPixelPosX(controlPoints.get(0)); 371 int y = getPixelPosY(controlPoints.get(0)); 372 polyline.moveTo(x, y); 373 374 for (int i = 1; i < cmc.getControlPointCount(); i++) 375 { 376 x = getPixelPosX(controlPoints.get(i)); 377 y = getPixelPosY(controlPoints.get(i)); 378 polyline.lineTo(x, y); 379 } 380 } 381 382 if (isFocused(cmc)) 383 g2.setColor(Color.lightGray); 384 else 385 g2.setColor(Color.black); 386 g2.setStroke(new BasicStroke(LINE_SIZE + 1)); 387 g2.draw(polyline); 388 389 g2.setColor(getColor(cmc)); 390 g2.setStroke(new BasicStroke(LINE_SIZE)); 391 g2.draw(polyline); 392 393 g2.dispose(); 394 } 395 396 private void drawControlPoints(Graphics2D g, IcyColorMapComponent cmc) 397 { 398 final Graphics2D g2 = (Graphics2D) g.create(); 399 400 // enable anti alias for better rendering 401 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 402 403 final int offset_oval = POINT_SIZE / 2; 404 405 // define color 406 final Color color = getColor(cmc); 407 final List<ControlPoint> controlPoints; 408 409 synchronized (cmc.getControlPoints()) 410 { 411 controlPoints = new ArrayList<IcyColorMapComponent.ControlPoint>(cmc.getControlPoints()); 412 } 413 414 for (ControlPoint controlPoint : controlPoints) 415 { 416 final int x = getPixelPosX(controlPoint); 417 final int y = getPixelPosY(controlPoint); 418 419 if (controlPoint.isFixed()) 420 { 421 // draw square control point 422 g2.setColor(color); 423 g2.fillRect(x - (offset_oval - 1), y - (offset_oval - 1), POINT_SIZE - 2, POINT_SIZE - 2); 424 g2.setColor(Color.darkGray); 425 g2.drawRect(x - (offset_oval - 1), y - (offset_oval - 1), POINT_SIZE - 2, POINT_SIZE - 2); 426 if (isFocused(controlPoint)) 427 g2.setColor(Color.white); 428 else 429 g2.setColor(Color.black); 430 g2.drawRect(x - (offset_oval - 0), y - (offset_oval - 0), POINT_SIZE - 0, POINT_SIZE - 0); 431 } 432 else 433 { 434 // draw round control point 435 g2.setColor(color); 436 g2.fillOval(x - (offset_oval - 1), y - (offset_oval - 1), POINT_SIZE - 2, POINT_SIZE - 2); 437 g2.setColor(Color.darkGray); 438 g2.drawOval(x - (offset_oval - 1), y - (offset_oval - 1), POINT_SIZE - 2, POINT_SIZE - 2); 439 if (isFocused(controlPoint)) 440 g2.setColor(Color.white); 441 else 442 g2.setColor(Color.black); 443 g2.drawOval(x - (offset_oval - 0), y - (offset_oval - 0), POINT_SIZE - 0, POINT_SIZE - 0); 444 } 445 } 446 447 g2.dispose(); 448 } 449 450 public boolean isAlphaEnabled() 451 { 452 return alphaEnabled; 453 } 454 455 public void setAlphaEnabled(boolean value) 456 { 457 if (alphaEnabled != value) 458 { 459 alphaEnabled = value; 460 461 if (!value) 462 colormap.alpha.removeAllControlPoint(); 463 464 repaint(); 465 } 466 } 467 468 /** 469 * set current controller or control point 470 */ 471 public void setCurrentElements(IcyColorMapComponent cmc, ControlPoint cp) 472 { 473 if (currentControlPoint != cp) 474 { 475 currentControlPoint = cp; 476 repaint(); 477 } 478 479 if (currentComponent != cmc) 480 { 481 currentComponent = cmc; 482 repaint(); 483 } 484 485 final int cursor; 486 487 if ((cmc != null) || (cp != null)) 488 cursor = Cursor.HAND_CURSOR; 489 else 490 cursor = Cursor.DEFAULT_CURSOR; 491 492 // set cursor only only if different 493 if (getCursor().getType() != cursor) 494 setCursor(new Cursor(cursor)); 495 } 496 497 private boolean isFocused(IcyColorMapComponent cmc) 498 { 499 return (cmc != null) && (currentComponent == cmc); 500 } 501 502 private boolean isFocused(ControlPoint cp) 503 { 504 return (cp != null) && (currentControlPoint == cp); 505 } 506 507 /** 508 * return the final color for specified index 509 */ 510 public Color getColor(int index) 511 { 512 return colormap.getColor(index); 513 } 514 515 /** 516 * get color of specified band 517 */ 518 public Color getColor(IcyColorMapComponent cmc) 519 { 520 if (cmc == colormap.red) 521 return Color.red; 522 if (cmc == colormap.green) 523 return Color.green; 524 if (cmc == colormap.blue) 525 return Color.blue; 526 if (cmc == colormap.gray) 527 return Color.gray; 528 if (cmc == colormap.alpha) 529 return Color.white; 530 531 return Color.black; 532 } 533 534 /** 535 * return the final color for specified pixel position 536 */ 537 public Color getColorFromPixel(int pixel) 538 { 539 return getColor(pixToIndex(pixel)); 540 } 541 542 /** 543 * update ratios for data <--> pix conversion 544 */ 545 private void updateRatios() 546 { 547 final int w = getClientWidth(); 548 final int h = getClientHeight(); 549 550 if (w <= 0) 551 { 552 indexToPixRatio = 0f; 553 pixToIndexRatio = 0f; 554 } 555 else 556 { 557 indexToPixRatio = (float) (w - 1) / (float) (IcyColorMap.SIZE - 1); 558 if (indexToPixRatio != 0f) 559 pixToIndexRatio = 1f / indexToPixRatio; 560 else 561 pixToIndexRatio = 0f; 562 } 563 564 if (h <= 0) 565 { 566 valueToPixRatio = 0f; 567 pixToValueRatio = 0f; 568 } 569 else 570 { 571 valueToPixRatio = (float) (h - 1) / (float) (IcyColorMap.MAX_LEVEL); 572 if (valueToPixRatio != 0f) 573 pixToValueRatio = 1f / valueToPixRatio; 574 else 575 pixToValueRatio = 0f; 576 } 577 } 578 579 // /** 580 // * Check if point is over any control point 581 // * 582 // * @param pos 583 // * point 584 // * @return boolean 585 // */ 586 // private boolean isOverControlPoint(Point pos) 587 // { 588 // boolean result = false; 589 // final IcyColorMapType type = colormap.getType(); 590 // 591 // // check only if alpha enabled 592 // if (alphaEnabled) 593 // result = result || isOverControlPoint(colormap.alpha, pos); 594 // 595 // // test according to display order (ARGB) 596 // if (type == IcyColorMapType.RGB) 597 // result = result || isOverControlPoint(colormap.red, pos) || 598 // isOverControlPoint(colormap.green, pos) 599 // || isOverControlPoint(colormap.blue, pos); 600 // if (type == IcyColorMapType.GRAY) 601 // result = result || isOverControlPoint(colormap.gray, pos); 602 // 603 // return result; 604 // } 605 606 // /** 607 // * Check if point is over any control point from this band 608 // * 609 // * @param pos 610 // * point 611 // * @return boolean 612 // */ 613 // private boolean isOverControlPoint(IcyColorMapComponent cmc, Point pos) 614 // { 615 // for (ControlPoint cp : cmc.getControlPoints()) 616 // if (isOverlapped(cp, pos)) 617 // return true; 618 // 619 // return false; 620 // } 621 622 /** 623 * Check if point is over any point in colormap 624 * 625 * @param pos 626 * point 627 * @return boolean 628 */ 629 public boolean isOverlapped(IcyColorMapComponent cmc, Point pos) 630 { 631 final int index_min = Math.max(0, pixToIndex(pos.x - LINE_SIZE)); 632 final int index_max = Math.min(IcyColorMap.MAX_INDEX, pixToIndex(pos.x + LINE_SIZE)); 633 634 for (int ind = index_min; ind < index_max; ind++) 635 if (Point2D.distance(pos.x, pos.y, indexToPix(ind), valueToPix(cmc.map[ind])) <= (LINE_SIZE + 1)) 636 return true; 637 638 return false; 639 } 640 641 /** 642 * Return true if pixel (x, y) is over the control point 643 * 644 * @param p 645 * point 646 * @return boolean 647 */ 648 public boolean isOverlapped(ControlPoint cp, Point p) 649 { 650 return getDistance(cp, p) <= (POINT_SIZE / 2); 651 } 652 653 /** 654 * Return distance between control point and the specified point 655 * 656 * @param p 657 * point 658 * @return boolean 659 */ 660 public double getDistance(ControlPoint cp, Point p) 661 { 662 return Point2D.distance(p.x, p.y, indexToPix(cp.getIndex()), valueToPix(cp.getValue())); 663 } 664 665 /** 666 * Set position from a pixel position 667 * 668 * @param x 669 * @param y 670 */ 671 public void setPixelPosition(ControlPoint cp, int x, int y) 672 { 673 cp.setPosition(pixToIndex(x), pixToValue(y)); 674 } 675 676 /** 677 * Get X pixel position 678 * 679 * @return X pixel position 680 */ 681 public int getPixelPosX(ControlPoint cp) 682 { 683 return indexToPix(cp.getIndex()); 684 } 685 686 /** 687 * Get Y pixel position 688 * 689 * @return Y pixel position 690 */ 691 public int getPixelPosY(ControlPoint cp) 692 { 693 return valueToPix(cp.getValue()); 694 } 695 696 /** 697 * Find the overlapped colormap band by specified point 698 * 699 * @param pos 700 * point 701 * @return ColormapController 702 */ 703 private IcyColorMapComponent getOverlappedColormapController(Point pos) 704 { 705 final IcyColorMapType type = colormap.getType(); 706 707 // test according to display order (ARGB) 708 if (type == IcyColorMapType.RGB) 709 { 710 if (isOverlapped(colormap.red, pos)) 711 return colormap.red; 712 if (isOverlapped(colormap.green, pos)) 713 return colormap.green; 714 if (isOverlapped(colormap.blue, pos)) 715 return colormap.blue; 716 } 717 if (type == IcyColorMapType.GRAY) 718 if (isOverlapped(colormap.gray, pos)) 719 return colormap.gray; 720 721 // check only if alpha enabled 722 if (alphaEnabled) 723 if (isOverlapped(colormap.alpha, pos)) 724 return colormap.alpha; 725 726 return null; 727 } 728 729 /** 730 * Find the closest overlapped control point by specified point 731 * 732 * @param pos 733 * point 734 * @return ControlPoint 735 */ 736 private ControlPoint getClosestOverlappedControlPoint(Point pos) 737 { 738 ControlPoint point; 739 final IcyColorMapType type = colormap.getType(); 740 741 // test according to display order (RGBA) 742 if (type == IcyColorMapType.RGB) 743 { 744 point = getClosestOverlappedControlPoint(colormap.red, pos); 745 if (point != null) 746 return point; 747 point = getClosestOverlappedControlPoint(colormap.green, pos); 748 if (point != null) 749 return point; 750 point = getClosestOverlappedControlPoint(colormap.blue, pos); 751 if (point != null) 752 return point; 753 } 754 if (type == IcyColorMapType.GRAY) 755 { 756 point = getClosestOverlappedControlPoint(colormap.gray, pos); 757 if (point != null) 758 return point; 759 } 760 761 // check only if alpha enabled 762 if (alphaEnabled) 763 { 764 point = getClosestOverlappedControlPoint(colormap.alpha, pos); 765 if (point != null) 766 return point; 767 } 768 769 return null; 770 } 771 772 /** 773 * Find the closest overlapped control point by specified point 774 * 775 * @param pos 776 * point 777 * @return ControlPoint 778 */ 779 private ControlPoint getClosestOverlappedControlPoint(IcyColorMapComponent cmc, Point pos) 780 { 781 final List<ControlPoint> overlapped = new ArrayList<ControlPoint>(); 782 783 // add all overlapped control points to the list 784 for (ControlPoint point : cmc.getControlPoints()) 785 if (isOverlapped(point, pos)) 786 overlapped.add(point); 787 788 final int size = overlapped.size(); 789 790 // we have at least one overlapped control point ? 791 if (size > 0) 792 { 793 // find the closest from the specified position 794 ControlPoint closestPoint = overlapped.get(0); 795 double minDist = getDistance(closestPoint, pos); 796 797 for (int i = 1; i < size; i++) 798 { 799 final ControlPoint currentPoint = overlapped.get(i); 800 final double curDist = getDistance(currentPoint, pos); 801 802 if (curDist < minDist) 803 { 804 closestPoint = currentPoint; 805 minDist = curDist; 806 } 807 } 808 809 return closestPoint; 810 } 811 812 return null; 813 } 814 815 /** 816 * Set a control point to specified index and value 817 * 818 * @param pos 819 * position 820 */ 821 ControlPoint setControlPoint(IcyColorMapComponent comp, Point pos) 822 { 823 return comp.setControlPoint(pixToIndex(pos.x), pixToValue(pos.y)); 824 } 825 826 /** 827 * show popup menu 828 */ 829 private void showPopupMenu(final Point pos) 830 { 831 // rebuild menu 832 menu.removeAll(); 833 834 // keep a copy of current control point 835 final ControlPoint cp = currentControlPoint; 836 837 if (cp != null) 838 { 839 // fixed control point --> no popup menu 840 if (cp.isFixed()) 841 return; 842 843 final JMenuItem removeItem = new JMenuItem("remove (Shift + Click)"); 844 845 removeItem.addActionListener(new ActionListener() 846 { 847 @Override 848 public void actionPerformed(ActionEvent e) 849 { 850 // remove the control point 851 cp.remove(); 852 } 853 }); 854 855 menu.add(removeItem); 856 } 857 else 858 { 859 final IcyColorMapType type = colormap.getType(); 860 861 if (type == IcyColorMapType.GRAY) 862 { 863 final JMenuItem addCPItem = new JMenuItem("add Gray point"); 864 865 addCPItem.addActionListener(new ActionListener() 866 { 867 @Override 868 public void actionPerformed(ActionEvent e) 869 { 870 // add gray control point 871 setControlPoint(colormap.gray, pos); 872 } 873 }); 874 875 menu.add(addCPItem); 876 } 877 if (type == IcyColorMapType.RGB) 878 { 879 final JMenuItem addCRPItem = new JMenuItem("add Red point"); 880 final JMenuItem addCGPItem = new JMenuItem("add Green point"); 881 final JMenuItem addCBPItem = new JMenuItem("add Blue point"); 882 883 addCRPItem.addActionListener(new ActionListener() 884 { 885 @Override 886 public void actionPerformed(ActionEvent e) 887 { 888 // add red control point 889 setControlPoint(colormap.red, pos); 890 } 891 }); 892 addCGPItem.addActionListener(new ActionListener() 893 { 894 @Override 895 public void actionPerformed(ActionEvent e) 896 { 897 // add green control point 898 setControlPoint(colormap.green, pos); 899 } 900 }); 901 addCBPItem.addActionListener(new ActionListener() 902 { 903 @Override 904 public void actionPerformed(ActionEvent e) 905 { 906 // add blue control point 907 setControlPoint(colormap.blue, pos); 908 } 909 }); 910 911 menu.add(addCRPItem); 912 menu.add(addCGPItem); 913 menu.add(addCBPItem); 914 } 915 916 if (alphaEnabled) 917 { 918 final JMenuItem addAlphaCPItem = new JMenuItem("add Alpha point"); 919 920 addAlphaCPItem.addActionListener(new ActionListener() 921 { 922 @Override 923 public void actionPerformed(ActionEvent e) 924 { 925 // add alpha control point 926 setControlPoint(colormap.alpha, pos); 927 } 928 }); 929 930 menu.add(addAlphaCPItem); 931 } 932 } 933 934 menu.pack(); 935 menu.validate(); 936 937 // display menu 938 menu.show(this, pos.x, pos.y); 939 } 940 941 /** 942 * update current controller and control point from mouse position 943 */ 944 private void updateCurrentElements(Point pos) 945 { 946 final IcyColorMapComponent cmc; 947 // by default we search for an overlapped control point 948 final ControlPoint cp = getClosestOverlappedControlPoint(pos); 949 950 // if no overlapped control point we search for overlapped controller 951 if (cp == null) 952 cmc = getOverlappedColormapController(pos); 953 else 954 cmc = null; 955 956 // define current elements 957 setCurrentElements(cmc, cp); 958 } 959 960 /** 961 * update colormap position info 962 */ 963 private void updateColormapPositionInfo(Point pos) 964 { 965 final ControlPoint cp; 966 final int index; 967 final int value; 968 969 if (action != ActionType.NULL) 970 cp = currentControlPoint; 971 else 972 cp = getClosestOverlappedControlPoint(pos); 973 974 if (cp != null) 975 { 976 index = cp.getIndex(); 977 value = cp.getValue(); 978 } 979 else 980 { 981 index = pixToIndex(pos.x); 982 value = pixToValue(pos.y); 983 } 984 985 // setToolTipText("<html>" + "index : " + index + "<br>" + "value : " + value); 986 987 colormapPositionChanged(index, value); 988 } 989 990 /** 991 * process on colormap change 992 */ 993 public void onColormapChanged() 994 { 995 // repaint the colormap 996 repaint(); 997 } 998 999 /** 1000 * Add a listener 1001 * 1002 * @param listener 1003 */ 1004 public void addColormapPositionListener(ColormapPositionListener listener) 1005 { 1006 colorMapPositionListeners.add(ColormapPositionListener.class, listener); 1007 } 1008 1009 /** 1010 * Remove a listener 1011 * 1012 * @param listener 1013 */ 1014 public void removeColormapPositionListener(ColormapPositionListener listener) 1015 { 1016 colorMapPositionListeners.remove(ColormapPositionListener.class, listener); 1017 } 1018 1019 /** 1020 * mouse position on colormap info changed 1021 */ 1022 public void colormapPositionChanged(int index, int value) 1023 { 1024 for (ColormapPositionListener listener : colorMapPositionListeners.getListeners(ColormapPositionListener.class)) 1025 listener.positionChanged(index, value); 1026 } 1027 1028 @Override 1029 public void lutChannelChanged(LUTChannelEvent e) 1030 { 1031 if (e.getType() == LUTChannelEventType.COLORMAP_CHANGED) 1032 onColormapChanged(); 1033 } 1034 1035 @Override 1036 public void mouseClicked(MouseEvent e) 1037 { 1038 // nothing to do here 1039 } 1040 1041 @Override 1042 public void mouseEntered(MouseEvent e) 1043 { 1044 // // get the focus while mouse is on the component 1045 // setFocusable(true); 1046 // requestFocus(); 1047 // 1048 // repaint(); 1049 1050 // KeyboardFocusManager.getCurrentKeyboardFocusManager().downFocusCycle(this); 1051 } 1052 1053 @Override 1054 public void mouseExited(MouseEvent e) 1055 { 1056 // clear position info 1057 colormapPositionChanged(-1, -1); 1058 // unfocus if no action 1059 if (action == ActionType.NULL) 1060 setCurrentElements(null, null); 1061 1062 // KeyboardFocusManager.getCurrentKeyboardFocusManager().focusPreviousComponent(this); 1063 1064 // // remove focus 1065 // setFocusable(false); 1066 // 1067 // // set focus back to last active viewer 1068 // final Viewer viewer = Icy.getMainInterface().getActiveViewer(); 1069 // if (viewer != null) 1070 // viewer.requestFocus(); 1071 // 1072 // repaint(); 1073 } 1074 1075 @Override 1076 public void mousePressed(MouseEvent e) 1077 { 1078 final Point pos = e.getPoint(); 1079 1080 if (EventUtil.isLeftMouseButton(e)) 1081 { 1082 // we have a selected control point ? 1083 if (currentControlPoint != null) 1084 { 1085 // Shift pressed --> remove control point 1086 if (EventUtil.isShiftDown(e)) 1087 currentControlPoint.remove(); 1088 // else we start modification 1089 else 1090 action = ActionType.MODIFY_CONTROLPOINT; 1091 } 1092 // we have a selected controller ? 1093 else if (currentComponent != null) 1094 { 1095 action = ActionType.MODIFY_CONTROLPOINT; 1096 // add a new control point to the controller which become the active control point 1097 setCurrentElements(null, setControlPoint(currentComponent, pos)); 1098 } 1099 } 1100 else if (EventUtil.isRightMouseButton(e)) 1101 { 1102 showPopupMenu(pos); 1103 } 1104 } 1105 1106 @Override 1107 public void mouseReleased(MouseEvent e) 1108 { 1109 if (EventUtil.isLeftMouseButton(e)) 1110 { 1111 action = ActionType.NULL; 1112 updateCurrentElements(e.getPoint()); 1113 } 1114 } 1115 1116 @Override 1117 public void mouseDragged(MouseEvent e) 1118 { 1119 final Point pos = e.getPoint(); 1120 1121 switch (action) 1122 { 1123 case MODIFY_CONTROLPOINT: 1124 setPixelPosition(currentControlPoint, pos.x, pos.y); 1125 break; 1126 } 1127 1128 updateColormapPositionInfo(pos); 1129 } 1130 1131 @Override 1132 public void mouseMoved(MouseEvent e) 1133 { 1134 final Point pos = e.getPoint(); 1135 1136 updateCurrentElements(pos); 1137 updateColormapPositionInfo(pos); 1138 } 1139}