1. /*
  2. * @(#)JColorChooser.java 1.44 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.beans.*;
  11. import java.io.*;
  12. import java.util.*;
  13. import javax.swing.colorchooser.*;
  14. import javax.swing.plaf.ColorChooserUI;
  15. import javax.swing.event.*;
  16. import javax.accessibility.*;
  17. /**
  18. * <code>JColorChooser</code> provides a pane of controls designed to allow
  19. * a user to manipulate and select a color.
  20. * For information about using color choosers, see
  21. * <a
  22. href="http://java.sun.com/docs/books/tutorial/uiswing/components/colorchooser.html">How to Use Color Choosers</a>,
  23. * a section in <em>The Java Tutorial</em>.
  24. *
  25. * <p>
  26. *
  27. * This class provides three levels of API:
  28. * <ol>
  29. * <li>A static convenience method which shows a modal color-chooser
  30. * dialog and returns the color selected by the user.
  31. * <li>A static convenience method for creating a color-chooser dialog
  32. * where <code>ActionListeners</code> can be specified to be invoked when
  33. * the user presses one of the dialog buttons.
  34. * <li>The ability to create instances of <code>JColorChooser</code> panes
  35. * directly (within any container). <code>PropertyChange</code> listeners
  36. * can be added to detect when the current "color" property changes.
  37. * </ol>
  38. * <p>
  39. * <strong>Warning:</strong>
  40. * Serialized objects of this class will not be compatible with
  41. * future Swing releases. The current serialization support is
  42. * appropriate for short term storage or RMI between applications running
  43. * the same version of Swing. As of 1.4, support for long term storage
  44. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  45. * has been added to the <code>java.beans</code> package.
  46. * Please see {@link java.beans.XMLEncoder}.
  47. *
  48. *
  49. * @beaninfo
  50. * attribute: isContainer false
  51. * description: A component that supports selecting a Color.
  52. *
  53. *
  54. * @version 1.44 01/23/03
  55. * @author James Gosling
  56. * @author Amy Fowler
  57. * @author Steve Wilson
  58. */
  59. public class JColorChooser extends JComponent implements Accessible {
  60. /**
  61. * @see #getUIClassID
  62. * @see #readObject
  63. */
  64. private static final String uiClassID = "ColorChooserUI";
  65. private ColorSelectionModel selectionModel;
  66. private JComponent previewPanel;
  67. private AbstractColorChooserPanel[] chooserPanels = new AbstractColorChooserPanel[0];
  68. private boolean dragEnabled;
  69. /**
  70. * The selection model property name.
  71. */
  72. public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
  73. /**
  74. * The preview panel property name.
  75. */
  76. public static final String PREVIEW_PANEL_PROPERTY = "previewPanel";
  77. /**
  78. * The chooserPanel array property name.
  79. */
  80. public static final String CHOOSER_PANELS_PROPERTY = "chooserPanels";
  81. /**
  82. * Shows a modal color-chooser dialog and blocks until the
  83. * dialog is hidden. If the user presses the "OK" button, then
  84. * this method hides/disposes the dialog and returns the selected color.
  85. * If the user presses the "Cancel" button or closes the dialog without
  86. * pressing "OK", then this method hides/disposes the dialog and returns
  87. * <code>null</code>.
  88. *
  89. * @param component the parent <code>Component</code> for the dialog
  90. * @param title the String containing the dialog's title
  91. * @param initialColor the initial Color set when the color-chooser is shown
  92. * @return the selected color or <code>null</code> if the user opted out
  93. * @exception HeadlessException if GraphicsEnvironment.isHeadless()
  94. * returns true.
  95. * @see java.awt.GraphicsEnvironment#isHeadless
  96. */
  97. public static Color showDialog(Component component,
  98. String title, Color initialColor) throws HeadlessException {
  99. final JColorChooser pane = new JColorChooser(initialColor != null?
  100. initialColor : Color.white);
  101. ColorTracker ok = new ColorTracker(pane);
  102. JDialog dialog = createDialog(component, title, true, pane, ok, null);
  103. dialog.addWindowListener(new ColorChooserDialog.Closer());
  104. dialog.addComponentListener(new ColorChooserDialog.DisposeOnClose());
  105. dialog.show(); // blocks until user brings dialog down...
  106. return ok.getColor();
  107. }
  108. /**
  109. * Creates and returns a new dialog containing the specified
  110. * <code>ColorChooser</code> pane along with "OK", "Cancel", and "Reset"
  111. * buttons. If the "OK" or "Cancel" buttons are pressed, the dialog is
  112. * automatically hidden (but not disposed). If the "Reset"
  113. * button is pressed, the color-chooser's color will be reset to the
  114. * color which was set the last time <code>show</code> was invoked on the
  115. * dialog and the dialog will remain showing.
  116. *
  117. * @param c the parent component for the dialog
  118. * @param title the title for the dialog
  119. * @param modal a boolean. When true, the remainder of the program
  120. * is inactive until the dialog is closed.
  121. * @param chooserPane the color-chooser to be placed inside the dialog
  122. * @param okListener the ActionListener invoked when "OK" is pressed
  123. * @param cancelListener the ActionListener invoked when "Cancel" is pressed
  124. * @return a new dialog containing the color-chooser pane
  125. * @exception HeadlessException if GraphicsEnvironment.isHeadless()
  126. * returns true.
  127. * @see java.awt.GraphicsEnvironment#isHeadless
  128. */
  129. public static JDialog createDialog(Component c, String title, boolean modal,
  130. JColorChooser chooserPane, ActionListener okListener,
  131. ActionListener cancelListener) throws HeadlessException {
  132. return new ColorChooserDialog(c, title, modal, chooserPane,
  133. okListener, cancelListener);
  134. }
  135. /**
  136. * Creates a color chooser pane with an initial color of white.
  137. */
  138. public JColorChooser() {
  139. this(Color.white);
  140. }
  141. /**
  142. * Creates a color chooser pane with the specified initial color.
  143. *
  144. * @param initialColor the initial color set in the chooser
  145. */
  146. public JColorChooser(Color initialColor) {
  147. this( new DefaultColorSelectionModel(initialColor) );
  148. }
  149. /**
  150. * Creates a color chooser pane with the specified
  151. * <code>ColorSelectionModel</code>.
  152. *
  153. * @param model the <code>ColorSelectionModel</code> to be used
  154. */
  155. public JColorChooser(ColorSelectionModel model) {
  156. selectionModel = model;
  157. updateUI();
  158. dragEnabled = false;
  159. }
  160. /**
  161. * Returns the L&F object that renders this component.
  162. *
  163. * @return the <code>ColorChooserUI</code> object that renders
  164. * this component
  165. */
  166. public ColorChooserUI getUI() {
  167. return (ColorChooserUI)ui;
  168. }
  169. /**
  170. * Sets the L&F object that renders this component.
  171. *
  172. * @param ui the <code>ColorChooserUI</code> L&F object
  173. * @see UIDefaults#getUI
  174. *
  175. * @beaninfo
  176. * bound: true
  177. * hidden: true
  178. * description: The UI object that implements the color chooser's LookAndFeel.
  179. */
  180. public void setUI(ColorChooserUI ui) {
  181. super.setUI(ui);
  182. }
  183. /**
  184. * Notification from the <code>UIManager</code> that the L&F has changed.
  185. * Replaces the current UI object with the latest version from the
  186. * <code>UIManager</code>.
  187. *
  188. * @see JComponent#updateUI
  189. */
  190. public void updateUI() {
  191. setUI((ColorChooserUI)UIManager.getUI(this));
  192. }
  193. /**
  194. * Returns the name of the L&F class that renders this component.
  195. *
  196. * @return the string "ColorChooserUI"
  197. * @see JComponent#getUIClassID
  198. * @see UIDefaults#getUI
  199. */
  200. public String getUIClassID() {
  201. return uiClassID;
  202. }
  203. /**
  204. * Gets the current color value from the color chooser.
  205. * By default, this delegates to the model.
  206. *
  207. * @return the current color value of the color chooser
  208. */
  209. public Color getColor() {
  210. return selectionModel.getSelectedColor();
  211. }
  212. /**
  213. * Sets the current color of the color chooser to the specified color.
  214. * The <code>ColorSelectionModel</code> will fire a <code>ChangeEvent</code>
  215. * @param color the color to be set in the color chooser
  216. * @see JComponent#addPropertyChangeListener
  217. *
  218. * @beaninfo
  219. * bound: false
  220. * hidden: false
  221. * description: The current color the chooser is to display.
  222. */
  223. public void setColor(Color color) {
  224. selectionModel.setSelectedColor(color);
  225. }
  226. /**
  227. * Sets the current color of the color chooser to the
  228. * specified RGB color. Note that the values of red, green,
  229. * and blue should be between the numbers 0 and 255, inclusive.
  230. *
  231. * @param r an int specifying the amount of Red
  232. * @param g an int specifying the amount of Green
  233. * @param b an int specifying the amount of Blue
  234. * @exception IllegalArgumentException if r,g,b values are out of range
  235. * @see java.awt.Color
  236. */
  237. public void setColor(int r, int g, int b) {
  238. setColor(new Color(r,g,b));
  239. }
  240. /**
  241. * Sets the current color of the color chooser to the
  242. * specified color.
  243. *
  244. * @param c an integer value that sets the current color in the chooser
  245. * where the low-order 8 bits specify the Blue value,
  246. * the next 8 bits specify the Green value, and the 8 bits
  247. * above that specify the Red value.
  248. */
  249. public void setColor(int c) {
  250. setColor((c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF);
  251. }
  252. /**
  253. * Sets the <code>dragEnabled</code> property,
  254. * which must be <code>true</code> to enable
  255. * automatic drag handling (the first part of drag and drop)
  256. * on this component.
  257. * The <code>transferHandler</code> property needs to be set
  258. * to a non-<code>null</code> value for the drag to do
  259. * anything. The default value of the <code>dragEnabled</code>
  260. * property
  261. * is <code>false</code>.
  262. *
  263. * <p>
  264. *
  265. * When automatic drag handling is enabled,
  266. * most look and feels begin a drag-and-drop operation
  267. * when the user presses the mouse button over the preview panel.
  268. * Some look and feels might not support automatic drag and drop;
  269. * they will ignore this property. You can work around such
  270. * look and feels by modifying the component
  271. * to directly call the <code>exportAsDrag</code> method of a
  272. * <code>TransferHandler</code>.
  273. *
  274. * @param b the value to set the <code>dragEnabled</code> property to
  275. * @exception HeadlessException if
  276. * <code>b</code> is <code>true</code> and
  277. * <code>GraphicsEnvironment.isHeadless()</code>
  278. * returns <code>true</code>
  279. *
  280. * @since 1.4
  281. *
  282. * @see java.awt.GraphicsEnvironment#isHeadless
  283. * @see #getDragEnabled
  284. * @see #setTransferHandler
  285. * @see TransferHandler
  286. *
  287. * @beaninfo
  288. * description: Determines whether automatic drag handling is enabled.
  289. * bound: false
  290. */
  291. public void setDragEnabled(boolean b) {
  292. if (b && GraphicsEnvironment.isHeadless()) {
  293. throw new HeadlessException();
  294. }
  295. dragEnabled = b;
  296. }
  297. /**
  298. * Gets the value of the <code>dragEnabled</code> property.
  299. *
  300. * @return the value of the <code>dragEnabled</code> property
  301. * @see #setDragEnabled
  302. * @since 1.4
  303. */
  304. public boolean getDragEnabled() {
  305. return dragEnabled;
  306. }
  307. /**
  308. * Sets the current preview panel.
  309. * This will fire a <code>PropertyChangeEvent</code> for the property
  310. * named "previewPanel".
  311. *
  312. * @param preview the <code>JComponent</code> which displays the current color
  313. * @see JComponent#addPropertyChangeListener
  314. *
  315. * @beaninfo
  316. * bound: true
  317. * hidden: true
  318. * description: The UI component which displays the current color.
  319. */
  320. public void setPreviewPanel(JComponent preview) {
  321. if (previewPanel != preview) {
  322. JComponent oldPreview = previewPanel;
  323. previewPanel = preview;
  324. firePropertyChange(JColorChooser.PREVIEW_PANEL_PROPERTY, oldPreview, preview);
  325. }
  326. }
  327. /**
  328. * Returns the preview panel that shows a chosen color.
  329. *
  330. * @return a <code>JComponent</code> object -- the preview panel
  331. */
  332. public JComponent getPreviewPanel() {
  333. return previewPanel;
  334. }
  335. /**
  336. * Adds a color chooser panel to the color chooser.
  337. *
  338. * @param panel the <code>AbstractColorChooserPanel</code> to be added
  339. */
  340. public void addChooserPanel( AbstractColorChooserPanel panel ) {
  341. AbstractColorChooserPanel[] oldPanels = getChooserPanels();
  342. AbstractColorChooserPanel[] newPanels = new AbstractColorChooserPanel[oldPanels.length+1];
  343. System.arraycopy(oldPanels, 0, newPanels, 0, oldPanels.length);
  344. newPanels[newPanels.length-1] = panel;
  345. setChooserPanels(newPanels);
  346. }
  347. /**
  348. * Removes the Color Panel specified.
  349. *
  350. * @param panel a string that specifies the panel to be removed
  351. * @return the color panel
  352. * @exception IllegalArgumentException if panel is not in list of
  353. * known chooser panels
  354. */
  355. public AbstractColorChooserPanel removeChooserPanel( AbstractColorChooserPanel panel ) {
  356. int containedAt = -1;
  357. for (int i = 0; i < chooserPanels.length; i++) {
  358. if (chooserPanels[i] == panel) {
  359. containedAt = i;
  360. break;
  361. }
  362. }
  363. if (containedAt == -1) {
  364. throw new IllegalArgumentException("chooser panel not in this chooser");
  365. }
  366. AbstractColorChooserPanel[] newArray = new AbstractColorChooserPanel[chooserPanels.length-1];
  367. if (containedAt == chooserPanels.length-1) { // at end
  368. System.arraycopy(chooserPanels, 0, newArray, 0, newArray.length);
  369. }
  370. else if (containedAt == 0) { // at start
  371. System.arraycopy(chooserPanels, 1, newArray, 0, newArray.length);
  372. }
  373. else { // in middle
  374. System.arraycopy(chooserPanels, 0, newArray, 0, containedAt);
  375. System.arraycopy(chooserPanels, containedAt+1,
  376. newArray, containedAt, (chooserPanels.length - containedAt - 1));
  377. }
  378. setChooserPanels(newArray);
  379. return panel;
  380. }
  381. /**
  382. * Specifies the Color Panels used to choose a color value.
  383. *
  384. * @param panels an array of <code>AbstractColorChooserPanel</code>
  385. * objects
  386. *
  387. * @beaninfo
  388. * bound: true
  389. * hidden: true
  390. * description: An array of different chooser types.
  391. */
  392. public void setChooserPanels( AbstractColorChooserPanel[] panels) {
  393. AbstractColorChooserPanel[] oldValue = chooserPanels;
  394. chooserPanels = panels;
  395. firePropertyChange(CHOOSER_PANELS_PROPERTY, oldValue, panels);
  396. }
  397. /**
  398. * Returns the specified color panels.
  399. *
  400. * @return an array of <code>AbstractColorChooserPanel</code> objects
  401. */
  402. public AbstractColorChooserPanel[] getChooserPanels() {
  403. return chooserPanels;
  404. }
  405. /**
  406. * Returns the data model that handles color selections.
  407. *
  408. * @return a <code>ColorSelectionModel</code> object
  409. */
  410. public ColorSelectionModel getSelectionModel() {
  411. return selectionModel;
  412. }
  413. /**
  414. * Sets the model containing the selected color.
  415. *
  416. * @param newModel the new <code>ColorSelectionModel</code> object
  417. *
  418. * @beaninfo
  419. * bound: true
  420. * hidden: true
  421. * description: The model which contains the currently selected color.
  422. */
  423. public void setSelectionModel(ColorSelectionModel newModel ) {
  424. ColorSelectionModel oldModel = selectionModel;
  425. selectionModel = newModel;
  426. firePropertyChange(JColorChooser.SELECTION_MODEL_PROPERTY, oldModel, newModel);
  427. }
  428. /**
  429. * See <code>readObject</code> and <code>writeObject</code> in
  430. * <code>JComponent</code> for more
  431. * information about serialization in Swing.
  432. */
  433. private void writeObject(ObjectOutputStream s) throws IOException {
  434. s.defaultWriteObject();
  435. if (getUIClassID().equals(uiClassID)) {
  436. byte count = JComponent.getWriteObjCounter(this);
  437. JComponent.setWriteObjCounter(this, --count);
  438. if (count == 0 && ui != null) {
  439. ui.installUI(this);
  440. }
  441. }
  442. }
  443. /**
  444. * Returns a string representation of this <code>JColorChooser</code>.
  445. * This method
  446. * is intended to be used only for debugging purposes, and the
  447. * content and format of the returned string may vary between
  448. * implementations. The returned string may be empty but may not
  449. * be <code>null</code>.
  450. *
  451. * @return a string representation of this <code>JColorChooser</code>
  452. */
  453. protected String paramString() {
  454. StringBuffer chooserPanelsString = new StringBuffer("");
  455. for (int i=0; i<chooserPanels.length; i++) {
  456. chooserPanelsString.append("[" + chooserPanels[i].toString()
  457. + "]");
  458. }
  459. String previewPanelString = (previewPanel != null ?
  460. previewPanel.toString() : "");
  461. return super.paramString() +
  462. ",chooserPanels=" + chooserPanelsString.toString() +
  463. ",previewPanel=" + previewPanelString;
  464. }
  465. /////////////////
  466. // Accessibility support
  467. ////////////////
  468. protected AccessibleContext accessibleContext = null;
  469. /**
  470. * Gets the AccessibleContext associated with this JColorChooser.
  471. * For color choosers, the AccessibleContext takes the form of an
  472. * AccessibleJColorChooser.
  473. * A new AccessibleJColorChooser instance is created if necessary.
  474. *
  475. * @return an AccessibleJColorChooser that serves as the
  476. * AccessibleContext of this JColorChooser
  477. */
  478. public AccessibleContext getAccessibleContext() {
  479. if (accessibleContext == null) {
  480. accessibleContext = new AccessibleJColorChooser();
  481. }
  482. return accessibleContext;
  483. }
  484. /**
  485. * This class implements accessibility support for the
  486. * <code>JColorChooser</code> class. It provides an implementation of the
  487. * Java Accessibility API appropriate to color chooser user-interface
  488. * elements.
  489. */
  490. protected class AccessibleJColorChooser extends AccessibleJComponent {
  491. /**
  492. * Get the role of this object.
  493. *
  494. * @return an instance of AccessibleRole describing the role of the
  495. * object
  496. * @see AccessibleRole
  497. */
  498. public AccessibleRole getAccessibleRole() {
  499. return AccessibleRole.COLOR_CHOOSER;
  500. }
  501. } // inner class AccessibleJColorChooser
  502. }
  503. /*
  504. * Class which builds a color chooser dialog consisting of
  505. * a JColorChooser with "Ok", "Cancel", and "Reset" buttons.
  506. *
  507. * Note: This needs to be fixed to deal with localization!
  508. */
  509. class ColorChooserDialog extends JDialog {
  510. private Color initialColor;
  511. private JColorChooser chooserPane;
  512. public ColorChooserDialog(Component c, String title, boolean modal,
  513. JColorChooser chooserPane,
  514. ActionListener okListener, ActionListener cancelListener)
  515. throws HeadlessException {
  516. super(JOptionPane.getFrameForComponent(c), title, modal);
  517. //setResizable(false);
  518. this.chooserPane = chooserPane;
  519. String okString = UIManager.getString("ColorChooser.okText");
  520. String cancelString = UIManager.getString("ColorChooser.cancelText");
  521. String resetString = UIManager.getString("ColorChooser.resetText");
  522. Container contentPane = getContentPane();
  523. contentPane.setLayout(new BorderLayout());
  524. contentPane.add(chooserPane, BorderLayout.CENTER);
  525. /*
  526. * Create Lower button panel
  527. */
  528. JPanel buttonPane = new JPanel();
  529. buttonPane.setLayout(new FlowLayout(FlowLayout.CENTER));
  530. JButton okButton = new JButton(okString);
  531. getRootPane().setDefaultButton(okButton);
  532. okButton.setActionCommand("OK");
  533. if (okListener != null) {
  534. okButton.addActionListener(okListener);
  535. }
  536. okButton.addActionListener(new ActionListener() {
  537. public void actionPerformed(ActionEvent e) {
  538. hide();
  539. }
  540. });
  541. buttonPane.add(okButton);
  542. JButton cancelButton = new JButton(cancelString);
  543. // The following few lines are used to register esc to close the dialog
  544. Action cancelKeyAction = new AbstractAction() {
  545. public void actionPerformed(ActionEvent e) {
  546. ((AbstractButton)e.getSource()).fireActionPerformed(e);
  547. }
  548. };
  549. KeyStroke cancelKeyStroke = KeyStroke.getKeyStroke((char)KeyEvent.VK_ESCAPE, false);
  550. InputMap inputMap = cancelButton.getInputMap(JComponent.
  551. WHEN_IN_FOCUSED_WINDOW);
  552. ActionMap actionMap = cancelButton.getActionMap();
  553. if (inputMap != null && actionMap != null) {
  554. inputMap.put(cancelKeyStroke, "cancel");
  555. actionMap.put("cancel", cancelKeyAction);
  556. }
  557. // end esc handling
  558. cancelButton.setActionCommand("cancel");
  559. if (cancelListener != null) {
  560. cancelButton.addActionListener(cancelListener);
  561. }
  562. cancelButton.addActionListener(new ActionListener() {
  563. public void actionPerformed(ActionEvent e) {
  564. hide();
  565. }
  566. });
  567. buttonPane.add(cancelButton);
  568. JButton resetButton = new JButton(resetString);
  569. resetButton.addActionListener(new ActionListener() {
  570. public void actionPerformed(ActionEvent e) {
  571. reset();
  572. }
  573. });
  574. int mnemonic = UIManager.getInt("ColorChooser.resetMnemonic", -1);
  575. if (mnemonic != -1) {
  576. resetButton.setMnemonic(mnemonic);
  577. }
  578. buttonPane.add(resetButton);
  579. contentPane.add(buttonPane, BorderLayout.SOUTH);
  580. if (JDialog.isDefaultLookAndFeelDecorated()) {
  581. boolean supportsWindowDecorations =
  582. UIManager.getLookAndFeel().getSupportsWindowDecorations();
  583. if (supportsWindowDecorations) {
  584. getRootPane().setWindowDecorationStyle(JRootPane.COLOR_CHOOSER_DIALOG);
  585. }
  586. }
  587. applyComponentOrientation(((c == null) ? getRootPane() : c).getComponentOrientation());
  588. pack();
  589. setLocationRelativeTo(c);
  590. }
  591. public void show() {
  592. initialColor = chooserPane.getColor();
  593. super.show();
  594. }
  595. public void reset() {
  596. chooserPane.setColor(initialColor);
  597. }
  598. static class Closer extends WindowAdapter implements Serializable{
  599. public void windowClosing(WindowEvent e) {
  600. Window w = e.getWindow();
  601. w.hide();
  602. }
  603. }
  604. static class DisposeOnClose extends ComponentAdapter implements Serializable{
  605. public void componentHidden(ComponentEvent e) {
  606. Window w = (Window)e.getComponent();
  607. w.dispose();
  608. }
  609. }
  610. }
  611. class ColorTracker implements ActionListener, Serializable {
  612. JColorChooser chooser;
  613. Color color;
  614. public ColorTracker(JColorChooser c) {
  615. chooser = c;
  616. }
  617. public void actionPerformed(ActionEvent e) {
  618. color = chooser.getColor();
  619. }
  620. public Color getColor() {
  621. return color;
  622. }
  623. }