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