Back to index

wims  3.65+svn20090927
EditablePlot.java
Go to the documentation of this file.
00001 /* Extension of plot that allows interactive modification of plot data.
00002 
00003  Copyright (c) 1998-2007 The Regents of the University of California.
00004  All rights reserved.
00005  Permission is hereby granted, without written agreement and without
00006  license or royalty fees, to use, copy, modify, and distribute this
00007  software and its documentation for any purpose, provided that the above
00008  copyright notice and the following two paragraphs appear in all copies
00009  of this software.
00010 
00011  IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
00012  FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
00013  ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
00014  THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
00015  SUCH DAMAGE.
00016 
00017  THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
00018  INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00019  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
00020  PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
00021  CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
00022  ENHANCEMENTS, OR MODIFICATIONS.
00023 
00024  PT_COPYRIGHT_VERSION_2
00025  COPYRIGHTENDKEY
00026 
00027  */
00028 package ptolemy.plot;
00029 
00030 import java.awt.Color;
00031 import java.awt.Graphics;
00032 import java.awt.event.InputEvent;
00033 import java.awt.event.KeyEvent;
00034 import java.awt.event.KeyListener;
00035 import java.awt.event.MouseEvent;
00036 import java.awt.event.MouseListener;
00037 import java.awt.event.MouseMotionListener;
00038 import java.util.Enumeration;
00039 import java.util.Stack;
00040 import java.util.Vector;
00041 
00044 
00084 public class EditablePlot extends Plot {
00087     public EditablePlot() {
00088         super();
00089         addMouseListener(new EditMouseListener());
00090         addMouseMotionListener(new ModifyListener());
00091         addKeyListener(new UndoListener());
00092     }
00093 
00096 
00101     public void addEditListener(EditListener listener) {
00102         if (_editListeners == null) {
00103             _editListeners = new Vector();
00104         } else {
00105             if (_editListeners.contains(listener)) {
00106                 return;
00107             }
00108         }
00109 
00110         _editListeners.addElement(listener);
00111     }
00112 
00119     public double[][] getData(int dataset) {
00120         _checkDatasetIndex(dataset);
00121 
00122         Vector pts = (Vector) _points.elementAt(dataset);
00123         int size = pts.size();
00124         double[][] result = new double[2][size];
00125 
00126         for (int i = 0; i < size; i++) {
00127             PlotPoint pt = (PlotPoint) pts.elementAt(i);
00128             result[0][i] = pt.x;
00129             result[1][i] = pt.y;
00130         }
00131 
00132         return result;
00133     }
00134 
00138     public void redo() {
00139         if (_redoStack.empty()) {
00140             return;
00141         }
00142 
00143         Object[] save = new Object[2];
00144         save[0] = Integer.valueOf(_dataset);
00145         save[1] = getData(_dataset);
00146         _undoStack.push(save);
00147 
00148         Object[] saved = (Object[]) _redoStack.pop();
00149         _setData(((Integer) saved[0]).intValue(), (double[][]) saved[1]);
00150 
00151         // Ensure replot of offscreen buffer.
00152         _plotImage = null;
00153         repaint();
00154         _notifyListeners(_dataset);
00155     }
00156 
00162     public void removeEditListener(EditListener listener) {
00163         if (_editListeners == null) {
00164             return;
00165         }
00166 
00167         _editListeners.removeElement(listener);
00168     }
00169 
00175     public void setEditable(int dataset) {
00176         if (dataset >= 0) {
00177             _checkDatasetIndex(dataset);
00178         }
00179 
00180         _dataset = dataset;
00181     }
00182 
00186     public void undo() {
00187         if (_undoStack.empty()) {
00188             return;
00189         }
00190 
00191         Object[] save = new Object[2];
00192         save[0] = Integer.valueOf(_dataset);
00193         save[1] = getData(_dataset);
00194         _redoStack.push(save);
00195 
00196         Object[] saved = (Object[]) _undoStack.pop();
00197         _setData(((Integer) saved[0]).intValue(), (double[][]) saved[1]);
00198 
00199         // Ensure replot of offscreen buffer.
00200         _plotImage = null;
00201         repaint();
00202         _notifyListeners(_dataset);
00203     }
00204 
00207     // Clear the editing spec and modify the dataset.
00208     private synchronized void _edit(int x, int y) {
00209         if (_dataset < 0) {
00210             return;
00211         }
00212 
00213         // Save for undo.
00214         Object[] save = new Object[2];
00215         save[0] = Integer.valueOf(_dataset);
00216         save[1] = getData(_dataset);
00217 
00218         // FIXME: Need a way to notify menus to enable items...
00219         _undoStack.push(save);
00220 
00221         // NOTE: the clear() method was added in jdk 1.2, so we don't
00222         // use it here for maximal compatibility...
00223         // _redoStack.clear();
00224         while (!_redoStack.empty()) {
00225             _redoStack.pop();
00226         }
00227 
00228         // constrain to be in range
00229         if (y > _lry) {
00230             y = _lry;
00231         }
00232 
00233         if (y < _uly) {
00234             y = _uly;
00235         }
00236 
00237         if (x > _lrx) {
00238             x = _lrx;
00239         }
00240 
00241         if (x < _ulx) {
00242             x = _ulx;
00243         }
00244 
00245         _editPoint(x, y);
00246 
00247         // Edit the points in the signal.
00248         Vector pts = (Vector) _points.elementAt(_dataset);
00249 
00250         for (int i = 0; i < pts.size(); i++) {
00251             PlotPoint pt = (PlotPoint) pts.elementAt(i);
00252 
00253             // Only bother with points in visual range
00254             if ((pt.x >= _xMin) && (pt.x <= _xMax)) {
00255                 int index = (int) ((pt.x - _xMin) * _xscale)
00256                         - (_lrx - _ulx - _editSpecX.length);
00257 
00258                 if ((index >= 0) && (index < _editSpecX.length)) {
00259                     if (_editSpecSet[index]) {
00260                         pt.y = _yMax - ((_editSpecY[index] - _uly) / _yscale);
00261 
00262                         // For auto-ranging, keep track of min and max.
00263                         if (pt.y < _yBottom) {
00264                             _yBottom = pt.y;
00265                         }
00266 
00267                         if (pt.y > _yTop) {
00268                             _yTop = pt.y;
00269                         }
00270                     }
00271                 }
00272             }
00273         }
00274 
00275         // Ensure replot of offscreen buffer.
00276         _plotImage = null;
00277         repaint();
00278 
00279         // Erase the guide
00280         // I don't think we need to do this, since we call repaint().
00281         //         graphics.setXORMode(_editColor);
00282         //         for (int i = 0; i < _editSpecX.length; i++) {
00283         //             if (_editSpecSet[i]) {
00284         //                 graphics.drawLine(_editSpecX[i], _editSpecY[i]-1,
00285         //                         _editSpecX[i], _editSpecY[i]+1);
00286         //             }
00287         //         }
00288         //         graphics.setPaintMode();
00289         _notifyListeners(_dataset);
00290     }
00291 
00292     // Make a record of a new edit point.
00293     private synchronized void _editPoint(int x, int y) {
00294         if (_dataset < 0) {
00295             return;
00296         }
00297 
00298         Graphics graphics = getGraphics();
00299 
00300         // constrain to be in range
00301         if (y > _lry) {
00302             y = _lry;
00303         }
00304 
00305         if (y < _uly) {
00306             y = _uly;
00307         }
00308 
00309         if (x > _lrx) {
00310             x = _lrx;
00311         }
00312 
00313         if (x < _ulx) {
00314             x = _ulx;
00315         }
00316 
00317         if ((x <= _currentEditX) || (x >= _lrx)) {
00318             // ignore
00319             return;
00320         }
00321 
00322         int step = _currentEditX;
00323 
00324         while (step <= x) {
00325             int index = step - (_lrx - _editSpecX.length);
00326             double proportion = (step - _currentEditX)
00327                     / (double) (x - _currentEditX);
00328             int newY = (int) (_currentEditY + (proportion * (y - _currentEditY)));
00329 
00330             if (!_editSpecSet[index]) {
00331                 _editSpecX[index] = step;
00332                 _editSpecY[index] = newY;
00333                 _editSpecSet[index] = true;
00334 
00335                 // Draw point, linearly interpolated from previous point
00336                 graphics.setXORMode(_editColor);
00337                 graphics.drawLine(step, newY - 1, step, newY + 1);
00338                 graphics.setPaintMode();
00339             }
00340 
00341             step++;
00342         }
00343 
00344         _currentEditX = x;
00345         _currentEditY = y;
00346     }
00347 
00348     // Make a record of the starting x and y position of an edit.
00349     private synchronized void _editStart(int x, int y) {
00350         if (_dataset < 0) {
00351             return;
00352         }
00353 
00354         // constrain to be in range
00355         if (y > _lry) {
00356             y = _lry;
00357         }
00358 
00359         if (y < _uly) {
00360             y = _uly;
00361         }
00362 
00363         if (x > _lrx) {
00364             x = _lrx;
00365         }
00366 
00367         if (x < _ulx) {
00368             x = _ulx;
00369         }
00370 
00371         // Allocate a vector to store the points.
00372         int size = _lrx - x + 1;
00373         _editSpecX = new int[size];
00374         _editSpecY = new int[size];
00375         _editSpecSet = new boolean[size];
00376 
00377         _editSpecX[0] = x;
00378         _editSpecY[0] = y;
00379         _editSpecSet[0] = true;
00380 
00381         _currentEditX = x;
00382         _currentEditY = y;
00383 
00384         Graphics graphics = getGraphics();
00385 
00386         // Draw point (as a 3 pixel vertical line, for thickness)
00387         graphics.setXORMode(_editColor);
00388         graphics.drawLine(x, y - 1, x, y + 1);
00389         graphics.setPaintMode();
00390     }
00391 
00392     // Notify all edit listeners that have registered.
00393     private void _notifyListeners(int dataset) {
00394         if (_editListeners == null) {
00395             return;
00396         } else {
00397             Enumeration listeners = _editListeners.elements();
00398 
00399             while (listeners.hasMoreElements()) {
00400                 ((EditListener) listeners.nextElement()).editDataModified(this,
00401                         dataset);
00402             }
00403         }
00404     }
00405 
00406     // Set the data in the specified dataset. The argument is of the
00407     // form returned by getData.
00408     private void _setData(int dataset, double[][] data) {
00409         _checkDatasetIndex(dataset);
00410 
00411         Vector pts = (Vector) _points.elementAt(dataset);
00412         int size = pts.size();
00413 
00414         if (data[0].length < size) {
00415             size = data[0].length;
00416         }
00417 
00418         for (int i = 0; i < size; i++) {
00419             PlotPoint pt = (PlotPoint) pts.elementAt(i);
00420             pt.x = data[0][i];
00421             pt.y = data[1][i];
00422         }
00423     }
00424 
00427     private int[] _editSpecX;
00428 
00431     private int[] _editSpecY;
00432 
00433     private boolean[] _editSpecSet;
00434 
00435     private int _currentEditX;
00436 
00437     private int _currentEditY;
00438 
00439     private int _dataset = 0;
00440 
00441     // Call setXORMode with a hardwired color because
00442     // _background does not work in an application,
00443     // and _foreground does not work in an applet
00444     private static final Color _editColor = Color.white;
00445 
00446     // Stack for undo.
00447     private Stack _undoStack = new Stack();
00448 
00449     private Stack _redoStack = new Stack();
00450 
00451     // Edit listeners.
00452     private Vector _editListeners = null;
00453 
00456     public class EditMouseListener implements MouseListener {
00457         public void mouseClicked(MouseEvent event) {
00458         }
00459 
00460         public void mouseEntered(MouseEvent event) {
00461         }
00462 
00463         public void mouseExited(MouseEvent event) {
00464         }
00465 
00466         public void mousePressed(MouseEvent event) {
00467             if ((event.getModifiers() & InputEvent.BUTTON3_MASK) != 0) {
00468                 EditablePlot.this._editStart(event.getX(), event.getY());
00469             }
00470         }
00471 
00472         public void mouseReleased(MouseEvent event) {
00473             if ((event.getModifiers() & InputEvent.BUTTON3_MASK) != 0) {
00474                 EditablePlot.this._edit(event.getX(), event.getY());
00475             }
00476         }
00477     }
00478 
00479     public class ModifyListener implements MouseMotionListener {
00480         public void mouseDragged(MouseEvent event) {
00481             if ((event.getModifiers() & InputEvent.BUTTON3_MASK) != 0) {
00482                 EditablePlot.this._editPoint(event.getX(), event.getY());
00483             }
00484         }
00485 
00486         public void mouseMoved(MouseEvent event) {
00487         }
00488     }
00489 
00490     public class UndoListener implements KeyListener {
00491         public void keyPressed(KeyEvent e) {
00492             int keycode = e.getKeyCode();
00493 
00494             switch (keycode) {
00495             case KeyEvent.VK_CONTROL:
00496                 _control = true;
00497                 break;
00498 
00499             case KeyEvent.VK_Z:
00500 
00501                 if (_control) {
00502                     undo();
00503                 }
00504 
00505                 break;
00506 
00507             case KeyEvent.VK_Y:
00508 
00509                 if (_control) {
00510                     redo();
00511                 }
00512 
00513                 break;
00514 
00515             default:
00516                 // None
00517             }
00518         }
00519 
00520         public void keyReleased(KeyEvent e) {
00521             int keycode = e.getKeyCode();
00522 
00523             switch (keycode) {
00524             case KeyEvent.VK_CONTROL:
00525                 _control = false;
00526                 break;
00527 
00528             default:
00529                 // None
00530             }
00531         }
00532 
00533         // The keyTyped method is broken in jdk 1.1.4.
00534         // It always gets "unknown key code".
00535         public void keyTyped(KeyEvent e) {
00536         }
00537 
00538         private boolean _control = false;
00539     }
00540 }