/* * Copyright 2011 Uwe Krueger. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.mandelsoft.mand.tool.cm; import com.mandelsoft.mand.cm.ColormapModel; import com.mandelsoft.mand.cm.InterpolationPointEvent; import com.mandelsoft.mand.cm.InterpolationPointEventListener; import com.mandelsoft.mand.cm.InterpolationPoint; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; import java.awt.Window; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.geom.Point2D; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.border.BevelBorder; import javax.swing.colorchooser.ColorSelectionModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import com.mandelsoft.mand.cm.Colormap; import com.mandelsoft.mand.cm.Colormaps; import com.mandelsoft.mand.tool.DefaultPositionSelectionModel; import com.mandelsoft.mand.tool.PositionSelectionModel; import com.mandelsoft.swing.BufferedComponent; import com.mandelsoft.swing.ScaleEvent; import com.mandelsoft.swing.ScaleEventListener; import com.mandelsoft.swing.colorchooser.ColorImageModel; /** * * @author Uwe Krueger */ public class ColormapComponent extends BufferedComponent { static public boolean debug=false; static public final int PIX_X=1; static public final int PIX_Y=1; static public final int COL_X=3; static public final int COL_Y=44; static public final int IP_H=6; private Colormap colormap; private Graphics2D g; private Map<InterpolationPoint,IPUI> ipuis; private Window owner; private ColormapModel model; private int highlight=-1; // shared state for intrepolation points private ColorImageModel cim; private ChangeListener cl=new ChangeListener() { public void stateChanged(ChangeEvent e) { ColormapModel m=(ColormapModel)e.getSource(); if (debug) System.out.println("colormap model change event"); if (colormap!=m.getColormap()) { if (debug) System.out.println("--> setting new colormap"); setColormap(m.getColormap()); } else { redraw(); } } }; private InterpolationPointEventListener ipl=new InterpolationPointEventListener() { public void stateChanged(InterpolationPointEvent e) { InterpolationPoint ip=e.getSource(); // System.out.println("C "+ip.getIndex()+" "+e.getId()); if (e.getId()==InterpolationPointEvent.IPE_ADDED) { if (ipuis !=null && ipuis.get(ip)==null) { IPUI ui=new IPUI(ip); ipuis.put(ip,ui); repaint(); } } } }; ////////////////////////////////////////////////////////////////////////// // Colormap Component ////////////////////////////////////////////////////////////////////////// static private int cnt=0; public ColormapComponent(Window owner) { this(owner,null); } public ColormapComponent(Window owner, ColormapModel model) { this.owner=owner; setBorder(new BevelBorder(BevelBorder.LOWERED)); setColormapModel(model==null?new ColormapModel():model); cim=new ColorImageModel(); ML ml=new ML(); addPaintHandler(new IndexHandler()); getContentPane().addMouseListener(ml); getContentPane().addMouseMotionListener(ml); addScaleEventListener(new ScaleEventListener() { public void componentScaled(ScaleEvent e) { if (debug) System.out.println("REPACK "+(++cnt)); repack(); } public boolean succeedScale(ScaleEvent e) { return getImage().getWidth()*e.getScaleX()>200 || (e.getScaleX()>=e.getOldX()); // return true; } }); setScaleMode(SCALEX); if (debug) System.out.println("scale colormap component"); setScaleX(((double)COL_X)/PIX_X); setScaleY(((double)COL_Y)/PIX_Y); } public boolean isModifiable() { return model.isModifiable(); } public ColormapModel getColormapModel() { return model; } public void setColormapModel(ColormapModel m) { if (model!=null) { model.removeChangeListener(cl); model.removeInterpolationPointEventListener(ipl); } this.model=m; model.addChangeListener(cl); model.addInterpolationPointEventListener(ipl); setColormap(m.getColormap()); } private void setColormap(Colormap map) { if (this.colormap==map) return; if (map==null) { if (debug) System.out.println("clear colormap in dialog"); } else { if (debug) System.out.println("set colormap in dialog "+map.getSize()); } if (ipuis!=null) { for (IPUI ipui:ipuis.values()) ipui.cleanup(); } this.colormap=map; this.highlight=-1; if (colormap!=null) { setImage(new BufferedImage(map.getSize()*PIX_X,PIX_Y, BufferedImage.TYPE_INT_RGB)); ipuis=new HashMap<InterpolationPoint,IPUI>(); for (InterpolationPoint ip:model.getInterpolationPoints()) { if (debug) System.out.println("found ip "+ip.getIndex()+": "+ip.getColor()); ipuis.put(ip,new IPUI(ip)); } } g=createGraphics(); repack(); } private void repack() { redraw(); revalidate(); Component c=this; while (!(c instanceof Window) && c.getParent()!=null) { c=c.getParent(); // System.out.println(c); } // System.out.println(""+getPreferredSize()); if (c!=null && (c instanceof Window)) { Window w=(Window)c; w.pack(); w.repaint(); } } public void redraw() { if (colormap!=null) { for (int i=0; i<colormap.getSize(); i++) { g.setColor(colormap.getColor(i)); g.fillRect(i*PIX_X, 0, PIX_X, PIX_Y); } repaint(); } } public void highLight(int ix) { //System.out.println("index = "+ix); if (highlight!=ix) repaint(); highlight=ix; } private class IndexHandler implements PaintHandler { private BasicStroke stroke=new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10, new float[]{4, 4}, 0); public void paintComponent(Graphics g) { for (IPUI ui:ipuis.values()) { ui.paintComponent(g); } // System.out.println("paint "+highlight); if (highlight>=0) { Graphics2D g2=(Graphics2D)g; g2.setColor(Color.RED); g2.setStroke(stroke); g2.drawLine(middleX(highlight), 0, middleX(highlight), COL_Y); } } } ////////////////////////////////////////////////////////////////////// // Interpolation point ipui ////////////////////////////////////////////////////////////////////// public class IPUI implements InterpolationPointEventListener { private InterpolationPoint ip; private ColorChooser chooser; private SliderSample slider; private javax.swing.event.ChangeListener colorListener; private javax.swing.event.ChangeListener positionListener; private int last_index; private IPUI(InterpolationPoint ip) { this.ip=ip; ip.addInterpolationPointEventListener(this); colorListener=new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent e) { ColorSelectionModel m=(ColorSelectionModel)e.getSource(); if (debug) System.out.println("color from dialog: "+m.getSelectedColor()); IPUI.this.ip.setColor(m.getSelectedColor()); } }; positionListener=new javax.swing.event.ChangeListener() { public void stateChanged(javax.swing.event.ChangeEvent e) { PositionSelectionModel m=(PositionSelectionModel)e.getSource(); if (debug) System.out.println("position from dialog: "+m.getSelectedPosition()); //undraw(); IPUI.this.ip.setRelativePosition(m.getSelectedPosition()); //draw(); } }; } private void cleanup() { ip.removeInterpolationPointEventListener(this); disposeChooser(); } private void setupChooser() { if (chooser==null) { //System.out.println("owner is "+owner); chooser=new ColorChooser(owner); chooser.setColorImageModel(cim); chooser.setModal(false); chooser.setDefaultCloseOperation( JDialog.HIDE_ON_CLOSE); chooser.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent we) { if (debug) System.out.println("closing color edit for "+ip.getIndex()); } }); chooser.getSelectionModel().addChangeListener(colorListener); if (!ip.isFixed()) { slider=new SliderSample(200,20); chooser.setSampleComponent(slider); slider.getPositionSelectionModel().setSelectedPosition(ip.getRelativePosition()); slider.getPositionSelectionModel().addChangeListener(positionListener); } } } public void showChooser() { setupChooser(); chooser.setColor(ip.getColor()); chooser.getSelectionModel().addChangeListener(colorListener); chooser.setVisible(true); } private void disposeChooser() { if (chooser!=null) { chooser.getSelectionModel().removeChangeListener(colorListener); chooser.dispose(); chooser=null; } } private void paintComponent(Graphics g) { last_index=ip.getIndex(); //System.out.println("draw "+last_index); int x=middleX(last_index)-COL_X/2; g.setColor(Color.WHITE); g.fillRect(x, 0, COL_X, IP_H); g.setColor(Color.BLACK); g.fillRect(x, COL_Y-IP_H, COL_X, IP_H); } public boolean match(int x) { int l=middleX(last_index)-COL_X/2; return l<=x && x<l+COL_X; } public void stateChanged(InterpolationPointEvent ipe) { //System.out.println("U "+last_index+" "+ipe.getId()); if (ipe.getId()==InterpolationPointEvent.IPE_NEIGHBOR_CHANGED) { if (slider!=null) slider.getPositionSelectionModel(). setSelectedPosition(ip.getRelativePosition()); } else { if (ipe.getId()==InterpolationPointEvent.IPE_DELETED) { ipuis.remove(ip); cleanup(); } repaint(); } } } private InterpolationPoint getInterpolationPoint(int index, int x) { Iterator<InterpolationPoint> i=model.interpolationPoints(); while (i.hasNext()) { InterpolationPoint ip=i.next(); IPUI ipui=ipuis.get(ip); if (ipui.match(x)) return ip; } return model.getInterpolationPoint(index); } private InterpolationPoint getInterpolationPoint(MouseEvent e) { int index=translateC(e); if (!valid(index)) return null; return getInterpolationPoint(index,e.getX()); } ////////////////////////////////////////////////////////////////////// // Event handling ////////////////////////////////////////////////////////////////////// static Cursor move_cursor=Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR); static Cursor cross_cursor=Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR); static Cursor def_cursor=Cursor.getDefaultCursor(); private int middleX(int c) { int x=translateColorToComponent(c)+((int)getScaleX())/2; return x; } private int translateC(Point2D p) { return translateX(toInt(p.getX())/PIX_X); } private int translateC(MouseEvent e) { return translateC(e.getPoint()); } private int translateColorToComponent(double c) { return translateToComponentX(toInt(c*PIX_X)); } private boolean valid(int index) { if (!isModifiable()) return false; if (index<1 || index>=colormap.getSize()) return false; return true; } public class ML extends MouseAdapter { private InterpolationPoint current=null; @Override public void mouseClicked(MouseEvent e) { int index=translateC(e); //System.out.println("clicked: map "+index+": "+e); if (!valid(index)) return; InterpolationPoint ip=getInterpolationPoint(index,e.getX()); if (debug) System.out.println("clicked "+index+": "+e); if (e.getButton()==MouseEvent.BUTTON1) { if (e.getClickCount()==1) { if (!e.isControlDown()) { if (ip==null) { if (debug) System.out.println(" create "+index); model.createInterpolationPoint(index); setCursor(cross_cursor); } else { IPUI ui=ipuis.get(ip); if (debug) System.out.println(" found "+ip.toString()); ui.showChooser(); } } else { if (ip!=null) { ip.delete(); } } } } } @Override public void mouseDragged(MouseEvent e) { int index=translateC(e); if (!valid(index)) return; //InterpolationPoint ip=getInterpolationPoint(index,e.getX()); //System.out.println("drag "+current+" to "+index+": "+e); if (current!=null&&!current.isFixed()) { //System.out.println("drag "+current.getIndex()+" "+index); if (index<=current.getPrev().getIndex()) index=current.getPrev().getIndex()+1; if (index>=current.getNext().getIndex()) index=current.getNext().getIndex()-1; current.setIndex(index,true); } } @Override public void mousePressed(MouseEvent e) { InterpolationPoint ip=getInterpolationPoint(e); if (e.getButton()==MouseEvent.BUTTON1) { if (ip!=null) { if (debug) System.out.println("select "+ip.getIndex()); setCursor(move_cursor); current=ip; } } } @Override public void mouseReleased(MouseEvent e) { InterpolationPoint ip=getInterpolationPoint(e); if (e.getButton()==MouseEvent.BUTTON1) { if (current!=null) { if (debug) System.out.println("reset cursor2"); setCursor(def_cursor); current=null; } } } @Override public void mouseMoved(MouseEvent e) { InterpolationPoint ip=getInterpolationPoint(e); if (ip==null) setCursor(def_cursor); else setCursor(cross_cursor); } } ////////////////////////////////////////////////////////////////////// // RGB Chooser Slider Sample ////////////////////////////////////////////////////////////////////// public class SliderSample extends ColorChooser.ColorSample { static public final int W=2; private Insets insets; private javax.swing.event.ChangeListener adjustHandler=new AdjustHandler(); private PositionSelectionModel position; public SliderSample(int width, int height) { super(width, height); insets=new Insets(0, 0, 0, 0); SliderHandler h=new SliderHandler(); adjustHandler=new AdjustHandler(); addMouseListener(h); addMouseMotionListener(h); getInsets(insets); setPositionSelectionModel(new DefaultPositionSelectionModel()); } public void setPositionSelectionModel(PositionSelectionModel p) { if (position!=null) position.removeChangeListener(adjustHandler); position=p; p.addChangeListener(adjustHandler); } public PositionSelectionModel getPositionSelectionModel() { return position; } protected int getPosition() { int max=getWidth()-insets.right-2*W-1-(insets.left+W); int x=(int)(position.getSelectedPosition()*max); return x+insets.left+W; } protected void setPosition(int p) { int x=p-insets.left-W; int max=getWidth()-insets.right-2*W-1-(insets.left+W); getPositionSelectionModel().setSelectedPosition(x/(double)(max)); //System.out.println("= "+getPositionSelectionModel(). // getSelectedPosition()*100); } @Override protected void paintComponent(Graphics g) { getInsets(insets); super.paintComponent(g); drawSlider(g); } private void drawSlider(Graphics g) { int pos=getPosition(); g.setColor(Color.BLACK); for (int i=1; i<=W; i++) { g.drawLine(pos-i, insets.top, pos-i, getHeight()-insets.bottom-1); g.drawLine(pos+W+i-1, insets.top, pos+W+i-1, getHeight()-insets.bottom-1); } g.setColor(Color.WHITE); for (int i=0; i<W; i++) { g.drawLine(pos+i, insets.top, pos+i, getHeight()-insets.bottom-1); } } private class AdjustHandler implements javax.swing.event.ChangeListener { public void stateChanged(javax.swing.event.ChangeEvent e) { repaint(); } } private class SliderHandler extends MouseAdapter { private boolean active; private int offset; public SliderHandler() { } private boolean isValidPosition(int x) { //return true; return x>=insets.left+W && x<getWidth()-insets.right-2*W; } private int adjustPosition(int x) { //return true; if (x<insets.left+W) return insets.left+W; if (x>=getWidth()-insets.right-2*W) return getWidth()-insets.right-2*W-1; return x; } @Override public void mouseDragged(MouseEvent e) { //System.out.println("drag"); if (active) { int x=adjustPosition(e.getX()-offset); //System.out.println("move to "+x); setPosition(x); } } @Override public void mousePressed(MouseEvent e) { getInsets(insets); if (e.getButton()==MouseEvent.BUTTON1) { int x=e.getX(); int position=getPosition(); if (debug) System.out.println("button pressed at "+x+": "+insets); if (x>=position-W&&x<position+2*W) { //System.out.println("activate"); offset=e.getX()-position; active=true; } } } @Override public void mouseReleased(MouseEvent e) { if (e.getButton()==MouseEvent.BUTTON1) { //System.out.println("deactivate"); active=false; } } } } ////////////////////////////////////////////////////////////////////// // main ////////////////////////////////////////////////////////////////////// public static void main(String[] args) { //Schedule a job for the event dispatch thread: //creating and showing this application's GUI. SwingUtilities.invokeLater(new Runnable() { public void run() { JFrame frame=new TestFrame(); frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); frame.setVisible(true); } }); } static class TestFrame extends JFrame { ColormapComponent cc; ColormapModel model; TestFrame() { setup(); } void setup() { cc=new ColormapComponent(this); model=new ColormapModel(); model.setColormap(new Colormaps.Simple(256,Color.BLUE,Color.WHITE)); model.setModifiable(true); add(cc); cc.setColormapModel(model); pack(); setResizable(false); } } }