/*
* Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* o Neither the name of Flamingo Kirill Grouchnikov nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.pushingpixels.flamingo.api.common.popup;
import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.JPanel;
import org.pushingpixels.flamingo.api.common.JCommandMenuButton;
import org.pushingpixels.flamingo.api.common.JCommandToggleMenuButton;
import org.pushingpixels.flamingo.internal.ui.common.popup.JColorSelectorComponent;
import org.pushingpixels.flamingo.internal.ui.common.popup.JColorSelectorPanel;
public class JColorSelectorPopupMenu extends JCommandPopupMenu {
private ColorSelectorCallback colorSelectorCallback;
private JColorSelectorPanel lastColorSelectorPanel;
private static LinkedList<Color> recentlySelected = new LinkedList<Color>();
public static interface ColorSelectorCallback {
public void onColorRollover(Color color);
public void onColorSelected(Color color);
}
public JColorSelectorPopupMenu(ColorSelectorCallback colorSelectorCallback) {
this.colorSelectorCallback = colorSelectorCallback;
}
public void addColorSectionWithDerived(String label, Color[] primaryColors) {
if ((primaryColors == null) || (primaryColors.length != 10)) {
throw new IllegalArgumentException("Must pass exactly 10 colors");
}
JPanel selectorContainer = new MultiRowSelector(primaryColors);
JColorSelectorPanel selector = new JColorSelectorPanel(label,
selectorContainer);
this.addMenuPanel(selector);
this.lastColorSelectorPanel = selector;
}
public void addColorSection(String label, Color[] primaryColors) {
if ((primaryColors == null) || (primaryColors.length != 10)) {
throw new IllegalArgumentException("Must pass exactly 10 colors");
}
JPanel selectorContainer = new SingleRowSelector(primaryColors);
JColorSelectorPanel selector = new JColorSelectorPanel(label,
selectorContainer);
this.addMenuPanel(selector);
this.lastColorSelectorPanel = selector;
}
public void addRecentSection(String label) {
JPanel recent = new SingleRowSelector(recentlySelected
.toArray(new Color[0]));
JColorSelectorPanel recentPanel = new JColorSelectorPanel(label, recent);
recentPanel.setLastPanel(true);
this.addMenuPanel(recentPanel);
this.lastColorSelectorPanel = recentPanel;
}
@Override
public void addMenuButton(JCommandMenuButton menuButton) {
super.addMenuButton(menuButton);
this.updateLastColorSelectorPanel();
}
@Override
public void addMenuButton(JCommandToggleMenuButton menuButton) {
super.addMenuButton(menuButton);
this.updateLastColorSelectorPanel();
}
@Override
public void addMenuSeparator() {
super.addMenuSeparator();
this.updateLastColorSelectorPanel();
}
private void updateLastColorSelectorPanel() {
if (this.lastColorSelectorPanel != null) {
this.lastColorSelectorPanel.setLastPanel(true);
this.lastColorSelectorPanel = null;
}
}
public ColorSelectorCallback getColorSelectorCallback() {
return this.colorSelectorCallback;
}
private static void wireToLRU(JColorSelectorComponent colorSelector) {
colorSelector
.addColorSelectorCallback(new JColorSelectorPopupMenu.ColorSelectorCallback() {
@Override
public void onColorSelected(Color color) {
addColorToRecentlyUsed(color);
}
@Override
public void onColorRollover(Color color) {
}
});
}
public synchronized static List<Color> getRecentlyUsedColors() {
return Collections.unmodifiableList(recentlySelected);
}
public synchronized static void addColorToRecentlyUsed(Color color) {
// is in?
if (recentlySelected.contains(color)) {
recentlySelected.remove(color);
recentlySelected.addLast(color);
return;
}
if (recentlySelected.size() == 10) {
recentlySelected.removeFirst();
}
recentlySelected.addLast(color);
}
private class SingleRowSelector extends JPanel {
public SingleRowSelector(final Color... colors) {
final JColorSelectorComponent[] comps = new JColorSelectorComponent[colors.length];
for (int i = 0; i < colors.length; i++) {
comps[i] = new JColorSelectorComponent(colors[i],
colorSelectorCallback);
wireToLRU(comps[i]);
this.add(comps[i]);
}
this.setLayout(new LayoutManager() {
@Override
public void addLayoutComponent(String name, Component comp) {
}
@Override
public void removeLayoutComponent(Component comp) {
}
@Override
public Dimension minimumLayoutSize(Container parent) {
return new Dimension(10, 10);
}
@Override
public Dimension preferredLayoutSize(Container parent) {
int gap = getGap();
int size = getSize();
return new Dimension(colors.length * size
+ (colors.length + 1) * gap, size + 2 * gap);
}
@Override
public void layoutContainer(Container parent) {
int gap = getGap();
int size = getSize();
if (parent.getComponentOrientation().isLeftToRight()) {
int x = gap;
int y = gap;
for (int i = 0; i < colors.length; i++) {
comps[i].setBounds(x, y, size, size);
x += (size + gap);
}
} else {
int x = getWidth() - gap;
int y = gap;
for (int i = 0; i < colors.length; i++) {
comps[i].setBounds(x - size, y, size, size);
x -= (size + gap);
}
}
}
private int getGap() {
return 4;
}
private int getSize() {
return 13;
}
});
}
}
private class MultiRowSelector extends JPanel {
static final int SECONDARY_ROWS = 5;
public MultiRowSelector(final Color... colors) {
final JColorSelectorComponent[][] comps = new JColorSelectorComponent[colors.length][1 + SECONDARY_ROWS];
for (int i = 0; i < colors.length; i++) {
Color primary = colors[i];
comps[i][0] = new JColorSelectorComponent(primary,
colorSelectorCallback);
wireToLRU(comps[i][0]);
this.add(comps[i][0]);
float[] primaryHsb = new float[3];
Color.RGBtoHSB(primary.getRed(), primary.getGreen(), primary
.getBlue(), primaryHsb);
for (int row = 1; row <= SECONDARY_ROWS; row++) {
float bFactor = (float) (row - 1)
/ (float) (SECONDARY_ROWS);
bFactor = (float) Math.pow(bFactor, 1.4f);
float brightness = 1.0f - bFactor;
if (primaryHsb[1] == 0.0f) {
// special handling for gray scale
float max = 0.5f + 0.5f * primaryHsb[2];
brightness = max * (SECONDARY_ROWS - row + 1)
/ SECONDARY_ROWS;
}
Color secondary = new Color(Color.HSBtoRGB(primaryHsb[0],
primaryHsb[1] * (row + 1) / (SECONDARY_ROWS + 1),
brightness));
comps[i][row] = new JColorSelectorComponent(secondary,
colorSelectorCallback);
comps[i][row].setTopOpen(row > 1);
comps[i][row].setBottomOpen(row < SECONDARY_ROWS);
wireToLRU(comps[i][row]);
this.add(comps[i][row]);
}
}
this.setLayout(new LayoutManager() {
@Override
public void addLayoutComponent(String name, Component comp) {
}
@Override
public void removeLayoutComponent(Component comp) {
}
@Override
public Dimension minimumLayoutSize(Container parent) {
return new Dimension(10, 10);
}
@Override
public Dimension preferredLayoutSize(Container parent) {
int gap = getGap();
int size = getSize();
return new Dimension(colors.length * size
+ (colors.length + 1) * gap, gap + size + gap
+ SECONDARY_ROWS * size + gap);
}
@Override
public void layoutContainer(Container parent) {
int gap = getGap();
int size = getSize();
if (parent.getComponentOrientation().isLeftToRight()) {
int y = gap;
for (int row = 0; row <= SECONDARY_ROWS; row++) {
int x = gap;
for (int i = 0; i < colors.length; i++) {
comps[i][row].setBounds(x, y, size, size);
x += (size + gap);
}
y += size;
if (row == 0) {
y += gap;
}
}
} else {
int y = gap;
for (int row = 0; row <= SECONDARY_ROWS; row++) {
int x = getWidth() - gap;
for (int i = 0; i < colors.length; i++) {
comps[i][row]
.setBounds(x - size, y, size, size);
x -= (size + gap);
}
y += size;
if (row == 0) {
y += gap;
}
}
}
}
private int getGap() {
return 4;
}
private int getSize() {
return 13;
}
});
}
}
}