package it.unibas.bartgui.view.panel.editor.Dependency.tableModel;

/**
 *
 * @author Grandinetti Giovanni <[email protected]>
 */
/* 
  * $Id$ 
  * 
  * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle, 
  * Santa Clara, California 95054, U.S.A. All rights reserved. 
  * 
  * This library is free software; you can redistribute it and/or 
  * modify it under the terms of the GNU Lesser General Public 
  * License as published by the Free Software Foundation; either 
  * version 2.1 of the License, or (at your option) any later version. 
  *  
  * This library is distributed in the hope that it will be useful, 
  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  * Lesser General Public License for more details. 
  *  
  * You should have received a copy of the GNU Lesser General Public 
  * License along with this library; if not, write to the Free Software 
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
  * 
  */ 

 import java.awt.Component; 
 import java.awt.Graphics2D; 
 import java.awt.Point; 
 import java.beans.PropertyChangeEvent; 
 import java.beans.PropertyChangeListener; 
  
 import org.jdesktop.swingx.JXTable; 
 import org.jdesktop.swingx.decorator.ComponentAdapter; 
 import org.jdesktop.swingx.decorator.PainterHighlighter; 
 import org.jdesktop.swingx.painter.AbstractLayoutPainter; 
 import org.jdesktop.swingx.painter.AbstractLayoutPainter.HorizontalAlignment; 
 import org.jdesktop.swingx.painter.AbstractPainter; 
 import org.jdesktop.swingx.painter.Painter; 
  
 // <snip> Relative Decorator 
 // Specialized PainterHighlighter which has a Relativizer 
@SuppressWarnings({"rawtypes","unchecked"})
 public class RelativePainterHighlighter extends PainterHighlighter { 
  
     private Relativizer relativizer; 
  
     // </snip> 
     public RelativePainterHighlighter() { 
         this(null); 
     } 
  
     public RelativePainterHighlighter(Painter delegate) { 
         super(delegate); 
     } 
  
     public void setHorizontalAlignment(HorizontalAlignment align) { 
         getPainter().setHorizontalAlignment(align); 
         fireStateChanged(); 
     } 
  
     public HorizontalAlignment getHorizontalAlignment() { 
         return getPainter().getHorizontalAlignment(); 
     } 
  
     /** 
      * @param maxValue the maxValue to set 
      */ 
     public void setRelativizer(Relativizer relativizer) { 
         this.relativizer = relativizer; 
         fireStateChanged(); 
     } 
  
     public Relativizer getRelativizer() { 
         return relativizer; 
     } 
  
     @Override 
     protected Component doHighlight(Component component, 
             ComponentAdapter adapter) { 
         // <snip> Relative Decorator 
         // configures the RelativePainter with the value returned by the 
         // Relativizer 
         float xPercent = relativizer.getRelativeValue(adapter); 
         getPainter().setXFactor(xPercent); 
         getPainter().setVisible(xPercent != Relativizer.ZERO); 
         return super.doHighlight(component, adapter); 
         // </snip> 
     } 
  
     /** 
      * Overridden to wrap a RelativePainter around the given, if not already is 
      * of type RelativePainter. 
      */ 
     // <snip> Relative Decorator 
     // Wraps Painter into RelativePainter (hack around missing api in swingx) 
     @Override 
     public void setPainter(Painter painter) { 
         if (!(painter instanceof RelativePainter)) { 
             painter = new RelativePainter(painter); 
         } 
         super.setPainter(painter); 
     } 
  
     // </snip> 
  
     @Override 
     public RelativePainter getPainter() { 
         return (RelativePainter) super.getPainter(); 
     } 
  
     @Override 
     protected boolean canHighlight(Component component, ComponentAdapter adapter) { 
         return relativizer != null && super.canHighlight(component, adapter); 
     } 
  
     // ------------------- Relativizer 
  
     // <snip> Relativizer 
     // One-method interface to map a cell value to a float 
     public static interface Relativizer { 
         /** 
          * Returns a float in the range of 0.0f to 1.0f inclusive which 
          * indicates the relative value of the given adapter's value. 
          *  
          * @param adapter 
          * @return 
          */ 
         public float getRelativeValue(ComponentAdapter adapter); 
  
         // </snip> 
  
         public static final float ZERO = 0.0f; 
  
         public static final float ONE = 1.0f; 
  
     } 
  
     public static class NumberRelativizer implements Relativizer { 
  
         private Number max; 
  
         private Number current; 
  
         private int valueColumn; 
  
         private boolean spread; 
  
         public NumberRelativizer(Number max) { 
             this(max, max); 
         } 
  
         public NumberRelativizer(Number max, Number current) { 
             this(0, max, current); 
         } 
  
         /** 
          * @param i 
          * @param max 
          * @param current 
          */ 
         public NumberRelativizer(int column, Number max, Number current) { 
             this(column, false, max, current); 
         } 
  
         public NumberRelativizer(int column, boolean spreadColumns, Number max, 
                 Number current) { 
             this.current = current; 
             this.max = max; 
             this.valueColumn = column; 
             this.spread = spreadColumns; 
         } 
  
         // <snip> Relativizer 
         // Simplistic base implementation of Relativizer which handles Numbers 
         @Override 
         public float getRelativeValue(ComponentAdapter adapter) { 
             if (getNumber(adapter) == null) { 
                 return ZERO; 
             } 
             float value = getNumber(adapter).floatValue(); 
             float limit = Math.min(getCurrent().floatValue(), value); 
             if (isZero(limit)) { 
                 return ZERO; 
             } 
             float percent = limit / getMax().floatValue(); 
  
             if (!spread) { 
                 return percent; 
             } 
  
             int width = adapter.getComponent().getWidth(); 
             int pixelLocation = (int) (percent * width); 
             int visualColumn = getColumnAt(adapter, pixelLocation); 
             if (adapter.column < visualColumn) { 
                 return ONE; 
             } else if (adapter.column > visualColumn) { 
                 return ZERO; 
             } 
  
             int visualColumnWidth = getColumnWidth(adapter, visualColumn); 
             int startColumn = getColumnLocation(adapter, visualColumn); 
             int valueWidth = pixelLocation - startColumn; 
             return (float) valueWidth / (float) visualColumnWidth; 
             // </snip> 
         } 
  
         /** 
          * Returns a Number representation of the cell value or null if it 
          * doesn't have any. Subclasses are meant to override this for custom 
          * mappings. 
          * <p> 
          *  
          * This implementation checks and returns the type of the current cell. 
          *  
          * @param adapter the ComponentAdapter which defines the current cell. 
          * @return a Number representing the current cell or null 
          */ 
         // <snip> Relativizer 
         // simple value-to-Number mapping which handles Number types. 
         protected Number getNumber(ComponentAdapter adapter) { 
             if (adapter.getValue() instanceof Number) 
                 return (Number) adapter.getValue(valueColumn); 
             return null; 
         } 
         // </snip> 
  
         private int getColumnLocation(ComponentAdapter adapter, int visualColumn) { 
             if (!(adapter.getComponent() instanceof JXTable)) { 
                 return 0; 
             } 
             JXTable table = (JXTable) adapter.getComponent(); 
             // PENDING JW: guard against null header 
             return table.getTableHeader().getHeaderRect(visualColumn).x; 
         } 
  
         private int getColumnWidth(ComponentAdapter adapter, int visualColumn) { 
             if (!(adapter.getComponent() instanceof JXTable)) { 
                 return adapter.getComponent().getWidth(); 
             } 
             JXTable table = (JXTable) adapter.getComponent(); 
             return table.getColumn(visualColumn).getWidth(); 
         } 
  
         private int getColumnAt(ComponentAdapter adapter, int pixelLocation) { 
             if (!(adapter.getComponent() instanceof JXTable)) { 
                 return 0; 
             } 
             JXTable table = (JXTable) adapter.getComponent(); 
             // PENDING JW: guard against null header 
             return table.getTableHeader().columnAtPoint( 
                     new Point(pixelLocation, 10)); 
         } 
  
         protected Number getCurrent() { 
             return current; 
         } 
  
         protected Number getMax() { 
             return max; 
         } 
  
         protected boolean isZero(float limit) { 
             return Math.abs(limit) < 0.002; 
         } 
  
         protected int getValueColumn() { 
             return valueColumn; 
         } 
     } 
  
     // --------- hack around missing size proportional painters 
  
     public static class RelativePainter<T> extends AbstractLayoutPainter<T> { 
  
         private Painter<? super T> painter; 
  
         private double xFactor; 
  
         private double yFactor; 
  
         private boolean visible; 
  
         private PropertyChangeListener painterListener; 
  
         public RelativePainter() { 
             this(null); 
         } 
  
         public RelativePainter(Painter<? super T> delegate) { 
             this.painter = delegate; 
             installPainterListener(); 
         } 
  
         public RelativePainter(Painter<? super T> delegate, double xPercent) { 
             this(delegate); 
             xFactor = xPercent; 
         } 
  
         public void setPainter(Painter<? super T> painter) { 
             uninstallPainterListener(); 
             Object old = getPainter(); 
             this.painter = painter; 
             installPainterListener(); 
             firePropertyChange("painter", old, getPainter()); 
         } 
  
         public Painter<? super T> getPainter() { 
             return painter; 
         } 
  
         public void setXFactor(double xPercent) { 
             double old = getXFactor(); 
             this.xFactor = xPercent; 
             firePropertyChange("xFactor", old, getXFactor()); 
         } 
  
         /** 
          * @return 
          */ 
         public double getXFactor() { 
             return xFactor; 
         } 
  
         public void setYFactor(double yPercent) { 
             this.yFactor = yPercent; 
         } 
  
         @Override 
         protected void doPaint(Graphics2D g, T object, int width, int height) { 
             if (painter == null) 
                 return; 
             // use epsilon 
             if (xFactor != 0.0) { 
                 int oldWidth = width; 
                 width = (int) (xFactor * width); 
                 if (getHorizontalAlignment() == HorizontalAlignment.RIGHT) { 
                     g.translate(oldWidth - width, 0); 
                 } 
             } 
             if (yFactor != 0.0) { 
                 int oldHeight = height; 
                 height = (int) (yFactor * height); 
                 if (getVerticalAlignment() == VerticalAlignment.BOTTOM) { 
                     g.translate(0, oldHeight - height); 
                 } 
             } 
  
             painter.paint(g, object, width, height); 
         } 
  
         /** 
          * Overridden to take over completely: super does strange things with 
          * dirty which result in property changes fired during painting. 
          */ 
         @Override 
         public boolean isVisible() { 
             return visible; 
         } 
  
         /** 
          * Overridden to take over completely: super does strange things with 
          * dirty which result in property changes fired during painting. 
          */ 
         @Override 
         public void setVisible(boolean visible) { 
             if (isVisible() == visible) 
                 return; 
             this.visible = visible; 
             firePropertyChange("visible", !visible, isVisible()); 
         } 
  
         /** 
          * Installs a listener to the painter if appropriate. This 
          * implementation registers its painterListener if the Painter is of 
          * type AbstractPainter. 
          */ 
         protected void installPainterListener() { 
             if (getPainter() instanceof AbstractPainter) { 
                 ((AbstractPainter) getPainter()) 
                         .addPropertyChangeListener(getPainterListener()); 
             } 
         } 
  
         /** 
          * Uninstalls a listener from the painter if appropriate. This 
          * implementation removes its painterListener if the Painter is of type 
          * AbstractPainter. 
          */ 
         protected void uninstallPainterListener() { 
             if (getPainter() instanceof AbstractPainter) { 
                 ((AbstractPainter) getPainter()) 
                         .removePropertyChangeListener(painterListener); 
             } 
         } 
  
         /** 
          * Lazyly creates and returns the property change listener used to 
          * listen to changes of the painter. 
          *  
          * @return the property change listener used to listen to changes of the 
          *         painter. 
          */ 
         protected final PropertyChangeListener getPainterListener() { 
             if (painterListener == null) { 
                 painterListener = createPainterListener(); 
             } 
             return painterListener; 
         } 
  
         /** 
          * Creates and returns the property change listener used to listen to 
          * changes of the painter. 
          * <p> 
          *  
          * This implementation fires a stateChanged on receiving any 
          * propertyChange, if the isAdjusting flag is false. Otherwise does 
          * nothing. 
          *  
          * @return the property change listener used to listen to changes of the 
          *         painter. 
          */ 
         protected PropertyChangeListener createPainterListener() { 
             PropertyChangeListener l = new PropertyChangeListener() { 
  
                 public void propertyChange(PropertyChangeEvent evt) { 
                     setDirty(true); 
                 } 
  
             }; 
             return l; 
         } 
  
     } 
  
 }