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