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