1 | /**
|
---|
2 | * AbstractChart.java
|
---|
3 | * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
|
---|
4 | *
|
---|
5 | * This program is free software: you can redistribute it and/or modify
|
---|
6 | * it under the terms of the GNU General Public License as published by
|
---|
7 | * the Free Software Foundation, either version 3 of the License, or
|
---|
8 | * (at your option) any later version.
|
---|
9 | *
|
---|
10 | * This program is distributed in the hope that it will be useful,
|
---|
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | * GNU General Public License for more details.
|
---|
14 | *
|
---|
15 | * You should have received a copy of the GNU General Public License
|
---|
16 | * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
---|
17 | */
|
---|
18 |
|
---|
19 | package org.expeditee.items.widgets.charts;
|
---|
20 |
|
---|
21 | import java.awt.BasicStroke;
|
---|
22 | import java.awt.Component;
|
---|
23 | import java.awt.Font;
|
---|
24 | import java.awt.Paint;
|
---|
25 | import java.awt.event.KeyEvent;
|
---|
26 | import java.awt.event.KeyListener;
|
---|
27 | import java.awt.event.MouseEvent;
|
---|
28 | import java.awt.event.MouseListener;
|
---|
29 | import java.util.Collection;
|
---|
30 | import java.util.LinkedHashMap;
|
---|
31 | import java.util.Map;
|
---|
32 |
|
---|
33 | import javax.swing.JPanel;
|
---|
34 |
|
---|
35 | import org.expeditee.core.Colour;
|
---|
36 | import org.expeditee.core.Point;
|
---|
37 | import org.expeditee.gio.gesture.StandardGestureActions;
|
---|
38 | import org.expeditee.gio.swing.SwingConversions;
|
---|
39 | import org.expeditee.gui.ColorUtils;
|
---|
40 | import org.expeditee.gui.DisplayController;
|
---|
41 | import org.expeditee.gui.Frame;
|
---|
42 | import org.expeditee.gui.FrameGraphics;
|
---|
43 | import org.expeditee.gui.FrameIO;
|
---|
44 | import org.expeditee.gui.FunctionKey;
|
---|
45 | import org.expeditee.items.Text;
|
---|
46 | import org.expeditee.items.widgets.DataFrameWidget;
|
---|
47 | import org.expeditee.settings.templates.TemplateSettings;
|
---|
48 | import org.jfree.chart.ChartPanel;
|
---|
49 | import org.jfree.chart.JFreeChart;
|
---|
50 | import org.jfree.chart.block.BlockBorder;
|
---|
51 | import org.jfree.chart.plot.Plot;
|
---|
52 | import org.jfree.chart.title.LegendTitle;
|
---|
53 | import org.jfree.chart.title.TextTitle;
|
---|
54 | import org.jfree.chart.title.Title;
|
---|
55 | import org.jfree.ui.RectangleInsets;
|
---|
56 |
|
---|
57 | public abstract class AbstractChart extends DataFrameWidget {
|
---|
58 |
|
---|
59 | protected static final String DEFAULT_TITLE = "";
|
---|
60 |
|
---|
61 | protected static final String DEFAULT_XAXIS = "X";
|
---|
62 |
|
---|
63 | protected static final String DEFAULT_YAXIS = "Y";
|
---|
64 |
|
---|
65 | private JFreeChart _chart = null;
|
---|
66 |
|
---|
67 | private LegendTitle _legend = null;
|
---|
68 |
|
---|
69 | protected Map<String, Paint> _paints;
|
---|
70 |
|
---|
71 | protected JFreeChart getChart() {
|
---|
72 | return _chart;
|
---|
73 | }
|
---|
74 |
|
---|
75 | public AbstractChart(Text source, String[] args) {
|
---|
76 | super(source, new JPanel(), 100, 300, -1, 100, 300, -1);
|
---|
77 | _paints = new LinkedHashMap<String, Paint>();
|
---|
78 | init();
|
---|
79 | refresh();
|
---|
80 | }
|
---|
81 |
|
---|
82 | @Override
|
---|
83 | protected String[] getArgs() {
|
---|
84 | return null;
|
---|
85 | }
|
---|
86 |
|
---|
87 | protected void init() {
|
---|
88 | // create a chart...
|
---|
89 | _chart = createNewChart();
|
---|
90 | _chart.getPlot().setNoDataMessage("Add data to chart");
|
---|
91 | _legend = _chart.getLegend();
|
---|
92 | ChartPanel cp = new ChartPanel(_chart);
|
---|
93 | cp.setPopupMenu(null);
|
---|
94 | cp.addKeyListener(new KeyListener() {
|
---|
95 | public void keyPressed(KeyEvent e) {
|
---|
96 | int index = e.getKeyCode() - KeyEvent.VK_F1 + 1;
|
---|
97 | // Make sure the key is within range
|
---|
98 | if (index < 0 || index >= FunctionKey.values().length) {
|
---|
99 | return;
|
---|
100 | }
|
---|
101 |
|
---|
102 | FunctionKey key = FunctionKey.values()[index];
|
---|
103 | switch (key) {
|
---|
104 | case SizeUp:
|
---|
105 | invalidateLink();
|
---|
106 | StandardGestureActions.SetSize(getFirstCorner(), 1, false, true, false);
|
---|
107 | invalidateSelf();
|
---|
108 | DisplayController.requestRefresh(true);
|
---|
109 | // FrameGraphics.requestRefresh(true);
|
---|
110 | break;
|
---|
111 | case SizeDown:
|
---|
112 | invalidateLink();
|
---|
113 | StandardGestureActions.SetSize(getFirstCorner(), -1, false,
|
---|
114 | true, false);
|
---|
115 | invalidateSelf();
|
---|
116 | DisplayController.requestRefresh(true);
|
---|
117 | // FrameGraphics.ForceRepaint();
|
---|
118 | // FrameGraphics.refresh(true);
|
---|
119 | // FrameGraphics.requestRefresh(true);
|
---|
120 | break;
|
---|
121 | case ChangeColor:
|
---|
122 | if (e.isControlDown()) {
|
---|
123 |
|
---|
124 | if (e.isShiftDown()) {
|
---|
125 | setSourceFillColor(null);
|
---|
126 | } else {
|
---|
127 | Colour newColor = ColorUtils.getNextColor(
|
---|
128 | getSource().getFillColor(),
|
---|
129 | TemplateSettings.BackgroundColorWheel.get(), null);
|
---|
130 | setSourceFillColor(newColor);
|
---|
131 | }
|
---|
132 | } else {
|
---|
133 | if (e.isShiftDown()) {
|
---|
134 | setBackgroundColor(null);
|
---|
135 | } else {
|
---|
136 | Colour newColor = ColorUtils.getNextColor(
|
---|
137 | getSource().getColor(), TemplateSettings.ColorWheel.get(),
|
---|
138 | null);
|
---|
139 | setSourceColor(newColor);
|
---|
140 | }
|
---|
141 | }
|
---|
142 | break;
|
---|
143 | default:
|
---|
144 | break;
|
---|
145 | }
|
---|
146 | }
|
---|
147 |
|
---|
148 | public void keyReleased(KeyEvent e) {
|
---|
149 | }
|
---|
150 |
|
---|
151 | public void keyTyped(KeyEvent e) {
|
---|
152 | StandardGestureActions.processChar(e.getKeyChar(), e
|
---|
153 | .isShiftDown());
|
---|
154 | }
|
---|
155 | });
|
---|
156 | cp.addMouseListener(new MouseListener() {
|
---|
157 |
|
---|
158 | public void mouseClicked(MouseEvent e) {
|
---|
159 | //StandardGestureActions.mouseClicked(translateEvent(e));
|
---|
160 | }
|
---|
161 |
|
---|
162 | public void mouseEntered(MouseEvent e) {
|
---|
163 | // FrameMouseActions.getInstance().mouseEntered(translateEvent(e));
|
---|
164 | }
|
---|
165 |
|
---|
166 | public void mouseExited(MouseEvent e) {
|
---|
167 | // FrameMouseActions.getInstance().mouseExited(translateEvent(e));
|
---|
168 | }
|
---|
169 |
|
---|
170 | public void mousePressed(MouseEvent e) {
|
---|
171 | //StandardGestureActions.mousePressed(translateEvent(e));
|
---|
172 | }
|
---|
173 |
|
---|
174 | private MouseEvent translateEvent(MouseEvent e) {
|
---|
175 | Point origin = getOrigin();
|
---|
176 | return new MouseEvent((Component) e.getSource(), e.getID(), e
|
---|
177 | .getWhen(), e.getModifiers(), origin.x + e.getX(),
|
---|
178 | origin.y + e.getY(), e.getXOnScreen(),
|
---|
179 | e.getYOnScreen(), e.getClickCount(), false, e
|
---|
180 | .getButton());
|
---|
181 | }
|
---|
182 |
|
---|
183 | public void mouseReleased(MouseEvent e) {
|
---|
184 | //StandardGestureActions.mouseReleased(translateEvent(e));
|
---|
185 | }
|
---|
186 |
|
---|
187 | });
|
---|
188 | super._swingComponent = cp;
|
---|
189 |
|
---|
190 | setSourceColor(getSource().getColor());
|
---|
191 | setSourceBorderColor(getSource().getBorderColor());
|
---|
192 | setSourceFillColor(getSource().getFillColor());
|
---|
193 | setBackgroundColor(getSource().getBackgroundColor());
|
---|
194 | setSourceThickness(getSource().getThickness(), false);
|
---|
195 | }
|
---|
196 |
|
---|
197 | protected abstract JFreeChart createNewChart();
|
---|
198 |
|
---|
199 | @Override
|
---|
200 | public final void refresh() {
|
---|
201 | super.refresh();
|
---|
202 | clearData();
|
---|
203 | // Get the data from the linked frame
|
---|
204 | Frame dataFrame = getDataFrame();
|
---|
205 | _paints.clear();
|
---|
206 | _chart.clearSubtitles();
|
---|
207 |
|
---|
208 | if (dataFrame != null) {
|
---|
209 | _chart.setTitle(dataFrame.getTitle());
|
---|
210 |
|
---|
211 | refreshData(dataFrame);
|
---|
212 |
|
---|
213 | if (dataFrame.hasAnnotation("legend")) {
|
---|
214 | _chart.addLegend(_legend);
|
---|
215 | }
|
---|
216 | if (dataFrame.hasAnnotation("subtitle")) {
|
---|
217 | getChart().addSubtitle(
|
---|
218 | new TextTitle(dataFrame.getAnnotationValue("subtitle"),
|
---|
219 | JFreeChart.DEFAULT_TITLE_FONT.deriveFont(
|
---|
220 | Font.ITALIC,
|
---|
221 | JFreeChart.DEFAULT_TITLE_FONT
|
---|
222 | .getSize2D() * .7F)));
|
---|
223 | }
|
---|
224 |
|
---|
225 | refreshPlot(dataFrame, _chart.getPlot());
|
---|
226 |
|
---|
227 | } else {
|
---|
228 | _chart.setTitle(this.getClass().getSimpleName() + " Chart");
|
---|
229 | }
|
---|
230 | _chart.getPlot().setDataPaints(_paints);
|
---|
231 | super._swingComponent.invalidate();
|
---|
232 | }
|
---|
233 |
|
---|
234 | /**
|
---|
235 | * @param dataFrame
|
---|
236 | */
|
---|
237 | protected abstract void refreshPlot(Frame dataFrame, Plot simplePlot);
|
---|
238 |
|
---|
239 | /**
|
---|
240 | * @param dataFrame
|
---|
241 | */
|
---|
242 | protected void refreshData(Frame dataFrame) {
|
---|
243 | clearSubjects();
|
---|
244 |
|
---|
245 | boolean swap = false;
|
---|
246 | if (dataFrame.hasAnnotation("swap")) {
|
---|
247 | swap = true;
|
---|
248 | }
|
---|
249 | boolean foundData = false;
|
---|
250 | // First do a pass to see if there are any line ends
|
---|
251 | Collection<Text> textItems = dataFrame.getNonAnnotationText(true);
|
---|
252 | textItems.remove(dataFrame.getTitleItem());
|
---|
253 |
|
---|
254 | // Search for line ends
|
---|
255 | for (Text text : textItems) {
|
---|
256 | if (!text.isLineEnd()) {
|
---|
257 | continue;
|
---|
258 | }
|
---|
259 | foundData |= addCategoryData(text.getText(), text
|
---|
260 | .getEnclosedNonAnnotationText(), swap);
|
---|
261 | }
|
---|
262 |
|
---|
263 | // As a second option look for linked items on the frame
|
---|
264 | if (!foundData) {
|
---|
265 | for (Text category : textItems) {
|
---|
266 | if (category.isLineEnd() || category.getLink() == null)
|
---|
267 | continue;
|
---|
268 | Frame linkFrame = FrameIO.LoadFrame(category.getAbsoluteLink());
|
---|
269 | if (linkFrame == null)
|
---|
270 | continue;
|
---|
271 |
|
---|
272 | String categoryName = category.getText();
|
---|
273 | if (foundData |= addCategoryData(categoryName, linkFrame
|
---|
274 | .getNonAnnotationText(true), swap)) {
|
---|
275 | Colour backgroundColor = category.getBackgroundColor();
|
---|
276 | if (backgroundColor != null)
|
---|
277 | _paints.put(categoryName, SwingConversions.toSwingColor(backgroundColor));
|
---|
278 | addSubject(linkFrame);
|
---|
279 | }
|
---|
280 | }
|
---|
281 | }
|
---|
282 |
|
---|
283 | // If there were no groupings in boxes then just get all the values on
|
---|
284 | // the frame
|
---|
285 | if (!foundData) {
|
---|
286 | foundData |= addCategoryData("", textItems, swap);
|
---|
287 | }
|
---|
288 | /* if (foundData) */{
|
---|
289 | /*
|
---|
290 | * At minimum add the data frame as a subject even if there is no data
|
---|
291 | * on it... yet
|
---|
292 | */
|
---|
293 | addSubject(dataFrame);
|
---|
294 | }
|
---|
295 | }
|
---|
296 |
|
---|
297 | protected boolean addCategoryData(String categoryName,
|
---|
298 | Collection<Text> items, boolean swap) {
|
---|
299 | return true;
|
---|
300 | }
|
---|
301 |
|
---|
302 | @Override
|
---|
303 | public float getMinimumBorderThickness() {
|
---|
304 | return 1.0F;
|
---|
305 | }
|
---|
306 |
|
---|
307 | protected abstract void clearData();
|
---|
308 |
|
---|
309 | @Override
|
---|
310 | public void setBackgroundColor(Colour c) {
|
---|
311 | super.setBackgroundColor(c);
|
---|
312 | if (_chart == null)
|
---|
313 | return;
|
---|
314 | if (c == null) {
|
---|
315 | _chart.setBackgroundPaint(JFreeChart.DEFAULT_BACKGROUND_PAINT);
|
---|
316 | } else {
|
---|
317 | _chart.setBackgroundPaint(SwingConversions.toSwingColor(c));
|
---|
318 | }
|
---|
319 | }
|
---|
320 |
|
---|
321 | @Override
|
---|
322 | public void setSourceColor(Colour c) {
|
---|
323 | super.setSourceColor(c);
|
---|
324 | if (_chart == null)
|
---|
325 | return;
|
---|
326 | if (c == null) {
|
---|
327 | _chart.getTitle().setPaint(TextTitle.DEFAULT_TEXT_PAINT);
|
---|
328 | } else {
|
---|
329 | _chart.getTitle().setPaint(SwingConversions.toSwingColor(c));
|
---|
330 | }
|
---|
331 | for (Title t : _chart.getSubtitles()) {
|
---|
332 | if (c == null) {
|
---|
333 | t.setPaint(TextTitle.DEFAULT_TEXT_PAINT);
|
---|
334 | } else {
|
---|
335 | t.setPaint(SwingConversions.toSwingColor(c));
|
---|
336 | }
|
---|
337 | }
|
---|
338 | LegendTitle legend = _chart.getLegend();
|
---|
339 | if (legend != null) {
|
---|
340 | if (c == null) {
|
---|
341 | legend.setFrame(new BlockBorder(SwingConversions.toSwingColor(Colour.BLACK)));
|
---|
342 | } else {
|
---|
343 | legend.setFrame(new BlockBorder(SwingConversions.toSwingColor(c)));
|
---|
344 | }
|
---|
345 | }
|
---|
346 | }
|
---|
347 |
|
---|
348 | @Override
|
---|
349 | public void setSourceBorderColor(Colour c) {
|
---|
350 | super.setSourceBorderColor(c);
|
---|
351 | if (_chart == null)
|
---|
352 | return;
|
---|
353 | if (c == null) {
|
---|
354 | _chart.getPlot().setOutlinePaint(Plot.DEFAULT_OUTLINE_PAINT);
|
---|
355 | } else {
|
---|
356 | _chart.getPlot().setOutlinePaint(SwingConversions.toSwingColor(c));
|
---|
357 | }
|
---|
358 | }
|
---|
359 |
|
---|
360 | @Override
|
---|
361 | public void setSourceFillColor(Colour c) {
|
---|
362 | super.setSourceFillColor(c);
|
---|
363 | if (_chart == null)
|
---|
364 | return;
|
---|
365 | LegendTitle legend = _chart.getLegend();
|
---|
366 | if (c == null) {
|
---|
367 | _chart.getPlot().setBackgroundPaint(Plot.DEFAULT_BACKGROUND_PAINT);
|
---|
368 | if (legend != null)
|
---|
369 | legend.setBackgroundPaint(null);
|
---|
370 | } else {
|
---|
371 | _chart.getPlot().setBackgroundPaint(SwingConversions.toSwingColor(c));
|
---|
372 | if (legend != null)
|
---|
373 | legend.setBackgroundPaint(SwingConversions.toSwingColor(c));
|
---|
374 | }
|
---|
375 | }
|
---|
376 |
|
---|
377 | @Override
|
---|
378 | public void setSourceThickness(float newThickness, boolean setConnected) {
|
---|
379 | super.setSourceThickness(newThickness, setConnected);
|
---|
380 | if (_chart == null)
|
---|
381 | return;
|
---|
382 | _chart.setBorderStroke(new BasicStroke(newThickness));
|
---|
383 | _chart.getPlot().setOutlineStroke(new BasicStroke(newThickness));
|
---|
384 |
|
---|
385 | float fontSize = getFontSize(newThickness, TextTitle.DEFAULT_FONT
|
---|
386 | .getSize2D());
|
---|
387 | LegendTitle legend = _chart.getLegend();
|
---|
388 | if (legend != null) {
|
---|
389 | legend.setFrame(new BlockBorder(newThickness, newThickness,
|
---|
390 | newThickness, newThickness, SwingConversions.toSwingColor(getSource().getPaintColor())));
|
---|
391 | legend.setItemFont(legend.getItemFont().deriveFont(fontSize));
|
---|
392 | float pad = newThickness + 1;
|
---|
393 | float pad2 = pad;
|
---|
394 | legend.setItemLabelPadding(new RectangleInsets(pad, pad, pad, pad));
|
---|
395 | legend.setLegendItemGraphicPadding(new RectangleInsets(pad2, pad2,
|
---|
396 | pad2, pad2));
|
---|
397 | legend.setMargin(newThickness, newThickness, newThickness,
|
---|
398 | newThickness);
|
---|
399 | }
|
---|
400 | float pad0 = newThickness - 1;
|
---|
401 | _chart.setPadding(new RectangleInsets(pad0, pad0, pad0, pad0));
|
---|
402 | TextTitle title = _chart.getTitle();
|
---|
403 | title.setFont(title.getFont().deriveFont(
|
---|
404 | getFontSize(newThickness, JFreeChart.DEFAULT_TITLE_FONT
|
---|
405 | .getSize2D())));
|
---|
406 |
|
---|
407 | for (Title t : _chart.getSubtitles()) {
|
---|
408 | if (t instanceof TextTitle) {
|
---|
409 | ((TextTitle) t).setFont(legend.getItemFont().deriveFont(
|
---|
410 | fontSize));
|
---|
411 | }
|
---|
412 | t.setPadding(pad0, pad0, pad0, pad0);
|
---|
413 | }
|
---|
414 | }
|
---|
415 |
|
---|
416 | static protected float getFontSize(float newThickness, float oldFontSize) {
|
---|
417 | return (newThickness + 5) * oldFontSize / 6;
|
---|
418 | }
|
---|
419 | }
|
---|