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:  not-yet-released
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    
029    package org.jfree.beans;
030    
031    import java.awt.Font;
032    import java.awt.Insets;
033    import java.awt.Paint;
034    import java.awt.event.MouseEvent;
035    import java.beans.PropertyChangeEvent;
036    import java.text.NumberFormat;
037    
038    import org.jfree.beans.events.CategoryItemClickEvent;
039    import org.jfree.beans.events.CategoryItemClickListener;
040    import org.jfree.chart.axis.AxisLocation;
041    import org.jfree.chart.entity.CategoryItemEntity;
042    import org.jfree.chart.entity.ChartEntity;
043    import org.jfree.chart.entity.EntityCollection;
044    import org.jfree.chart.labels.StandardCategoryToolTipGenerator;
045    import org.jfree.chart.plot.CategoryPlot;
046    import org.jfree.chart.plot.PlotOrientation;
047    import org.jfree.chart.renderer.category.CategoryItemRenderer;
048    
049    /**
050     * A base class for chart beans that use a {@link CategoryPlot}.
051     */
052    public 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    }