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