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