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