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