1 package edu.psu.geovista.category;
2
3 /***
4 * <p>Title: Studio applications</p>
5 * <p>Description: </p>
6 * <p>Copyright: Copyright (c) 2002</p>
7 * <p>Company: GeoVSITA Center</p>
8 * @author Xiping Dai
9 * @version 1.0
10 */
11
12 import java.lang.*;
13 import java.awt.*;
14 import javax.swing.*;
15 import java.awt.event.*;
16 import java.util.Vector;
17 import edu.psu.geovista.app.scatterplot.*;
18 import edu.psu.geovista.data.ArraySort2D;
19 import edu.psu.geovista.data.StatisticsVectors;
20 import edu.psu.geovista.classification.MultiGaussian;
21 import javax.swing.border.*;
22 import javax.swing.BorderFactory;
23 import cern.colt.matrix.impl.DenseDoubleMatrix2D;
24 import cern.colt.matrix.impl.DenseDoubleMatrix1D;
25 import cern.colt.matrix.linalg.EigenvalueDecomposition;
26
27 public class MultiClassDistributions2D extends JPanel implements MouseListener, ComponentListener
28 {
29 private static double AXISSPACEPORTION = 1.0/6.0;
30 private Color background;
31 private Color foreground;
32 private boolean axisOn = true;
33 private int plotOriginX;
34 private int plotOriginY;
35 private int plotEndX;
36 private int plotEndY;
37 private Vector[] dataVector;
38 private String[] variableNames;
39 private int classNumber;
40 private Vector CovarianceVector = new Vector();
41 private Vector meanVector = new Vector();
42 private Vector meanIntVector = new Vector();
43 private Vector dataXYVector = new Vector();
44 private int displayXVarIdx;
45 private int displayYVarIdx;
46 private double xAxisMin;
47 private double xAxisMax;
48 private double yAxisMin;
49 private double yAxisMax;
50 private Vector exsIntVector = new Vector();
51 private Vector whyIntVector = new Vector();
52 private boolean gauss;
53 private JPopupMenu popup;
54 private JCheckBoxMenuItem[] classCheckBox;
55 private int currentCheckBox;
56 private MultiGaussian[] eachClassGaussian;
57 private DenseDoubleMatrix2D[] covarianceMatrices;
58 private EigenvalueDecomposition[] eigenDecomposition;
59 private Vector eigenVectors = new Vector();
60 private Vector sdVectors = new Vector();
61 private Vector sdIntVectors = new Vector();
62 //private EventListenerList listenerListAction = new EventListenerList();
63
64 public MultiClassDistributions2D(){
65 this.setBorder(BorderFactory.createLineBorder(Color.gray));
66 popup = new JPopupMenu();
67 }
68
69 public void setDisplayXVariableIndex(int index){
70 this.displayXVarIdx = index;
71 this.initialize();
72 }
73
74 public int getDisplayXVariableIndex(){
75 return this.displayXVarIdx;
76 }
77
78 public void setDisplayYVariableIndex(int index){
79 this.displayYVarIdx = index;
80 this.initialize();
81 //repaint();
82 }
83
84 public int getDisplayYVariableIndex(){
85 return this.displayYVarIdx;
86 }
87
88 public void setVariableNames(String[] variableNames){
89 this.variableNames = variableNames;
90 }
91
92 public void setDataVector(Vector[] dataVector){
93 this.dataVector = dataVector;
94 this.classNumber = this.dataVector.length;
95
96 this.classCheckBox = new JCheckBoxMenuItem[this.classNumber];
97 for(int i = 0; i < this.classNumber; i ++){
98 this.classCheckBox[i] = new JCheckBoxMenuItem("class" + new Integer(i).toString());
99 this.classCheckBox[i].setName(new Integer(i).toString());
100 this.classCheckBox[i].setSelected(true);
101 this.popup.add(this.classCheckBox[i], i);
102 this.currentCheckBox = i;
103 classCheckBox[currentCheckBox].addActionListener(new ActionListener() {
104
105 /***
106 * put your documentation comment here
107 * @param e
108 */
109 public void actionPerformed (ActionEvent e) {
110 boolean isSelected;
111 if (classCheckBox[currentCheckBox] != null){
112 isSelected = classCheckBox[currentCheckBox].isSelected();
113 classCheckBox[currentCheckBox].setSelected(!isSelected);
114 }
115 repaint();
116 }
117 });
118 }
119 addMouseListener(this);
120
121 this.initialize();
122 }
123
124 public Vector[] getDataVector(){
125 return this.dataVector;
126 }
127
128 public void setAxisOn (boolean axisOn){
129 this.axisOn = axisOn;
130 }
131
132 public boolean getAxisOn (){
133 return this.axisOn;
134 }
135
136 public void setGaussOn (boolean gauss){
137 this.gauss = gauss;
138 }
139
140 public boolean getGaussOn (){
141 return this.gauss;
142 }
143
144 /***
145 * put your documentation comment here
146 * @param c
147 */
148 public void setBackground (Color c) {
149 if (c == null)
150 return;
151 this.background = c;
152 int colorTotal = c.getRed() + c.getGreen() + c.getBlue();
153 int greyColor = 128 * 3;
154 if (colorTotal < greyColor)
155 this.foreground = Color.white;
156 else
157 this.foreground = Color.black;
158 this.repaint();
159 }
160
161 private void initialize(){
162
163 this.eachClassGaussian = new MultiGaussian[this.classNumber];
164 this.covarianceMatrices = new DenseDoubleMatrix2D[this.classNumber];
165 this.eigenDecomposition = new EigenvalueDecomposition[this.classNumber];
166 this.CovarianceVector.clear();
167 this.dataXYVector.clear();
168 this.eigenVectors.clear();
169 this.exsIntVector.setSize(this.classNumber);
170 this.whyIntVector.setSize(this.classNumber);
171 for(int i = 0; i < this.classNumber; i ++){
172 int len = this.dataVector[i].size();
173 double[][] oneClassData = new double[2][len];
174 for(int j = 0; j < len; j ++){
175 if (((double[])(this.dataVector[i].get(j))).length <= this.displayXVarIdx){
176 return;
177 }
178 oneClassData[0][j] = ((double[])(this.dataVector[i].get(j)))[this.displayXVarIdx];
179 oneClassData[1][j] = ((double[])(this.dataVector[i].get(j)))[this.displayYVarIdx];
180 }
181 this.dataXYVector.add(i, oneClassData.clone());
182 }
183
184 for(int i = 0; i < this.classNumber; i ++){
185 this.eachClassGaussian[i] = new MultiGaussian();
186 double[][] dataForGaussian = new double[this.dataVector[i].size()][2];
187 for (int m = 0; m < 2; m ++){
188 for (int n = 0; n < this.dataVector[i].size(); n ++){
189 dataForGaussian[n][m] = ((double[][])dataXYVector.get(i))[m][n];
190 }
191 }
192 this.eachClassGaussian[i].setTrainingData(dataForGaussian);
193 double[][] covariance = new double[2][2];
194 double[] mean = new double[2];
195 DenseDoubleMatrix2D eigenVector;
196 DenseDoubleMatrix1D eigenValues;
197 covariance = this.eachClassGaussian[i].getCovarianceMatrix();
198 mean = this.eachClassGaussian[i].getMeanVector();
199 this.CovarianceVector.add(i, covariance.clone());
200 this.meanVector.add(i, mean.clone());
201 this.covarianceMatrices[i] = new DenseDoubleMatrix2D((double[][])this.CovarianceVector.get(i));
202 this.eigenDecomposition[i] = new EigenvalueDecomposition(this.covarianceMatrices[i]);
203 eigenVector = (DenseDoubleMatrix2D)this.eigenDecomposition[i].getV();
204 this.eigenVectors.add(i, eigenVector.toArray());
205 eigenValues = (DenseDoubleMatrix1D)this.eigenDecomposition[i].getRealEigenvalues();
206 double[] sd = new double[2];
207 //sd[0] = Math.sqrt(Math.abs(eigenVector.toArray()[0][0]));
208 //sd[1] = Math.sqrt(Math.abs(eigenVector.toArray()[0][1]));
209 sd[0] = Math.sqrt(Math.abs(eigenValues.toArray()[0]));
210 sd[1] = Math.sqrt(Math.abs(eigenValues.toArray()[1]));
211 this.sdVectors.add(i, sd.clone());
212 }
213
214 this.addComponentListener(this);
215
216 this.setBackground(Color.white);
217
218 this.xAxisMin = 0;
219 this.xAxisMax = 0;
220
221 this.yAxisMin = 0;
222 this.yAxisMax = 0;
223 for(int i = 0; i < this.classNumber; i ++){
224 DataArray xDataArray = new DataArray(((double[][])this.dataXYVector.get(i))[0]);
225 DataArray yDataArray = new DataArray(((double[][])this.dataXYVector.get(i))[1]);
226 double xMin = ((double[])xDataArray.getExtent().clone())[0];
227 double xMax = ((double[])xDataArray.getExtent().clone())[1];
228 double yMin = ((double[])yDataArray.getExtent().clone())[0];
229 double yMax = ((double[])yDataArray.getExtent().clone())[1];
230 this.yAxisMin = (this.yAxisMin <= yMin) ? yAxisMin : yMin;
231 this.yAxisMax = (this.yAxisMax >= yMax) ? yAxisMax : yMax;
232
233 this.xAxisMin = (this.xAxisMin <= xMin) ? xAxisMin : xMin;
234 this.xAxisMax = (this.xAxisMax >= xMax) ? xAxisMax : xMax;
235 }
236
237 setupDataforDisplay();
238 this.validate();
239 }
240
241 private void setupDataforDisplay(){
242
243 if (axisOn){
244 plotOriginX = (int)(this.getWidth()*AXISSPACEPORTION);
245 plotOriginY = (int)(this.getHeight()*(1 - AXISSPACEPORTION));
246 plotEndX = (int)(this.getWidth()) - (int)(this.getWidth()*AXISSPACEPORTION/2);
247 plotEndY = (int)(this.getHeight()*AXISSPACEPORTION/2);
248 }else {
249 plotOriginX = 0;
250 plotOriginY = (int)(this.getSize().getHeight() - 2);
251 plotEndX = (int)(this.getSize().getWidth()) - 3;
252 plotEndY = 3;
253 }
254
255 //get positions on screen
256 double scaleX, scaleY;
257 scaleX = getScale(plotOriginX, plotEndX, xAxisMin, xAxisMax);
258 scaleY = getScale(plotOriginY, plotEndY, yAxisMin, yAxisMax);
259
260 this.exsIntVector.clear();
261 this.whyIntVector.clear();
262
263 for(int cl = 0; cl < this.classNumber; cl ++){
264 int len = this.dataVector[cl].size();
265 int[] exsInt = new int[len];
266 int[] whyInt = new int[len];
267 exsInt = getValueScreen(((double[][])dataXYVector.get(cl))[0], scaleX, plotOriginX, xAxisMin);
268 whyInt = getValueScreen(((double[][])dataXYVector.get(cl))[1], scaleY, plotOriginY, yAxisMin);
269 this.exsIntVector.add(cl, exsInt.clone());
270 this.whyIntVector.add(cl, whyInt.clone());
271 }
272
273 if (gauss == true){
274 for(int cl = 0; cl < this.classNumber; cl ++){
275 int[] sdInt = new int[2];
276 //sdInt[0] = getValueScreen(((double[])this.sdVectors.get(cl))[0], scaleX, plotOriginX, xAxisMin);
277 //sdInt[1] = getValueScreen(((double[])this.sdVectors.get(cl))[1], scaleY, plotOriginY, yAxisMin);
278 sdInt[0] = (int)(((double[])this.sdVectors.get(cl))[0] * scaleX);
279 sdInt[1] = (int)(((double[])this.sdVectors.get(cl))[1] * scaleX);
280 this.sdIntVectors.add(cl,sdInt.clone());
281 int[] meanInt = new int[2];
282 meanInt[0] = getValueScreen(((double[])this.meanVector.get(cl))[0], scaleX, plotOriginX, xAxisMin);
283 meanInt[1] = getValueScreen(((double[])this.meanVector.get(cl))[1], scaleY, plotOriginY, yAxisMin);
284 this.meanIntVector.add(cl, meanInt.clone());
285 }
286 }
287 }
288
289 public void paintComponent (Graphics g) {
290 g.setColor(background);
291 g.fillRect(0, 0, getSize().width, getSize().height);
292 g.setColor(foreground);
293 if (this.axisOn == true){
294 drawAxis(g);
295 }
296 drawPlot(g);
297 }
298
299 private void drawPlot (Graphics g) {
300 if (this.displayXVarIdx == this.displayYVarIdx){
301 int plotWidth, plotHeight;
302 plotWidth = (int)this.getWidth();
303 plotHeight = (int)this.getHeight();
304 int size;
305 size = (plotWidth < plotHeight) ? plotWidth : plotHeight;
306 String attributeX = new String();
307 attributeX = this.variableNames[this.displayXVarIdx];
308 if (attributeX.length()>12){
309 g.drawString(attributeX, 2, plotHeight/2);
310 }else if (attributeX.length()<=7){
311 g.drawString(attributeX, plotWidth/4, plotHeight/2);
312 }else {
313 g.drawString(attributeX, plotWidth/8, plotHeight/2);
314 }
315 //font.this.getSize() = (int)plotWidth/12;
316 Font font1 = new Font("", Font.PLAIN, (int)size/12);
317 g.setFont(font1);
318 g.drawLine(0, 0, 5, 5);
319 String maxString = Float.toString((float)(xAxisMax));
320 g.drawString(maxString, 6, (int)(plotHeight*AXISSPACEPORTION/2) + 2);
321 g.drawLine(0, plotHeight, 5, plotHeight - 5);
322 String minString = Float.toString((float)(xAxisMin));
323 g.drawString(minString, 6, plotHeight - 5);
324 g.drawLine(plotWidth, plotHeight, plotWidth - 5, plotHeight - 5);
325 g.drawString(maxString, plotWidth - (int)(plotWidth*AXISSPACEPORTION + 5),
326 plotHeight - 5);
327 }else{
328
329
330 for(int cl = 0; cl < this.classNumber; cl ++){
331 //int[] exsInt = (int[])this.exsIntVector.get(cl);
332 //int[] whyInt = (int[])this.whyIntVector.get(cl);
333 if(cl == 0){
334 g.setColor(Color.red);
335 }else if(cl == 1){
336 g.setColor(Color.BLUE);
337 }else if(cl == 2){
338 g.setColor(Color.green);
339 }else if(cl == 3){
340 g.setColor(Color.orange);
341 }else if(cl == 4){
342 g.setColor(Color.darkGray);
343 }
344 if (this.classCheckBox[cl].isSelected() == true){
345 for (int i = 0; i < this.dataVector[cl].size(); i ++){
346 g.drawOval(((int[])this.exsIntVector.get(cl))[i], ((int[])this.whyIntVector.get(cl))[i], 2, 2);
347 }
348 g.drawOval(((int[])this.meanIntVector.get(cl))[0], ((int[])this.meanIntVector.get(cl))[1], 4, 4);
349 if(gauss == true){
350 int[] mean = (int[])this.meanIntVector.get(cl);
351 int[] sd = (int[])this.sdIntVectors.get(cl);
352 double[][] eigenVector = (double[][])this.eigenVectors.get(cl);
353 Graphics2D g2d = (Graphics2D)g;
354 g2d.rotate((Math.atan(eigenVector[0][0]/eigenVector[1][0])-Math.PI/2), mean[0], mean[1]);
355 g2d.drawOval(mean[0]-sd[0], mean[1]-sd[1], 2*sd[0], 2*sd[1]);
356 g2d.drawOval(mean[0]-2*sd[0], mean[1]-2*sd[1], 4*sd[0], 4*sd[1]);
357 g2d.rotate((-Math.atan(eigenVector[0][0]/eigenVector[1][0])+Math.PI/2), mean[0], mean[1]);
358 }
359 g.setColor(foreground);
360 }
361 }
362 }
363 }
364
365
366 private void drawAxis (Graphics g) {
367 int plotWidth, plotHeight;
368 plotWidth = (int)this.getSize().getWidth();
369 plotHeight = (int)this.getSize().getHeight();
370 g.setColor(foreground);
371 g.drawLine(plotOriginX, plotEndY, plotOriginX, plotOriginY);
372 g.drawLine(plotOriginX, plotOriginY, plotEndX, plotOriginY);
373 // draw tick bars for scales on Y coordinate
374 int fontSize;
375 if (plotWidth < plotHeight){
376 if (plotWidth < 300){
377 fontSize = 9;
378 } else {
379 fontSize = (int)(plotWidth/32);
380 }
381 }else {
382 if (plotHeight < 300){
383 fontSize = 9;
384 } else {
385 fontSize = (int)(plotHeight/32);
386 }
387 }
388 Font font = new Font("", Font.PLAIN, fontSize);
389 g.setFont(font);
390 //draw the labels on y axis (frequency).
391 /*String scaleStringY;
392 double barNumber = this.pdfDataArray.getTickNumber();
393 double yBarDistance = ((plotOriginY - plotEndY)/barNumber);
394 //System.out.println("drawaxis: "+plotOriginY+" "+plotEndY+" "+yBarDistance+" "+barNumber);
395 for (int i = 0; i <= barNumber; i++) {
396 g.drawLine(plotOriginX - 3, plotEndY + (int)(i*yBarDistance), plotOriginX,
397 plotEndY + (int)(i*yBarDistance));
398 if (Math.abs(this.pdfDataArray.getMajorTick()) <= 1) {
399 scaleStringY = Float.toString((float)(yAxisMax - i*this.pdfDataArray.getMajorTick()));
400 }
401 else {
402 scaleStringY = Integer.toString((int)(yAxisMax - i*this.pdfDataArray.getMajorTick()));
403 }
404 g.drawString(scaleStringY, plotOriginX - (int)(plotWidth*AXISSPACEPORTION/2),
405 plotEndY + (int)(i*yBarDistance + yBarDistance*1/6));
406 }*/
407 //draw the labels on x axis.
408 //First tick.
409 String scaleStringX;
410 g.drawLine(plotOriginX, plotOriginY, plotOriginX, plotOriginY + 3);
411 if (Math.abs(xAxisMin) <= 1) {
412 scaleStringX = Float.toString((float)xAxisMin);
413 } else {
414 scaleStringX = Integer.toString((int)xAxisMin);
415 }
416 g.drawString(scaleStringX, plotOriginX - 3, plotOriginY + (int)(plotHeight*AXISSPACEPORTION/4));
417 //Last tick.
418 g.drawLine(plotEndX, plotOriginY, plotEndX, plotOriginY + 3);
419 if (Math.abs(xAxisMax) <= 1) {
420 scaleStringX = Float.toString((float)xAxisMax);
421 } else {
422 scaleStringX = Integer.toString((int)xAxisMax);
423 }
424 g.drawString(scaleStringX, plotEndX - 8, plotOriginY + (int)(plotHeight*AXISSPACEPORTION/4));
425 font = new Font("", Font.PLAIN, fontSize + 3);
426 g.setFont(font);
427 //draw X axis attribute string
428 //g.drawString(this.variableName, plotOriginX + (plotEndX - plotOriginX)/2 - plotWidth/12,
429 // plotOriginY + plotHeight/6 - 5);
430 //draw Y axis attribute string. Need rotation for drawing the string vertically.
431 Graphics2D g2d = (Graphics2D)g;
432 g2d.rotate(-Math.PI/2, plotOriginX - plotWidth/9, plotOriginY - (plotOriginY
433 - plotEndY)/3);
434 //g2d.drawString("Density", plotOriginX - plotWidth/9, plotOriginY - (plotOriginY
435 // - plotEndY)/3);
436 g2d.rotate(+Math.PI/2, plotOriginX - plotWidth/9, plotOriginY - (plotOriginY
437 - plotEndY)/3);
438 }
439
440 /***
441 * Calculate scale between real data and integer data for showing up on screen.
442 * @param min
443 * @param max
444 * @param dataMin
445 * @param dataMax
446 * @return scale
447 */
448 private double getScale (int min, int max, double dataMin, double dataMax) {
449 double scale;
450 scale = (max - min)/(dataMax - dataMin);
451 return scale;
452 }
453 /***
454 * Convert the single value to integer value worked on screen.
455 * @param data
456 * @param scale
457 * @param min
458 * @param dataMin
459 * @return valueScreen
460 */
461 private int getValueScreen (double data, double scale, int min, double dataMin) {
462 int valueScreen;
463 if (Double.isNaN(data)) {
464 valueScreen = Integer.MIN_VALUE;
465 }
466 else {
467 valueScreen = (int)((data - dataMin)*scale + min);
468 }
469 return valueScreen;
470 }
471 /***
472 * Convert the numeric values of observations to integer value worked on screen.
473 * @param dataArray
474 * @param scale
475 * @param min
476 * @param dataMin
477 * @return valueScreen
478 */
479 private int[] getValueScreen (double[] dataArray, double scale, int min, double dataMin) {
480 int[] valueScreen = new int[dataArray.length];
481 for (int i = 0; i < dataArray.length; i++) {
482 if (Double.isNaN(dataArray[i])) {
483 valueScreen[i] = Integer.MIN_VALUE;
484 }
485 else {
486 valueScreen[i] = (int)((dataArray[i] - dataMin)*scale + min);
487 }
488 }
489 return valueScreen;
490 }
491
492 public void componentHidden(ComponentEvent e) {
493
494 }
495
496 public void componentMoved(ComponentEvent e) {
497
498 }
499
500 public void componentResized(ComponentEvent e) {
501 this.setupDataforDisplay();
502 this.repaint();
503 }
504
505 public void componentShown(ComponentEvent e) {
506 }
507
508 /***
509 * put your documentation comment here
510 * @param e
511 */
512 private void maybeShowPopup (MouseEvent e) {
513 if (e.isPopupTrigger()) {
514 popup.show(e.getComponent(), e.getX(), e.getY());
515 }
516 }
517
518 public void mouseClicked(MouseEvent e){
519 int count = e.getClickCount();
520 //double click, pop up a detail scatter plot.
521 if (count == 2){ // This is a double-click or triple...
522 MultiClassDistributions2D detailSP = new MultiClassDistributions2D();
523 detailSP.setAxisOn(true);
524 detailSP.setBackground(background);
525 detailSP.setGaussOn(true);
526 detailSP.setDisplayXVariableIndex(this.displayXVarIdx);
527 detailSP.setDisplayYVariableIndex(this.displayYVarIdx);
528 detailSP.setDataVector(this.dataVector);
529 //detailSP.setElementPosition(dataIndices);
530 //detailSP.setColorArrayForObs(this.colorArrayForObs);
531 //detailSP.setSelectedObservations(selRecords);
532 //detailSP.setSelections(this.selections);
533 JFrame dlgSP = new JFrame();
534 //JDialog dlgSP = new JDialog(dummyFrame, "Detailed Distributions", true);
535 dlgSP.setLocation(300, 300);
536 dlgSP.setSize(300, 300);
537 dlgSP.getContentPane().setLayout(new BorderLayout());
538 dlgSP.getContentPane().add(detailSP, BorderLayout.CENTER);
539 /*detailSP.addActionListener(new ActionListener() {
540
541 /**
542 * put your documentation comment here
543 * @param e
544 */
545 /*public void actionPerformed (ActionEvent e) {
546 //System.out.println("something came from detailed one.");
547 ScatterPlot detailSP = (ScatterPlot)e.getSource();
548 String command = e.getActionCommand();
549 if (command.compareTo(ScatterPlot.COMMAND_POINT_SELECTED) == 0) {
550 //System.out.println("SPMC.plotUnitPanel.actionPerformed(), point selected");
551 //Vector selRecords = detailSP.getSelectedObservations();
552 int[] selections = detailSP.getSelections();
553 // Don't recall the scatterplot which generated the original event
554 //ScatterPlot.this.setSelectedObservations(selRecords);
555 ScatterPlot.this.setSelections(selections);
556 ScatterPlot.this.fireActionPerformed(COMMAND_POINT_SELECTED);
557 }
558 else if(command.compareTo(ScatterPlot.COMMAND_DATARANGE_SET)==0){
559 double[] dataArrayX = detailSP.getXAxisExtents();
560 double[] dataArrayY = detailSP.getYAxisExtents();
561 ScatterPlot.this.setXAxisExtents(dataArrayX);
562 ScatterPlot.this.setYAxisExtents(dataArrayY);
563 fireActionPerformed(COMMAND_DATARANGE_SET);
564 }
565 //System.err.println("Unknown command! = " + command);
566 }
567 });*/
568 dlgSP.show();
569 }
570 }
571
572 public void mousePressed(MouseEvent e){
573 if (e.isPopupTrigger())
574 maybeShowPopup(e);
575 }
576
577 public void mouseReleased(MouseEvent e){
578 if (e.isPopupTrigger()){
579 maybeShowPopup(e);
580 }
581 }
582
583 public void mouseEntered(MouseEvent e){
584 }
585
586 public void mouseExited(MouseEvent e){
587 }
588
589 }
This page was automatically generated by Maven