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