KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > info > monitorenter > gui > chart > layout > ChartPanel


1 /*
2  * ChartPanel.java, a decoration of a Chart2D that adds popup menues for traces and the chart.
3  * Copyright (C) Achim Westermann, created on 19.05.2005, 22:01:51
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  * If you modify or optimize the code in a useful way please let me know.
20  * Achim.Westermann@gmx.de
21  *
22  */

23 package info.monitorenter.gui.chart.layout;
24
25 import info.monitorenter.gui.chart.Chart2D;
26 import info.monitorenter.gui.chart.ITrace2D;
27 import info.monitorenter.gui.chart.traces.Trace2DLtd;
28
29 import java.awt.Color JavaDoc;
30 import java.awt.Component JavaDoc;
31 import java.awt.Dimension JavaDoc;
32 import java.awt.FlowLayout JavaDoc;
33 import java.awt.GridBagConstraints JavaDoc;
34 import java.awt.GridBagLayout JavaDoc;
35 import java.awt.event.MouseListener JavaDoc;
36 import java.awt.event.WindowAdapter JavaDoc;
37 import java.awt.event.WindowEvent JavaDoc;
38 import java.beans.PropertyChangeEvent JavaDoc;
39 import java.beans.PropertyChangeListener JavaDoc;
40 import java.util.Iterator JavaDoc;
41
42 import javax.swing.JFrame JavaDoc;
43 import javax.swing.JLabel JavaDoc;
44 import javax.swing.JPanel JavaDoc;
45
46 /**
47  * A decoration for {@link info.monitorenter.gui.chart.Chart2D} that adds
48  * various controls for a {@link info.monitorenter.gui.chart.Chart2D} and it's
49  * {@link info.monitorenter.gui.chart.ITrace2D} instances in form of popup
50  * menues.
51  * <p>
52  * <h2>Performance note</h2>
53  * The context menu items register themselves with the chart to adapt their
54  * basic UI properties (font, foreground color, background color) via
55  * unreferenced instances of
56  * {@link info.monitorenter.gui.chart.layout.LayoutFactory.BasicPropertyAdaptSupport}.
57  * This ensures that dropping a complete menu tree from the UI makes them
58  * garbage collectable without introduction of highly unstable and
59  * unmaintainable active memory management code. A side effect is that these
60  * listeners remain in the property change listener list of the chart unless
61  * they are finalized.
62  * <p>
63  * Adding and removing many traces to / from charts that are wrapped in
64  * {@link ChartPanel} without {@link java.lang.System#gc()} followed by
65  * {@link java.lang.System#runFinalization()} in your code will leave a huge
66  * amount of listeners for non-visible uncleaned menu items in the chart which
67  * causes a high cpu throttle for increasing the listener list.
68  * <p>
69  * The reason seems to be the implementation of ({@link javax.swing.event.EventListenerList}
70  * that is used by {@link javax.swing.event.SwingPropertyChangeSupport}). It is
71  * based upon an array an grows only for the space of an additional listener by
72  * using
73  * {@link java.lang.System#arraycopy(java.lang.Object, int, java.lang.Object, int, int)}
74  * (ouch, this should be changed).
75  * <p>
76  *
77  * Profiling a day with
78  * {@link info.monitorenter.gui.chart.layout.TestChartPanelMemoryLeak} showed
79  * that up to 2000 dead listeners remained in the list. The cpu load increased
80  * after about 200 add / remove trace operations. Good news is that no memory
81  * leak could be detected.
82  * <p>
83  * If those add and remove trace operations on {@link ChartPanel} - connected
84  * charts are performed with intermediate UI action property change events on
85  * dead listeners will let them remove themselves from the listener list thus
86  * avoiding the cpu overhead. So UI / user - controlled applications will
87  * unlikely suffer from this problem.
88  * <p>
89  *
90  * @author <a HREF="mailto:Achim.Westermann@gmx.de">Achim Westermann </a>
91  *
92  */

93 public class ChartPanel extends JPanel JavaDoc implements PropertyChangeListener JavaDoc {
94
95   /**
96    * Generated <code>serialVersionUID</code>.
97    */

98   private static final long serialVersionUID = 3905801963714197560L;
99
100   /**
101    * Main enbtry for demo app.
102    * <p>
103    *
104    * @param args
105    * ignored.
106    */

107   public static void main(final String JavaDoc[] args) {
108     // some data:
109
double[] data = new double[100];
110     for (int i = 0; i < 100; i++) {
111       data[i] = Math.random() * i + 1;
112     }
113     JFrame JavaDoc frame = new JFrame JavaDoc("ChartPanel demo");
114     Chart2D chart = new Chart2D();
115     // trace 1
116
ITrace2D trace1 = new Trace2DLtd(100);
117     trace1.setName("Trace 1");
118
119     // AbstractDataCollector collector1 = new
120
// RandomDataCollectorOffset(trace1,500);
121
// trace2
122
ITrace2D trace2 = new Trace2DLtd(100);
123     trace2.setName("Trace 2");
124     // AbstractDataCollector collector2 = new
125
// RandomDataCollectorOffset(trace2,500);
126
for (int i = 0; i < 100; i++) {
127       trace1.addPoint(i + 2, data[i]);
128       trace2.addPoint(i + 2, 100 - data[i]);
129     }
130
131     // add to chart
132
chart.addTrace(trace1);
133     chart.addTrace(trace2);
134     frame.getContentPane().add(new ChartPanel(chart));
135     frame.setSize(new Dimension JavaDoc(400, 600));
136     frame.addWindowListener(new WindowAdapter JavaDoc() {
137       public void windowClosing(final WindowEvent JavaDoc w) {
138         System.exit(0);
139       }
140
141     });
142     frame.setJMenuBar(LayoutFactory.getInstance().createMenuBar(chart, false));
143     frame.setVisible(true);
144   }
145
146   /** The decorated chart. */
147   private Chart2D m_chart;
148
149   /**
150    * <p>
151    * An internal panel for the labels of the traces that uses a
152    * {@link FlowLayout}.
153    * </p>
154    *
155    */

156   protected JPanel JavaDoc m_labelPanel;
157
158   /**
159    * Creates an instance that decorates the given chart with controls in form of
160    * popup menues.
161    * <p>
162    *
163    * @param chart
164    * A configured Chart2D instance that will be displayed and
165    * controlled by this panel.
166    */

167   public ChartPanel(final Chart2D chart) {
168     super();
169     this.m_chart = chart;
170     this.setBackground(chart.getBackground());
171     // we paint our own labels
172
chart.setPaintLabels(false);
173     LayoutFactory.getInstance().createPopupMenu(chart, true);
174
175     // layouting
176
GridBagLayout JavaDoc gridbag = new GridBagLayout JavaDoc();
177     GridBagConstraints JavaDoc c = new GridBagConstraints JavaDoc();
178     this.setLayout(gridbag);
179     c.fill = GridBagConstraints.BOTH;
180     c.gridwidth = GridBagConstraints.REMAINDER;
181     c.weightx = 1.0;
182     c.weighty = 0.99;
183
184     gridbag.setConstraints(chart, c);
185     this.add(chart);
186     Iterator JavaDoc itTraces = chart.iterator();
187     ITrace2D trace;
188     // initial Labels
189
// put to a flow layout panel
190
this.m_labelPanel = new JPanel JavaDoc();
191     this.m_labelPanel.setFont(chart.getFont());
192     this.m_labelPanel.setLayout(new FlowLayout JavaDoc(FlowLayout.LEFT));
193     this.m_labelPanel.setBackground(chart.getBackground());
194     JLabel JavaDoc label;
195     while (itTraces.hasNext()) {
196       trace = (ITrace2D) itTraces.next();
197       label = LayoutFactory.getInstance().createContextMenuLable(chart, trace);
198       this.m_labelPanel.add(label);
199     }
200     c.weighty = 0.01;
201     gridbag.setConstraints(this.m_labelPanel, c);
202     this.add(this.m_labelPanel);
203     chart.addPropertyChangeListener(Chart2D.PROPERTY_BACKGROUND_COLOR, this);
204     // listen to new Charts and deleted ones.
205
chart.addPropertyChangeListener(Chart2D.PROPERTY_ADD_REMOVE_TRACE, this);
206
207   }
208
209   /**
210    * Listens for property "background" of the <code>Chart2D</code> instance
211    * that is contained in this component and sets the background color.
212    * <p>
213    *
214    * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent)
215    */

216   public void propertyChange(final PropertyChangeEvent JavaDoc evt) {
217     String JavaDoc prop = evt.getPropertyName();
218     if (prop.equals(Chart2D.PROPERTY_BACKGROUND_COLOR)) {
219       Color JavaDoc color = (Color JavaDoc) evt.getNewValue();
220       this.setBackground(color);
221       this.m_labelPanel.setBackground(color);
222     } else if (prop.equals(Chart2D.PROPERTY_ADD_REMOVE_TRACE)) {
223       ITrace2D oldTrace = (ITrace2D) evt.getOldValue();
224       ITrace2D newTrace = (ITrace2D) evt.getNewValue();
225       JLabel JavaDoc label;
226       if (oldTrace == null && newTrace != null) {
227         label = LayoutFactory.getInstance().createContextMenuLable(this.m_chart, newTrace);
228         this.m_labelPanel.add(label);
229         synchronized (this.m_chart) {
230           // this.doLayout();
231
// this.getLayout().layoutContainer(this);
232
this.invalidate();
233           this.m_labelPanel.invalidate();
234           this.validateTree();
235         }
236         this.m_labelPanel.doLayout();
237       } else if (oldTrace != null && newTrace == null) {
238         // search for label:
239
String JavaDoc labelName = oldTrace.getName();
240         Component JavaDoc[] labels = (this.m_labelPanel.getComponents());
241         for (int i = 0; i < labels.length; i++) {
242           if (((JLabel JavaDoc) labels[i]).getText().equals(labelName)) {
243             this.m_labelPanel.remove(labels[i]);
244             this.m_chart.removePropertyChangeListener((PropertyChangeListener JavaDoc) labels[i]);
245             oldTrace.removePropertyChangeListener((PropertyChangeListener JavaDoc) labels[i]);
246             // clear the popup menu listeners too:
247
MouseListener JavaDoc[] mouseListeners = labels[i].getMouseListeners();
248             for (int j = 0; j < mouseListeners.length; j++) {
249               labels[i].removeMouseListener(mouseListeners[j]);
250             }
251             this.m_labelPanel.doLayout();
252             this.doLayout();
253             break;
254           }
255         }
256       } else {
257         throw new IllegalArgumentException JavaDoc("Bad property change event for add / remove trace.");
258       }
259     }
260   }
261
262 }
263
Popular Tags