001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.ui; 030 031import java.awt.BorderLayout; 032import java.awt.Color; 033import java.awt.Component; 034import java.awt.Container; 035import java.awt.Dimension; 036import java.awt.Graphics; 037import java.awt.Insets; 038import java.awt.LayoutManager; 039import java.awt.event.ActionEvent; 040import java.awt.event.ActionListener; 041import java.awt.event.FocusAdapter; 042import java.awt.event.FocusEvent; 043import java.awt.event.KeyAdapter; 044import java.awt.event.KeyEvent; 045import java.awt.event.KeyListener; 046import java.awt.event.MouseAdapter; 047import java.awt.event.MouseEvent; 048import java.awt.event.MouseListener; 049import java.beans.PropertyChangeEvent; 050import java.beans.PropertyChangeListener; 051import java.util.ArrayList; 052import java.util.List; 053 054import javax.accessibility.AccessibleContext; 055import javax.swing.Icon; 056import javax.swing.JColorChooser; 057import javax.swing.JLabel; 058import javax.swing.JPanel; 059import javax.swing.UIManager; 060import javax.swing.colorchooser.AbstractColorChooserPanel; 061 062import org.jdesktop.beans.AbstractBean; 063 064/** 065 * This has been essentially ripped out of the (wonderful) GNU Classpath 066 * project. Initial implementation of persistable recent colors courtesy of 067 * 068 * http://stackoverflow.com/a/11080701 069 * 070 * (though I had to hack things up a bit) 071 */ 072public class PersistableSwatchChooserPanel extends AbstractColorChooserPanel implements PropertyChangeListener { 073 074// private static final Logger logger = LoggerFactory.getLogger(PersistableSwatchChooserPanel.class); 075 076 /** The main panel that holds the set of choosable colors. */ 077 MainSwatchPanel mainPalette; 078 079 /** A panel that holds the recent colors. */ 080 RecentSwatchPanel recentPalette; 081 082 /** The mouse handlers for the panels. */ 083 MouseListener mouseHandler; 084 085 /** Main Palette {@code KeyListener}. */ 086 KeyListener mainSwatchKeyListener; 087 088 /** Recent palette {@code KeyListener}. */ 089 KeyListener recentSwatchKeyListener; 090 091 ColorTracker tracker; 092 093 /** 094 * This the base class for all swatch panels. Swatch panels are panels that 095 * hold a set of blocks where colors are displayed. 096 */ 097 abstract static class SwatchPanel extends JPanel { 098 099 /** Width of each block. */ 100 protected int cellWidth = 10; 101 102 /** Height of each block. */ 103 protected int cellHeight = 10; 104 105 /** Gap between blocks. */ 106 protected int gap = 1; 107 108 /** Number of rows in the swatch panel. */ 109 protected int numRows; 110 111 /** Number of columns in the swatch panel. */ 112 protected int numCols; 113 114 /** Row of the selected color's cell. */ 115 protected int selRow; 116 117 /** Column of the selected color's cell. */ 118 protected int selCol; 119 120 /** 121 * Creates a new SwatchPanel object. 122 */ 123 SwatchPanel() { 124 selRow = 0; 125 selCol = 0; 126 setBackground(Color.WHITE); 127 setFocusable(true); 128 129 addFocusListener(new FocusAdapter() { 130 @Override public void focusGained(FocusEvent e) { 131 repaint(); 132 } 133 134 @Override public void focusLost(FocusEvent e) { 135 repaint(); 136 } 137 }); 138 139 addKeyListener(new KeyAdapter() { 140 @Override public void keyPressed(KeyEvent e) { 141 int code = e.getKeyCode(); 142 switch (code) { 143 case KeyEvent.VK_UP: 144 if (selRow > 0) { 145 selRow--; 146 repaint(); 147 } 148 break; 149 case KeyEvent.VK_DOWN: 150 if (selRow < numRows - 1) { 151 selRow++; 152 repaint(); 153 } 154 break; 155 case KeyEvent.VK_LEFT: 156 if (selCol > 0 && SwatchPanel.this.getComponentOrientation().isLeftToRight()) { 157 selCol--; 158 repaint(); 159 } else if (selCol < numCols -1 && !SwatchPanel.this.getComponentOrientation().isLeftToRight()) { 160 selCol++; 161 repaint(); 162 } 163 break; 164 case KeyEvent.VK_RIGHT: 165 if (selCol < numCols - 1 166 && SwatchPanel.this.getComponentOrientation().isLeftToRight()) { 167 selCol++; 168 repaint(); 169 } else if (selCol > 0 && !SwatchPanel.this.getComponentOrientation().isLeftToRight()) { 170 selCol--; 171 repaint(); 172 } 173 break; 174 case KeyEvent.VK_HOME: 175 selCol = 0; 176 selRow = 0; 177 repaint(); 178 break; 179 case KeyEvent.VK_END: 180 selCol = numCols - 1; 181 selRow = numRows - 1; 182 repaint(); 183 break; 184 } 185 } 186 }); 187 } 188 189 /** 190 * This method returns the preferred size of the swatch panel based on the 191 * number of rows and columns and the size of each cell. 192 * 193 * @return Preferred size of the swatch panel. 194 */ 195 @Override public Dimension getPreferredSize() { 196 int height = (numRows * cellHeight) + ((numRows - 1) * gap); 197 int width = (numCols * cellWidth) + ((numCols - 1) * gap); 198 Insets insets = getInsets(); 199 200 return new Dimension(width + insets.left + insets.right, 201 height + insets.top + insets.bottom); 202 } 203 204 /** 205 * Return the {@literal "selected"} color. 206 * 207 * @return The color at {@code selRow} and {@code selCol}. 208 */ 209 public Color getSelectedColor() { 210 return getColorForCell(selRow, selCol); 211 } 212 213 /** 214 * This method returns the color for the given position. 215 * 216 * @param x X coordinate of the position. 217 * @param y Y coordinate of the position. 218 * 219 * @return The color at the given position. 220 */ 221 public abstract Color getColorForPosition(int x, int y); 222 223 /** 224 * Return the color at a given cell. 225 * 226 * @param row Cell row. 227 * @param column Cell column. 228 * 229 * @return Color of the cell at {@code row} and {@code column}. 230 */ 231 public abstract Color getColorForCell(int row, int column); 232 233 /** 234 * This method initializes the colors for the swatch panel. 235 */ 236 protected abstract void initializeColors(); 237 238 /** 239 * Set the {@literal "selected"} cell using screen location. 240 * 241 * @param x X coordinate of the position. 242 * @param y Y coordinate of the position. 243 */ 244 protected abstract void setSelectedCellFromPosition(int x, int y); 245 } 246 247 /** 248 * This is the main swatch panel. This panel sits in the middle and allows a 249 * set of colors to be picked which will move to the recent swatch panel. 250 */ 251 static class MainSwatchPanel extends SwatchPanel { 252 /** The color describing (204, 255, 255) */ 253 public static final Color C204255255 = new Color(204, 204, 255); 254 255 /** The color describing (255, 204, 204) */ 256 public static final Color C255204204 = new Color(255, 204, 204); 257 258 /** The color describing (204, 255, 204) */ 259 public static final Color C204255204 = new Color(204, 255, 204); 260 261 /** The color describing (204, 204, 204) */ 262 public static final Color C204204204 = new Color(204, 204, 204); 263 264 /** The color (153, 153, 255). */ 265 public static final Color C153153255 = new Color(153, 153, 255); 266 267 /** The color (51, 51, 255). */ 268 public static final Color C051051255 = new Color(51, 51, 255); 269 270 /** The color (153, 0, 153). */ 271 public static final Color C153000153 = new Color(153, 0, 153); 272 273 /** The color (0, 51, 51). */ 274 public static final Color C000051051 = new Color(0, 51, 51); 275 276 /** The color (51, 0, 51). */ 277 public static final Color C051000051 = new Color(51, 0, 51); 278 279 /** The color (51, 51, 0). */ 280 public static final Color C051051000 = new Color(51, 51, 0); 281 282 /** The color (102, 102, 0). */ 283 public static final Color C102102000 = new Color(102, 102, 0); 284 285 /** The color (153, 255, 153). */ 286 public static final Color C153255153 = new Color(153, 255, 153); 287 288 /** The color (102, 255, 102). */ 289 public static final Color C102255102 = new Color(102, 255, 102); 290 291 /** The color (0, 102, 102). */ 292 public static final Color C000102102 = new Color(0, 102, 102); 293 294 /** The color (102, 0, 102). */ 295 public static final Color C102000102 = new Color(102, 0, 102); 296 297 /** The color (0, 153, 153). */ 298 public static final Color C000153153 = new Color(0, 153, 153); 299 300 /** The color (153, 153, 0). */ 301 public static final Color C153153000 = new Color(153, 153, 0); 302 303 /** The color (204, 204, 0). */ 304 public static final Color C204204000 = new Color(204, 204, 0); 305 306 /** The color (204, 0, 204). */ 307 public static final Color C204000204 = new Color(204, 0, 204); 308 309 /** The color (0, 204, 204). */ 310 public static final Color C000204204 = new Color(0, 204, 204); 311 312 /** The color (51, 255, 51). */ 313 public static final Color C051255051 = new Color(51, 255, 51); 314 315 /** The color (255, 51, 51). */ 316 public static final Color C255051051 = new Color(255, 51, 51); 317 318 /** The color (255, 102, 102). */ 319 public static final Color C255102102 = new Color(255, 102, 102); 320 321 /** The color (102, 102, 255). */ 322 public static final Color C102102255 = new Color(102, 102, 255); 323 324 /** The color (255, 153, 153). */ 325 public static final Color C255153153 = new Color(255, 153, 153); 326 static Color[] colors = 327 { 328 // Row 1 329 Color.WHITE, new Color(204, 255, 255), C204255255, C204255255, C204255255, 330 C204255255, C204255255, C204255255, C204255255, 331 C204255255, C204255255, new Color(255, 204, 255), 332 C255204204, C255204204, C255204204, C255204204, 333 C255204204, C255204204, C255204204, C255204204, 334 C255204204, new Color(255, 255, 204), C204255204, 335 C204255204, C204255204, C204255204, C204255204, 336 C204255204, C204255204, C204255204, C204255204, 337 338 // Row 2 339 C204204204, new Color(153, 255, 255), new Color(153, 204, 255), C153153255, 340 C153153255, C153153255, C153153255, C153153255, 341 C153153255, C153153255, new Color(204, 153, 255), 342 new Color(255, 153, 255), 343 new Color(255, 153, 204), C255153153, C255153153, 344 C255153153, C255153153, C255153153, C255153153, 345 C255153153, new Color(255, 204, 153), 346 new Color(255, 255, 153), 347 new Color(204, 255, 153), C153255153, C153255153, 348 C153255153, C153255153, C153255153, C153255153, 349 C153255153, new Color(153, 255, 204), 350 351 // Row 3 352 C204204204, new Color(102, 255, 255), new Color(102, 204, 255), 353 new Color(102, 153, 255), C102102255, C102102255, 354 C102102255, C102102255, C102102255, 355 new Color(153, 102, 255), 356 new Color(204, 102, 255), 357 new Color(255, 102, 255), 358 new Color(255, 102, 204), 359 new Color(255, 102, 153), C255102102, C255102102, 360 C255102102, C255102102, C255102102, 361 new Color(255, 153, 102), 362 new Color(255, 204, 102), 363 new Color(255, 255, 102), 364 new Color(204, 255, 102), 365 new Color(153, 255, 102), C102255102, C102255102, 366 C102255102, C102255102, C102255102, 367 new Color(102, 255, 153), 368 new Color(102, 255, 204), 369 370 // Row 4 371 new Color(153, 153, 153), new Color(51, 255, 255), new Color(51, 204, 255), 372 new Color(51, 153, 255), new Color(51, 102, 255), 373 C051051255, C051051255, C051051255, 374 new Color(102, 51, 255), new Color(153, 51, 255), 375 new Color(204, 51, 255), new Color(255, 51, 255), 376 new Color(255, 51, 204), new Color(255, 51, 153), 377 new Color(255, 51, 102), C255051051, C255051051, 378 C255051051, new Color(255, 102, 51), 379 new Color(255, 153, 51), new Color(255, 204, 51), 380 new Color(255, 255, 51), new Color(204, 255, 51), 381 new Color(153, 255, 51), new Color(102, 255, 51), 382 C051255051, C051255051, C051255051, 383 new Color(51, 255, 102), new Color(51, 255, 153), 384 new Color(51, 255, 204), 385 386 // Row 5 387 new Color(153, 153, 153), new Color(0, 255, 255), new Color(0, 204, 255), 388 new Color(0, 153, 255), new Color(0, 102, 255), 389 new Color(0, 51, 255), new Color(0, 0, 255), 390 new Color(51, 0, 255), new Color(102, 0, 255), 391 new Color(153, 0, 255), new Color(204, 0, 255), 392 new Color(255, 0, 255), new Color(255, 0, 204), 393 new Color(255, 0, 153), new Color(255, 0, 102), 394 new Color(255, 0, 51), new Color(255, 0, 0), 395 new Color(255, 51, 0), new Color(255, 102, 0), 396 new Color(255, 153, 0), new Color(255, 204, 0), 397 new Color(255, 255, 0), new Color(204, 255, 0), 398 new Color(153, 255, 0), new Color(102, 255, 0), 399 new Color(51, 255, 0), new Color(0, 255, 0), 400 new Color(0, 255, 51), new Color(0, 255, 102), 401 new Color(0, 255, 153), new Color(0, 255, 204), 402 403 // Row 6 404 new Color(102, 102, 102), C000204204, C000204204, new Color(0, 153, 204), 405 new Color(0, 102, 204), new Color(0, 51, 204), 406 new Color(0, 0, 204), new Color(51, 0, 204), 407 new Color(102, 0, 204), new Color(153, 0, 204), 408 C204000204, C204000204, C204000204, 409 new Color(204, 0, 153), new Color(204, 0, 102), 410 new Color(204, 0, 51), new Color(204, 0, 0), 411 new Color(204, 51, 0), new Color(204, 102, 0), 412 new Color(204, 153, 0), C204204000, C204204000, 413 C204204000, new Color(153, 204, 0), 414 new Color(102, 204, 0), new Color(51, 204, 0), 415 new Color(0, 204, 0), new Color(0, 204, 51), 416 new Color(0, 204, 102), new Color(0, 204, 153), 417 new Color(0, 204, 204), 418 419 // Row 7 420 new Color(102, 102, 102), C000153153, C000153153, C000153153, 421 new Color(0, 102, 153), new Color(0, 51, 153), 422 new Color(0, 0, 153), new Color(51, 0, 153), 423 new Color(102, 0, 153), C153000153, C153000153, 424 C153000153, C153000153, C153000153, 425 new Color(153, 0, 102), new Color(153, 0, 51), 426 new Color(153, 0, 0), new Color(153, 51, 0), 427 new Color(153, 102, 0), C153153000, C153153000, 428 C153153000, C153153000, C153153000, 429 new Color(102, 153, 0), new Color(51, 153, 0), 430 new Color(0, 153, 0), new Color(0, 153, 51), 431 new Color(0, 153, 102), C000153153, C000153153, 432 433 // Row 8 434 new Color(51, 51, 51), C000102102, C000102102, C000102102, C000102102, 435 new Color(0, 51, 102), new Color(0, 0, 102), 436 new Color(51, 0, 102), C102000102, C102000102, 437 C102000102, C102000102, C102000102, C102000102, 438 C102000102, new Color(102, 0, 51), 439 new Color(102, 0, 0), new Color(102, 51, 0), 440 C102102000, C102102000, C102102000, C102102000, 441 C102102000, C102102000, C102102000, 442 new Color(51, 102, 0), new Color(0, 102, 0), 443 new Color(0, 102, 51), C000102102, C000102102, 444 C000102102, 445 446 // Row 9. 447 Color.BLACK, C000051051, C000051051, C000051051, C000051051, C000051051, 448 new Color(0, 0, 51), C051000051, C051000051, 449 C051000051, C051000051, C051000051, C051000051, 450 C051000051, C051000051, C051000051, 451 new Color(51, 0, 0), C051051000, C051051000, 452 C051051000, C051051000, C051051000, C051051000, 453 C051051000, C051051000, new Color(0, 51, 0), 454 C000051051, C000051051, C000051051, C000051051, 455 new Color(51, 51, 51) 456 }; 457 458 /** 459 * Creates a new MainSwatchPanel object. 460 */ 461 MainSwatchPanel() { 462 numCols = 31; 463 numRows = 9; 464 initializeColors(); 465 // incredibly, this setToolTipText call is how you register to 466 // listen for events 467 setToolTipText(""); 468 revalidate(); 469 } 470 471 /** 472 * This method returns the color for the given position. 473 * 474 * @param x X location for the position. 475 * @param y Y location for the position. 476 * 477 * @return {@link Color} for the given position. 478 */ 479 @Override public Color getColorForPosition(int x, int y) { 480 if (((x % (cellWidth + gap)) > cellWidth) || ((y % (cellHeight + gap)) > cellHeight)) { 481 // position is located in gap. 482 return null; 483 } 484 485 int row = y / (cellHeight + gap); 486 int col = x / (cellWidth + gap); 487 return colors[row * numCols + col]; 488 } 489 490 @Override protected void setSelectedCellFromPosition(int x, int y) { 491 if (((x % (cellWidth + gap)) > cellWidth) || ((y % (cellHeight + gap)) > cellHeight)) { 492 // position is located in gap. 493 return; 494 } 495 selRow = y / (cellHeight + gap); 496 selCol = x / (cellWidth + gap); 497 } 498 499 @Override public Color getColorForCell(int row, int column) { 500 return colors[row * numCols + column]; 501 } 502 503 /** 504 * This method initializes the colors for the main swatch panel. 505 */ 506 @Override protected void initializeColors() { 507 // Unnecessary 508 } 509 510 /** 511 * This method paints the main graphics panel with the given Graphics 512 * object. 513 * 514 * @param graphics The Graphics object to paint with. 515 */ 516 @Override public void paint(Graphics graphics) { 517 int index = 0; 518 Insets insets = getInsets(); 519 int currX = insets.left; 520 int currY = insets.top; 521 Color saved = graphics.getColor(); 522 523 for (int i = 0; i < numRows; i++) { 524 for (int j = 0; j < numCols; j++) { 525 Color current = colors[index++]; 526 graphics.setColor(current); 527 graphics.fill3DRect(currX, currY, cellWidth, cellHeight, true); 528 if ((selRow == i) && (selCol == j) && this.isFocusOwner()) { 529 Color cursorColor = new Color(current.getRed() < 125 ? 255 : 0, 530 current.getGreen() < 125 ? 255 : 0, 531 current.getBlue() < 125 ? 255 : 0); 532 533 graphics.setColor(cursorColor); 534 graphics.drawLine(currX, currY, currX + cellWidth - 1, currY); 535 graphics.drawLine(currX, currY, currX, currY + cellHeight - 1); 536 graphics.drawLine(currX + cellWidth - 1, currY, currX + cellWidth- 1, currY + cellHeight - 1); 537 graphics.drawLine(currX, currY + cellHeight - 1, currX + cellWidth - 1, currY + cellHeight - 1); 538 graphics.drawLine(currX, currY, currX + cellWidth - 1, currY + cellHeight - 1); 539 graphics.drawLine(currX, currY + cellHeight - 1, currX + cellWidth - 1, currY); 540 } 541 currX += gap + cellWidth; 542 } 543 currX = insets.left; 544 currY += gap + cellHeight; 545 } 546 graphics.setColor(saved); 547 } 548 549 /** 550 * This method returns the tooltip text for the given MouseEvent. 551 * 552 * @param e The MouseEvent to find tooltip text for. 553 * 554 * @return The tooltip text. 555 */ 556 @Override public String getToolTipText(MouseEvent e) { 557 Color c = getColorForPosition(e.getX(), e.getY()); 558 String tip = null; 559 if (c != null) { 560 tip = c.getRed() + ", " + c.getGreen() + ", " + c.getBlue(); 561 } 562 return tip; 563 } 564 } 565 566 /** 567 * This class is the recent swatch panel. It holds recently selected colors. 568 */ 569 static class RecentSwatchPanel extends SwatchPanel { 570 571 /** The array for storing recently stored colors. */ 572 Color[] colors; 573 574 /** The index of the array that is the start. */ 575 int start = 0; 576 577 /** 578 * Creates a new RecentSwatchPanel object. 579 */ 580 RecentSwatchPanel() { 581 numCols = 5; 582 numRows = 7; 583 initializeColors(); 584 // incredibly, this setToolTipText call is how you register to 585 // listen for events 586 setToolTipText(""); 587 revalidate(); 588 } 589 590 /** 591 * This method returns the color for the given position. 592 * 593 * @param x The x coordinate of the position. 594 * @param y The y coordinate of the position. 595 * 596 * @return The color for the given position. 597 */ 598 @Override public Color getColorForPosition(int x, int y) { 599 if (((x % (cellWidth + gap)) > cellWidth) || ((y % (cellHeight + gap)) > cellHeight)) { 600 // position is located in gap. 601 return null; 602 } 603 604 int row = y / (cellHeight + gap); 605 int col = x / (cellWidth + gap); 606 607 return colors[getIndexForCell(row, col)]; 608 } 609 610 @Override protected void setSelectedCellFromPosition(int x, int y) { 611 if (((x % (cellWidth + gap)) > cellWidth) || ((y % (cellHeight + gap)) > cellHeight)) { 612 // position is located in gap. 613 return; 614 } 615 selRow = y / (cellHeight + gap); 616 selCol = x / (cellWidth + gap); 617 } 618 619 /** 620 * This method initializes the colors for the recent swatch panel. 621 */ 622 @Override protected void initializeColors() { 623 final Color defaultColor = 624 UIManager.getColor("ColorChooser.swatchesDefaultRecentColor", getLocale()); 625 colors = new Color[numRows * numCols]; 626 for (int i = 0; i < colors.length; i++) { 627 colors[i] = defaultColor; 628 } 629 } 630 631 /** 632 * This method returns the array index for the given row and column. 633 * 634 * @param row The row. 635 * @param col The column. 636 * 637 * @return The array index for the given row and column. 638 */ 639 private int getIndexForCell(int row, int col) { 640 return ((row * numCols) + col + start) % (numRows * numCols); 641 } 642 643 public Color getColorForCell(int row, int column) { 644 return colors[getIndexForCell(row, column)]; 645 } 646 647 /** 648 * This method adds the given color to the beginning of the swatch panel. 649 * Package-private to avoid an accessor method. 650 * 651 * @param c The color to add. 652 */ 653 void addColorToQueue(Color c) { 654 if (--start == -1) { 655 start = numRows * numCols - 1; 656 } 657 colors[start] = c; 658 } 659 660 void addColorsToQueue(List<Color> colorsToAdd) { 661 if ((colorsToAdd != null) && !colorsToAdd.isEmpty()) { 662 for (int i = colorsToAdd.size() - 1; i >= 0; i--) { 663 addColorToQueue(colorsToAdd.get(i)); 664 } 665 } 666 } 667 668 /** 669 * This method paints the panel with the given Graphics object. 670 * 671 * @param g The Graphics object to paint with. 672 */ 673 @Override public void paint(Graphics g) { 674 Color saved = g.getColor(); 675 Insets insets = getInsets(); 676 int currX = insets.left; 677 int currY = insets.top; 678 679 for (int i = 0; i < numRows; i++) { 680 for (int j = 0; j < numCols; j++) { 681 Color current = colors[getIndexForCell(i, j)]; 682 g.setColor(current); 683 g.fill3DRect(currX, currY, cellWidth, cellHeight, true); 684 if ((selRow == i) && (selCol == j) && this.isFocusOwner()) { 685 Color cursorColor = new Color(current.getRed() < 125 ? 255 : 0, 686 current.getGreen() < 125 ? 255 : 0, 687 current.getBlue() < 125 ? 255 : 0); 688 g.setColor(cursorColor); 689 g.drawLine(currX, currY, currX + cellWidth - 1, currY); 690 g.drawLine(currX, currY, currX, currY + cellHeight - 1); 691 g.drawLine(currX + cellWidth - 1, currY, currX + cellWidth- 1, currY + cellHeight - 1); 692 g.drawLine(currX, currY + cellHeight - 1, currX + cellWidth - 1, currY + cellHeight - 1); 693 g.drawLine(currX, currY, currX + cellWidth - 1, currY + cellHeight - 1); 694 g.drawLine(currX, currY + cellHeight - 1, currX + cellWidth - 1, currY); 695 } 696 currX += cellWidth + gap; 697 } 698 currX = insets.left; 699 currY += cellWidth + gap; 700 } 701 } 702 703 /** 704 * This method returns the tooltip text for the given MouseEvent. 705 * 706 * @param e The MouseEvent. 707 * 708 * @return The tooltip text. 709 */ 710 @Override public String getToolTipText(MouseEvent e) { 711 Color c = getColorForPosition(e.getX(), e.getY()); 712// logger.trace("x={} y={} c={}", e.getX(), e.getY(), c); 713 String tip = null; 714 if (c != null) { 715 tip = c.getRed() + ", " + c.getGreen() + ", " + c.getBlue(); 716 } 717 return tip; 718 } 719 } 720 721 /** 722 * This class handles mouse events for the two swatch panels. 723 */ 724 class MouseHandler extends MouseAdapter { 725 /** 726 * This method is called whenever the mouse is pressed. 727 * 728 * @param e The MouseEvent. 729 */ 730 @Override public void mousePressed(MouseEvent e) { 731 if (isEnabled()) { 732 SwatchPanel panel = (SwatchPanel)e.getSource(); 733 Color c = panel.getColorForPosition(e.getX(), e.getY()); 734 panel.setSelectedCellFromPosition(e.getX(), e.getY()); 735 // yes, the "!=" is intentional. 736 if (panel != recentPalette) { 737 recentPalette.addColorToQueue(c); 738 if (tracker != null) { 739 tracker.addColor(c); 740 } 741 } 742 PersistableSwatchChooserPanel.this.getColorSelectionModel().setSelectedColor(c); 743 PersistableSwatchChooserPanel.this.repaint(); 744 panel.requestFocusInWindow(); 745 } 746 } 747 } 748 749 /** 750 * This class handles the user {@literal "selecting"} a recently used 751 * color using the space key. 752 */ 753 private class RecentSwatchKeyListener extends KeyAdapter { 754 public void keyPressed(KeyEvent e) { 755 if (KeyEvent.VK_SPACE == e.getKeyCode()) { 756 Color color = recentPalette.getSelectedColor(); 757 PersistableSwatchChooserPanel.this.getColorSelectionModel().setSelectedColor(color); 758 PersistableSwatchChooserPanel.this.repaint(); 759 } 760 } 761 } 762 763 /** 764 * This class handles the user {@literal "selecting"} a color using the 765 * space key. 766 */ 767 private class MainSwatchKeyListener extends KeyAdapter { 768 public void keyPressed(KeyEvent e) { 769 if (KeyEvent.VK_SPACE == e.getKeyCode()) { 770 Color color = mainPalette.getSelectedColor(); 771 recentPalette.addColorToQueue(color); 772 if (tracker != null) { 773 tracker.addColor(color); 774 } 775 PersistableSwatchChooserPanel.this.getColorSelectionModel().setSelectedColor(color); 776 PersistableSwatchChooserPanel.this.repaint(); 777 } 778 } 779 } 780 781 /** 782 * This is the layout manager for the main panel. 783 */ 784 static class MainPanelLayout implements LayoutManager { 785 /** 786 * This method is called when a new component is added to the container. 787 * 788 * @param name The name of the component. 789 * @param comp The added component. 790 */ 791 @Override public void addLayoutComponent(String name, Component comp) { 792 // Nothing to do here. 793 } 794 795 /** 796 * This method is called to set the size and position of the child 797 * components for the given container. 798 * 799 * @param parent The container to lay out. 800 */ 801 @Override public void layoutContainer(Container parent) { 802 Component[] comps = parent.getComponents(); 803 Insets insets = parent.getInsets(); 804 Dimension[] pref = new Dimension[comps.length]; 805 806 int xpos = 0; 807 int ypos = 0; 808 int maxHeight = 0; 809 int totalWidth = 0; 810 811 for (int i = 0; i < comps.length; i++) { 812 pref[i] = comps[i].getPreferredSize(); 813 if (pref[i] == null) { 814 return; 815 } 816 maxHeight = Math.max(maxHeight, pref[i].height); 817 totalWidth += pref[i].width; 818 } 819 820 ypos = (parent.getSize().height - maxHeight) / 2 + insets.top; 821 xpos = insets.left + (parent.getSize().width - totalWidth) / 2; 822 823 for (int i = 0; i < comps.length; i++) { 824 if (pref[i] == null) { 825 continue; 826 } 827 comps[i].setBounds(xpos, ypos, pref[i].width, pref[i].height); 828 xpos += pref[i].width; 829 } 830 } 831 832 /** 833 * This method is called when a component is removed from the container. 834 * 835 * @param comp The component that was removed. 836 */ 837 @Override public void removeLayoutComponent(Component comp) { 838 // Nothing to do here. 839 } 840 841 /** 842 * This methods calculates the minimum layout size for the container. 843 * 844 * @param parent The container. 845 * 846 * @return The minimum layout size. 847 */ 848 @Override public Dimension minimumLayoutSize(Container parent) { 849 return preferredLayoutSize(parent); 850 } 851 852 /** 853 * This method returns the preferred layout size for the given container. 854 * 855 * @param parent The container. 856 * 857 * @return The preferred layout size. 858 */ 859 @Override public Dimension preferredLayoutSize(Container parent) { 860 int xmax = 0; 861 int ymax = 0; 862 863 Component[] comps = parent.getComponents(); 864 865 for (Component comp : comps) { 866 Dimension pref = comp.getPreferredSize(); 867 if (pref == null) { 868 continue; 869 } 870 xmax += pref.width; 871 ymax = Math.max(ymax, pref.height); 872 } 873 874 Insets insets = parent.getInsets(); 875 876 return new Dimension(insets.left + insets.right + xmax, 877 insets.top + insets.bottom + ymax); 878 } 879 } 880 881 /** 882 * This is the layout manager for the recent swatch panel. 883 */ 884 static class RecentPanelLayout implements LayoutManager { 885 /** 886 * This method is called when a component is added to the container. 887 * 888 * @param name The name of the component. 889 * @param comp The added component. 890 */ 891 @Override public void addLayoutComponent(String name, Component comp) { 892 // Nothing needs to be done. 893 } 894 895 /** 896 * This method sets the size and position of the child components of the 897 * given container. 898 * 899 * @param parent The container to lay out. 900 */ 901 @Override public void layoutContainer(Container parent) { 902 Component[] comps = parent.getComponents(); 903 Dimension parentSize = parent.getSize(); 904 Insets insets = parent.getInsets(); 905 int currY = insets.top; 906 907 for (Component comp : comps) { 908 Dimension pref = comp.getPreferredSize(); 909 if (pref == null) { 910 continue; 911 } 912 comp.setBounds(insets.left, currY, pref.width, pref.height); 913 currY += pref.height; 914 } 915 } 916 917 /** 918 * This method calculates the minimum layout size for the given container. 919 * 920 * @param parent The container. 921 * 922 * @return The minimum layout size. 923 */ 924 @Override public Dimension minimumLayoutSize(Container parent) { 925 return preferredLayoutSize(parent); 926 } 927 928 /** 929 * This method calculates the preferred layout size for the given 930 * container. 931 * 932 * @param parent The container. 933 * 934 * @return The preferred layout size. 935 */ 936 @Override public Dimension preferredLayoutSize(Container parent) { 937 int width = 0; 938 int height = 0; 939 Insets insets = parent.getInsets(); 940 Component[] comps = parent.getComponents(); 941 for (Component comp : comps) { 942 Dimension pref = comp.getPreferredSize(); 943 if (pref != null) { 944 width = Math.max(width, pref.width); 945 height += pref.height; 946 } 947 } 948 949 return new Dimension(width + insets.left + insets.right, 950 height + insets.top + insets.bottom); 951 } 952 953 /** 954 * This method is called whenever a component is removed from the 955 * container. 956 * 957 * @param comp The removed component. 958 */ 959 @Override public void removeLayoutComponent(Component comp) { 960 // Nothing needs to be done. 961 } 962 } 963 964 /** 965 * Creates a new DefaultSwatchChooserPanel object. 966 */ 967 PersistableSwatchChooserPanel() { 968 super(); 969 } 970 971 /** 972 * This method updates the chooser panel with the new value from the 973 * JColorChooser. 974 */ 975 @Override public void updateChooser() { 976 // Nothing to do here yet. 977 } 978 979 /** 980 * This method builds the chooser panel. 981 */ 982 @Override protected void buildChooser() { 983 // The structure of the swatch panel is: 984 // One large panel (minus the insets). 985 // Inside that panel, there are two panels, one holds the palette. 986 // The other holds the label and the recent colors palette. 987 // The two palettes are two custom swatch panels. 988 setLayout(new MainPanelLayout()); 989 990 JPanel mainPaletteHolder = new JPanel(); 991 JPanel recentPaletteHolder = new JPanel(); 992 993 mainPalette = new MainSwatchPanel(); 994 mainPalette.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, getDisplayName()); 995 mainPalette.setInheritsPopupMenu(true); 996 997 String recentLabel = UIManager.getString("ColorChooser.swatchesRecentText", getLocale()); 998 recentPalette = new RecentSwatchPanel(); 999 recentPalette.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY, recentLabel); 1000 recentPalette.setInheritsPopupMenu(true); 1001 1002 JLabel label = new JLabel(recentLabel); 1003 label.setLabelFor(recentPalette); 1004 1005 mouseHandler = new MouseHandler(); 1006 mainPalette.addMouseListener(mouseHandler); 1007 recentPalette.addMouseListener(mouseHandler); 1008 1009 mainSwatchKeyListener = new MainSwatchKeyListener(); 1010 mainPalette.addKeyListener(mainSwatchKeyListener); 1011 1012 recentSwatchKeyListener = new RecentSwatchKeyListener(); 1013 recentPalette.addKeyListener(recentSwatchKeyListener); 1014 1015 mainPaletteHolder.setLayout(new BorderLayout()); 1016 mainPaletteHolder.add(mainPalette, BorderLayout.CENTER); 1017 mainPaletteHolder.setInheritsPopupMenu(true); 1018 1019 recentPaletteHolder.setLayout(new RecentPanelLayout()); 1020 recentPaletteHolder.add(label); 1021 recentPaletteHolder.add(recentPalette); 1022 recentPaletteHolder.setInheritsPopupMenu(true); 1023 1024 JPanel main = new JPanel(); 1025 main.add(mainPaletteHolder); 1026 main.add(recentPaletteHolder); 1027 1028 this.add(main); 1029 } 1030 1031 /** 1032 * This method removes the chooser panel from the JColorChooser. 1033 * 1034 * @param chooser The JColorChooser this panel is being removed from. 1035 */ 1036 @Override public void uninstallChooserPanel(JColorChooser chooser) { 1037 mainPalette.removeMouseListener(mouseHandler); 1038 mainPalette.removeKeyListener(mainSwatchKeyListener); 1039 1040 recentPalette.removeMouseListener(mouseHandler); 1041 recentPalette.removeKeyListener(recentSwatchKeyListener); 1042 1043 recentPalette = null; 1044 mainPalette = null; 1045 mouseHandler = null; 1046 mainSwatchKeyListener = null; 1047 recentSwatchKeyListener = null; 1048 removeAll(); 1049 super.uninstallChooserPanel(chooser); 1050 } 1051 1052 /** 1053 * This method returns the JTabbedPane displayed name. 1054 * 1055 * @return The name displayed in the JTabbedPane. 1056 */ 1057 @Override public String getDisplayName() { 1058 return "Swatches"; 1059 } 1060 1061 /** 1062 * This method returns the small display icon. 1063 * 1064 * @return The small display icon. 1065 */ 1066 @Override public Icon getSmallDisplayIcon() { 1067 return null; 1068 } 1069 1070 /** 1071 * This method returns the large display icon. 1072 * 1073 * @return The large display icon. 1074 */ 1075 @Override public Icon getLargeDisplayIcon() { 1076 return null; 1077 } 1078 1079 /** 1080 * This method paints the chooser panel with the given Graphics object. 1081 * 1082 * @param g The Graphics object to paint with. 1083 */ 1084 @Override public void paint(Graphics g) { 1085 super.paint(g); 1086 } 1087 1088 /** 1089 * This method returns the tooltip text for the given MouseEvent. 1090 * 1091 * @param e The MouseEvent. 1092 * 1093 * @return The tooltip text. 1094 */ 1095 @Override public String getToolTipText(MouseEvent e) { 1096 return null; 1097 } 1098 1099 /** 1100 * Set the color tracking object. 1101 * 1102 * @param tracker 1103 */ 1104 public void setColorTracker(ColorTracker tracker) { 1105 this.tracker = tracker; 1106 if (tracker != null) { 1107 tracker.addPropertyChangeListener("colors", this); 1108 } 1109 updateRecentSwatchPanel(); 1110 } 1111 1112 @Override public void propertyChange(PropertyChangeEvent evt) { 1113// logger.trace("old='{}' new='{}'", evt.getOldValue(), evt.getNewValue()); 1114// updateRecentSwatchPanel(); 1115 } 1116 1117 /** 1118 * A method updating the recent colors in the swatchPanel 1119 * This is called whenever necessary, specifically after building the panel, 1120 * on changes of the tracker, from the mouseListener 1121 */ 1122 protected void updateRecentSwatchPanel() { 1123 if (recentPalette != null) { 1124 recentPalette.addColorsToQueue(tracker != null ? tracker.getColors() : null); 1125 } 1126 } 1127 1128 /** 1129 * This class is used to save and restore the recent color choices.. 1130 */ 1131 public static class ColorTracker extends AbstractBean implements ActionListener { 1132 1133 /** The list of recent {@link Color Colors}. */ 1134 private List<Color> colors = new ArrayList<>(); 1135 1136 /** 1137 * Add a {@link Color} to the list of recent color choices. This method 1138 * will fire off a {@literal "colors"} property change. 1139 * 1140 * @param color {@code Color} to be added. 1141 */ 1142 public void addColor(Color color) { 1143 List<Color> old = getColors(); 1144 colors.add(0, color); 1145 firePropertyChange("colors", old, getColors()); 1146 } 1147 1148 /** 1149 * Set the list of recent color choices. This method is what should be 1150 * called when {@literal "restoring"} the recent colors panel. 1151 * 1152 * <p>This method will fire off a {@literal "colors"} property change. 1153 * </p> 1154 * 1155 * @param colors {@code List} of recent color choices. {@code null} 1156 * is allowed, but will result in {@link #colors} being empty. 1157 */ 1158 public void setColors(List<Color> colors) { 1159 List<Color> old = getColors(); 1160 this.colors = new ArrayList<>(colors); 1161 firePropertyChange("colors", old, getColors()); 1162 } 1163 1164 /** 1165 * Get the recent color choices. 1166 * 1167 * @return {@link ArrayList} containing the recently picked colors. 1168 * May be empty. 1169 */ 1170 public List<Color> getColors() { 1171 return new ArrayList<>(colors); 1172 } 1173 1174 /** 1175 * Returns the user's last {@link Color} selection. 1176 * 1177 * @return Either the last {@code Color} that was selected, or 1178 * {@code null} if no colors have been selected. 1179 */ 1180 public Color getMostRecentColor() { 1181 Color c = null; 1182 if (!colors.isEmpty()) { 1183 c = colors.get(0); 1184 } 1185 return c; 1186 } 1187 1188 /** 1189 * This method currently does nothing. 1190 * 1191 * @param e Ignored. 1192 */ 1193 @Override public void actionPerformed(ActionEvent e) { 1194 // noop 1195 } 1196 } 1197}