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