001    package ui.recognizer;
002    
003    import javax.swing.*;
004    import java.awt.*;
005    import java.awt.event.*;
006    import java.awt.geom.*;
007    import java.awt.image.*;
008    import java.net.*;
009    import java.io.*;
010    import java.text.*;
011    import java.util.*;
012    import java.util.Collection.*;
013    
014    import java.awt.dnd.*;
015    import java.awt.datatransfer.*;
016    
017    public class PlotRecognizer extends JFrame implements ItemListener, ActionListener, DropTargetListener {
018    
019        public static final String FILE            = "File";
020        public static final String FILE_LOAD       = "Load Image";
021        public static final String FILE_SAVE       = "Save Data";  // disabled until data available
022        public static final String FILE_QUIT       = "Quit";
023        JMenuItem saveMenuItem; // disabled until data available
024    
025        JMenu editMenu;         // disabled until image is loaded
026        public static final String EDIT            = "Edit";
027        public static final String EDIT_CLEAN      = "Remove blemishes";
028        public static final String EDIT_REMOVE_GRID= "Remove grid";
029        public static final String EDIT_CALIBRATE  = "Calibrate axes";
030        public static final String EDIT_WINDOW     = "Restrict data to window";
031        public static final String EDIT_RECOGNIZE  = "Recognize plot";
032    
033        JMenu viewMenu;         // disabled until image is loaded
034        public static final String VIEW            = "View";
035        public static final String VIEW_ZOOM_IN    = "Zoom out";
036        public static final String VIEW_ZOOM_OUT   = "Zoom in";
037        public static final String VIEW_ZOOM_NORMAL= "Zoom normal";
038    
039        JMenu dataMenu;         // disabled until data available
040        public static final String DATA            = "Data";
041        public static final String DATA_MODIFY     = "View or modify data";
042        public static final String DATA_SMOOTH     = "Smooth data";
043        public static final String DATA_FLATEN     = "Flaten data";
044        public static final String DATA_OVERLAY    = "Show overlay";
045    
046        public static int WIDTH = 1024;
047        public static int HEIGHT = 768;
048    
049        JFileChooser loadFileChooser = new PlotFileChooser("Load a GIF or PNG plot image to be recognized",
050                                                           "Image Files", new String[] {"gif", "png"});
051        JFileChooser saveFileChooser = new PlotFileChooser("Save recognized plot data as x, y file",
052                                                           "Data Files", new String[] {"txt", "dat"});
053    
054        // fields which determine the state of the plot processor
055    
056        DataCoordinate[] data;
057        ImageCoordinate[] imagePoints;
058        ImageIcon imageIcon = null;
059    
060        //Rectangle plotBorder = null;  // the image coordinates of the plot border
061        Calibration calibration;
062    
063        public static String DATA_OUTPUT_FORMAT_TEXT = "text";
064        public static String DATA_OUTPUT_FORMAT_XML  = "xml";
065        public static String DATA_OUTPUT_FORMAT_FITS = "fits";
066    
067        private String dataOutputFormat = DATA_OUTPUT_FORMAT_TEXT;
068        private String xTextOutputFormat = "0.00";
069        private String yTextOutputFormat = "0.0000";
070    
071        private int blackThreshold = 128;
072    
073        // the data ranges (will be different for most plots)
074        private double xlow  = 0.0d;
075        private double xhigh = 1.0d;
076        private double ylow  = 0.0d;
077        private double yhigh = 1.0d;
078    
079        private int samples = 0;
080        private String dataFileType = ".dat";
081        private String dataFileName;
082        private String imageFileName;
083        private boolean imageLoaded   = false;
084        private boolean dataAvailable = false;
085        private boolean overlay = true;
086        private boolean axesCalibrated = false;
087    
088        JScrollPane scrollPane = new JScrollPane(new JLabel("Load Image"));
089    
090        public PlotRecognizer(String aTitle) {
091            super(aTitle);
092        }
093    
094        public void init() {
095            JMenuBar menuBar = createMenu();
096            setJMenuBar(menuBar);
097            scrollPane.setPreferredSize(new Dimension(400, 100));
098            getContentPane().add(scrollPane);
099            setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
100    
101            // TODO: create a mousemotion listener and show coordinates in status bar or title bar ...
102            setSize(new Dimension(WIDTH, HEIGHT));
103            setDropTarget(new DropTarget(this, this));   // set this JFrame as a drag and drop drop target
104            pack();
105            setVisible(true);
106        }
107    
108        public JMenuBar createMenu() {
109            //Create the menu bar.
110            JMenuBar menuBar = new JMenuBar();
111    
112            JMenu fileMenu = new JMenu(FILE);
113            //fileMenu.setMnemonic('f');
114            fileMenu.getAccessibleContext().setAccessibleDescription("The File menu");
115            fileMenu.add(createSimpleMenuItem(FILE, FILE_LOAD, "Load an image containing a plot"));
116            saveMenuItem = createSimpleMenuItem(FILE, FILE_SAVE, "Save the recognized plot data");
117            saveMenuItem.setEnabled(isDataAvailable());
118            fileMenu.add(saveMenuItem);
119            fileMenu.add(createSimpleMenuItem(FILE, FILE_QUIT, "Quit the application without saving data"));
120            menuBar.add(fileMenu);
121    
122            editMenu = new JMenu(EDIT);
123            editMenu.getAccessibleContext().setAccessibleDescription("The Edit menu");
124            editMenu.add(createSimpleMenuItem(EDIT, EDIT_CLEAN, "Remove blemishes such as isolated pixels and dust shadows"));
125            editMenu.add(createSimpleMenuItem(EDIT, EDIT_REMOVE_GRID, "Remove grids"));
126            editMenu.add(createSimpleMenuItem(EDIT, EDIT_CALIBRATE, "Remove blemishes such as isolated pixels and dust shadows"));
127            editMenu.add(createSimpleMenuItem(EDIT, EDIT_WINDOW, "Restrict the data to a window"));
128            editMenu.add(createSimpleMenuItem(EDIT, EDIT_RECOGNIZE, "Recognize the plot and produce plot data"));
129            editMenu.setEnabled(isImageLoaded());
130            menuBar.add(editMenu);
131    
132            viewMenu = new JMenu(VIEW);
133            viewMenu.getAccessibleContext().setAccessibleDescription("The View menu");
134            viewMenu.add(createSimpleMenuItem(VIEW, VIEW_ZOOM_IN, "Zoom in image"));
135            viewMenu.add(createSimpleMenuItem(VIEW, VIEW_ZOOM_OUT, "Zoom out image"));
136            viewMenu.add(createSimpleMenuItem(VIEW, VIEW_ZOOM_NORMAL, "Zoom normal"));
137            viewMenu.setEnabled(isImageLoaded());
138            menuBar.add(viewMenu);
139    
140            dataMenu = new JMenu(DATA);
141            dataMenu.getAccessibleContext().setAccessibleDescription("The Data menu");
142            dataMenu.add(createSimpleMenuItem(DATA, DATA_MODIFY, "View and/or modify the data in a table"));
143            dataMenu.add(createSimpleMenuItem(DATA, DATA_SMOOTH, "Smooth the data to eliminate high frequencies"));
144            dataMenu.add(createSimpleMenuItem(DATA, DATA_FLATEN, "Flatent the data to eliminate low frequencies"));
145            dataMenu.add(createCheckBoxMenuItem(DATA_OVERLAY, DATA_OVERLAY, "Show overlay", isOverlay()));
146            dataMenu.setEnabled(isDataAvailable());
147            menuBar.add(dataMenu);
148    
149            return menuBar;
150        }
151    
152        private JMenuItem createSimpleMenuItem(String aMenu, String aLabel, String anAccessibleDescription) {
153            JMenuItem simpleMenuItem = new JMenuItem(aLabel);
154            simpleMenuItem.getAccessibleContext().setAccessibleDescription(anAccessibleDescription);
155            simpleMenuItem.setName(aMenu);
156            simpleMenuItem.setActionCommand(aLabel);
157            simpleMenuItem.addActionListener(this);
158            return simpleMenuItem;
159        }
160    
161        private JMenuItem createSimpleMenuItem(String aMenu, String aLabel, String anAccessibleDescription, int aShortcut) {
162            JMenuItem simpleMenuItem = createSimpleMenuItem(aMenu, aLabel, anAccessibleDescription);
163            simpleMenuItem.setAccelerator(KeyStroke.getKeyStroke(aShortcut, ActionEvent.CTRL_MASK));
164            return simpleMenuItem;
165        }
166    
167        private JMenuItem createCheckBoxMenuItem(String aMenu, String aLabel, String anAccessibleDescription, boolean aSelected) {
168            JCheckBoxMenuItem checkBoxMenuItem = new JCheckBoxMenuItem(aLabel);
169            checkBoxMenuItem.getAccessibleContext().setAccessibleDescription(anAccessibleDescription);
170            checkBoxMenuItem.setName(aMenu);
171            checkBoxMenuItem.setActionCommand(aLabel);
172            checkBoxMenuItem.setSelected(aSelected);  // must be set before adding item listener
173            checkBoxMenuItem.addItemListener(this);
174            return checkBoxMenuItem;
175        }
176    
177        // BUG: this never gets called, why ?
178        public void itemStateChanged(ItemEvent e) {
179            JMenuItem source = (JMenuItem) (e.getSource());     // this property is the action 'grouper'
180            String name = source.getName();                    // this property is the specific selection
181            boolean repaint = false;
182            if (e.getStateChange() == ItemEvent.SELECTED) {
183                if (name.equals(DATA_OVERLAY)) {
184                    setOverlay(true);
185                    repaint = true;
186                }
187            } else if (e.getStateChange() == ItemEvent.DESELECTED) {
188                if (name.equals(DATA_OVERLAY)) {
189                    setOverlay(false);
190                    repaint = true;
191                }
192            }
193            if (repaint) reDraw();
194        }
195    
196        public void reDraw() {
197          // TODO:
198        };
199    
200        // implementation for ActionListener (menus and popup menus)
201        public void actionPerformed(ActionEvent e) {
202            JMenuItem source = (JMenuItem) (e.getSource());
203            String name = source.getName();
204            String action = source.getActionCommand();
205            boolean repaint = false;
206            if (name.equals(FILE)) {
207                if (action.equals(FILE_LOAD)) {
208                    loadImage();
209                } else if (action.equals(FILE_SAVE)) {
210                    saveData();
211                } else if (action.equals(FILE_QUIT)) {
212                    System.exit(0);
213                }
214            } else if (name.equals(EDIT)) {
215                if (action.equals(EDIT_CALIBRATE)) {
216                    calibrate();
217                } else if (action.equals(EDIT_RECOGNIZE)) {
218                    recognize();
219                }
220            }
221            // outline the graph frame (if no outline is specified pixel coordinates are used as a metric and bounds are edges of image)
222            // manual : click on at least three corners (parallelogram like-behavior)
223            // automatic (a statitical estimate is made of the location and orientation of the axis lines)
224            if (repaint) reDraw();
225        }
226    
227        public void loadImage() {
228            setDataAvailable(false);   // new image must be recognized before data becomes available
229            if (loadFileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {     // BUG: 'up' icon in file chooser is disabled
230                // if File is a directory then process all files in that directory
231                //UIUtilities.setWaitCursor(this, true);
232                loadFileChooser.getSelectedFile();
233                loadImage(loadFileChooser.getSelectedFile());
234             }
235        }
236    
237        private XRule xRule;
238        private YRule yRule;
239    
240        private JToggleButton isDataUnits;
241    
242        public void loadImage(File aFile) {
243            setDataAvailable(false);
244            if (scrollPane == null) return;
245            try {
246                setImageFileName(aFile.getName());
247                imageIcon = new ImageIcon(aFile.toURL());
248    
249                setUpScrollPane(imageIcon);
250    
251                //Image image = Toolkit.getDefaultToolkit().getImage(getFileName());
252                //Image img = UIUtilities.getImage(getFileName());
253    
254                setImageLoaded(true);
255            } catch(Exception ex) {
256                System.out.println("Could not open file : " + ex);
257            }
258        }
259    
260        void setUpScrollPane(ImageIcon anImageIcon) {
261    
262            // default calibration
263            Rectangle plotBorder = new Rectangle(0, 0, anImageIcon.getIconWidth(), anImageIcon.getIconHeight());
264            //Rectangle ticZone = plotBorder;
265            calibration = new Calibration(plotBorder);
266    
267            // create the rulers
268            xRule = new XRule(true, calibration, null);
269            xRule.setPreferredWidth(anImageIcon.getIconWidth());
270            yRule = new YRule(true, calibration);
271            yRule.setPreferredHeight(anImageIcon.getIconHeight());
272    
273            // Create the corners
274            JPanel buttonCorner = new JPanel();
275            isDataUnits = new JToggleButton("Data", true);
276            isDataUnits.setFont(new Font("SansSerif", Font.PLAIN, 11));
277            isDataUnits.setMargin(new Insets(2,2,2,2));
278            isDataUnits.addItemListener(new UnitsListener());
279            buttonCorner.add(isDataUnits); //Use the default FlowLayout
280    
281            // Create the main view
282            JLabel label = new JLabel(anImageIcon);
283            label.setVerticalAlignment(JLabel.TOP);
284            label.setHorizontalAlignment(JLabel.LEFT);
285    
286            scrollPane.setViewportView(label);
287            scrollPane.setPreferredSize(new Dimension(300, 250));
288            scrollPane.setViewportBorder(BorderFactory.createLineBorder(Color.black));
289            scrollPane.setColumnHeaderView(xRule);
290            scrollPane.setRowHeaderView(yRule);
291            scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, buttonCorner);
292            scrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER, new Corner());
293            scrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, new Corner());
294        }
295    
296        class UnitsListener implements ItemListener {
297            public void itemStateChanged(ItemEvent e) {
298                if (e.getStateChange() == ItemEvent.SELECTED) {
299                    // Turn it to metric.
300                    yRule.setDataUnits(true);
301                    xRule.setDataUnits(true);
302                } else {
303                    // Turn it to inches.
304                    yRule.setDataUnits(false);
305                    xRule.setDataUnits(false);
306                }
307                //picture.setMaxUnitIncrement(yRule.getIncrement());
308            }
309        }
310    
311        /** Bring up a file requestor and save the current data into that file.
312         */
313        public void saveData() {
314            // TODO: use the file name for the current image, strip its terminator and append .txt
315            // BUG: 'up' icon in file chooser is disabled
316            if (saveFileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION)
317                saveData(saveFileChooser.getSelectedFile());
318        }
319    
320        /** Save the current data as file, the file will be saved in the current export format
321         * @param aFile a file in which to save the data
322         */
323        public void saveData(File aFile) {
324            String format = getDataOutputFormat();
325            DecimalFormat xFormatter = new DecimalFormat(getXTextOutputFormat());
326            DecimalFormat yFormatter = new DecimalFormat(getYTextOutputFormat());
327            //String output = xFormatter.format(value);
328    
329            if (format != null && format.equals(DATA_OUTPUT_FORMAT_TEXT)) {
330                for (int i = 0; i < getSamples(); i++) {
331                    DataCoordinate sample = data[i];
332                    System.out.println(xFormatter.format(sample.getX()) + " " +
333                                       yFormatter.format(sample.getY()) );
334                }
335            }
336        }
337    
338        /** Process a list of image files. The list could be a product of a
339         * drag and drop operation involving multiple files or it could be constructed from a special file
340         * containing a list of filenames. The calibration and recognition steps are performed automatically.
341         * The calibration must be the same for all plots, they must have the same x-range.
342         * @param aListOfFiles a collection of File objects to be processed.
343         */
344        public void processFiles(java.util.List aListOfFiles) {
345            int files = aListOfFiles.size();
346            ProgressMonitor progressMonitor = new ProgressMonitor(this, "Recognizing a list of images", "", 0, files);
347            progressMonitor.setMillisToDecideToPopup(500);
348            for(int i = 0; i < files; i++ ) {
349                File file = (File) aListOfFiles.get(i);
350                if (file.isFile()) {
351                    System.out.println("Processing file : " + file.getAbsolutePath());
352                    loadImage(file);
353                    recognize();
354                  //saveData(new File(file.getName() + ".dat"));
355                    progressMonitor.setProgress(i);
356                    progressMonitor.setNote("Plot : " + getImageFileName());
357                } else if (file.isDirectory()){
358                    System.out.println("Processing directory : " + file.getAbsolutePath());
359                    //TODO: process all files in the dictory called 'file'
360                    File[] fileList = file.listFiles();
361                    processFiles(Arrays.asList(fileList));  // recursive call
362                }
363            }
364            progressMonitor.close();
365        }
366    
367        /** Calibrate the image by asking for the user to click on the axes and to define a numerical value for the coordinates of those points.
368         */
369        public void calibrate() {
370            // setSamples(imageIcon.getWidth());
371        }
372    
373        /** Recognize the image by iterating over the pixexl columns of the calibrated image window and by searching for
374         *  the plot 'trace' or 'pen' which represents the function y=f(x).
375         */
376        public void recognize() {
377            Image image = imageIcon.getImage();
378            int width = image.getWidth(this);
379            int height = image.getHeight(this);
380            setSamples(width);
381            data = new DataCoordinate[getSamples()];
382            imagePoints = new ImageCoordinate[getSamples()];
383            BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY); // TYPE_INT_RGB
384            //bufferedImage = createImage(width, height);
385            Graphics2D g2 = bufferedImage.createGraphics();
386            g2.drawImage(image, 0, 0, this);
387    
388            WritableRaster raster = bufferedImage.getRaster();
389            SampleModel sampleModel = raster.getSampleModel();
390            DataBufferByte dataBuffer = (DataBufferByte) raster.getDataBuffer();
391            byte[] byteData = dataBuffer.getData();
392    
393    
394            int edge = 40; // borders are about 40 pixels from the edge for LBQS quasars
395            // this area defines a rectangle which should be obtained during the calibration
396            int xEdgeLow  = edge;
397            int xEdgeHigh = width - edge;
398            int yEdgeLow  = edge;
399            int yEdgeHigh = height - edge;
400    
401            // data for HD124448;
402            //edge = 100;
403            //int xEdgeLow  = 0;
404            //int xEdgeHigh = width;
405            //int yEdgeLow  = edge;
406            //int yEdgeHigh = height;
407    
408            double x, y;
409            int penHeight;
410            double dataX, dataY;
411            for (int i = 0; i < getSamples(); i++) {
412                byte aByte;
413                penHeight = 0;
414                if (i > xEdgeLow && i < xEdgeHigh) {
415                    int sumj = 0;         // the sum of the height of the 'black' pixels
416                    int blackPixels = 0;  // the number of 'black' pixels
417                    int offset = yEdgeLow * width + i;
418                    for (int j = yEdgeLow; j < yEdgeHigh; j++) {
419                        aByte = byteData[offset];     // -1 is white, 0 is black,
420                        offset += width;
421                        if (aByte >= 0 && aByte < getBlackThreshold()) {
422                            sumj += j;
423                            blackPixels++;
424                        }
425                    }
426                    if (blackPixels > 0)
427                        penHeight = sumj / blackPixels;   // average of the height of the 'black' pixels
428                }
429                //dataX = 3200.0d + (7000.0d - 3200.0d) * (double) i / (double) width;
430                dataX = 3200.0d + (5160.0d - 3200.0d) * (double) i / (double) width;
431                if (penHeight == 0)
432                    dataY = 0.0d;
433                else
434                    dataY = (double) (height - penHeight)/ (double) height;
435    
436                data[i] = new DataCoordinate(dataX, dataY);
437                imagePoints[i] = new ImageCoordinate(i, penHeight);
438            }
439            setDataAvailable(true);
440            xRule.setImagePoints(imagePoints);  // let the x rule display a gray representation of plot data
441        }
442    
443        public void overlay() {
444    
445            // TODO: how do we draw into the viewport, use createImage(w,h) ?
446            //Graphics g = imageIcon.getImage().getGraphics();
447    
448            if (imageIcon != null) {
449                ImageIcon newImageIcon;
450                if (isDataAvailable() && isOverlay()) {
451                    Image image = imageIcon.getImage();
452                    int width = imageIcon.getIconWidth();
453                    int height = imageIcon.getIconHeight();
454    
455                    Image bufferedImage = createImage(width, height);
456                    Graphics2D g2 = (Graphics2D) bufferedImage.getGraphics();
457                    g2.drawImage(image, 0, 0, this);
458                    g2.setColor(Color.red);   // overlay color
459    
460                    ImageCoordinate point;
461                    int x, y, lastx = 0, lasty = height-1;
462                    for (int i = 0; i < getSamples(); i++) {
463                        point = imagePoints[i];
464                        if (point != null) {
465                            x = (int) point.getX();
466                            y = (int) point.getY();
467                            if (y == 0) y = height-1;
468                            g2.drawLine(lastx, lasty, x, y);
469                            lastx = x;
470                            lasty = y;
471                        }
472                    }
473                    newImageIcon = new ImageIcon(bufferedImage);
474                } else {
475                    newImageIcon = imageIcon;  // clear overlay
476                }
477                JLabel label = new JLabel(newImageIcon);
478                label.setVerticalAlignment(JLabel.TOP);
479                label.setHorizontalAlignment(JLabel.LEFT);
480                scrollPane.setViewportView(label);
481            }
482        }
483    
484        public static void main(String argv[]) {
485            final PlotRecognizer recognizer = new PlotRecognizer("Plot Recognizer v1.0");
486            recognizer.init();
487        }
488    
489        //-----Accessor methods---------------------------------------------------------------------------------
490    
491        /** Set the data output format such as text or xml.
492         * @param aDataOutputFormat the data output format
493         */
494        public void setDataOutputFormat(String aDataOutputFormat) {
495            dataOutputFormat = aDataOutputFormat;
496        }
497    
498        /** Get the data output format such as text or xml.
499         * @return the data output format
500         */
501        public String getDataOutputFormat() {
502            return dataOutputFormat;
503        }
504    
505        /** Get the text output format string for the x data values, which determines the precision or number of significant digits when exporting the x value as text
506         * @return a decimal format string for the x values
507         */
508        public String getXTextOutputFormat() {
509            return xTextOutputFormat;
510        }
511    
512        /** Set the text output format string for the x data values.
513         * @param aXTextOutputFormat a string representing the decimal text output format for the x values
514         */
515        public void setXTextOutputFormat(String aXTextOutputFormat) {
516            xTextOutputFormat = aXTextOutputFormat;
517        }
518    
519        /** Get the text output format string for the y data values, which determines the precision or number of significant digits when exporting the y value as text
520         * @return a decimal format string for the y values
521         */
522        public String getYTextOutputFormat() {
523            return yTextOutputFormat;
524        }
525    
526        /** Set the text output format string for the y data values.
527         * @param aYTextOutputFormat a string representing the decimal text output format for the y values
528         */
529        public void setYTextOutputFormat(String aYTextOutputFormat) {
530            yTextOutputFormat = aYTextOutputFormat;
531        }
532    
533        /** Get the gray color intensity below which a pixel is considered 'black'
534         * @return the threshold intensity for black, an integer between 0 and 255
535         */
536        public int getBlackThreshold() {
537            return blackThreshold;
538        }
539    
540        /** Set the gray color intensity below which a pixel is considered 'black'
541         * @param aBlackThreshold the threshold intensity for black, an integer between 0 and 255
542         */
543        public void setBlackThreshold(int aBlackThreshold) {
544            blackThreshold = aBlackThreshold;
545        }
546    
547        /** Get the number of data samples in the plot
548         * @return the number of data samples in the plot
549         */
550        public int getSamples() {
551            return samples;
552        }
553    
554        /** Set the number of data samples in the plot
555         * @param aSamples the number of data samples in the plot
556         */
557        public void setSamples(int aSamples) {
558            samples = aSamples;
559        }
560    
561        /** Get the data file type.
562         * @return a string representing the data file type.
563         */
564        public String getDataFileType() {
565            return dataFileType;
566        }
567    
568        /** Set the file name for the plot data.
569         * @param aDataFileType a string representing the data file name
570         */
571        public void setDataFileType(String aDataFileType) {
572            dataFileType = aDataFileType;
573        }
574    
575        /** Get the file name for the plot data.
576         * @return a string representing the data file name.
577         */
578        public String getDataFileName() {
579            return dataFileName;
580        }
581    
582        /** Set the file name for the plot data.
583         * @param aDataFileName a string representing the data file name
584         */
585        public void setDataFileName(String aDataFileName) {
586            dataFileName = aDataFileName;
587        }
588    
589        /** Get the file name for the image.
590         * @return a string representing the image file name.
591         */
592        public String getImageFileName() {
593            return imageFileName;
594        }
595    
596        public void setImageFileName(String anImageFileName) {
597            imageFileName = anImageFileName;
598        }
599    
600        /** Get the status of the axes calibration procedure.
601         * @return true if the axes have been calibrated according to the axis labels
602         */
603        public boolean isAxesCalibrated() {
604            return axesCalibrated;
605        }
606    
607        /** Set the status of the axis calibration procedure.
608         * @param aAxesCalibrated boolean flag indicating if axes have been calibrated
609         */
610        public void setAxesCalibrated(boolean aAxesCalibrated) {
611            axesCalibrated = aAxesCalibrated;
612        }
613    
614        /** Get the status of the available processed data.
615         * @return true if image has been processed and data is available for saving etc...
616         */
617        public boolean isDataAvailable() {
618            return dataAvailable;
619        }
620    
621        /** Set the status of the available processed data.
622         * @param aDataAvailable a boolean flag indicating if the data is available
623         */
624        public void setDataAvailable(boolean aDataAvailable) {
625            dataAvailable = aDataAvailable;
626            // these menus are disabled until some data is available
627            dataMenu.setEnabled(isDataAvailable());
628            saveMenuItem.setEnabled(isDataAvailable());
629            if (!isDataAvailable()) {
630                imagePoints = null;     // clear data arrays
631                data = null;
632            }
633            overlay();
634        }
635    
636        /** Get the status of the image loading process.
637         * @return true if image is loaded and ready for processing
638         */
639        public boolean isImageLoaded() {
640            return imageLoaded;
641        }
642    
643        /** Set the status of the image loading process.
644         */
645        public void setImageLoaded(boolean anImageLoaded) {
646            imageLoaded = anImageLoaded;
647            // these menus are disabled until an image is loaded
648            editMenu.setEnabled(isImageLoaded());
649            viewMenu.setEnabled(isImageLoaded());
650        }
651    
652        /** Get the overlay option.
653         * @return true if overlay is to be plotted when available
654         */
655        public boolean isOverlay() {
656            return overlay;
657        }
658    
659        /** Set the overlay option.
660         * @param anOverlay a boolean flag representing the choice to plot data over the image
661         */
662        public void setOverlay(boolean anOverlay) {
663            overlay = anOverlay;
664            overlay();
665        }
666    
667        //-------------------- drag and drop --------------------------------------------------
668    
669        // TODO: if a series of files are dropped then they are processed automatically
670        // each is recognized and its data is saved with the same filename but with .dat extension
671    
672        public void dragEnter(DropTargetDragEvent e) {
673            e.acceptDrag(DnDConstants.ACTION_COPY);
674        }
675    
676        public void dragOver(DropTargetDragEvent e) {
677            e.acceptDrag(DnDConstants.ACTION_COPY);
678        }
679    
680        public void dragExit(DropTargetEvent e) {
681        }
682    
683        public void drop(DropTargetDropEvent e) {
684            DropTargetContext targetContext = e.getDropTargetContext();
685    
686            boolean outcome = false;
687    
688            if ((e.getSourceActions() & DnDConstants.ACTION_COPY) != 0)
689                e.acceptDrop(DnDConstants.ACTION_COPY);
690            else {
691                e.rejectDrop();
692                return;
693            }
694    
695            DataFlavor[] dataFlavors = e.getCurrentDataFlavors();
696            DataFlavor transferDataFlavor = null;
697    
698            for (int i = 0; i < dataFlavors.length; i++) {
699                if (dataFlavors[i].isFlavorJavaFileListType()) {
700                    transferDataFlavor = dataFlavors[i];
701                    break;
702                }
703            }
704    
705            // If a DataFlavor is specified with a MIME "Content-Type" of application/x-java-file-list;class=java.util.List
706            // the Drag and Drop system will expect the list elements to be a homogeneous list of objects of type java.io.File.
707    
708            if (transferDataFlavor != null) {
709                Transferable t  = e.getTransferable();
710                try {
711                    java.util.List fileList = (java.util.List) t.getTransferData(DataFlavor.javaFileListFlavor);
712                    processFiles(fileList);  // progress monitor will not appear within this event handler method
713                    outcome = true;
714                } catch(java.io.IOException ioe) {
715                    System.out.println(ioe);
716                } catch(java.awt.datatransfer.UnsupportedFlavorException ex) {
717                    System.out.println(ex);
718                }
719            }
720            targetContext.dropComplete(outcome);
721        }
722    
723        public void dragScroll(DropTargetDragEvent e) {
724        }
725    
726        public void dropActionChanged(DropTargetDragEvent e) {
727            // System.err.println("[Target] dropActionChanged");
728        }
729    }
730