001/* ======================================================
002 * Orson : a free chart beans library based on JFreeChart
003 * ======================================================
004 *
005 * (C) Copyright 2007, by Object Refinery Limited.
006 *
007 * Project Info:  http://www.jfree.org/orson/
008 *
009 * This library is free software; you can redistribute it and/or modify it 
010 * under the terms of the GNU Lesser General Public License as published by 
011 * the Free Software Foundation; either version 2.1 of the License, or 
012 * (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but 
015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
017 * License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this library; if not, write to the Free Software
021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
022 * USA.  
023 *
024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
025 * in the United States and other countries.]
026 * 
027 */
028
029package org.jfree.beans;
030
031import java.awt.Font;
032import java.awt.Insets;
033import java.awt.Paint;
034import java.awt.event.MouseEvent;
035import java.beans.PropertyChangeEvent;
036import java.text.NumberFormat;
037
038import org.jfree.beans.events.CategoryItemClickEvent;
039import org.jfree.beans.events.CategoryItemClickListener;
040import org.jfree.chart.axis.AxisLocation;
041import org.jfree.chart.entity.CategoryItemEntity;
042import org.jfree.chart.entity.ChartEntity;
043import org.jfree.chart.entity.EntityCollection;
044import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
045import org.jfree.chart.plot.CategoryPlot;
046import org.jfree.chart.plot.PlotOrientation;
047import org.jfree.chart.renderer.category.CategoryItemRenderer;
048
049/**
050 * A base class for chart beans that use a {@link CategoryPlot}.
051 */
052public abstract class AbstractCategoryChart extends AbstractChart {
053
054    /**
055     * Creates a new instance.
056     */
057    public AbstractCategoryChart() {
058        super();
059    }
060    
061    /**
062     * Returns the orientation for the plot.
063     * 
064     * @return The orientation for the plot.
065     * 
066     * @see #setOrientation(PlotOrientation)
067     */
068    public PlotOrientation getOrientation() {
069        PlotOrientation result = null;
070        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
071        if (plot != null) {
072            result = plot.getOrientation();
073        }
074        return result;        
075    }
076    
077    /**
078     * Sets the orientation for the plot and fires a 
079     * {@link PropertyChangeEvent} for the <code>orientation</code> property.
080     * 
081     * @param orientation  the orientation (<code>null</code> not permitted).
082     * 
083     * @see #getOrientation()
084     */
085    public void setOrientation(PlotOrientation orientation) {
086        if (orientation == null) {
087            throw new IllegalArgumentException("Null 'orientation' argument.");
088        }
089        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
090        if (plot != null) {
091            PlotOrientation old = plot.getOrientation();
092            plot.setOrientation(orientation);
093            firePropertyChange("orientation", old, orientation);
094        }        
095    }
096    
097    /**
098     * Returns the category axis label.
099     * 
100     * @return The category axis label (possibly <code>null</code>).
101     * 
102     * @see #setCategoryAxisLabel(String)
103     */
104    public String getCategoryAxisLabel() {
105        String result = null;
106        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
107        if (plot != null) {
108            result = plot.getDomainAxis().getLabel();
109        }
110        return result;
111    }
112    
113    /**
114     * Sets the category axis label and fires a {@link PropertyChangeEvent} for 
115     * the <code>categoryAxisLabel</code> property.
116     * 
117     * @param label  the label (<code>null</code> permitted).
118     * 
119     * @see #getCategoryAxisLabel()
120     */
121    public void setCategoryAxisLabel(String label) {
122        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
123        if (plot != null) {
124            String old = plot.getDomainAxis().getLabel();
125            plot.getDomainAxis().setLabel(label);
126            firePropertyChange("categoryAxisLabel", old, label);
127        }                
128    }
129    
130    /**
131     * Returns the font used for the main label on the category axis.
132     * 
133     * @return The font.
134     * 
135     * @see #setCategoryAxisLabelFont(Font)
136     */
137    public Font getCategoryAxisLabelFont() {
138        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
139        if (plot != null) {
140            return plot.getDomainAxis().getLabelFont();
141        }
142        return null;
143    }
144    
145    /**
146     * Sets the font used for the main label on the category axis and fires a
147     * {@link PropertyChangeEvent} for the <code>categoryAxisLabelFont</code> 
148     * property.
149     * 
150     * @param font  the font (<code>null</code> permitted).
151     */
152    public void setCategoryAxisLabelFont(Font font) {
153        if (font == null) {
154            throw new IllegalArgumentException("Null 'font' argument.");
155        }
156        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
157        if (plot != null) {
158            Font old = plot.getDomainAxis().getLabelFont();
159            plot.getDomainAxis().setLabelFont(font);
160            firePropertyChange("categoryAxisLabelFont", old, font);
161        }
162        
163    }
164
165    /**
166     * Returns the paint used for the main label on the category axis.
167     * 
168     * @return The paint.
169     * 
170     * @see #setCategoryAxisLabelPaint(Paint)
171     */
172    public Paint getCategoryAxisLabelPaint() {
173        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
174        if (plot != null) {
175            return plot.getDomainAxis().getLabelPaint();
176        }
177        return null;
178    }
179    
180    /**
181     * Sets the paint used for the main label on the category axis and fires a 
182     * {@link PropertyChangeEvent} for the <code>categoryAxisLabelPaint</code> 
183     * property.
184     * 
185     * @param paint  the paint (<code>null</code> not permitted).
186     * 
187     * @see #getCategoryAxisLabelPaint()
188     */
189    public void setCategoryAxisLabelPaint(Paint paint) {
190        if (paint == null) {
191            throw new IllegalArgumentException("Null 'paint' argument.");
192        }
193        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
194        if (plot != null) {
195            Paint old = plot.getDomainAxis().getLabelPaint();
196            plot.getDomainAxis().setLabelPaint(paint);
197            firePropertyChange("categoryAxisLabelPaint", old, paint);
198        }
199    }
200    
201    /**
202     * Returns the lower margin for the category axis.
203     * 
204     * @return The lower margin.
205     * 
206     * @see #setCategoryAxisLowerMargin(double)
207     */
208    public double getCategoryAxisLowerMargin() {
209        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
210        if (plot != null) {
211            return plot.getDomainAxis().getLowerMargin();
212        }
213        return -1.0;
214    }
215    
216    /**
217     * Sets the lower margin for the category axis and fires a 
218     * {@link PropertyChangeEvent} for the <code>categoryAxisLowerMargin</code>
219     * property.
220     * 
221     * @param margin  the margin.
222     * 
223     * @see #getCategoryAxisLowerMargin()
224     */
225    public void setCategoryAxisLowerMargin(double margin) {
226        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
227        if (plot != null) {
228            double old = plot.getDomainAxis().getLowerMargin();
229            plot.getDomainAxis().setLowerMargin(margin);
230            firePropertyChange("categoryAxisLowerMargin", old, margin);
231        }                
232    }
233    
234    /**
235     * Returns the upper margin for the category axis.
236     * 
237     * @return The upper margin for the category axis.
238     * 
239     * @see #setCategoryAxisUpperMargin(double)
240     */
241    public double getCategoryAxisUpperMargin() {
242        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
243        if (plot != null) {
244            return plot.getDomainAxis().getUpperMargin();
245        }
246        return -1.0;
247    }
248
249    /**
250     * Sets the upper margin for the category axis and fires a 
251     * {@link PropertyChangeEvent} for the <code>categoryAxisUpperMargin</code>
252     * property.
253     * 
254     * @param margin  the margin.
255     * 
256     * @see #getCategoryAxisUpperMargin()
257     */
258    public void setCategoryAxisUpperMargin(double margin) {
259        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
260        if (plot != null) {
261            double old = plot.getDomainAxis().getUpperMargin();
262            plot.getDomainAxis().setUpperMargin(margin);
263            firePropertyChange("categoryAxisUpperMargin", old, margin);
264        }                
265    }
266    
267    /**
268     * Returns the margin between categories along the axis.
269     * 
270     * @return The margin.
271     * 
272     * @see #setCategoryAxisMargin(double)
273     */
274    public double getCategoryAxisMargin() {
275        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
276        if (plot != null) {
277            return plot.getDomainAxis().getCategoryMargin();
278        }
279        return -1.0;
280    }
281    
282    /**
283     * Sets the total space allocated to the margin between categories 
284     * along the axis and fires a {@link PropertyChangeEvent} for 
285     * the <code>categoryAxisMargin</code> property.
286     * 
287     * @param margin  the margin.
288     * 
289     * @see #getCategoryAxisMargin()
290     */
291    public void setCategoryAxisMargin(double margin) {
292        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
293        if (plot != null) {
294            double old = plot.getDomainAxis().getCategoryMargin();
295            plot.getDomainAxis().setCategoryMargin(margin);
296            firePropertyChange("categoryAxisMargin", old, margin);
297        }                
298    }
299    
300    /**
301     * Returns the label for the value axis.
302     * 
303     * @return The label for the value axis.
304     * 
305     * @see #setValueAxisLabel(String)
306     */
307    public String getValueAxisLabel() {
308        String result = null;
309        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
310        if (plot != null) {
311            result = plot.getRangeAxis().getLabel();
312        }
313        return result;
314    }
315    
316    /**
317     * Sets the label for the value axis and fires a 
318     * {@link PropertyChangeEvent} for the <code>valueAxisLabel</code> property.
319     * 
320     * @param label  the label.
321     * 
322     * @see #getValueAxisLabel()
323     */
324    public void setValueAxisLabel(String label) {
325        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
326        if (plot != null) {
327            String old = plot.getRangeAxis().getLabel();
328            plot.getRangeAxis().setLabel(label);
329            firePropertyChange("valueAxisLabel", old, label);
330        }                
331    }
332    
333    /**
334     * Returns <code>true</code> if the value axis is inverted, and 
335     * <code>false</code> otherwise.
336     * 
337     * @return A boolean.
338     * 
339     * @see #setValueAxisInverted(boolean)
340     */
341    public boolean isValueAxisInverted() {
342        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
343        if (plot != null) {
344            return plot.getRangeAxis().isInverted();
345        }
346        return false;
347    }
348
349    /**
350     * Sets a flag that controls whether or not the value axis is inverted and 
351     * fires a {@link PropertyChangeEvent} for the 
352     * <code>valueAxisInverted</code> property.
353     * 
354     * @param inverted  the new flag value.
355     * 
356     * @see #isValueAxisInverted()
357     */
358    public void setValueAxisInverted(boolean inverted) {
359        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
360        if (plot != null) {
361            boolean old = plot.getRangeAxis().isInverted();
362            plot.getRangeAxis().setInverted(inverted);
363            firePropertyChange("valueAxisInverted", old, inverted);
364        }                
365    }
366    
367    /**
368     * Returns the lower margin for the value axis.
369     * 
370     * @return The lower margin.
371     * 
372     * @see #setValueAxisLowerMargin(double)
373     */
374    public double getValueAxisLowerMargin() {
375        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
376        if (plot != null) {
377            return plot.getRangeAxis().getLowerMargin();
378        }
379        return -1.0;
380    }
381    
382    /**
383     * Sets the lower margin for the value axis and fires a 
384     * {@link PropertyChangeEvent} for the <code>valueAxisLowerMargin</code> 
385     * property.
386     * 
387     * @param margin  the margin.
388     * 
389     * @see #getValueAxisLowerMargin()
390     */
391    public void setValueAxisLowerMargin(double margin) {
392        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
393        if (plot != null) {
394            double old = plot.getRangeAxis().getLowerMargin();
395            plot.getRangeAxis().setLowerMargin(margin);
396            firePropertyChange("valueAxisLowerMargin", old, margin);
397        }                
398    }
399    
400    /**
401     * Returns the upper margin for the value axis.
402     * 
403     * @return The upper margin for the value axis.
404     * 
405     * @see #setValueAxisUpperMargin(double)
406     */
407    public double getValueAxisUpperMargin() {
408        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
409        if (plot != null) {
410            return plot.getRangeAxis().getUpperMargin();
411        }
412        return -1.0;
413    }
414
415    /**
416     * Sets the upper margin for the value axis and fires a 
417     * {@link PropertyChangeEvent} for the <code>valueAxisUpperMargin</code> 
418     * property.
419     * 
420     * @param margin  the margin.
421     * 
422     * @see #getValueAxisUpperMargin()
423     */
424    public void setValueAxisUpperMargin(double margin) {
425        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
426        if (plot != null) {
427            double old = plot.getRangeAxis().getUpperMargin();
428            plot.getRangeAxis().setUpperMargin(margin);
429            firePropertyChange("valueAxisUpperMargin", old, margin);
430        }                
431    }
432    
433    /**
434     * Returns <code>true</code> if the value axis gridlines are visible, and 
435     * <code>false</code> otherwise.
436     * 
437     * @return A boolean.
438     * 
439     * @see #setValueAxisGridlinesVisible(boolean)
440     */
441    public boolean isValueAxisGridlinesVisible() {
442        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
443        if (plot != null) {
444            return plot.isRangeGridlinesVisible();
445        }
446        return false;
447    }
448
449    /**
450     * Sets a flag that controls whether or not the value-axis gridlines are
451     * drawn and fires a {@link PropertyChangeEvent} for the 
452     * <code>valueAxisGridlinesVisible</code> property.
453     * 
454     * @param visible  the new flag value.
455     * 
456     * @see #isValueAxisGridlinesVisible()
457     */
458    public void setValueAxisGridlinesVisible(boolean visible) {
459        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
460        if (plot != null) {
461            boolean old = plot.isRangeGridlinesVisible();
462            plot.setRangeGridlinesVisible(visible);
463            firePropertyChange("valueAxisGridlinesVisible", old, visible);
464        }                
465    }
466    
467    /**
468     * Returns the flag that controls whether or not the value axis draws a
469     * line running the length of the axis.
470     * 
471     * @return A boolean.
472     * 
473     * @see #setValueAxisLineVisible(boolean)
474     */
475    public boolean isValueAxisLineVisible() {
476        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
477        if (plot != null) {
478            return plot.getRangeAxis().isAxisLineVisible();
479        }
480        return false;        
481    }
482    
483    /**
484     * Sets the flag that controls whether or not the value axis draws a line
485     * running the length of the axis and fires a {@link PropertyChangeEvent} 
486     * for the <code>valueAxisLineVisible</code> property.
487     * 
488     * @param visible  the new flag value.
489     * 
490     * @see #isValueAxisLineVisible()
491     */
492    public void setValueAxisLineVisible(boolean visible) {
493        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
494        if (plot != null) {
495            boolean old = plot.getRangeAxis().isAxisLineVisible();
496            plot.getRangeAxis().setAxisLineVisible(visible);
497            firePropertyChange("valueAxisLineVisible", old, visible);
498        }                
499    }
500
501    /**
502     * Returns a flag that conrtols whether or not the category axis
503     * draws a line running the length of the axis.
504     * 
505     * @return A boolean.
506     * 
507     * @see #setCategoryAxisLineVisible(boolean)
508     */
509    public boolean isCategoryAxisLineVisible() {
510        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
511        if (plot != null) {
512            return plot.getDomainAxis().isAxisLineVisible();
513        }
514        return false;        
515    }
516    
517    /**
518     * Sets the flag that controls whether or not the category axis draws a
519     * line running the length of the axis and fires a 
520     * {@link PropertyChangeEvent} for the <code>categoryAxisLineVisible</code>
521     * property.
522     * 
523     * @param visible  the new flag value.
524     * 
525     * @see #isCategoryAxisLineVisible()
526     */
527    public void setCategoryAxisLineVisible(boolean visible) {
528        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
529        if (plot != null) {
530            boolean old = plot.getDomainAxis().isAxisLineVisible();
531            plot.getDomainAxis().setAxisLineVisible(visible);
532            firePropertyChange("categoryAxisLineVisible", old, visible);
533        }                
534    }
535
536    /**
537     * Returns the permitted axis locations for the category axis.
538     * 
539     * @return The axis location.
540     * 
541     * @see #setCategoryAxisLocation(AxisLocation)
542     */
543    public AxisLocation getCategoryAxisLocation() {
544        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
545        if (plot != null) {
546            return plot.getDomainAxisLocation();
547        }
548        return null;                
549    }
550    
551    /**
552     * Sets the axis location for the category axis and fires a 
553     * {@link PropertyChangeEvent} for the <code>categoryAxisLocation</code> 
554     * property.
555     * 
556     * @param location  the location (<code>null</code> not permitted).
557     * 
558     * @see #getCategoryAxisLocation()
559     */
560    public void setCategoryAxisLocation(AxisLocation location) {
561        if (location == null) {
562            throw new IllegalArgumentException("Null 'location' argument.");
563        }
564        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
565        if (plot != null) {
566            AxisLocation old = plot.getDomainAxisLocation();
567            plot.setDomainAxisLocation(location);
568            firePropertyChange("categoryAxisLocation", old, location);
569        }
570    }
571    
572    /**
573     * Returns the permitted axis locations for the value axis.
574     * 
575     * @return The axis location.
576     * 
577     * @see #setValueAxisLocation(AxisLocation)
578     */
579    public AxisLocation getValueAxisLocation() {
580        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
581        if (plot != null) {
582            return plot.getRangeAxisLocation();
583        }
584        return null;                
585    }
586    
587    /**
588     * Sets the axis location for the value axis and fires a 
589     * {@link PropertyChangeEvent} for the <code>valueAxisLocation</code> 
590     * property.
591     * 
592     * @param location  the location (<code>null</code> not permitted).
593     */
594    public void setValueAxisLocation(AxisLocation location) {
595        if (location == null) {
596            throw new IllegalArgumentException("Null 'location' argument.");
597        }
598        CategoryPlot plot = (CategoryPlot) this.chart.getPlot();
599        if (plot != null) {
600            AxisLocation old = plot.getRangeAxisLocation();
601            plot.setRangeAxisLocation(location);
602            firePropertyChange("valueAxisLocation", old, location);
603        }
604    }
605    
606    /**
607     * Returns the format string for the item tool tips.
608     * 
609     * @return The format string.
610     * 
611     * @see #setToolTipFormat(String)
612     */
613    public String getToolTipFormat() {
614        CategoryPlot p = (CategoryPlot) this.chart.getPlot();
615        if (p == null) {
616            return "";
617        }
618        CategoryItemRenderer r = p.getRenderer();
619        if (r == null) {
620            return "";
621        }
622        StandardCategoryToolTipGenerator g = (StandardCategoryToolTipGenerator) 
623                r.getBaseToolTipGenerator();
624        if (g == null) {
625            return "";
626        }
627        return g.getLabelFormat();
628    }
629    
630    /**
631     * Sets the format string for the section tool tips and fires a 
632     * {@link PropertyChangeEvent} for the <code>toolTipFormat</code> property.
633     * 
634     * @param format  the format string.
635     * 
636     * @see #getToolTipFormat()
637     */
638    public void setToolTipFormat(String format) {
639        CategoryPlot p = (CategoryPlot) this.chart.getPlot();
640        if (p == null) {
641            return;
642        }
643        CategoryItemRenderer r = p.getRenderer();
644        if (r == null) {
645            return;
646        }
647        if (format.equals("")) {
648            r.setBaseToolTipGenerator(null);
649        }
650        else {
651            r.setBaseToolTipGenerator(new StandardCategoryToolTipGenerator(
652                    format, NumberFormat.getInstance()));   
653        }
654        // FIXME: what to use for the oldValue
655        firePropertyChange("toolTipFormat", null, format);
656    }
657    
658    /**
659     * Registers a listener to receive notification of category item clicks.
660     * 
661     * @param listener  the listener (<code>null</code> not permitted).
662     */
663    public void addCategoryItemClickListener(
664            CategoryItemClickListener listener) {
665        if (listener == null) {
666            throw new IllegalArgumentException("Null 'listener' argument.");
667        }
668        this.listeners.add(CategoryItemClickListener.class, listener);
669    }
670    
671    /**
672     * Unregisters a listener so that it no longer receives notification of 
673     * category item clicks.
674     * 
675     * @param listener  the listener (<code>null</code> not permitted).
676     */
677    public void removeCategoryItemClickListener(CategoryItemClickListener 
678            listener) {
679        if (listener == null) {
680            throw new IllegalArgumentException("Null 'listener' argument.");
681        }
682        this.listeners.remove(CategoryItemClickListener.class, listener);        
683    }
684    
685    /**
686     * Fires a category item click event.
687     * 
688     * @param event  the event.
689     */
690    public void fireCategoryItemClickEvent(CategoryItemClickEvent event) {
691        Object[] listeners = this.listeners.getListeners(
692                CategoryItemClickListener.class);
693        for (int i = listeners.length - 1; i >= 0; i -= 1) {
694            ((CategoryItemClickListener) listeners[i]).onCategoryItemClick(
695                    event);
696        }                
697        
698    }
699    
700    /**
701     * If the user clicks on the chart, see if that translates into an event
702     * that we report...
703     * 
704     * @param event  the event.
705     */
706    public void mouseClicked(MouseEvent event) {
707        // if no-one is listening, just return...
708        Object[] listeners = this.listeners.getListeners(
709                CategoryItemClickListener.class);
710        if (listeners.length == 0) {
711            super.mouseClicked(event);
712        }
713
714        Insets insets = getInsets();
715        int x = event.getX() - insets.left;
716        int y = event.getY() - insets.top;
717
718        ChartEntity entity = null;
719        if (this.info != null) {
720            EntityCollection entities = this.info.getEntityCollection();
721            if (entities != null) {
722                entity = entities.getEntity(x, y);
723            }
724        }
725        if (entity instanceof CategoryItemEntity) {
726            CategoryItemEntity cie = (CategoryItemEntity) entity;
727            CategoryItemClickEvent lce = new CategoryItemClickEvent(this, 
728                    cie.getDataset(), cie.getRowKey(), cie.getColumnKey());
729            fireCategoryItemClickEvent(lce);
730        }
731        else {
732            super.mouseClicked(event);
733        }
734        
735    }
736
737}