001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the files COPYING and Copyright.html. *
009 * COPYING can be found at the root of the source code distribution tree.    *
010 * Or, see https://support.hdfgroup.org/products/licenses.html               *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view;
016
017import java.io.BufferedInputStream;
018import java.io.BufferedOutputStream;
019import java.io.File;
020import java.io.FileOutputStream;
021import java.net.URL;
022import java.util.ArrayList;
023import java.util.Arrays;
024import java.util.Enumeration;
025import java.util.Iterator;
026import java.util.List;
027
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import org.eclipse.jface.preference.PreferenceManager;
032import org.eclipse.swt.SWT;
033import org.eclipse.swt.custom.SashForm;
034import org.eclipse.swt.custom.ScrolledComposite;
035import org.eclipse.swt.dnd.DND;
036import org.eclipse.swt.dnd.DropTarget;
037import org.eclipse.swt.dnd.DropTargetEvent;
038import org.eclipse.swt.dnd.DropTargetListener;
039import org.eclipse.swt.dnd.FileTransfer;
040import org.eclipse.swt.dnd.Transfer;
041import org.eclipse.swt.events.DisposeEvent;
042import org.eclipse.swt.events.DisposeListener;
043import org.eclipse.swt.events.KeyAdapter;
044import org.eclipse.swt.events.KeyEvent;
045import org.eclipse.swt.events.SelectionAdapter;
046import org.eclipse.swt.events.SelectionEvent;
047import org.eclipse.swt.graphics.Font;
048import org.eclipse.swt.graphics.Image;
049import org.eclipse.swt.graphics.Point;
050import org.eclipse.swt.graphics.Rectangle;
051import org.eclipse.swt.layout.FillLayout;
052import org.eclipse.swt.layout.GridData;
053import org.eclipse.swt.layout.GridLayout;
054import org.eclipse.swt.layout.RowLayout;
055import org.eclipse.swt.widgets.Button;
056import org.eclipse.swt.widgets.Combo;
057import org.eclipse.swt.widgets.Composite;
058import org.eclipse.swt.widgets.Control;
059import org.eclipse.swt.widgets.Dialog;
060import org.eclipse.swt.widgets.Display;
061import org.eclipse.swt.widgets.Event;
062import org.eclipse.swt.widgets.FileDialog;
063import org.eclipse.swt.widgets.Label;
064import org.eclipse.swt.widgets.Listener;
065import org.eclipse.swt.widgets.Menu;
066import org.eclipse.swt.widgets.MenuItem;
067import org.eclipse.swt.widgets.Monitor;
068import org.eclipse.swt.widgets.Shell;
069import org.eclipse.swt.widgets.Text;
070import org.eclipse.swt.widgets.ToolBar;
071import org.eclipse.swt.widgets.ToolItem;
072
073import hdf.HDFVersions;
074import hdf.object.DataFormat;
075import hdf.object.FileFormat;
076import hdf.object.HObject;
077import hdf.view.ViewProperties.DataViewType;
078import hdf.view.DataView.DataView;
079import hdf.view.DataView.DataViewFactory;
080import hdf.view.DataView.DataViewFactoryProducer;
081import hdf.view.DataView.DataViewManager;
082import hdf.view.HelpView.HelpView;
083import hdf.view.MetaDataView.MetaDataView;
084import hdf.view.TableView.TableView;
085import hdf.view.TreeView.DefaultTreeView;
086import hdf.view.TreeView.TreeView;
087import hdf.view.dialog.ImageConversionDialog;
088import hdf.view.dialog.InputDialog;
089import hdf.view.dialog.UserOptionsDialog;
090import hdf.view.dialog.UserOptionsGeneralPage;
091import hdf.view.dialog.UserOptionsHDFPage;
092import hdf.view.dialog.UserOptionsNode;
093import hdf.view.dialog.UserOptionsViewModulesPage;
094
095
096/**
097 * HDFView is the main class of this HDF visual tool. It is used to layout the
098 * graphical components of the hdfview. The major GUI components of the HDFView
099 * include Menubar, Toolbar, TreeView, ContentView, and MessageArea.
100 *
101 * The HDFView is designed in such a way that it does not have direct access to
102 * the HDF library. All the HDF library access is done through HDF objects.
103 * Therefore, the HDFView package depends on the object package but not the
104 * library package. The source code of the view package (hdf.view) should
105 * be compiled with the library package (hdf.hdflib and hdf.hdf5lib).
106 *
107 * @author Jordan T. Henderson
108 * @version 2.4 //2015
109 */
110public class HDFView implements DataViewManager
111{
112    private static final Logger log = LoggerFactory.getLogger(HDFView.class);
113
114    private static Display             display;
115    private static Shell               mainWindow;
116
117    /* Determines whether HDFView is being executed for GUI testing */
118    private boolean                    isTesting = false;
119
120    /* The directory where HDFView is installed */
121    private String                     rootDir;
122
123    /* The initial directory where HDFView looks for files */
124    private String                     startDir;
125
126    /* The current working directory */
127    private String                     currentDir;
128
129    /* The current working file */
130    private String                     currentFile = null;
131
132    /* The view properties */
133    private ViewProperties             props;
134
135    /* A list of tree view implementations. */
136    private static List<String>        treeViews;
137
138    /* A list of image view implementations. */
139    private static List<String>        imageViews;
140
141    /* A list of tree table implementations. */
142    private static List<?>             tableViews;
143
144    /* A list of metadata view implementations. */
145    private static List<?>             metaDataViews;
146
147    /* A list of palette view implementations. */
148    private static List<?>             paletteViews;
149
150    /* A list of help view implementations. */
151    private static List<?>             helpViews;
152
153    /* The list of GUI components related to NetCDF3 */
154    private final List<MenuItem>       n3GUIs = new ArrayList<>();
155
156    /* The list of GUI components related to HDF4 */
157    private final List<MenuItem>       h4GUIs = new ArrayList<>();
158
159    /* The list of GUI components related to HDF5 */
160    private final List<MenuItem>       h5GUIs = new ArrayList<>();
161
162    /* The list of GUI components related to editing */
163    //private final List<?>            editGUIs;
164
165    /* GUI component: the TreeView */
166    private TreeView                   treeView = null;
167
168    private static final String        JAVA_VERSION = HDFVersions.getPropertyVersionJava();
169    private static final String        HDF4_VERSION = HDFVersions.getPropertyVersionHDF4();
170    private static final String        HDF5_VERSION = HDFVersions.getPropertyVersionHDF5();
171    private static final String        HDFVIEW_VERSION = HDFVersions.getPropertyVersionView();
172    private static final String        HDFVIEW_USERSGUIDE_URL = "https://portal.hdfgroup.org/display/HDFVIEW/HDFView+3.x+User%27s+Guide";
173    private static final String        JAVA_COMPILER = "jdk " + JAVA_VERSION;
174    private static final String        JAVA_VER_INFO = "Compiled at " + JAVA_COMPILER + "\nRunning at " + System.getProperty("java.version");
175
176    private static final String        ABOUT_HDFVIEW = "HDF Viewer, " + "Version " + ViewProperties.VERSION + "\n"
177            + "For " + System.getProperty("os.name") + "\n\n"
178            + "Copyright " + '\u00a9' + " 2006 The HDF Group.\n"
179            + "All rights reserved.";
180
181    /* GUI component: The toolbar for open, close, help and hdf4 and hdf5 library information */
182    private ToolBar                    toolBar;
183
184    /* GUI component: The text area for showing status messages */
185    private Text                       status;
186
187    /* GUI component: The area for quick general view */
188    private ScrolledComposite          generalArea;
189
190    /* GUI component: To add and display URLs */
191    private Combo                      urlBar;
192
193    private Button                     recentFilesButton;
194    private Button                     clearTextButton;
195
196    /* GUI component: A list of current data windows */
197    private Menu                       windowMenu;
198
199    /* GUI component: File menu on the menubar */
200    //private final Menu               fileMenu;
201
202    /* The font to be used for display text on all Controls */
203    private Font                       currentFont;
204
205    private UserOptionsDialog          userOptionDialog;
206
207    /** State of refresh. */
208    public boolean viewerState = false;
209
210    /** Timer for refresh functions. */
211    private final Runnable timer = new Runnable() {
212        public void run() {
213            // refresh each table displaying data
214            Shell[] shellList = display.getShells();
215            if (shellList != null) {
216                for (int i = 0; i < shellList.length; i++) {
217                    if (shellList[i].equals(mainWindow))
218                        showMetaData(treeView.getCurrentObject());
219                    else {
220                        DataView view = (DataView) shellList[i].getData();
221                        if ((view != null) && (view instanceof TableView)) {
222                            HObject obj = view.getDataObject();
223                            if (obj == null || obj.getFileFormat() == null || !(obj instanceof DataFormat))
224                                continue;
225
226                            FileFormat file = obj.getFileFormat();
227                            if (file.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)))
228                                ((TableView)view).refreshDataTable();
229                        }
230                    }
231                }
232            }
233            log.trace("viewerState = {}", viewerState);
234            if (viewerState)
235                display.timerExec(ViewProperties.getTimerRefresh(), timer);
236            else
237                display.timerExec(-1, timer);
238        }
239    };
240
241    /**
242     * Constructs HDFView with a given root directory, where the HDFView is
243     * installed, and opens the given files in the viewer.
244     *
245     * @param root
246     *            the directory where the HDFView is installed.
247     * @param start_dir
248     *            the starting directory for file searches
249     */
250    public HDFView(String root, String start_dir) {
251        log.debug("Root is {}", root);
252
253        if (display == null || display.isDisposed())
254            display = new Display();
255
256        rootDir = root;
257        startDir = start_dir;
258
259        //editGUIs = new Vector<Object>();
260
261        props = new ViewProperties(rootDir, startDir);
262        try {
263            props.load();
264        }
265        catch (Exception ex) {
266            log.debug("Failed to load View Properties from {}", rootDir);
267        }
268
269        ViewProperties.loadIcons();
270
271        String workDir = System.getProperty("hdfview.workdir");
272        if (workDir != null)
273            currentDir = workDir;
274        else
275            currentDir = ViewProperties.getWorkDir();
276
277        if (currentDir == null)
278            currentDir = System.getProperty("user.dir");
279
280        log.info("Current directory is {}", currentDir);
281
282        try {
283            currentFont = new Font(
284                    display,
285                    ViewProperties.getFontType(),
286                    ViewProperties.getFontSize(),
287                    SWT.NORMAL);
288        }
289        catch (Exception ex) {
290            currentFont = null;
291        }
292
293        treeViews = ViewProperties.getTreeViewList();
294        metaDataViews = ViewProperties.getMetaDataViewList();
295        tableViews = ViewProperties.getTableViewList();
296        imageViews = ViewProperties.getImageViewList();
297        paletteViews = ViewProperties.getPaletteViewList();
298        helpViews = ViewProperties.getHelpViewList();
299
300        log.debug("Constructor exit");
301    }
302
303
304    /**
305     * Creates HDFView with a given size, and opens the given files in the viewer.
306     *
307     * @param flist
308     *            a list of files to open.
309     * @param width
310     *            the width of the app in pixels
311     * @param height
312     *            the height of the app in pixels
313     * @param x
314     *            the coord x of the app in pixels
315     * @param y
316     *            the coord y of the app in pixels
317     *
318     * @return
319     *            the newly-created HDFView Shell
320     */
321    public Shell openMainWindow(List<File> flist, int width, int height, int x, int y) {
322        log.debug("openMainWindow enter current directory is {}", currentDir);
323
324        // Initialize all GUI components
325        mainWindow = createMainWindow();
326
327        try {
328            Font font = null;
329            String fType = ViewProperties.getFontType();
330            int fSize = ViewProperties.getFontSize();
331
332            try {
333                font = new Font(display, fType, fSize, SWT.NORMAL);
334            }
335            catch (Exception ex) {
336                font = null;
337            }
338
339            if (font != null)
340                updateFont(font);
341        }
342        catch (Exception ex) {
343            log.debug("Failed to load Font properties");
344        }
345
346        // Make sure all GUI components are in place before
347        // opening any files
348        mainWindow.pack();
349
350        int nfiles = flist.size();
351        File theFile = null;
352        for (int i = 0; i < nfiles; i++) {
353            theFile = flist.get(i);
354
355            if (theFile.isFile()) {
356                currentDir = theFile.getParentFile().getAbsolutePath();
357                currentFile = theFile.getAbsolutePath();
358
359                try {
360                    int access_mode = FileFormat.WRITE;
361                    if (ViewProperties.isReadOnly())
362                        access_mode = FileFormat.READ;
363                    else if (ViewProperties.isReadSWMR())
364                        access_mode = FileFormat.READ | FileFormat.MULTIREAD;
365                    treeView.openFile(currentFile, access_mode);
366
367                    try {
368                        urlBar.remove(currentFile);
369                    }
370                    catch (Exception ex) {}
371
372                    // first entry is always the workdir
373                    urlBar.add(currentFile, 1);
374                    urlBar.select(1);
375                }
376                catch (Exception ex) {
377                    showError(ex.toString());
378                }
379            }
380            else {
381                currentDir = theFile.getAbsolutePath();
382            }
383
384            log.info("CurrentDir is {}", currentDir);
385        }
386
387        if (FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3) == null)
388            setEnabled(n3GUIs, false);
389
390        if (FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4) == null)
391            setEnabled(h4GUIs, false);
392
393        if (FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5) == null)
394            setEnabled(h5GUIs, false);
395
396        // Set size of main window
397        // float inset = 0.17f; // for UG only.
398        float inset = 0.04f;
399        Point winDim = new Point(width, height);
400
401        // If given height and width are too small, adjust accordingly
402        if (height <= 300)
403            winDim.y = (int) ((1 - 2 * inset) * mainWindow.getSize().y);
404
405        if (width <= 300)
406            winDim.x = (int) (0.9 * mainWindow.getSize().y);
407
408        mainWindow.setLocation(x, y);
409        mainWindow.setSize(winDim.x + 200, winDim.y);
410
411        // Display the window
412        mainWindow.open();
413        log.debug("openMainWindow exit");
414        return mainWindow;
415    }
416
417    /** switch processing to the main application window */
418    public void runMainWindow() {
419        log.debug("runMainWindow enter");
420
421        while(!mainWindow.isDisposed()) {
422            // ===================================================
423            // Wrap each event dispatch in an exception handler
424            // so that if any event causes an exception it does
425            // not break the main UI loop
426            // ===================================================
427            try {
428                if (!display.readAndDispatch())
429                    display.sleep();
430            }
431            catch (Exception e) {
432                e.printStackTrace();
433            }
434        }
435
436        if (!isTesting)
437            display.dispose();
438        log.debug("runMainWindow exit");
439    }
440
441    /**
442     * Creates and lays out GUI components.
443     *
444     * <pre>
445     * ||=========||=============================||
446     * ||         ||                             ||
447     * ||         ||                             ||
448     * || TreeView||       ContentPane           ||
449     * ||         ||                             ||
450     * ||=========||=============================||
451     * ||            Message Area                ||
452     * ||========================================||
453     * </pre>
454     */
455    private Shell createMainWindow() {
456        // Create a new display window
457        final Shell shell = new Shell(display);
458        shell.setImages(ViewProperties.getHdfIcons());
459        shell.setFont(currentFont);
460        shell.setText("HDFView " + HDFVIEW_VERSION);
461        shell.setLayout(new GridLayout(3, false));
462        shell.addDisposeListener(new DisposeListener() {
463            @Override
464            public void widgetDisposed(DisposeEvent e) {
465                ViewProperties.setRecentFiles(new ArrayList<>(Arrays.asList(urlBar.getItems())));
466
467                try {
468                    props.save();
469                }
470                catch (Exception ex) {}
471
472                closeAllWindows();
473
474                // Close all open files
475                try {
476                    List<FileFormat> filelist = treeView.getCurrentFiles();
477
478                    if ((filelist != null) && !filelist.isEmpty()) {
479                        Object[] files = filelist.toArray();
480
481                        for (int i = 0; i < files.length; i++) {
482                            try {
483                                treeView.closeFile((FileFormat) files[i]);
484                            }
485                            catch (Exception ex) {
486                                continue;
487                            }
488                        }
489                    }
490                }
491                catch (Exception ex) {}
492
493                if (currentFont != null)
494                    currentFont.dispose();
495            }
496        });
497
498        createMenuBar(shell);
499        createToolbar(shell);
500        createUrlToolbar(shell);
501        createContentArea(shell);
502
503        log.info("Main Window created");
504
505        return shell;
506    }
507
508    private void createMenuBar(final Shell shell) {
509        Menu menu = new Menu(shell, SWT.BAR);
510        shell.setMenuBar(menu);
511
512        MenuItem menuItem = new MenuItem(menu, SWT.CASCADE);
513        menuItem.setText("&File");
514
515        Menu fileMenu = new Menu(menuItem);
516        menuItem.setMenu(fileMenu);
517
518        MenuItem item = new MenuItem(fileMenu, SWT.PUSH);
519        item.setText("&Open\tCtrl-O");
520        item.setAccelerator(SWT.MOD1 + 'O');
521        item.addSelectionListener(new SelectionAdapter() {
522            @Override
523            public void widgetSelected(SelectionEvent e) {
524                openLocalFile(null, -1);
525            }
526        });
527
528        item = new MenuItem(fileMenu, SWT.CASCADE);
529        item.setText("Open As");
530
531        Menu openAsMenu = new Menu(item);
532        item.setMenu(openAsMenu);
533
534        item = new MenuItem(openAsMenu, SWT.PUSH);
535        item.setText("Read-Only");
536        item.addSelectionListener(new SelectionAdapter() {
537            @Override
538            public void widgetSelected(SelectionEvent e) {
539                openLocalFile(null, FileFormat.READ);
540            }
541        });
542
543        item = new MenuItem(openAsMenu, SWT.PUSH);
544        item.setText("SWMR Read-Only");
545        item.addSelectionListener(new SelectionAdapter() {
546            @Override
547            public void widgetSelected(SelectionEvent e) {
548                openLocalFile(null, FileFormat.READ | FileFormat.MULTIREAD);
549            }
550        });
551
552        item = new MenuItem(openAsMenu, SWT.PUSH);
553        item.setText("Read/Write");
554        item.addSelectionListener(new SelectionAdapter() {
555            @Override
556            public void widgetSelected(SelectionEvent e) {
557                openLocalFile(null, FileFormat.WRITE);
558            }
559        });
560
561        new MenuItem(fileMenu, SWT.SEPARATOR);
562
563        MenuItem fileNewMenu = new MenuItem(fileMenu, SWT.CASCADE);
564        fileNewMenu.setText("New");
565
566        Menu newMenu = new Menu(fileNewMenu);
567        fileNewMenu.setMenu(newMenu);
568
569        item = new MenuItem(newMenu, SWT.PUSH);
570        item.setText("HDF&4");
571        h4GUIs.add(item);
572        item.addSelectionListener(new SelectionAdapter() {
573            @Override
574            public void widgetSelected(SelectionEvent e) {
575                if (currentDir != null)
576                    currentDir += File.separator;
577                else
578                    currentDir = "";
579
580                String filename = null;
581
582                if (!isTesting) {
583                    FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
584                    fChooser.setFileName(Tools.checkNewFile(currentDir, ".hdf").getName());
585
586                    DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF4();
587                    fChooser.setFilterExtensions(new String[] {filter.getExtensions()});
588                    fChooser.setFilterNames(new String[] {filter.getDescription()});
589                    fChooser.setFilterIndex(0);
590
591                    filename = fChooser.open();
592                }
593                else {
594                    // Prepend test file directory to filename
595                    filename = currentDir.concat(new InputDialog(mainWindow, "Enter a file name", "").open());
596                }
597
598                if(filename == null)
599                    return;
600
601                try {
602                    log.trace("HDFView create hdf4 file");
603                    FileFormat theFile = Tools.createNewFile(filename, currentDir,
604                            FileFormat.FILE_TYPE_HDF4, getTreeView().getCurrentFiles());
605
606                    if (theFile == null)
607                        return;
608
609                    currentDir = theFile.getParent();
610                }
611                catch (Exception ex) {
612                    Tools.showError(mainWindow, "New", ex.getMessage());
613                    return;
614                }
615
616                try {
617                    treeView.openFile(filename, FileFormat.WRITE);
618                    currentFile = filename;
619
620                    try {
621                        urlBar.remove(filename);
622                    }
623                    catch (Exception ex) {
624                        log.debug("unable to remove {} from urlBar", filename);
625                    }
626
627                    // first entry is always the workdir
628                    urlBar.add(filename, 1);
629                    urlBar.select(1);
630                }
631                catch (Exception ex) {
632                    display.beep();
633                    Tools.showError(mainWindow, "New", ex.getMessage() + "\n" + filename);
634                }
635            }
636        });
637
638        item = new MenuItem(newMenu, SWT.PUSH);
639        item.setText("HDF&5");
640        h5GUIs.add(item);
641        item.addSelectionListener(new SelectionAdapter() {
642            @Override
643            public void widgetSelected(SelectionEvent e) {
644                if (currentDir != null)
645                    currentDir += File.separator;
646                else
647                    currentDir = "";
648
649                String filename = null;
650
651                if (!isTesting) {
652                    FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
653                    fChooser.setFileName(Tools.checkNewFile(currentDir, ".h5").getName());
654
655                    DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF5();
656                    fChooser.setFilterExtensions(new String[] {filter.getExtensions()});
657                    fChooser.setFilterNames(new String[] {filter.getDescription()});
658                    fChooser.setFilterIndex(0);
659
660                    filename = fChooser.open();
661                }
662                else {
663                    // Prepend test file directory to filename
664                    filename = currentDir.concat(new InputDialog(mainWindow, "Enter a file name", "").open());
665                }
666
667                if(filename == null)
668                    return;
669
670                try {
671                    log.trace("HDFView create hdf5 file");
672                    FileFormat theFile = Tools.createNewFile(filename, currentDir,
673                            FileFormat.FILE_TYPE_HDF5, getTreeView().getCurrentFiles());
674
675                    if (theFile == null)
676                        return;
677
678                    currentDir = theFile.getParent();
679                }
680                catch (Exception ex) {
681                    Tools.showError(mainWindow, "New", ex.getMessage());
682                    return;
683                }
684
685                try {
686                    treeView.openFile(filename, FileFormat.WRITE);
687                    currentFile = filename;
688
689                    try {
690                        urlBar.remove(filename);
691                    }
692                    catch (Exception ex) {
693                        log.debug("unable to remove {} from urlBar", filename);
694                    }
695
696                    // first entry is always the workdir
697                    urlBar.add(filename, 1);
698                    urlBar.select(1);
699                }
700                catch (Exception ex) {
701                    display.beep();
702                    Tools.showError(mainWindow, "New", ex.getMessage() + "\n" + filename);
703                }
704            }
705        });
706
707        new MenuItem(fileMenu, SWT.SEPARATOR);
708
709        item = new MenuItem(fileMenu, SWT.PUSH);
710        item.setText("&Close");
711        item.addSelectionListener(new SelectionAdapter() {
712            @Override
713            public void widgetSelected(SelectionEvent e) {
714                closeFile(treeView.getSelectedFile());
715            }
716        });
717
718        item = new MenuItem(fileMenu, SWT.PUSH);
719        item.setText("Close &All");
720        item.addSelectionListener(new SelectionAdapter() {
721            @Override
722            public void widgetSelected(SelectionEvent e) {
723                closeAllWindows();
724
725                List<FileFormat> files = treeView.getCurrentFiles();
726                while (!files.isEmpty()) {
727                    try {
728                        treeView.closeFile(files.get(0));
729                    }
730                    catch (Exception ex) {
731                        log.trace("unable to close {} in treeView", files.get(0));
732                    }
733                }
734
735                currentFile = null;
736
737                for (Control control : generalArea.getChildren())
738                    control.dispose();
739                generalArea.setContent(null);
740
741                urlBar.setText("");
742            }
743        });
744
745        new MenuItem(fileMenu, SWT.SEPARATOR);
746
747        item = new MenuItem(fileMenu, SWT.PUSH);
748        item.setText("&Save");
749        item.addSelectionListener(new SelectionAdapter() {
750            @Override
751            public void widgetSelected(SelectionEvent e) {
752                if (treeView.getCurrentFiles().isEmpty()) {
753                    Tools.showError(mainWindow, "Save", "No files currently open.");
754                    return;
755                }
756
757                if (treeView.getSelectedFile() == null) {
758                    Tools.showError(mainWindow, "Save", "No files currently selected.");
759                    return;
760                }
761
762                // Save what has been changed in memory into file
763                writeDataToFile(treeView.getSelectedFile());
764            }
765        });
766
767        item = new MenuItem(fileMenu, SWT.PUSH);
768        item.setText("S&ave As");
769        item.addSelectionListener(new SelectionAdapter() {
770            @Override
771            public void widgetSelected(SelectionEvent e) {
772                if (treeView.getCurrentFiles().isEmpty()) {
773                    Tools.showError(mainWindow, "Save", "No files currently open.");
774                    return;
775                }
776
777                if (treeView.getSelectedFile() == null) {
778                    Tools.showError(mainWindow, "Save", "No files currently selected.");
779                    return;
780                }
781
782                try {
783                    treeView.saveFile(treeView.getSelectedFile());
784                }
785                catch (Exception ex) {
786                    display.beep();
787                    Tools.showError(mainWindow, "Save", ex.getMessage());
788                }
789            }
790        });
791
792        new MenuItem(fileMenu, SWT.SEPARATOR);
793
794        item = new MenuItem(fileMenu, SWT.PUSH);
795        item.setText("E&xit\tCtrl-Q");
796        item.setAccelerator(SWT.MOD1 + 'Q');
797        item.addSelectionListener(new SelectionAdapter() {
798            @Override
799            public void widgetSelected(SelectionEvent e) {
800                mainWindow.dispose();
801            }
802        });
803
804        menuItem = new MenuItem(menu, SWT.CASCADE);
805        menuItem.setText("&Window");
806
807        windowMenu = new Menu(menuItem);
808        menuItem.setMenu(windowMenu);
809
810        item = new MenuItem(windowMenu, SWT.PUSH);
811        item.setText("&Cascade");
812        item.addSelectionListener(new SelectionAdapter() {
813            @Override
814            public void widgetSelected(SelectionEvent e) {
815                cascadeWindows();
816            }
817        });
818
819        item = new MenuItem(windowMenu, SWT.PUSH);
820        item.setText("&Tile");
821        item.addSelectionListener(new SelectionAdapter() {
822            @Override
823            public void widgetSelected(SelectionEvent e) {
824                tileWindows();
825            }
826        });
827
828        new MenuItem(windowMenu, SWT.SEPARATOR);
829
830        item = new MenuItem(windowMenu, SWT.PUSH);
831        item.setText("Close &All");
832        item.addSelectionListener(new SelectionAdapter() {
833            @Override
834            public void widgetSelected(SelectionEvent e) {
835                closeAllWindows();
836            }
837        });
838
839        new MenuItem(windowMenu, SWT.SEPARATOR);
840
841        menuItem = new MenuItem(menu, SWT.CASCADE);
842        menuItem.setText("&Tools");
843
844        Menu toolsMenu = new Menu(menuItem);
845        menuItem.setMenu(toolsMenu);
846
847        MenuItem convertMenuItem = new MenuItem(toolsMenu, SWT.CASCADE);
848        convertMenuItem.setText("Convert Image To");
849
850        Menu convertMenu = new Menu(convertMenuItem);
851        convertMenuItem.setMenu(convertMenu);
852
853        item = new MenuItem(convertMenu, SWT.PUSH);
854        item.setText("HDF4");
855        item.addSelectionListener(new SelectionAdapter() {
856            @Override
857            public void widgetSelected(SelectionEvent e) {
858                convertFile(Tools.FILE_TYPE_IMAGE, FileFormat.FILE_TYPE_HDF4);
859            }
860        });
861        h4GUIs.add(item);
862
863        item = new MenuItem(convertMenu, SWT.PUSH);
864        item.setText("HDF5");
865        item.addSelectionListener(new SelectionAdapter() {
866            @Override
867            public void widgetSelected(SelectionEvent e) {
868                convertFile(Tools.FILE_TYPE_IMAGE, FileFormat.FILE_TYPE_HDF5);
869            }
870        });
871        h5GUIs.add(item);
872
873        new MenuItem(toolsMenu, SWT.SEPARATOR);
874
875        item = new MenuItem(toolsMenu, SWT.PUSH);
876        item.setText("User &Options");
877        item.addSelectionListener(new SelectionAdapter() {
878            @Override
879            public void widgetSelected(SelectionEvent e) {
880                // Create the preference manager
881                PreferenceManager mgr = new PreferenceManager();
882
883                // Create the nodes
884                UserOptionsNode one = new UserOptionsNode("general", new UserOptionsGeneralPage());
885                UserOptionsNode two = new UserOptionsNode("hdf", new UserOptionsHDFPage());
886                UserOptionsNode three = new UserOptionsNode("modules", new UserOptionsViewModulesPage());
887
888                // Add the nodes
889                mgr.addToRoot(one);
890                mgr.addToRoot(two);
891                mgr.addToRoot(three);
892
893                // Create the preferences dialog
894                userOptionDialog = new UserOptionsDialog(shell, mgr, rootDir);
895
896                // Set the preference store
897                userOptionDialog.setPreferenceStore(props);
898                userOptionDialog.create();
899
900                // Open the dialog
901                userOptionDialog.open();
902
903                // TODO: this functionality is currently broken because isWorkDirChanged() is not exposed correctly.
904                // if (userOptionDialog.isWorkDirChanged())
905                // this will always overwrite the currentDir until isWorkDirChanged() is fixed
906                currentDir = ViewProperties.getWorkDir();
907
908                //if (userOptionDialog.isFontChanged()) {
909                Font font = null;
910
911                try {
912                    font = new Font(display, ViewProperties.getFontType(), ViewProperties.getFontSize(), SWT.NORMAL);
913                }
914                catch (Exception ex) {
915                    font = null;
916                }
917
918                updateFont(font);
919                //}
920            }
921        });
922
923        new MenuItem(toolsMenu, SWT.SEPARATOR);
924
925        item = new MenuItem(toolsMenu, SWT.PUSH);
926        item.setText("&Register File Format");
927        item.addSelectionListener(new SelectionAdapter() {
928            @Override
929            public void widgetSelected(SelectionEvent e) {
930                registerFileFormat();
931            }
932        });
933
934        item = new MenuItem(toolsMenu, SWT.PUSH);
935        item.setText("&Unregister File Format");
936        item.addSelectionListener(new SelectionAdapter() {
937            @Override
938            public void widgetSelected(SelectionEvent e) {
939                unregisterFileFormat();
940            }
941        });
942
943        menuItem = new MenuItem(menu, SWT.CASCADE);
944        menuItem.setText("&Help");
945
946        Menu helpMenu = new Menu(menuItem);
947        menuItem.setMenu(helpMenu);
948
949        item = new MenuItem(helpMenu, SWT.PUSH);
950        item.setText("&User's Guide");
951        item.addSelectionListener(new SelectionAdapter() {
952            @Override
953            public void widgetSelected(SelectionEvent e) {
954                org.eclipse.swt.program.Program.launch(HDFVIEW_USERSGUIDE_URL);
955            }
956        });
957
958        if ((helpViews != null) && !helpViews.isEmpty()) {
959            int n = helpViews.size();
960            for (int i = 0; i < n; i++) {
961                HelpView theView = (HelpView) helpViews.get(i);
962                item = new MenuItem(helpMenu, SWT.PUSH);
963                item.setText(theView.getLabel());
964                //item.setActionCommand(theView.getActionCommand());
965            }
966        }
967
968        new MenuItem(helpMenu, SWT.SEPARATOR);
969
970        item = new MenuItem(helpMenu, SWT.PUSH);
971        item.setText("HDF&4 Library Version");
972        h4GUIs.add(item);
973        item.addSelectionListener(new SelectionAdapter() {
974            @Override
975            public void widgetSelected(SelectionEvent e) {
976                new LibraryVersionDialog(shell, FileFormat.FILE_TYPE_HDF4).open();
977            }
978        });
979
980        item = new MenuItem(helpMenu, SWT.PUSH);
981        item.setText("HDF&5 Library Version");
982        h5GUIs.add(item);
983        item.addSelectionListener(new SelectionAdapter() {
984            @Override
985            public void widgetSelected(SelectionEvent e) {
986                new LibraryVersionDialog(shell, FileFormat.FILE_TYPE_HDF5).open();
987            }
988        });
989
990        item = new MenuItem(helpMenu, SWT.PUSH);
991        item.setText("&Java Version");
992        item.addSelectionListener(new SelectionAdapter() {
993            @Override
994            public void widgetSelected(SelectionEvent e) {
995                new JavaVersionDialog(mainWindow).open();
996            }
997        });
998
999        new MenuItem(helpMenu, SWT.SEPARATOR);
1000
1001        item = new MenuItem(helpMenu, SWT.PUSH);
1002        item.setText("Supported Fi&le Formats");
1003        item.addSelectionListener(new SelectionAdapter() {
1004            @Override
1005            public void widgetSelected(SelectionEvent e) {
1006                new SupportedFileFormatsDialog(mainWindow).open();
1007            }
1008        });
1009
1010        new MenuItem(helpMenu, SWT.SEPARATOR);
1011
1012        item = new MenuItem(helpMenu, SWT.PUSH);
1013        item.setText("&About...");
1014        item.addSelectionListener(new SelectionAdapter() {
1015            @Override
1016            public void widgetSelected(SelectionEvent e) {
1017                new AboutDialog(mainWindow).open();
1018            }
1019        });
1020
1021        setEnabled(Arrays.asList(windowMenu.getItems()), false);
1022
1023        log.info("Menubar created");
1024    }
1025
1026    private void createToolbar(final Shell shell) {
1027        toolBar = new ToolBar(shell, SWT.HORIZONTAL | SWT.RIGHT);
1028        toolBar.setFont(Display.getCurrent().getSystemFont());
1029        toolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
1030
1031        ToolItem openItem = new ToolItem(toolBar, SWT.PUSH);
1032        openItem.setToolTipText("Open");
1033        openItem.setImage(ViewProperties.getFileopenIcon());
1034        openItem.addSelectionListener(new SelectionAdapter() {
1035            @Override
1036            public void widgetSelected(SelectionEvent e) {
1037                openLocalFile(null, -1);
1038            }
1039        });
1040
1041        new ToolItem(toolBar, SWT.SEPARATOR).setWidth(4);
1042
1043        ToolItem closeItem = new ToolItem(toolBar, SWT.PUSH);
1044        closeItem.setImage(ViewProperties.getFilecloseIcon());
1045        closeItem.setToolTipText("Close");
1046        closeItem.addSelectionListener(new SelectionAdapter() {
1047            @Override
1048            public void widgetSelected(SelectionEvent e) {
1049                closeFile(treeView.getSelectedFile());
1050            }
1051        });
1052
1053        new ToolItem(toolBar, SWT.SEPARATOR).setWidth(20);
1054
1055        ToolItem helpItem = new ToolItem(toolBar, SWT.PUSH);
1056        helpItem.setImage(ViewProperties.getHelpIcon());
1057        helpItem.setToolTipText("Help");
1058        helpItem.addSelectionListener(new SelectionAdapter() {
1059            @Override
1060            public void widgetSelected(SelectionEvent e) {
1061                String ugPath = ViewProperties.getUsersGuide();
1062
1063                if(ugPath == null || !ugPath.startsWith("http://")) {
1064                    String sep = File.separator;
1065                    File tmpFile = new File(ugPath);
1066
1067                    if(!(tmpFile.exists())) {
1068                        ugPath = rootDir + sep + "UsersGuide" + sep + "index.html";
1069                        tmpFile = new File(ugPath);
1070
1071                        if(!(tmpFile.exists()))
1072                            ugPath = HDFVIEW_USERSGUIDE_URL;
1073
1074                        ViewProperties.setUsersGuide(ugPath);
1075                    }
1076                }
1077
1078                try {
1079                    org.eclipse.swt.program.Program.launch(ugPath);
1080                }
1081                catch (Exception ex) {
1082                    Tools.showError(shell, "Help", ex.getMessage());
1083                }
1084            }
1085        });
1086
1087        new ToolItem(toolBar, SWT.SEPARATOR).setWidth(4);
1088
1089        ToolItem hdf4Item = new ToolItem(toolBar, SWT.PUSH);
1090        hdf4Item.setImage(ViewProperties.getH4Icon());
1091        hdf4Item.setToolTipText("HDF4 Library Version");
1092        hdf4Item.addSelectionListener(new SelectionAdapter() {
1093            @Override
1094            public void widgetSelected(SelectionEvent e) {
1095                new LibraryVersionDialog(shell, FileFormat.FILE_TYPE_HDF4).open();
1096            }
1097        });
1098
1099        if(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4) == null)
1100            hdf4Item.setEnabled(false);
1101
1102        new ToolItem(toolBar, SWT.SEPARATOR).setWidth(4);
1103
1104        ToolItem hdf5Item = new ToolItem(toolBar, SWT.PUSH);
1105        hdf5Item.setImage(ViewProperties.getH5Icon());
1106        hdf5Item.setToolTipText("HDF5 Library Version");
1107        hdf5Item.addSelectionListener(new SelectionAdapter() {
1108            @Override
1109            public void widgetSelected(SelectionEvent e) {
1110                new LibraryVersionDialog(shell, FileFormat.FILE_TYPE_HDF5).open();
1111            }
1112        });
1113
1114        if(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5) == null)
1115            hdf5Item.setEnabled(false);
1116
1117        // Make the toolbar as wide as the window and as
1118        // tall as the buttons
1119        toolBar.setSize(shell.getClientArea().width, openItem.getBounds().height);
1120        toolBar.setLocation(0, 0);
1121
1122        log.info("Toolbar created");
1123    }
1124
1125    private void createUrlToolbar(final Shell shell) {
1126        // Recent Files button
1127        recentFilesButton = new Button(shell, SWT.PUSH);
1128        recentFilesButton.setFont(currentFont);
1129        recentFilesButton.setText("Recent Files");
1130        recentFilesButton.setToolTipText("List of recent files");
1131        recentFilesButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
1132        recentFilesButton.addSelectionListener(new SelectionAdapter() {
1133            @Override
1134            public void widgetSelected(SelectionEvent e) {
1135                urlBar.setListVisible(true);
1136            }
1137        });
1138
1139        // Recent files combo box
1140        urlBar = new Combo(shell, SWT.BORDER | SWT.SINGLE);
1141        urlBar.setFont(currentFont);
1142        urlBar.setItems(ViewProperties.getMRF().toArray(new String[0]));
1143        urlBar.setVisibleItemCount(ViewProperties.MAX_RECENT_FILES);
1144        urlBar.deselectAll();
1145        urlBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
1146        urlBar.addKeyListener(new KeyAdapter() {
1147            @Override
1148            public void keyPressed(KeyEvent e) {
1149                if(e.keyCode == SWT.CR) {
1150                    String filename = urlBar.getText();
1151                    if (filename == null || filename.length() < 1 || filename.equals(currentFile))
1152                        return;
1153
1154                    if (!(filename.startsWith("http://") || filename.startsWith("https://") || filename.startsWith("ftp://"))) {
1155                        openLocalFile(filename, -1);
1156                    }
1157                    else {
1158                        String remoteFile = openRemoteFile(filename);
1159
1160                        if (remoteFile != null)
1161                            openLocalFile(remoteFile, -1);
1162                    }
1163                }
1164            }
1165        });
1166        urlBar.addSelectionListener(new SelectionAdapter() {
1167            @Override
1168            public void widgetSelected(SelectionEvent e) {
1169                String filename = urlBar.getText();
1170                if (filename == null || filename.length() < 1 || filename.equals(currentFile)) {
1171                    return;
1172                }
1173
1174                if (!(filename.startsWith("http://") || filename.startsWith("https://") || filename.startsWith("ftp://"))) {
1175                    openLocalFile(filename, -1);
1176                }
1177                else {
1178                    String remoteFile = openRemoteFile(filename);
1179
1180                    if (remoteFile != null)
1181                        openLocalFile(remoteFile, -1);
1182                }
1183            }
1184        });
1185
1186        clearTextButton = new Button(shell, SWT.PUSH);
1187        clearTextButton.setToolTipText("Clear current selection");
1188        clearTextButton.setFont(currentFont);
1189        clearTextButton.setText("Clear Text");
1190        clearTextButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
1191        clearTextButton.addSelectionListener(new SelectionAdapter() {
1192            @Override
1193            public void widgetSelected(SelectionEvent e) {
1194                urlBar.setText("");
1195                urlBar.deselectAll();
1196            }
1197        });
1198
1199        log.info("URL Toolbar created");
1200    }
1201
1202    private void createContentArea(final Shell shell) {
1203        SashForm content = new SashForm(shell, SWT.VERTICAL);
1204        content.setSashWidth(10);
1205        content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
1206
1207        // Add Data content area and Status Area to main window
1208        Composite container = new Composite(content, SWT.NONE);
1209        container.setLayout(new FillLayout());
1210
1211        Composite statusArea = new Composite(content, SWT.NONE);
1212        statusArea.setLayout(new FillLayout(SWT.HORIZONTAL));
1213
1214        final SashForm contentArea = new SashForm(container, SWT.HORIZONTAL);
1215        contentArea.setSashWidth(10);
1216
1217        // Add TreeView and DataView to content area pane
1218        ScrolledComposite treeArea = new ScrolledComposite(contentArea, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
1219        treeArea.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
1220        treeArea.setExpandHorizontal(true);
1221        treeArea.setExpandVertical(true);
1222
1223        generalArea = new ScrolledComposite(contentArea, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
1224        generalArea.setExpandHorizontal(true);
1225        generalArea.setExpandVertical(true);
1226        generalArea.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
1227        generalArea.setMinHeight(contentArea.getSize().y - 2);
1228
1229        // Create status area for displaying messages and metadata
1230        status = new Text(statusArea, SWT.V_SCROLL | SWT.MULTI | SWT.BORDER);
1231        status.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
1232        status.setEditable(false);
1233        status.setFont(currentFont);
1234
1235        contentArea.addListener(SWT.Resize, new Listener() {
1236            @Override
1237            public void handleEvent(Event arg0) {
1238                generalArea.setMinHeight(contentArea.getSize().y - 2);
1239            }
1240        });
1241
1242        // Add drag and drop support for opening files
1243        DropTarget target = new DropTarget(treeArea, DND.DROP_COPY);
1244        final FileTransfer fileTransfer = FileTransfer.getInstance();
1245        target.setTransfer(new Transfer[] { fileTransfer });
1246        target.addDropListener(new DropTargetListener() {
1247            @Override
1248            public void dragEnter(DropTargetEvent e) {
1249                e.detail = DND.DROP_COPY;
1250            }
1251            @Override
1252            public void dragOver(DropTargetEvent e) {
1253                // Intentional
1254            }
1255            @Override
1256            public void dragOperationChanged(DropTargetEvent e) {
1257                // Intentional
1258            }
1259            @Override
1260            public void dragLeave(DropTargetEvent e) {
1261                // Intentional
1262            }
1263            @Override
1264            public void dropAccept(DropTargetEvent e) {
1265                // Intentional
1266            }
1267            @Override
1268            public void drop(DropTargetEvent e) {
1269                if (fileTransfer.isSupportedType(e.currentDataType)) {
1270                    String[] files = (String[]) e.data;
1271                    for (int i = 0; i < files.length; i++)
1272                        openLocalFile(files[i], -1);
1273                }
1274            }
1275        });
1276
1277        showStatus("HDFView root - " + rootDir);
1278        showStatus("User property file - " + ViewProperties.getPropertyFile());
1279
1280        content.setWeights(new int[] { 9, 1 });
1281        contentArea.setWeights(new int[] { 1, 3 });
1282
1283        DataViewFactory treeViewFactory = null;
1284        try {
1285            treeViewFactory = DataViewFactoryProducer.getFactory(DataViewType.TREEVIEW);
1286        }
1287        catch (Exception ex) {
1288            log.debug("createContentArea(): error occurred while instantiating TreeView factory class", ex);
1289            this.showError("Error occurred while instantiating TreeView factory class");
1290            return;
1291        }
1292
1293        if (treeViewFactory == null) {
1294            log.debug("createContentArea(): TreeView factory is null");
1295            return;
1296        }
1297
1298        try {
1299            treeView = treeViewFactory.getTreeView(treeArea, this);
1300
1301            if (treeView == null) {
1302                log.debug("createContentArea(): error occurred while instantiating TreeView class");
1303                this.showError("Error occurred while instantiating TreeView class");
1304                return;
1305            }
1306        }
1307        catch (ClassNotFoundException ex) {
1308            log.debug("createContentArea(): no suitable TreeView class found");
1309            this.showError("Unable to find suitable TreeView class");
1310            return;
1311        }
1312
1313        treeArea.setContent(treeView.getTree());
1314
1315        log.info("Content Area created");
1316    }
1317
1318    /**
1319     * @return a list of treeview implementations.
1320     */
1321    public static final List<String> getListOfTreeViews() {
1322        return treeViews;
1323    }
1324
1325    /**
1326     * @return a list of imageview implementations.
1327     */
1328    public static final List<String> getListOfImageViews() {
1329        return imageViews;
1330    }
1331
1332    /**
1333     * @return a list of tableview implementations.
1334     */
1335    public static final List<?> getListOfTableViews() {
1336        return tableViews;
1337    }
1338
1339    /**
1340     * @return a list of metaDataview implementations.
1341     */
1342    public static final List<?> getListOfMetaDataViews() {
1343        return metaDataViews;
1344    }
1345
1346    /**
1347     * @return a list of paletteview implementations.
1348     */
1349    public static final List<?> getListOfPaletteViews() {
1350        return paletteViews;
1351    }
1352
1353    @Override
1354    public TreeView getTreeView() {
1355        return treeView;
1356    }
1357
1358    /**
1359     * @return the combobox associated with a URL entry.
1360     */
1361    public Combo getUrlBar() {
1362        return urlBar;
1363    }
1364
1365    /**
1366     * Start stop a timer.
1367     *
1368     * @param toggleTimer
1369     *            -- true: start timer, false stop timer.
1370     */
1371    @Override
1372    public final void executeTimer(boolean toggleTimer) {
1373        showStatus("toggleTimer: " + toggleTimer);
1374        viewerState = toggleTimer;
1375        if (viewerState)
1376            display.timerExec(ViewProperties.getTimerRefresh(), timer);
1377        else
1378            display.timerExec(-1, timer);
1379    }
1380
1381    /**
1382     * Display feedback message.
1383     *
1384     * @param msg
1385     *            the message to display.
1386     */
1387    @Override
1388    public void showStatus(String msg) {
1389        if (status == null) {
1390            log.debug("showStatus(): status area is null");
1391            return;
1392        }
1393
1394        status.append(msg);
1395        status.append("\n");
1396    }
1397
1398    /**
1399     * Display error message
1400     *
1401     * @param errMsg
1402     *            the error message to display
1403     */
1404    @Override
1405    public void showError(String errMsg) {
1406        if (status == null) {
1407            log.debug("showError(): status area is null");
1408            return;
1409        }
1410
1411        status.append(" *** ");
1412        status.append(errMsg);
1413        if (log.isDebugEnabled())
1414            status.append(" - see log for more info");
1415        status.append(" *** ");
1416        status.append("\n");
1417    }
1418
1419    /**
1420     * Display the metadata view for an object
1421     *
1422     * @param obj
1423     *            the object containing the metadata to show
1424     */
1425    public void showMetaData(final HObject obj) {
1426        for (Control control : generalArea.getChildren())
1427            control.dispose();
1428        generalArea.setContent(null);
1429
1430        if (obj == null)
1431            return;
1432
1433        DataViewFactory metaDataViewFactory = null;
1434        try {
1435            metaDataViewFactory = DataViewFactoryProducer.getFactory(DataViewType.METADATA);
1436        }
1437        catch (Exception ex) {
1438            log.debug("showMetaData(): error occurred while instantiating MetaDataView factory class", ex);
1439            this.showError("Error occurred while instantiating MetaDataView factory class");
1440            return;
1441        }
1442
1443        if (metaDataViewFactory == null) {
1444            log.debug("showMetaData(): MetaDataView factory is null");
1445            return;
1446        }
1447
1448        MetaDataView theView;
1449        try {
1450            theView = metaDataViewFactory.getMetaDataView(generalArea, this, obj);
1451
1452            if (theView == null) {
1453                log.debug("showMetaData(): error occurred while instantiating MetaDataView class");
1454                this.showError("Error occurred while instantiating MetaDataView class");
1455                return;
1456            }
1457        }
1458        catch (ClassNotFoundException ex) {
1459            log.debug("showMetaData(): no suitable MetaDataView class found");
1460            this.showError("Unable to find suitable MetaDataView class");
1461            return;
1462        }
1463    }
1464
1465    /**
1466     * close the file currently selected in the application
1467     *
1468     * @param theFile
1469     *        the file selected or specified
1470     */
1471    public void closeFile(FileFormat theFile) {
1472        if (theFile == null) {
1473            display.beep();
1474            Tools.showError(mainWindow, "Close", "Select a file to close");
1475            return;
1476        }
1477
1478        // Close all the data windows of this file
1479        Shell[] views = display.getShells();
1480        if (views != null) {
1481            for (int i = 0; i < views.length; i++) {
1482                Object shellData = views[i].getData();
1483
1484                if (!(shellData instanceof DataView))
1485                    continue;
1486
1487                if ((DataView) shellData != null) {
1488                    HObject obj = ((DataView) shellData).getDataObject();
1489
1490                    if (obj == null || obj.getFileFormat() == null)
1491                        continue;
1492
1493                    if (obj.getFileFormat().equals(theFile)) {
1494                        views[i].dispose();
1495                        views[i] = null;
1496                    }
1497                }
1498            }
1499        }
1500
1501        int index = urlBar.getSelectionIndex();
1502        if (index >= 0) {
1503            String fName = urlBar.getItem(urlBar.getSelectionIndex());
1504            if (theFile.getFilePath().equals(fName)) {
1505                currentFile = null;
1506                urlBar.setText("");
1507            }
1508        }
1509
1510        try {
1511            treeView.closeFile(theFile);
1512        }
1513        catch (Exception ex) {
1514            // Intentional
1515        }
1516
1517        for (Control control : generalArea.getChildren())
1518            control.dispose();
1519        generalArea.setContent(null);
1520
1521        System.gc();
1522    }
1523
1524    /**
1525     * Write the change of data to the given file.
1526     *
1527     * @param theFile
1528     *           The file to be updated.
1529     */
1530    public void writeDataToFile(FileFormat theFile) {
1531        try {
1532            Shell[] openShells = display.getShells();
1533
1534            if (openShells != null) {
1535                for (int i = 0; i < openShells.length; i++) {
1536                    DataView theView = (DataView) openShells[i].getData();
1537
1538                    if (theView instanceof TableView) {
1539                        TableView tableView = (TableView) theView;
1540                        FileFormat file = tableView.getDataObject().getFileFormat();
1541                        if (file.equals(theFile))
1542                            tableView.updateValueInFile();
1543                    }
1544                }
1545            }
1546        }
1547        catch (Exception ex) {
1548            display.beep();
1549            Tools.showError(mainWindow, "Save", ex.getMessage());
1550        }
1551    }
1552
1553    @Override
1554    public void addDataView(DataView dataView) {
1555        if (dataView == null || dataView instanceof MetaDataView)
1556            return;
1557
1558        // Check if the data content is already displayed
1559        Shell[] shellList = display.getShells();
1560        if (shellList != null) {
1561            for (int i = 0; i < shellList.length; i++) {
1562                if (dataView.equals(shellList[i].getData()) && shellList[i].isVisible()) {
1563                    showWindow(shellList[i]);
1564                    return;
1565                }
1566            }
1567        }
1568
1569        // First window being added
1570        if (shellList != null && shellList.length == 2)
1571            setEnabled(Arrays.asList(windowMenu.getItems()), true);
1572
1573        HObject obj = dataView.getDataObject();
1574        String fullPath = ((obj.getPath() == null) ? "" : obj.getPath()) + ((obj.getName() == null) ? "" : obj.getName());
1575
1576        MenuItem item = new MenuItem(windowMenu, SWT.PUSH);
1577        item.setText(fullPath);
1578        item.addSelectionListener(new SelectionAdapter() {
1579            @Override
1580            public void widgetSelected(SelectionEvent e) {
1581                Shell[] sList = display.getShells();
1582
1583                for (int i = 0; i < sList.length; i++) {
1584                    DataView view = (DataView) sList[i].getData();
1585
1586                    if (view != null) {
1587                        HObject obj = view.getDataObject();
1588
1589                        if (obj.getFullName().equals(((MenuItem) e.widget).getText()))
1590                            showWindow(sList[i]);
1591                    }
1592                }
1593            }
1594        });
1595
1596        mainWindow.setCursor(null);
1597    }
1598
1599    @Override
1600    public void removeDataView(DataView dataView) {
1601        if (mainWindow.isDisposed())
1602            return;
1603
1604        HObject obj = dataView.getDataObject();
1605        if (obj == null)
1606            return;
1607
1608        MenuItem[] items = windowMenu.getItems();
1609        for (int i = 0; i < items.length; i++) {
1610            if(items[i].getText().equals(obj.getFullName()))
1611                items[i].dispose();
1612        }
1613
1614        // Last window being closed
1615        if (display.getShells().length == 2)
1616            for (MenuItem item : windowMenu.getItems()) item.setEnabled(false);
1617    }
1618
1619    @Override
1620    public DataView getDataView(HObject dataObject) {
1621        Shell[] openShells = display.getShells();
1622        DataView view = null;
1623        HObject currentObj = null;
1624        FileFormat currentDataViewFile = null;
1625
1626        for (int i = 0; i < openShells.length; i++) {
1627            view = (DataView) openShells[i].getData();
1628
1629            if (view != null) {
1630                currentObj = view.getDataObject();
1631                if (currentObj == null)
1632                    continue;
1633
1634                currentDataViewFile = currentObj.getFileFormat();
1635
1636                if (currentObj.equals(dataObject) && currentDataViewFile.equals(dataObject.getFileFormat()))
1637                    return view;
1638            }
1639        }
1640
1641        return null;
1642    }
1643
1644    /**
1645     * Set the testing state that determines if HDFView
1646     * is being executed for GUI testing.
1647     *
1648     * @param testing
1649     *           Provides SWTBot native dialog compatibility
1650     *           workarounds if set to true.
1651     */
1652    public void setTestState(boolean testing) {
1653        isTesting = testing;
1654    }
1655
1656    /**
1657     * Get the testing state that determines if HDFView
1658     * is being executed for GUI testing.
1659     *
1660     * @return true if HDFView is being executed for GUI testing.
1661     */
1662    public boolean getTestState() {
1663        return isTesting;
1664    }
1665
1666    /**
1667     * Set default UI fonts.
1668     */
1669    private void updateFont(Font font) {
1670        if (currentFont != null)
1671            currentFont.dispose();
1672
1673        currentFont = font;
1674
1675        mainWindow.setFont(font);
1676        recentFilesButton.setFont(font);
1677        urlBar.setFont(font);
1678        clearTextButton.setFont(font);
1679        status.setFont(font);
1680
1681        // On certain platforms the url_bar items don't update their size after
1682        // a font change. Removing and replacing them fixes this.
1683        for (String item : urlBar.getItems()) {
1684            urlBar.remove(item);
1685            urlBar.add(item);
1686        }
1687
1688        if (treeView.getSelectedFile() != null)
1689            urlBar.select(0);
1690
1691        if (treeView instanceof DefaultTreeView)
1692            ((DefaultTreeView) treeView).updateFont(font);
1693
1694        mainWindow.layout();
1695    }
1696
1697    /**
1698     * Bring window to the front.
1699     *
1700     * @param name
1701     *               the name of the window to show.
1702     */
1703    private void showWindow(final Shell shell) {
1704        shell.getDisplay().asyncExec(new Runnable() {
1705            @Override
1706            public void run() {
1707                shell.forceActive();
1708            }
1709        });
1710    }
1711
1712    /**
1713     * Cascade all windows.
1714     */
1715    private void cascadeWindows() {
1716        Shell[] sList = display.getShells();
1717
1718        // Return if main window (shell) is the only open shell
1719        if (sList.length <= 1)
1720            return;
1721
1722        Shell shell = null;
1723
1724        Rectangle bounds = Display.getCurrent().getPrimaryMonitor().getClientArea();
1725        int w = Math.max(50, bounds.width - 100);
1726        int h = Math.max(50, bounds.height - 100);
1727
1728        int x = bounds.x;
1729        int y = bounds.y;
1730
1731        for (int i = 0; i < sList.length; i++) {
1732            shell = sList[i];
1733            shell.setBounds(x, y, w, h);
1734            shell.setActive();
1735            x += 20;
1736            y += 20;
1737        }
1738    }
1739
1740    /**
1741     * Tile all windows.
1742     */
1743    private void tileWindows() {
1744        Shell[] sList = display.getShells();
1745
1746        // Return if main window (shell) is the only open shell
1747        if (sList.length <= 1)
1748            return;
1749
1750        int x = 0;
1751        int y = 0;
1752        int idx = 0;
1753        Shell shell = null;
1754
1755        int n = sList.length;
1756        int cols = (int) Math.sqrt(n);
1757        int rows = (int) Math.ceil((double) n / (double) cols);
1758
1759        Rectangle bounds = Display.getCurrent().getPrimaryMonitor().getClientArea();
1760        int w = bounds.width / cols;
1761        int h = bounds.height / rows;
1762
1763        y = bounds.y;
1764        for (int i = 0; i < rows; i++) {
1765            x = bounds.x;
1766
1767            for (int j = 0; j < cols; j++) {
1768                idx = i * cols + j;
1769                if (idx >= n)
1770                    return;
1771
1772                shell = sList[idx];
1773                shell.setBounds(x, y, w, h);
1774                shell.setActive();
1775                x += w;
1776            }
1777
1778            y += h;
1779        }
1780    }
1781
1782    /**
1783     * Closes all windows.
1784     */
1785    private void closeAllWindows() {
1786        Shell[] sList = display.getShells();
1787
1788        for (int i = 0; i < sList.length; i++) {
1789            if (sList[i].equals(mainWindow)) continue;
1790            sList[i].dispose();
1791        }
1792    }
1793
1794    /* Enable and disable GUI components */
1795    private static void setEnabled(List<MenuItem> list, boolean b) {
1796        Iterator<MenuItem> it = list.iterator();
1797
1798        while (it.hasNext())
1799            it.next().setEnabled(b);
1800    }
1801
1802    /** Open local file */
1803    private void openLocalFile(String filename, int fileAccessID) {
1804        log.trace("openLocalFile {},{}",filename, fileAccessID);
1805
1806        /*
1807         * If given a specific access mode, use it without changing it. If not given a
1808         * specific access mode, check the current status of the "is read only" property
1809         * to determine how to open the file. This is to allow one time overrides of the
1810         * default file access mode when opening a file.
1811         */
1812        int accessMode = fileAccessID;
1813        if (accessMode < 0) {
1814            if (ViewProperties.isReadOnly())
1815                accessMode = FileFormat.READ;
1816            else if (ViewProperties.isReadSWMR())
1817                accessMode = FileFormat.READ | FileFormat.MULTIREAD;
1818            else
1819                accessMode = FileFormat.WRITE;
1820        }
1821
1822        String[] selectedFilenames = null;
1823        File[] chosenFiles = null;
1824
1825        if (filename != null) {
1826            File file = new File(filename);
1827            if(!file.exists()) {
1828                Tools.showError(mainWindow, "Open", "File " + filename + " does not exist.");
1829                return;
1830            }
1831
1832            if(file.isDirectory()) {
1833                currentDir = filename;
1834                openLocalFile(null, -1);
1835            }
1836            else {
1837                currentFile = filename;
1838
1839                try {
1840                    treeView.openFile(filename, accessMode);
1841                }
1842                catch (Exception ex) {
1843                    try {
1844                        treeView.openFile(filename, FileFormat.READ);
1845                    }
1846                    catch (Exception ex2) {
1847                        display.beep();
1848                        urlBar.deselectAll();
1849                        Tools.showError(mainWindow, "Open", "Failed to open file " + filename + "\n" + ex2);
1850                        currentFile = null;
1851                    }
1852                }
1853            }
1854
1855            try {
1856                urlBar.remove(filename);
1857            }
1858            catch (Exception ex) {
1859                log.trace("unable to remove {} from urlBar", filename);
1860            }
1861
1862            // first entry is always the workdir
1863            urlBar.add(filename, 1);
1864            urlBar.select(1);
1865        }
1866        else {
1867            if (!isTesting) {
1868                log.trace("openLocalFile filename is null");
1869                FileDialog fChooser = new FileDialog(mainWindow, SWT.OPEN | SWT.MULTI);
1870                String modeStr = "Read/Write";
1871                boolean isSWMRFile = (FileFormat.MULTIREAD == (accessMode & FileFormat.MULTIREAD));
1872                if (isSWMRFile)
1873                    modeStr = "SWMR Read-only";
1874                else if (accessMode == FileFormat.READ)
1875                    modeStr = "Read-only";
1876                fChooser.setText(mainWindow.getText() + " - Open File " + modeStr);
1877                fChooser.setFilterPath(currentDir);
1878
1879                DefaultFileFilter filter = DefaultFileFilter.getFileFilter();
1880                fChooser.setFilterExtensions(new String[] {"*", filter.getExtensions()});
1881                fChooser.setFilterNames(new String[] {"All Files", filter.getDescription()});
1882                fChooser.setFilterIndex(1);
1883
1884                fChooser.open();
1885
1886                selectedFilenames = fChooser.getFileNames();
1887                if(selectedFilenames.length <= 0)
1888                    return;
1889
1890                chosenFiles = new File[selectedFilenames.length];
1891                for(int i = 0; i < chosenFiles.length; i++) {
1892                    log.trace("openLocalFile selectedFilenames[{}]: {}",i,selectedFilenames[i]);
1893                    chosenFiles[i] = new File(fChooser.getFilterPath() + File.separator + selectedFilenames[i]);
1894
1895                    if(!chosenFiles[i].exists()) {
1896                        Tools.showError(mainWindow, "Open", "File " + chosenFiles[i].getName() + " does not exist.");
1897                        continue;
1898                    }
1899
1900                    if (chosenFiles[i].isDirectory())
1901                        currentDir = chosenFiles[i].getPath();
1902                    else
1903                        currentDir = chosenFiles[i].getParent();
1904
1905                    try {
1906                        urlBar.remove(chosenFiles[i].getAbsolutePath());
1907                    }
1908                    catch (Exception ex) {
1909                        log.trace("unable to remove {} from urlBar", chosenFiles[i].getAbsolutePath());
1910                    }
1911
1912                    // first entry is always the workdir
1913                    urlBar.add(chosenFiles[i].getAbsolutePath(), 1);
1914                    urlBar.select(1);
1915
1916                    log.trace("openLocalFile treeView.openFile(accessMode={} chosenFiles[{}]: {}",accessMode,i,chosenFiles[i].getAbsolutePath());
1917                    try {
1918                        treeView.openFile(chosenFiles[i].getAbsolutePath(), accessMode + FileFormat.OPEN_NEW);
1919                    }
1920                    catch (Exception ex) {
1921                        try {
1922                            treeView.openFile(chosenFiles[i].getAbsolutePath(), FileFormat.READ);
1923                        }
1924                        catch (Exception ex2) {
1925                            display.beep();
1926                            urlBar.deselectAll();
1927                            Tools.showError(mainWindow, "Open", "Failed to open file " + selectedFilenames[i] + "\n" + ex2);
1928                            currentFile = null;
1929                        }
1930                    }
1931                }
1932
1933                currentFile = chosenFiles[0].getAbsolutePath();
1934            }
1935            else {
1936                // Prepend test file directory to filename
1937                String fName = currentDir + File.separator + new InputDialog(mainWindow, "Enter a file name", "").open();
1938
1939                File chosenFile = new File(fName);
1940
1941                if(!chosenFile.exists()) {
1942                    Tools.showError(mainWindow, "Open", "File " + chosenFile.getName() + " does not exist.");
1943                    return;
1944                }
1945
1946                if (chosenFile.isDirectory())
1947                    currentDir = chosenFile.getPath();
1948                else
1949                    currentDir = chosenFile.getParent();
1950
1951                try {
1952                    urlBar.remove(chosenFile.getAbsolutePath());
1953                }
1954                catch (Exception ex) {
1955                    log.trace("unable to remove {} from urlBar", chosenFile.getAbsolutePath());
1956                }
1957
1958                // first entry is always the workdir
1959                urlBar.add(chosenFile.getAbsolutePath(), 1);
1960                urlBar.select(1);
1961
1962                log.trace("openLocalFile treeView.openFile(chosenFile[{}]: {}", chosenFile.getAbsolutePath(), accessMode + FileFormat.OPEN_NEW);
1963                try {
1964                    treeView.openFile(chosenFile.getAbsolutePath(), accessMode + FileFormat.OPEN_NEW);
1965                }
1966                catch (Exception ex) {
1967                    try {
1968                        treeView.openFile(chosenFile.getAbsolutePath(), FileFormat.READ);
1969                    }
1970                    catch (Exception ex2) {
1971                        display.beep();
1972                        urlBar.deselectAll();
1973                        Tools.showError(mainWindow, "Open", "Failed to open file " + chosenFile + "\n" + ex2);
1974                        currentFile = null;
1975                    }
1976                }
1977
1978                currentFile = chosenFile.getAbsolutePath();
1979            }
1980        }
1981    }
1982
1983    /** Load remote file and save it to local temporary directory */
1984    private String openRemoteFile(String urlStr) {
1985        if (urlStr == null)
1986            return null;
1987
1988        String localFile = null;
1989
1990        if(urlStr.startsWith("http://"))
1991            localFile = urlStr.substring(7);
1992        else if (urlStr.startsWith("https://"))
1993            localFile = urlStr.substring(8);
1994        else if (urlStr.startsWith("ftp://"))
1995            localFile = urlStr.substring(6);
1996        else
1997            return null;
1998
1999        localFile = localFile.replace('/', '@');
2000        localFile = localFile.replace('\\', '@');
2001
2002        // Search the local file cache
2003        String tmpDir = System.getProperty("java.io.tmpdir");
2004
2005        File tmpFile = new File(tmpDir);
2006        if (!tmpFile.canWrite())
2007            tmpDir = System.getProperty("user.home");
2008
2009        localFile = tmpDir + File.separator + localFile;
2010
2011        tmpFile = new File(localFile);
2012        if (tmpFile.exists())
2013            return localFile;
2014
2015        URL url = null;
2016
2017        try {
2018            url = new URL(urlStr);
2019        }
2020        catch (Exception ex) {
2021            url = null;
2022            display.beep();
2023            Tools.showError(mainWindow, "Open", ex.getMessage());
2024            return null;
2025        }
2026
2027        try (BufferedInputStream in = new BufferedInputStream(url.openStream())) {
2028            try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(tmpFile))) {
2029                mainWindow.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
2030                byte[] buff = new byte[512]; // set default buffer size to 512
2031                int n = 0;
2032                while ((n = in.read(buff)) > 0)
2033                    out.write(buff, 0, n);
2034            }
2035            catch (Exception ex) {
2036                log.debug("Remote file: ", ex);
2037                throw ex;
2038            }
2039        }
2040        catch (Exception ex) {
2041            display.beep();
2042            Tools.showError(mainWindow, "Open", ex.getMessage());
2043            // Want to call setCursor always
2044            localFile = null;
2045        }
2046
2047        mainWindow.setCursor(null);
2048
2049        return localFile;
2050    }
2051
2052    private void convertFile(String typeFrom, String typeTo) {
2053        ImageConversionDialog dialog = new ImageConversionDialog(mainWindow, typeFrom, typeTo,
2054                currentDir, treeView.getCurrentFiles());
2055        dialog.open();
2056
2057        if (dialog.isFileConverted()) {
2058            String filename = dialog.getConvertedFile();
2059            File theFile = new File(filename);
2060
2061            if (!theFile.exists())
2062                return;
2063
2064            currentDir = theFile.getParentFile().getAbsolutePath();
2065            currentFile = theFile.getAbsolutePath();
2066
2067            try {
2068                treeView.openFile(filename, FileFormat.WRITE);
2069
2070                try {
2071                    urlBar.remove(filename);
2072                }
2073                catch (Exception ex) {
2074                    log.trace("unable to remove {} from urlBar", filename);
2075                }
2076
2077                // first entry is always the workdir
2078                urlBar.add(filename, 1);
2079                urlBar.select(1);
2080            }
2081            catch (Exception ex) {
2082                showError(ex.toString());
2083            }
2084        }
2085    }
2086
2087    private void registerFileFormat() {
2088        String msg = "Register a new file format by \nKEY:FILE_FORMAT:FILE_EXTENSION\n"
2089                + "where, KEY: the unique identifier for the file format"
2090                + "\n           FILE_FORMAT: the full class name of the file format"
2091                + "\n           FILE_EXTENSION: the file extension for the file format" + "\n\nFor example, "
2092                + "\n\t to add NetCDF, \"NetCDF:hdf.object.nc2.NC2File:nc\""
2093                + "\n\t to add FITS, \"FITS:hdf.object.fits.FitsFile:fits\"\n\n";
2094
2095        // TODO:Add custom HDFLarge icon to dialog
2096        InputDialog dialog = new InputDialog(mainWindow, "Register a file format", msg, SWT.ICON_INFORMATION);
2097
2098        String str = dialog.open();
2099
2100        if ((str == null) || (str.length() < 1))
2101            return;
2102
2103        int idx1 = str.indexOf(':');
2104        int idx2 = str.lastIndexOf(':');
2105
2106        if ((idx1 < 0) || (idx2 <= idx1)) {
2107            Tools.showError(mainWindow, "Register File Format", "Failed to register " + str
2108                    + "\n\nMust in the form of KEY:FILE_FORMAT:FILE_EXTENSION");
2109            return;
2110        }
2111
2112        String key = str.substring(0, idx1);
2113        String className = str.substring(idx1 + 1, idx2);
2114        String extension = str.substring(idx2 + 1);
2115
2116        // Check if the file format has been registered or the key is taken.
2117        String theKey = null;
2118        String theClassName = null;
2119        Enumeration<?> localEnum = FileFormat.getFileFormatKeys();
2120        while (localEnum.hasMoreElements()) {
2121            theKey = (String) localEnum.nextElement();
2122            if (theKey.endsWith(key)) {
2123                Tools.showError(mainWindow, "Register File Format", "Invalid key: " + key + " is taken.");
2124                return;
2125            }
2126
2127            theClassName = FileFormat.getFileFormat(theKey).getClass().getName();
2128            if (theClassName.endsWith(className)) {
2129                Tools.showError(mainWindow, "Register File Format", "The file format has already been registered: " + className);
2130                return;
2131            }
2132        }
2133
2134        // Enables use of JHDF5 in JNLP (Web Start) applications, the system
2135        // class loader with reflection first.
2136        Class<?> theClass = null;
2137        try {
2138            theClass = Class.forName(className);
2139        }
2140        catch (Exception ex) {
2141            try {
2142                theClass = ViewProperties.loadExtClass().loadClass(className);
2143            }
2144            catch (Exception ex2) {
2145                theClass = null;
2146            }
2147        }
2148
2149        if (theClass == null)
2150            return;
2151
2152        try {
2153            Object theObject = theClass.newInstance();
2154            if (theObject instanceof FileFormat)
2155                FileFormat.addFileFormat(key, (FileFormat) theObject);
2156        }
2157        catch (Exception ex) {
2158            Tools.showError(mainWindow, "Register File Format", "Failed to register " + str + "\n\n" + ex);
2159            return;
2160        }
2161
2162        if ((extension != null) && (extension.length() > 0)) {
2163            extension = extension.trim();
2164            String ext = ViewProperties.getFileExtension();
2165            ext += ", " + extension;
2166            ViewProperties.setFileExtension(ext);
2167        }
2168    }
2169
2170    private void unregisterFileFormat() {
2171        Enumeration<?> keys = FileFormat.getFileFormatKeys();
2172        ArrayList<Object> keyList = new ArrayList<>();
2173
2174        while (keys.hasMoreElements())
2175            keyList.add(keys.nextElement());
2176
2177        String theKey = new UnregisterFileFormatDialog(mainWindow, SWT.NONE, keyList).open();
2178
2179        if (theKey == null)
2180            return;
2181
2182        FileFormat.removeFileFormat(theKey);
2183    }
2184
2185    private class LibraryVersionDialog extends Dialog
2186    {
2187        private String message;
2188
2189        public LibraryVersionDialog(Shell parent, String libType) {
2190            super(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
2191
2192            if (libType.equals(FileFormat.FILE_TYPE_HDF4))
2193                setMessage("HDF " + HDF4_VERSION);
2194            else if (libType.equals(FileFormat.FILE_TYPE_HDF5))
2195                setMessage("HDF5 " + HDF5_VERSION);
2196        }
2197
2198        public void setMessage(String message) {
2199            this.message = message;
2200        }
2201
2202        public void open() {
2203            Shell dialog = new Shell(getParent(), getStyle());
2204            dialog.setFont(currentFont);
2205            dialog.setText("HDF Library Version");
2206
2207            createContents(dialog);
2208
2209            dialog.pack();
2210
2211            Point computedSize = dialog.computeSize(SWT.DEFAULT, SWT.DEFAULT);
2212            dialog.setSize(computedSize.x + 50, computedSize.y + 50);
2213
2214            // Center the window relative to the main HDFView window
2215            Point winCenter = new Point(
2216                    mainWindow.getBounds().x + (mainWindow.getBounds().width / 2),
2217                    mainWindow.getBounds().y + (mainWindow.getBounds().height / 2));
2218
2219            dialog.setLocation(winCenter.x - (dialog.getSize().x / 2), winCenter.y - (dialog.getSize().y / 2));
2220
2221            dialog.open();
2222
2223            Display display = getParent().getDisplay();
2224            while (!dialog.isDisposed()) {
2225                if (!display.readAndDispatch())
2226                    display.sleep();
2227            }
2228        }
2229
2230        private void createContents(final Shell shell) {
2231            shell.setLayout(new GridLayout(2, false));
2232
2233            Image hdfImage = ViewProperties.getLargeHdfIcon();
2234
2235            Label imageLabel = new Label(shell, SWT.CENTER);
2236            imageLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
2237            imageLabel.setImage(hdfImage);
2238
2239            Label versionLabel = new Label(shell, SWT.CENTER);
2240            versionLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
2241            versionLabel.setFont(currentFont);
2242            versionLabel.setText(message);
2243
2244            // Draw HDF Icon and Version string
2245            Composite buttonComposite = new Composite(shell, SWT.NONE);
2246            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
2247            RowLayout buttonLayout = new RowLayout();
2248            buttonLayout.center = true;
2249            buttonLayout.justify = true;
2250            buttonLayout.type = SWT.HORIZONTAL;
2251            buttonComposite.setLayout(buttonLayout);
2252
2253            Button okButton = new Button(buttonComposite, SWT.PUSH);
2254            okButton.setFont(currentFont);
2255            okButton.setText("   &OK   ");
2256            shell.setDefaultButton(okButton);
2257            okButton.addSelectionListener(new SelectionAdapter() {
2258                @Override
2259                public void widgetSelected(SelectionEvent e) {
2260                    shell.dispose();
2261                }
2262            });
2263        }
2264    }
2265
2266    private class JavaVersionDialog extends Dialog
2267    {
2268        public JavaVersionDialog(Shell parent) {
2269            super(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
2270        }
2271
2272        public void open() {
2273            final Shell dialog = new Shell(getParent(), getStyle());
2274            dialog.setFont(currentFont);
2275            dialog.setText("HDFView Java Version");
2276            dialog.setLayout(new GridLayout(2, false));
2277
2278            Image hdfImage = ViewProperties.getLargeHdfIcon();
2279
2280            Label imageLabel = new Label(dialog, SWT.CENTER);
2281            imageLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
2282            imageLabel.setImage(hdfImage);
2283
2284            Label versionLabel = new Label(dialog, SWT.CENTER);
2285            versionLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
2286            versionLabel.setFont(currentFont);
2287            versionLabel.setText(JAVA_VER_INFO);
2288
2289            Composite buttonComposite = new Composite(dialog, SWT.NONE);
2290            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
2291            RowLayout buttonLayout = new RowLayout();
2292            buttonLayout.center = true;
2293            buttonLayout.justify = true;
2294            buttonLayout.type = SWT.HORIZONTAL;
2295            buttonComposite.setLayout(buttonLayout);
2296
2297            Button okButton = new Button(buttonComposite, SWT.PUSH);
2298            okButton.setFont(currentFont);
2299            okButton.setText("   &OK   ");
2300            dialog.setDefaultButton(okButton);
2301            okButton.addSelectionListener(new SelectionAdapter() {
2302                @Override
2303                public void widgetSelected(SelectionEvent e) {
2304                    dialog.dispose();
2305                }
2306            });
2307
2308            dialog.pack();
2309
2310            Point computedSize = dialog.computeSize(SWT.DEFAULT, SWT.DEFAULT);
2311            dialog.setSize(computedSize.x + 50, computedSize.y + 50);
2312
2313            // Center the window relative to the main HDFView window
2314            Point winCenter = new Point(
2315                    mainWindow.getBounds().x + (mainWindow.getBounds().width / 2),
2316                    mainWindow.getBounds().y + (mainWindow.getBounds().height / 2));
2317
2318            dialog.setLocation(winCenter.x - (dialog.getSize().x / 2), winCenter.y - (dialog.getSize().y / 2));
2319
2320            dialog.open();
2321
2322            Display openDisplay = getParent().getDisplay();
2323            while (!dialog.isDisposed()) {
2324                if (!openDisplay.readAndDispatch())
2325                    openDisplay.sleep();
2326            }
2327        }
2328    }
2329
2330    private class SupportedFileFormatsDialog extends Dialog
2331    {
2332        public SupportedFileFormatsDialog(Shell parent) {
2333            super(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
2334        }
2335
2336        public void open() {
2337            final Shell dialog = new Shell(getParent(), getStyle());
2338            dialog.setFont(currentFont);
2339            dialog.setText("Supported File Formats");
2340            dialog.setLayout(new GridLayout(2, false));
2341
2342            Image hdfImage = ViewProperties.getLargeHdfIcon();
2343
2344            Label imageLabel = new Label(dialog, SWT.CENTER);
2345            imageLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
2346            imageLabel.setImage(hdfImage);
2347
2348            Enumeration<?> formatKeys = FileFormat.getFileFormatKeys();
2349
2350            StringBuilder formats = new StringBuilder("\nSupported File Formats: \n");
2351            while (formatKeys.hasMoreElements())
2352                formats.append("    ").append(formatKeys.nextElement()).append("\n");
2353            formats.append("\n");
2354
2355            Label formatsLabel = new Label(dialog, SWT.LEFT);
2356            formatsLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
2357            formatsLabel.setFont(currentFont);
2358            formatsLabel.setText(formats.toString());
2359
2360            Composite buttonComposite = new Composite(dialog, SWT.NONE);
2361            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
2362            RowLayout buttonLayout = new RowLayout();
2363            buttonLayout.center = true;
2364            buttonLayout.justify = true;
2365            buttonLayout.type = SWT.HORIZONTAL;
2366            buttonComposite.setLayout(buttonLayout);
2367
2368            Button okButton = new Button(buttonComposite, SWT.PUSH);
2369            okButton.setFont(currentFont);
2370            okButton.setText("   &OK   ");
2371            dialog.setDefaultButton(okButton);
2372            okButton.addSelectionListener(new SelectionAdapter() {
2373                @Override
2374                public void widgetSelected(SelectionEvent e) {
2375                    dialog.dispose();
2376                }
2377            });
2378
2379            dialog.pack();
2380
2381            Point computedSize = dialog.computeSize(SWT.DEFAULT, SWT.DEFAULT);
2382            dialog.setSize(computedSize.x + 50, computedSize.y + 50);
2383
2384            // Center the window relative to the main HDFView window
2385            Point winCenter = new Point(
2386                    mainWindow.getBounds().x + (mainWindow.getBounds().width / 2),
2387                    mainWindow.getBounds().y + (mainWindow.getBounds().height / 2));
2388
2389            dialog.setLocation(winCenter.x - (dialog.getSize().x / 2), winCenter.y - (dialog.getSize().y / 2));
2390
2391            dialog.open();
2392
2393            Display openDisplay = getParent().getDisplay();
2394            while (!dialog.isDisposed()) {
2395                if (!openDisplay.readAndDispatch())
2396                    openDisplay.sleep();
2397            }
2398        }
2399    }
2400
2401    private class AboutDialog extends Dialog
2402    {
2403        public AboutDialog(Shell parent) {
2404            super(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
2405        }
2406
2407        public void open() {
2408            final Shell dialog = new Shell(getParent(), getStyle());
2409            dialog.setFont(currentFont);
2410            dialog.setText("About HDFView");
2411            dialog.setLayout(new GridLayout(2, false));
2412
2413            Image hdfImage = ViewProperties.getLargeHdfIcon();
2414
2415            Label imageLabel = new Label(dialog, SWT.CENTER);
2416            imageLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
2417            imageLabel.setImage(hdfImage);
2418
2419            Label aboutLabel = new Label(dialog, SWT.LEFT);
2420            aboutLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false));
2421            aboutLabel.setFont(currentFont);
2422            aboutLabel.setText(ABOUT_HDFVIEW);
2423
2424            Composite buttonComposite = new Composite(dialog, SWT.NONE);
2425            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
2426            RowLayout buttonLayout = new RowLayout();
2427            buttonLayout.center = true;
2428            buttonLayout.justify = true;
2429            buttonLayout.type = SWT.HORIZONTAL;
2430            buttonComposite.setLayout(buttonLayout);
2431
2432            Button okButton = new Button(buttonComposite, SWT.PUSH);
2433            okButton.setFont(currentFont);
2434            okButton.setText("   &OK   ");
2435            dialog.setDefaultButton(okButton);
2436            okButton.addSelectionListener(new SelectionAdapter() {
2437                @Override
2438                public void widgetSelected(SelectionEvent e) {
2439                    dialog.dispose();
2440                }
2441            });
2442
2443            dialog.pack();
2444
2445            Point computedSize = dialog.computeSize(SWT.DEFAULT, SWT.DEFAULT);
2446            dialog.setSize(computedSize.x + 50, computedSize.y + 50);
2447
2448            // Center the window relative to the main HDFView window
2449            Point winCenter = new Point(
2450                    mainWindow.getBounds().x + (mainWindow.getBounds().width / 2),
2451                    mainWindow.getBounds().y + (mainWindow.getBounds().height / 2));
2452
2453            dialog.setLocation(winCenter.x - (dialog.getSize().x / 2), winCenter.y - (dialog.getSize().y / 2));
2454
2455            dialog.open();
2456
2457            Display openDisplay = getParent().getDisplay();
2458            while (!dialog.isDisposed()) {
2459                if (!openDisplay.readAndDispatch())
2460                    openDisplay.sleep();
2461            }
2462        }
2463    }
2464
2465    private class UnregisterFileFormatDialog extends Dialog
2466    {
2467        private List<Object> keyList;
2468        private String formatChoice = null;
2469
2470        public UnregisterFileFormatDialog(Shell parent, int style, List<Object> keyList) {
2471            super(parent, style);
2472
2473            this.keyList = keyList;
2474        }
2475
2476        public String open() {
2477            Shell parent = getParent();
2478            final Shell shell = new Shell(parent, SWT.APPLICATION_MODAL | SWT.DIALOG_TRIM);
2479            shell.setFont(currentFont);
2480            shell.setText("Unregister a file format");
2481            shell.setLayout(new GridLayout(2, false));
2482
2483            Image hdfImage = ViewProperties.getLargeHdfIcon();
2484
2485            Label imageLabel = new Label(shell, SWT.CENTER);
2486            imageLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
2487            imageLabel.setImage(hdfImage);
2488
2489
2490            final Combo formatChoiceCombo = new Combo(shell, SWT.SINGLE | SWT.DROP_DOWN | SWT.READ_ONLY);
2491            formatChoiceCombo.setFont(currentFont);
2492            formatChoiceCombo.setItems(keyList.toArray(new String[0]));
2493            formatChoiceCombo.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
2494            formatChoiceCombo.select(0);
2495            formatChoiceCombo.addSelectionListener(new SelectionAdapter() {
2496                @Override
2497                public void widgetSelected(SelectionEvent e) {
2498                    formatChoice = formatChoiceCombo.getItem(formatChoiceCombo.getSelectionIndex());
2499                }
2500            });
2501
2502            Composite buttonComposite = new Composite(shell, SWT.NONE);
2503            buttonComposite.setLayout(new GridLayout(2, true));
2504            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
2505
2506            Button okButton = new Button(buttonComposite, SWT.PUSH);
2507            okButton.setFont(currentFont);
2508            okButton.setText("   &OK   ");
2509            okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
2510            okButton.addSelectionListener(new SelectionAdapter() {
2511                @Override
2512                public void widgetSelected(SelectionEvent e) {
2513                    shell.dispose();
2514                }
2515            });
2516
2517            Button cancelButton = new Button(buttonComposite, SWT.PUSH);
2518            cancelButton.setFont(currentFont);
2519            cancelButton.setText(" &Cancel ");
2520            cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
2521            cancelButton.addSelectionListener(new SelectionAdapter() {
2522                @Override
2523                public void widgetSelected(SelectionEvent e) {
2524                    shell.dispose();
2525                }
2526            });
2527
2528            shell.pack();
2529
2530            Point computedSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
2531            shell.setSize(computedSize.x + 50, computedSize.y + 50);
2532
2533            Rectangle parentBounds = parent.getBounds();
2534            Point shellSize = shell.getSize();
2535            shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
2536                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
2537
2538            shell.open();
2539
2540            Display openDisplay = parent.getDisplay();
2541            while(!shell.isDisposed()) {
2542                if (!openDisplay.readAndDispatch())
2543                    openDisplay.sleep();
2544            }
2545
2546            return formatChoice;
2547        }
2548    }
2549
2550    /**
2551     * The starting point of this application.
2552     *
2553     * <pre>
2554     * Usage: java(w)
2555     *        -Dhdf.hdf5lib.H5.hdf5lib="your HDF5 library path"
2556     *        -Dhdf.hdflib.HDFLibrary.hdflib="your HDF4 library path"
2557     *        -root "the directory where the HDFView is installed"
2558     *        -start "the directory HDFView searches for files"
2559     *        -geometry or -g "the preferred window size as WIDTHxHEIGHT+XOFF+YOFF"
2560     *        -java.version "show the version of jave used to build the HDFView and exit"
2561     *        [filename] "the file to open"
2562     * </pre>
2563     *
2564     * @param args  the command line arguments
2565     */
2566    public static void main(String[] args) {
2567        if (display == null || display.isDisposed())
2568            display = new Display();
2569
2570        String rootDir = System.getProperty("hdfview.root");
2571        if (rootDir == null)
2572            rootDir = System.getProperty("user.dir");
2573        String startDir = System.getProperty("user.dir");
2574        log.trace("main: rootDir = {}  startDir = {}", rootDir, startDir);
2575
2576        File tmpFile = null;
2577        Monitor primaryMonitor = display.getPrimaryMonitor();
2578        Point margin = new Point(primaryMonitor.getBounds().width, primaryMonitor.getBounds().height);
2579
2580        int j = args.length;
2581        int W = margin.x / 2;
2582        int H = margin.y;
2583        int X = 0;
2584        int Y = 0;
2585
2586        for(int i = 0; i < args.length; i++) {
2587            if ("-root".equalsIgnoreCase(args[i])) {
2588                j--;
2589                try {
2590                    j--;
2591                    tmpFile = new File(args[++i]);
2592
2593                    if(tmpFile.isDirectory())
2594                        rootDir = tmpFile.getPath();
2595                    else if(tmpFile.isFile())
2596                        rootDir = tmpFile.getParent();
2597                }
2598                catch (Exception ex) {}
2599            }
2600            else if ("-start".equalsIgnoreCase(args[i])) {
2601                j--;
2602                try {
2603                    j--;
2604                    tmpFile = new File(args[++i]);
2605
2606                    if(tmpFile.isDirectory())
2607                        startDir = tmpFile.getPath();
2608                    else if(tmpFile.isFile())
2609                        startDir = tmpFile.getParent();
2610                }
2611                catch (Exception ex) {}
2612            }
2613            else if("-g".equalsIgnoreCase(args[i]) || "-geometry".equalsIgnoreCase(args[i])) {
2614                j--;
2615                // -geometry WIDTHxHEIGHT+XOFF+YOFF
2616                try {
2617                    String geom = args[++i];
2618                    j--;
2619
2620                    int idx = 0;
2621                    int idx2 = geom.lastIndexOf('-');
2622                    int idx3 = geom.lastIndexOf('+');
2623
2624                    idx = Math.max(idx2, idx3);
2625                    if(idx > 0) {
2626                        Y = Integer.parseInt(geom.substring(idx + 1));
2627
2628                        if(idx == idx2)
2629                            Y = -Y;
2630
2631                        geom = geom.substring(0, idx);
2632                        idx2 = geom.lastIndexOf('-');
2633                        idx3 = geom.lastIndexOf('+');
2634                        idx = Math.max(idx2, idx3);
2635
2636                        if(idx > 0) {
2637                            X = Integer.parseInt(geom.substring(idx + 1));
2638
2639                            if(idx == idx2)
2640                                X = -X;
2641
2642                            geom = geom.substring(0, idx);
2643                        }
2644                    }
2645
2646                    idx = geom.indexOf('x');
2647
2648                    if(idx > 0) {
2649                        W = Integer.parseInt(geom.substring(0, idx));
2650                        H = Integer.parseInt(geom.substring(idx + 1));
2651                    }
2652
2653                }
2654                catch (Exception ex) {
2655                    ex.printStackTrace();
2656                }
2657            }
2658            else if("-java.version".equalsIgnoreCase(args[i])) {
2659                /* Set icon to ViewProperties.getLargeHdfIcon() */
2660                Tools.showInformation(mainWindow, "Java Version", JAVA_VER_INFO);
2661                System.exit(0);
2662            }
2663        }
2664
2665        ArrayList<File> fList = new ArrayList<>();
2666
2667        if(j >= 0) {
2668            for(int i = args.length - j; i < args.length; i++) {
2669                tmpFile = new File(args[i]);
2670                if(!tmpFile.isAbsolute())
2671                    tmpFile = new File(rootDir, args[i]);
2672                log.trace("main: filelist - file = {} ", tmpFile.getAbsolutePath());
2673                log.trace("main: filelist - add file = {} exists={} isFile={} isDir={}", tmpFile, tmpFile.exists(), tmpFile.isFile(), tmpFile.isDirectory());
2674                if(tmpFile.exists() && (tmpFile.isFile() || tmpFile.isDirectory())) {
2675                    log.trace("main: flist - add file = {}", tmpFile.getAbsolutePath());
2676                    fList.add(new File(tmpFile.getAbsolutePath()));
2677                }
2678            }
2679        }
2680
2681        final ArrayList<File> theFileList = fList;
2682        final String the_rootDir = rootDir;
2683        final String the_startDir = startDir;
2684        final int the_X = X, the_Y = Y, the_W = W, the_H = H;
2685
2686        display.syncExec(new Runnable() {
2687            @Override
2688            public void run() {
2689                HDFView app = new HDFView(the_rootDir, the_startDir);
2690
2691                // TODO: Look for a better solution to native dialog problem
2692                app.setTestState(false);
2693
2694                app.openMainWindow(theFileList, the_W, the_H, the_X, the_Y);
2695                app.runMainWindow();
2696            }
2697        });
2698    }
2699}