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.DataView.DataView;
076import hdf.view.DataView.DataViewFactory;
077import hdf.view.DataView.DataViewFactoryProducer;
078import hdf.view.DataView.DataViewManager;
079import hdf.view.HelpView.HelpView;
080import hdf.view.MetaDataView.MetaDataView;
081import hdf.view.TableView.TableView;
082import hdf.view.TreeView.DefaultTreeView;
083import hdf.view.TreeView.TreeView;
084import hdf.view.dialog.ImageConversionDialog;
085import hdf.view.dialog.InputDialog;
086import hdf.view.dialog.UserOptionsDialog;
087import hdf.view.dialog.UserOptionsGeneralPage;
088import hdf.view.dialog.UserOptionsHDFPage;
089import hdf.view.dialog.UserOptionsNode;
090import hdf.view.dialog.UserOptionsViewModulesPage;
091
092
093/**
094 * HDFView is the main class of this HDF visual tool. It is used to layout the
095 * graphical components of the hdfview. The major GUI components of the HDFView
096 * include Menubar, Toolbar, TreeView, ContentView, and MessageArea.
097 * <p>
098 * The HDFView is designed in such a way that it does not have direct access to
099 * the HDF library. All the HDF library access is done through HDF objects.
100 * Therefore, the HDFView package depends on the object package but not the
101 * library package. The source code of the view package (hdf.view) should
102 * be compiled with the library package (hdf.hdflib and hdf.hdf5lib).
103 *
104 * @author Jordan T. Henderson
105 * @version 2.4 //2015
106 */
107public class HDFView implements DataViewManager {
108
109    private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(HDFView.class);
110
111    private static Display             display;
112    private static Shell               mainWindow;
113
114    /* Determines whether HDFView is being executed for GUI testing */
115    private boolean                    isTesting = false;
116
117    /* The directory where HDFView is installed */
118    private String                     rootDir;
119
120    /* The current working directory */
121    private String                     currentDir;
122
123    /* The current working file */
124    private String                     currentFile = null;
125
126    /* The view properties */
127    private ViewProperties             props;
128
129    /* A list of tree view implementations. */
130    private static List<String>        treeViews;
131
132    /* A list of image view implementations. */
133    private static List<String>        imageViews;
134
135    /* A list of tree table implementations. */
136    private static List<?>             tableViews;
137
138    /* A list of metadata view implementations. */
139    private static List<?>             metaDataViews;
140
141    /* A list of palette view implementations. */
142    private static List<?>             paletteViews;
143
144    /* A list of help view implementations. */
145    private static List<?>             helpViews;
146
147    /* The list of GUI components related to HDF4 */
148    private final List<MenuItem>       h4GUIs = new Vector<>();
149
150    /* The list of GUI components related to HDF5 */
151    private final List<MenuItem>       h5GUIs = new Vector<>();
152
153    /* The list of GUI components related to editing */
154    //private final List<?>            editGUIs;
155
156    /* GUI component: the TreeView */
157    private TreeView                   treeView = null;
158
159    private static final String        HDF4_VERSION = HDFVersions.HDF4_VERSION;
160    private static final String        HDF5_VERSION = HDFVersions.HDF5_VERSION;
161    private static final String        HDFVIEW_VERSION = HDFVersions.HDFVIEW_VERSION;
162    private static final String        HDFVIEW_USERSGUIDE_URL = "https://support.hdfgroup.org/products/java/hdfview/index.html";
163    private static final String        JAVA_COMPILER = "jdk 1.7";
164    private static final String        JAVA_VER_INFO = "Compiled at " + JAVA_COMPILER + "\nRunning at " + System.getProperty("java.version");
165
166    private static final String        aboutHDFView = "HDF Viewer, " + "Version " + ViewProperties.VERSION + "\n"
167            + "For " + System.getProperty("os.name") + "\n\n"
168            + "Copyright " + '\u00a9' + " 2006-2017 The HDF Group.\n"
169            + "All rights reserved.";
170
171    /* GUI component: The toolbar for open, close, help and hdf4 and hdf5 library information */
172    private ToolBar                    toolBar;
173
174    /* GUI component: The text area for showing status messages */
175    private Text                       status;
176
177    /* GUI component: The area for quick general view */
178    private ScrolledComposite          generalArea;
179
180    /* GUI component: To add and display URLs */
181    private Combo                      url_bar;
182
183    private Button                     recentFilesButton;
184    private Button                     clearTextButton;
185
186    /* GUI component: A list of current data windows */
187    private Menu                       windowMenu;
188
189    /* GUI component: File menu on the menubar */
190    //private final Menu               fileMenu;
191
192    /* The font to be used for display text on all Controls */
193    private Font                       currentFont;
194
195    private UserOptionsDialog          userOptionDialog;
196
197    /**
198     * Constructs HDFView with a given root directory, where the HDFView is
199     * installed, and opens the given files in the viewer.
200     *
201     * @param root
202     *            the directory where the HDFView is installed.
203     */
204    public HDFView(String root) {
205        log.debug("Root is {}", root);
206
207        if (display == null || display.isDisposed()) display = new Display();
208
209        rootDir = root;
210
211        //editGUIs = new Vector<Object>();
212
213        props = new ViewProperties(rootDir);
214        try {
215            props.load();
216        }
217        catch (Exception ex) {
218            log.debug("Failed to load View Properties from {}", rootDir);
219        }
220
221        ViewProperties.loadIcons();
222
223        currentDir = ViewProperties.getWorkDir();
224        if (currentDir == null) currentDir = System.getProperty("user.home");
225
226        log.info("Current directory is {}", currentDir);
227
228        try {
229            currentFont = new Font(
230                    display,
231                    ViewProperties.getFontType(),
232                    ViewProperties.getFontSize(),
233                    SWT.NORMAL);
234        }
235        catch (Exception ex) {
236            currentFont = null;
237        }
238
239        treeViews = ViewProperties.getTreeViewList();
240        metaDataViews = ViewProperties.getMetaDataViewList();
241        tableViews = ViewProperties.getTableViewList();
242        imageViews = ViewProperties.getImageViewList();
243        paletteViews = ViewProperties.getPaletteViewList();
244        helpViews = ViewProperties.getHelpViewList();
245
246        log.debug("Constructor exit");
247    }
248
249
250    /**
251     * Creates HDFView with a given size, and opens the given files in the viewer.
252     *
253     * @param flist
254     *            a list of files to open.
255     * @param width
256     *            the width of the app in pixels
257     * @param height
258     *            the height of the app in pixels
259     * @param x
260     *            the coord x of the app in pixels
261     * @param y
262     *            the coord y of the app in pixels
263     *
264     * @return
265     *            the newly-created HDFView Shell
266     */
267    public Shell openMainWindow(List<File> flist, int width, int height, int x, int y) {
268        log.debug("openMainWindow enter");
269
270        // Initialize all GUI components
271        mainWindow = createMainWindow();
272
273        try {
274            Font font = null;
275            String fType = ViewProperties.getFontType();
276            int fSize = ViewProperties.getFontSize();
277
278            try {
279                font = new Font(display, fType, fSize, SWT.NORMAL);
280            }
281            catch (Exception ex) {
282                font = null;
283            }
284
285            if (font != null)
286                updateFont(font);
287        }
288        catch (Exception ex) {
289            log.debug("Failed to load Font properties");
290        }
291
292        // Make sure all GUI components are in place before
293        // opening any files
294        mainWindow.pack();
295
296        int nfiles = flist.size();
297        File theFile = null;
298        for (int i = 0; i < nfiles; i++) {
299            theFile = flist.get(i);
300
301            if (theFile.isFile()) {
302                currentDir = theFile.getParentFile().getAbsolutePath();
303                currentFile = theFile.getAbsolutePath();
304
305                try {
306                    treeView.openFile(currentFile, FileFormat.WRITE);
307
308                    try {
309                        url_bar.remove(currentFile);
310                    }
311                    catch (Exception ex) {}
312
313                    url_bar.add(currentFile, 0);
314                    url_bar.select(0);
315                }
316                catch (Exception ex) {
317                    showStatus(ex.toString());
318                }
319            }
320            else {
321                currentDir = theFile.getAbsolutePath();
322            }
323
324            log.info("CurrentDir is {}", currentDir);
325        }
326
327        if (FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4) == null)
328            setEnabled(h4GUIs, false);
329
330        if (FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5) == null)
331            setEnabled(h5GUIs, false);
332
333        // Set size of main window
334        // float inset = 0.17f; // for UG only.
335        float inset = 0.04f;
336        Point winDim = new Point(width, height);
337
338        // If given height and width are too small, adjust accordingly
339        if (height <= 300) {
340            winDim.y = (int) ((1 - 2 * inset) * mainWindow.getSize().y);
341        }
342
343        if (width <= 300) {
344            winDim.x = (int) (0.9 * mainWindow.getSize().y);
345        }
346
347        mainWindow.setLocation(x, y);
348        mainWindow.setSize(winDim.x + 200, winDim.y);
349
350        // Display the window
351        mainWindow.open();
352        log.debug("openMainWindow exit");
353        return mainWindow;
354    }
355
356    public void runMainWindow() {
357        log.debug("runMainWindow enter");
358
359        while(!mainWindow.isDisposed()) {
360            // ===================================================
361            // Wrap each event dispatch in an exception handler
362            // so that if any event causes an exception it does
363            // not break the main UI loop
364            // ===================================================
365            try {
366                if (!display.readAndDispatch()) {
367                    display.sleep();
368                }
369            }
370            catch (Exception e) {
371                e.printStackTrace();
372            }
373        }
374
375        if (!isTesting) display.dispose();
376        log.debug("runMainWindow exit");
377    }
378
379    /**
380     * Creates and lays out GUI components.
381     *
382     * <pre>
383     * ||=========||=============================||
384     * ||         ||                             ||
385     * ||         ||                             ||
386     * || TreeView||       ContentPane           ||
387     * ||         ||                             ||
388     * ||=========||=============================||
389     * ||            Message Area                ||
390     * ||========================================||
391     * </pre>
392     */
393    private Shell createMainWindow() {
394        // Create a new display window
395        final Shell shell = new Shell(display);
396        shell.setImage(ViewProperties.getHdfIcon());
397        shell.setFont(currentFont);
398        shell.setText("HDFView " + HDFVIEW_VERSION);
399        shell.setLayout(new GridLayout(3, false));
400        shell.addDisposeListener(new DisposeListener() {
401            @Override
402            public void widgetDisposed(DisposeEvent e) {
403                ViewProperties.setRecentFiles(new Vector<>(Arrays.asList(url_bar.getItems())));
404
405                try {
406                    props.save();
407                }
408                catch (Exception ex) {}
409
410                closeAllWindows();
411
412                // Close all open files
413                try {
414                    List<FileFormat> filelist = treeView.getCurrentFiles();
415
416                    if((filelist != null) && (filelist.size() > 0)) {
417                        Object[] files = filelist.toArray();
418
419                        for (int i = 0; i < files.length; i++) {
420                            try {
421                                treeView.closeFile((FileFormat) files[i]);
422                            }
423                            catch (Throwable ex) {
424                                continue;
425                            }
426                        }
427                    }
428                }
429                catch (Exception ex) {}
430
431                if (currentFont != null) currentFont.dispose();
432            }
433        });
434
435        createMenuBar(shell);
436        createToolbar(shell);
437        createUrlToolbar(shell);
438        createContentArea(shell);
439
440        log.info("Main Window created");
441
442        return shell;
443    }
444
445    private void createMenuBar(final Shell shell) {
446        Menu menu = new Menu(shell, SWT.BAR);
447        shell.setMenuBar(menu);
448
449        MenuItem menuItem = new MenuItem(menu, SWT.CASCADE);
450        menuItem.setText("&File");
451
452        Menu fileMenu = new Menu(menuItem);
453        menuItem.setMenu(fileMenu);
454
455        MenuItem item = new MenuItem(fileMenu, SWT.PUSH);
456        item.setText("&Open\tCtrl-O");
457        item.setAccelerator(SWT.MOD1 + 'O');
458        item.addSelectionListener(new SelectionAdapter() {
459            @Override
460            public void widgetSelected(SelectionEvent e) {
461                openLocalFile(null, -1);
462            }
463        });
464
465        item = new MenuItem(fileMenu, SWT.CASCADE);
466        item.setText("Open As");
467
468        Menu openAsMenu = new Menu(item);
469        item.setMenu(openAsMenu);
470
471        item = new MenuItem(openAsMenu, SWT.PUSH);
472        item.setText("Read-Only");
473        item.addSelectionListener(new SelectionAdapter() {
474            @Override
475            public void widgetSelected(SelectionEvent e) {
476                openLocalFile(null, FileFormat.READ);
477            }
478        });
479
480        item = new MenuItem(openAsMenu, SWT.PUSH);
481        item.setText("Read/Write");
482        item.addSelectionListener(new SelectionAdapter() {
483            @Override
484            public void widgetSelected(SelectionEvent e) {
485                openLocalFile(null, FileFormat.WRITE);
486            }
487        });
488
489        new MenuItem(fileMenu, SWT.SEPARATOR);
490
491        MenuItem fileNewMenu = new MenuItem(fileMenu, SWT.CASCADE);
492        fileNewMenu.setText("New");
493
494        Menu newMenu = new Menu(fileNewMenu);
495        fileNewMenu.setMenu(newMenu);
496
497        item = new MenuItem(newMenu, SWT.PUSH);
498        item.setText("HDF&4");
499        h4GUIs.add(item);
500        item.addSelectionListener(new SelectionAdapter() {
501            @Override
502            public void widgetSelected(SelectionEvent e) {
503                if (currentDir != null) {
504                    currentDir += File.separator;
505                }
506                else {
507                    currentDir = "";
508                }
509
510                String filename = null;
511
512                if (!isTesting) {
513                    FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
514                    fChooser.setFileName(Tools.checkNewFile(currentDir, ".hdf").getName());
515
516                    DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF4();
517                    fChooser.setFilterExtensions(new String[] {filter.getExtensions()});
518                    fChooser.setFilterNames(new String[] {filter.getDescription()});
519                    fChooser.setFilterIndex(0);
520
521                    filename = fChooser.open();
522                }
523                else {
524                    // Prepend test file directory to filename
525                    filename = currentDir.concat(new InputDialog(mainWindow, "Enter a file name", "").open());
526                }
527
528                if(filename == null) return;
529
530                try {
531                    log.trace("HDFView create hdf4 file");
532                    FileFormat theFile = Tools.createNewFile(filename, currentDir,
533                            FileFormat.FILE_TYPE_HDF4, getTreeView().getCurrentFiles());
534
535                    if (theFile == null) return;
536
537                    currentDir = theFile.getParent();
538                }
539                catch (Exception ex) {
540                    Tools.showError(mainWindow, "New", ex.getMessage());
541                    return;
542                }
543
544                try {
545                    treeView.openFile(filename, FileFormat.WRITE);
546                    currentFile = filename;
547
548                    try {
549                        url_bar.remove(filename);
550                    }
551                    catch (Exception ex) {}
552
553                    url_bar.add(filename, 0);
554                    url_bar.select(0);
555                }
556                catch (Exception ex) {
557                    display.beep();
558                    Tools.showError(mainWindow, "New", ex.getMessage() + "\n" + filename);
559                }
560            }
561        });
562
563        item = new MenuItem(newMenu, SWT.PUSH);
564        item.setText("HDF&5");
565        h5GUIs.add(item);
566        item.addSelectionListener(new SelectionAdapter() {
567            @Override
568            public void widgetSelected(SelectionEvent e) {
569                if (currentDir != null) {
570                    currentDir += File.separator;
571                }
572                else {
573                    currentDir = "";
574                }
575
576                String filename = null;
577
578                if (!isTesting) {
579                    FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
580                    fChooser.setFileName(Tools.checkNewFile(currentDir, ".h5").getName());
581
582                    DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF5();
583                    fChooser.setFilterExtensions(new String[] {filter.getExtensions()});
584                    fChooser.setFilterNames(new String[] {filter.getDescription()});
585                    fChooser.setFilterIndex(0);
586
587                    filename = fChooser.open();
588                }
589                else {
590                    // Prepend test file directory to filename
591                    filename = currentDir.concat(new InputDialog(mainWindow, "Enter a file name", "").open());
592                }
593
594                if(filename == null) return;
595
596                try {
597                    log.trace("HDFView create hdf5 file");
598                    FileFormat theFile = Tools.createNewFile(filename, currentDir,
599                            FileFormat.FILE_TYPE_HDF5, getTreeView().getCurrentFiles());
600
601                    if (theFile == null) return;
602
603                    currentDir = theFile.getParent();
604                }
605                catch (Exception ex) {
606                    Tools.showError(mainWindow, "New", ex.getMessage());
607                    return;
608                }
609
610                try {
611                    treeView.openFile(filename, FileFormat.WRITE);
612                    currentFile = filename;
613
614                    try {
615                        url_bar.remove(filename);
616                    }
617                    catch (Exception ex) {}
618
619                    url_bar.add(filename, 0);
620                    url_bar.select(0);
621                }
622                catch (Exception ex) {
623                    display.beep();
624                    Tools.showError(mainWindow, "New", ex.getMessage() + "\n" + filename);
625                }
626            }
627        });
628
629        new MenuItem(fileMenu, SWT.SEPARATOR);
630
631        item = new MenuItem(fileMenu, SWT.PUSH);
632        item.setText("&Close");
633        item.addSelectionListener(new SelectionAdapter() {
634            @Override
635            public void widgetSelected(SelectionEvent e) {
636                closeFile(treeView.getSelectedFile());
637            }
638        });
639
640        item = new MenuItem(fileMenu, SWT.PUSH);
641        item.setText("Close &All");
642        item.addSelectionListener(new SelectionAdapter() {
643            @Override
644            public void widgetSelected(SelectionEvent e) {
645                closeAllWindows();
646
647                List<FileFormat> files = treeView.getCurrentFiles();
648                while (!files.isEmpty()) {
649                    try {
650                        treeView.closeFile(files.get(0));
651                    }
652                    catch (Exception ex) {}
653                }
654
655                currentFile = null;
656
657                for (Control control : generalArea.getChildren()) control.dispose();
658                generalArea.setContent(null);
659
660                url_bar.setText("");
661            }
662        });
663
664        new MenuItem(fileMenu, SWT.SEPARATOR);
665
666        item = new MenuItem(fileMenu, SWT.PUSH);
667        item.setText("&Save");
668        item.addSelectionListener(new SelectionAdapter() {
669            @Override
670            public void widgetSelected(SelectionEvent e) {
671                if (treeView.getCurrentFiles().size() <= 0) {
672                    Tools.showError(mainWindow, "Save", "No files currently open.");
673                    return;
674                }
675
676                if (treeView.getSelectedFile() == null) {
677                    Tools.showError(mainWindow, "Save", "No files currently selected.");
678                    return;
679                }
680
681                // Save what has been changed in memory into file
682                writeDataToFile(treeView.getSelectedFile());
683            }
684        });
685
686        item = new MenuItem(fileMenu, SWT.PUSH);
687        item.setText("S&ave As");
688        item.addSelectionListener(new SelectionAdapter() {
689            @Override
690            public void widgetSelected(SelectionEvent e) {
691                if (treeView.getCurrentFiles().size() <= 0) {
692                    Tools.showError(mainWindow, "Save", "No files currently open.");
693                    return;
694                }
695
696                if (treeView.getSelectedFile() == null) {
697                    Tools.showError(mainWindow, "Save", "No files currently selected.");
698                    return;
699                }
700
701                try {
702                    treeView.saveFile(treeView.getSelectedFile());
703                }
704                catch (Exception ex) {
705                    display.beep();
706                    Tools.showError(mainWindow, "Save", ex.getMessage());
707                }
708            }
709        });
710
711        new MenuItem(fileMenu, SWT.SEPARATOR);
712
713        item = new MenuItem(fileMenu, SWT.PUSH);
714        item.setText("E&xit\tCtrl-Q");
715        item.setAccelerator(SWT.MOD1 + 'Q');
716        item.addSelectionListener(new SelectionAdapter() {
717            @Override
718            public void widgetSelected(SelectionEvent e) {
719                mainWindow.dispose();
720            }
721        });
722
723        menuItem = new MenuItem(menu, SWT.CASCADE);
724        menuItem.setText("&Window");
725
726        windowMenu = new Menu(menuItem);
727        menuItem.setMenu(windowMenu);
728
729        item = new MenuItem(windowMenu, SWT.PUSH);
730        item.setText("&Cascade");
731        item.addSelectionListener(new SelectionAdapter() {
732            @Override
733            public void widgetSelected(SelectionEvent e) {
734                cascadeWindows();
735            }
736        });
737
738        item = new MenuItem(windowMenu, SWT.PUSH);
739        item.setText("&Tile");
740        item.addSelectionListener(new SelectionAdapter() {
741            @Override
742            public void widgetSelected(SelectionEvent e) {
743                tileWindows();
744            }
745        });
746
747        new MenuItem(windowMenu, SWT.SEPARATOR);
748
749        item = new MenuItem(windowMenu, SWT.PUSH);
750        item.setText("Close &All");
751        item.addSelectionListener(new SelectionAdapter() {
752            @Override
753            public void widgetSelected(SelectionEvent e) {
754                closeAllWindows();
755            }
756        });
757
758        new MenuItem(windowMenu, SWT.SEPARATOR);
759
760        menuItem = new MenuItem(menu, SWT.CASCADE);
761        menuItem.setText("&Tools");
762
763        Menu toolsMenu = new Menu(menuItem);
764        menuItem.setMenu(toolsMenu);
765
766        MenuItem convertMenuItem = new MenuItem(toolsMenu, SWT.CASCADE);
767        convertMenuItem.setText("Convert Image To");
768
769        Menu convertMenu = new Menu(convertMenuItem);
770        convertMenuItem.setMenu(convertMenu);
771
772        item = new MenuItem(convertMenu, SWT.PUSH);
773        item.setText("HDF4");
774        item.addSelectionListener(new SelectionAdapter() {
775            @Override
776            public void widgetSelected(SelectionEvent e) {
777                convertFile(Tools.FILE_TYPE_IMAGE, FileFormat.FILE_TYPE_HDF4);
778            }
779        });
780        h4GUIs.add(item);
781
782        item = new MenuItem(convertMenu, SWT.PUSH);
783        item.setText("HDF5");
784        item.addSelectionListener(new SelectionAdapter() {
785            @Override
786            public void widgetSelected(SelectionEvent e) {
787                convertFile(Tools.FILE_TYPE_IMAGE, FileFormat.FILE_TYPE_HDF5);
788            }
789        });
790        h5GUIs.add(item);
791
792        new MenuItem(toolsMenu, SWT.SEPARATOR);
793
794        item = new MenuItem(toolsMenu, SWT.PUSH);
795        item.setText("User &Options");
796        item.addSelectionListener(new SelectionAdapter() {
797            @Override
798            public void widgetSelected(SelectionEvent e) {
799                // Create the preference manager
800                PreferenceManager mgr = new PreferenceManager();
801
802                // Create the nodes
803                UserOptionsNode one = new UserOptionsNode("general", new UserOptionsGeneralPage());
804                UserOptionsNode two = new UserOptionsNode("hdf", new UserOptionsHDFPage());
805                UserOptionsNode three = new UserOptionsNode("modules", new UserOptionsViewModulesPage());
806
807                // Add the nodes
808                mgr.addToRoot(one);
809                mgr.addToRoot(two);
810                mgr.addToRoot(three);
811
812                // Create the preferences dialog
813                userOptionDialog = new UserOptionsDialog(shell, mgr, rootDir);
814
815                // Set the preference store
816                userOptionDialog.setPreferenceStore(props);
817                userOptionDialog.create();
818
819                // Open the dialog
820                userOptionDialog.open();
821
822                //if (userOptionDialog.isWorkDirChanged())
823                currentDir = ViewProperties.getWorkDir();
824
825                //if (userOptionDialog.isFontChanged()) {
826                Font font = null;
827
828                try {
829                    font = new Font(display, ViewProperties.getFontType(), ViewProperties.getFontSize(), SWT.NORMAL);
830                }
831                catch (Exception ex) {
832                    font = null;
833                }
834
835                updateFont(font);
836                //}
837            }
838        });
839
840        new MenuItem(toolsMenu, SWT.SEPARATOR);
841
842        item = new MenuItem(toolsMenu, SWT.PUSH);
843        item.setText("&Register File Format");
844        item.addSelectionListener(new SelectionAdapter() {
845            @Override
846            public void widgetSelected(SelectionEvent e) {
847                registerFileFormat();
848            }
849        });
850
851        item = new MenuItem(toolsMenu, SWT.PUSH);
852        item.setText("&Unregister File Format");
853        item.addSelectionListener(new SelectionAdapter() {
854            @Override
855            public void widgetSelected(SelectionEvent e) {
856                unregisterFileFormat();
857            }
858        });
859
860        menuItem = new MenuItem(menu, SWT.CASCADE);
861        menuItem.setText("&Help");
862
863        Menu helpMenu = new Menu(menuItem);
864        menuItem.setMenu(helpMenu);
865
866        item = new MenuItem(helpMenu, SWT.PUSH);
867        item.setText("&User's Guide");
868        item.addSelectionListener(new SelectionAdapter() {
869            @Override
870            public void widgetSelected(SelectionEvent e) {
871                org.eclipse.swt.program.Program.launch(HDFVIEW_USERSGUIDE_URL);
872            }
873        });
874
875        if ((helpViews != null) && (helpViews.size() > 0)) {
876            int n = helpViews.size();
877            for (int i = 0; i < n; i++) {
878                HelpView theView = (HelpView) helpViews.get(i);
879                item = new MenuItem(helpMenu, SWT.PUSH);
880                item.setText(theView.getLabel());
881                //item.setActionCommand(theView.getActionCommand());
882            }
883        }
884
885        new MenuItem(helpMenu, SWT.SEPARATOR);
886
887        item = new MenuItem(helpMenu, SWT.PUSH);
888        item.setText("HDF&4 Library Version");
889        h4GUIs.add(item);
890        item.addSelectionListener(new SelectionAdapter() {
891            @Override
892            public void widgetSelected(SelectionEvent e) {
893                new LibraryVersionDialog(shell, FileFormat.FILE_TYPE_HDF4).open();
894            }
895        });
896
897        item = new MenuItem(helpMenu, SWT.PUSH);
898        item.setText("HDF&5 Library Version");
899        h5GUIs.add(item);
900        item.addSelectionListener(new SelectionAdapter() {
901            @Override
902            public void widgetSelected(SelectionEvent e) {
903                new LibraryVersionDialog(shell, FileFormat.FILE_TYPE_HDF5).open();
904            }
905        });
906
907        item = new MenuItem(helpMenu, SWT.PUSH);
908        item.setText("&Java Version");
909        item.addSelectionListener(new SelectionAdapter() {
910            @Override
911            public void widgetSelected(SelectionEvent e) {
912                new JavaVersionDialog(mainWindow).open();
913            }
914        });
915
916        new MenuItem(helpMenu, SWT.SEPARATOR);
917
918        item = new MenuItem(helpMenu, SWT.PUSH);
919        item.setText("Supported Fi&le Formats");
920        item.addSelectionListener(new SelectionAdapter() {
921            @Override
922            public void widgetSelected(SelectionEvent e) {
923                new SupportedFileFormatsDialog(mainWindow).open();
924            }
925        });
926
927        new MenuItem(helpMenu, SWT.SEPARATOR);
928
929        item = new MenuItem(helpMenu, SWT.PUSH);
930        item.setText("&About...");
931        item.addSelectionListener(new SelectionAdapter() {
932            @Override
933            public void widgetSelected(SelectionEvent e) {
934                new AboutDialog(mainWindow).open();
935            }
936        });
937
938        setEnabled(Arrays.asList(windowMenu.getItems()), false);
939
940        log.info("Menubar created");
941    }
942
943    private void createToolbar(final Shell shell) {
944        toolBar = new ToolBar(shell, SWT.HORIZONTAL | SWT.RIGHT);
945        toolBar.setFont(Display.getCurrent().getSystemFont());
946        toolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1));
947
948        ToolItem openItem = new ToolItem(toolBar, SWT.PUSH);
949        openItem.setToolTipText("Open");
950        openItem.setImage(ViewProperties.getFileopenIcon());
951        openItem.addSelectionListener(new SelectionAdapter() {
952            @Override
953            public void widgetSelected(SelectionEvent e) {
954                openLocalFile(null, -1);
955            }
956        });
957
958        new ToolItem(toolBar, SWT.SEPARATOR).setWidth(4);
959
960        ToolItem closeItem = new ToolItem(toolBar, SWT.PUSH);
961        closeItem.setImage(ViewProperties.getFilecloseIcon());
962        closeItem.setToolTipText("Close");
963        closeItem.addSelectionListener(new SelectionAdapter() {
964            @Override
965            public void widgetSelected(SelectionEvent e) {
966                closeFile(treeView.getSelectedFile());
967            }
968        });
969
970        new ToolItem(toolBar, SWT.SEPARATOR).setWidth(20);
971
972        ToolItem helpItem = new ToolItem(toolBar, SWT.PUSH);
973        helpItem.setImage(ViewProperties.getHelpIcon());
974        helpItem.setToolTipText("Help");
975        helpItem.addSelectionListener(new SelectionAdapter() {
976            @Override
977            public void widgetSelected(SelectionEvent e) {
978                String ugPath = ViewProperties.getUsersGuide();
979
980                if(ugPath == null || !ugPath.startsWith("http://")) {
981                    String sep = File.separator;
982                    File tmpFile = new File(ugPath);
983
984                    if(!(tmpFile.exists())) {
985                        ugPath = rootDir + sep + "UsersGuide" + sep + "index.html";
986                        tmpFile = new File(ugPath);
987
988                        if(!(tmpFile.exists())) {
989                            ugPath = HDFVIEW_USERSGUIDE_URL;
990                        }
991
992                        ViewProperties.setUsersGuide(ugPath);
993                    }
994                }
995
996                try {
997                    org.eclipse.swt.program.Program.launch(ugPath);
998                }
999                catch (Exception ex) {
1000                    Tools.showError(shell, "Help", ex.getMessage());
1001                }
1002            }
1003        });
1004
1005        new ToolItem(toolBar, SWT.SEPARATOR).setWidth(4);
1006
1007        ToolItem hdf4Item = new ToolItem(toolBar, SWT.PUSH);
1008        hdf4Item.setImage(ViewProperties.getH4Icon());
1009        hdf4Item.setToolTipText("HDF4 Library Version");
1010        hdf4Item.addSelectionListener(new SelectionAdapter() {
1011            @Override
1012            public void widgetSelected(SelectionEvent e) {
1013                new LibraryVersionDialog(shell, FileFormat.FILE_TYPE_HDF4).open();
1014            }
1015        });
1016
1017        if(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4) == null) {
1018            hdf4Item.setEnabled(false);
1019        }
1020
1021        new ToolItem(toolBar, SWT.SEPARATOR).setWidth(4);
1022
1023        ToolItem hdf5Item = new ToolItem(toolBar, SWT.PUSH);
1024        hdf5Item.setImage(ViewProperties.getH5Icon());
1025        hdf5Item.setToolTipText("HDF5 Library Version");
1026        hdf5Item.addSelectionListener(new SelectionAdapter() {
1027            @Override
1028            public void widgetSelected(SelectionEvent e) {
1029                new LibraryVersionDialog(shell, FileFormat.FILE_TYPE_HDF5).open();
1030            }
1031        });
1032
1033        if(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5) == null) {
1034            hdf5Item.setEnabled(false);
1035        }
1036
1037        // Make the toolbar as wide as the window and as
1038        // tall as the buttons
1039        toolBar.setSize(shell.getClientArea().width, openItem.getBounds().height);
1040        toolBar.setLocation(0, 0);
1041
1042        log.info("Toolbar created");
1043    }
1044
1045    private void createUrlToolbar(final Shell shell) {
1046        // Recent Files button
1047        recentFilesButton = new Button(shell, SWT.PUSH);
1048        recentFilesButton.setFont(currentFont);
1049        recentFilesButton.setText("Recent Files");
1050        recentFilesButton.setToolTipText("List of recent files");
1051        recentFilesButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
1052        recentFilesButton.addSelectionListener(new SelectionAdapter() {
1053            @Override
1054            public void widgetSelected(SelectionEvent e) {
1055                url_bar.setListVisible(true);
1056            }
1057        });
1058
1059        // Recent files combo box
1060        url_bar = new Combo(shell, SWT.BORDER | SWT.SINGLE);
1061        url_bar.setFont(currentFont);
1062        url_bar.setItems(ViewProperties.getMRF().toArray(new String[0]));
1063        url_bar.setVisibleItemCount(ViewProperties.MAX_RECENT_FILES);
1064        url_bar.deselectAll();
1065        url_bar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
1066        url_bar.addKeyListener(new KeyAdapter() {
1067            @Override
1068            public void keyPressed(KeyEvent e) {
1069                if(e.keyCode == SWT.CR) {
1070                    String filename = url_bar.getText();
1071                    if (filename == null || filename.length() < 1 || filename.equals(currentFile)) {
1072                        return;
1073                    }
1074
1075                    if (!(filename.startsWith("http://") || filename.startsWith("https://") || filename.startsWith("ftp://"))) {
1076                        openLocalFile(filename, -1);
1077                    }
1078                    else {
1079                        String remoteFile = openRemoteFile(filename);
1080
1081                        if (remoteFile != null) openLocalFile(remoteFile, -1);
1082                    }
1083                }
1084            }
1085        });
1086        url_bar.addSelectionListener(new SelectionAdapter() {
1087            @Override
1088            public void widgetSelected(SelectionEvent e) {
1089                String filename = url_bar.getText();
1090                if (filename == null || filename.length() < 1 || filename.equals(currentFile)) {
1091                    return;
1092                }
1093
1094                if (!(filename.startsWith("http://") || filename.startsWith("https://") || filename.startsWith("ftp://"))) {
1095                    openLocalFile(filename, -1);
1096                }
1097                else {
1098                    String remoteFile = openRemoteFile(filename);
1099
1100                    if (remoteFile != null) openLocalFile(remoteFile, -1);
1101                }
1102            }
1103        });
1104
1105        clearTextButton = new Button(shell, SWT.PUSH);
1106        clearTextButton.setToolTipText("Clear current selection");
1107        clearTextButton.setFont(currentFont);
1108        clearTextButton.setText("Clear Text");
1109        clearTextButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
1110        clearTextButton.addSelectionListener(new SelectionAdapter() {
1111            @Override
1112            public void widgetSelected(SelectionEvent e) {
1113                url_bar.setText("");
1114                url_bar.deselectAll();
1115            }
1116        });
1117
1118        log.info("URL Toolbar created");
1119    }
1120
1121    private void createContentArea(final Shell shell) {
1122        SashForm content = new SashForm(shell, SWT.VERTICAL);
1123        content.setSashWidth(10);
1124        content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1));
1125
1126        // Add Data content area and Status Area to main window
1127        Composite container = new Composite(content, SWT.NONE);
1128        container.setLayout(new FillLayout());
1129
1130        Composite statusArea = new Composite(content, SWT.NONE);
1131        statusArea.setLayout(new FillLayout(SWT.HORIZONTAL));
1132
1133        final SashForm contentArea = new SashForm(container, SWT.HORIZONTAL);
1134        contentArea.setSashWidth(10);
1135
1136        // Add TreeView and DataView to content area pane
1137        ScrolledComposite treeArea = new ScrolledComposite(contentArea, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
1138        treeArea.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
1139        treeArea.setExpandHorizontal(true);
1140        treeArea.setExpandVertical(true);
1141
1142        generalArea = new ScrolledComposite(contentArea, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
1143        generalArea.setExpandHorizontal(true);
1144        generalArea.setExpandVertical(true);
1145        generalArea.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
1146        generalArea.setMinHeight(contentArea.getSize().y - 2);
1147
1148        // Create status area for displaying messages and metadata
1149        status = new Text(statusArea, SWT.V_SCROLL | SWT.MULTI | SWT.BORDER);
1150        status.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
1151        status.setEditable(false);
1152        status.setFont(currentFont);
1153
1154        contentArea.addListener(SWT.Resize, new Listener() {
1155            @Override
1156            public void handleEvent(Event arg0) {
1157                generalArea.setMinHeight(contentArea.getSize().y - 2);
1158            }
1159        });
1160
1161        // Add drag and drop support for opening files
1162        DropTarget target = new DropTarget(treeArea, DND.DROP_COPY);
1163        final FileTransfer fileTransfer = FileTransfer.getInstance();
1164        target.setTransfer(new Transfer[] { fileTransfer });
1165        target.addDropListener(new DropTargetListener() {
1166            @Override
1167            public void dragEnter(DropTargetEvent e) {
1168                e.detail = DND.DROP_COPY;
1169            }
1170            @Override
1171            public void dragOver(DropTargetEvent e) {}
1172            @Override
1173            public void dragOperationChanged(DropTargetEvent e) { }
1174            @Override
1175            public void dragLeave(DropTargetEvent e) {}
1176            @Override
1177            public void dropAccept(DropTargetEvent e) {}
1178            @Override
1179            public void drop(DropTargetEvent e) {
1180                if (fileTransfer.isSupportedType(e.currentDataType)) {
1181                    String[] files = (String[]) e.data;
1182                    for (int i = 0; i < files.length; i++) {
1183                        openLocalFile(files[i], -1);
1184                    }
1185                }
1186            }
1187        });
1188
1189        showStatus("HDFView root - " + rootDir);
1190        showStatus("User property file - " + ViewProperties.getPropertyFile());
1191
1192        content.setWeights(new int[] { 9, 1 });
1193        contentArea.setWeights(new int[] { 1, 3 });
1194
1195        log.trace("createContentArea(): load TreeView");
1196
1197        DataViewFactory treeViewFactory = null;
1198        try {
1199            treeViewFactory = DataViewFactoryProducer.getFactory(DataViewType.TREEVIEW);
1200        }
1201        catch (Exception ex) {
1202            log.debug("createContentArea(): error occurred while instantiating TreeView factory class", ex);
1203            this.showStatus("Error occurred while instantiating TreeView factory class - see log for more info");
1204            return;
1205        }
1206
1207        if (treeViewFactory == null) {
1208            log.debug("createContentArea(): TreeView factory is null");
1209            return;
1210        }
1211
1212        try {
1213            treeView = treeViewFactory.getTreeView(treeArea, this);
1214
1215            if (treeView == null) {
1216                log.debug("createContentArea(): error occurred while instantiating TreeView class");
1217                this.showStatus("Error occurred while instantiating TreeView class - see log for more info");
1218                return;
1219            }
1220        }
1221        catch (ClassNotFoundException ex) {
1222            log.debug("createContentArea(): no suitable TreeView class found");
1223            this.showStatus("Unable to find suitable TreeView class");
1224            return;
1225        }
1226
1227        treeArea.setContent(treeView.getTree());
1228
1229        log.info("Content Area created");
1230    }
1231
1232    /**
1233     * @return a list of treeview implementations.
1234     */
1235    public static final List<String> getListOfTreeViews() {
1236        return treeViews;
1237    }
1238
1239    /**
1240     * @return a list of imageview implementations.
1241     */
1242    public static final List<String> getListOfImageViews() {
1243        return imageViews;
1244    }
1245
1246    /**
1247     * @return a list of tableview implementations.
1248     */
1249    public static final List<?> getListOfTableViews() {
1250        return tableViews;
1251    }
1252
1253    /**
1254     * @return a list of metaDataview implementations.
1255     */
1256    public static final List<?> getListOfMetaDataViews() {
1257        return metaDataViews;
1258    }
1259
1260    /**
1261     * @return a list of paletteview implementations.
1262     */
1263    public static final List<?> getListOfPaletteViews() {
1264        return paletteViews;
1265    }
1266
1267    @Override
1268    public TreeView getTreeView() {
1269        return treeView;
1270    }
1271
1272    public Combo getUrlBar() {
1273        return url_bar;
1274    }
1275
1276    /**
1277     * Display feedback message.
1278     *
1279     * @param msg
1280     *            the message to display.
1281     */
1282    @Override
1283    public void showStatus(String msg) {
1284        if (status == null) {
1285            log.debug("showStatus(): status area is null");
1286            return;
1287        }
1288
1289        status.append(msg);
1290        status.append("\n");
1291    }
1292
1293    public void showMetaData(final HObject obj) {
1294        for (Control control : generalArea.getChildren()) control.dispose();
1295        generalArea.setContent(null);
1296
1297        if (obj == null) return;
1298
1299        log.trace("showMetaData(): start");
1300
1301        DataViewFactory metaDataViewFactory = null;
1302        try {
1303            metaDataViewFactory = DataViewFactoryProducer.getFactory(DataViewType.METADATA);
1304        }
1305        catch (Exception ex) {
1306            log.debug("showMetaData(): error occurred while instantiating MetaDataView factory class", ex);
1307            this.showStatus("Error occurred while instantiating MetaDataView factory class - see log for more info");
1308            return;
1309        }
1310
1311        if (metaDataViewFactory == null) {
1312            log.debug("showMetaData(): MetaDataView factory is null");
1313            return;
1314        }
1315
1316        MetaDataView theView;
1317        try {
1318            theView = metaDataViewFactory.getMetaDataView(generalArea, this, obj);
1319
1320            if (theView == null) {
1321                log.debug("showMetaData(): error occurred while instantiating MetaDataView class");
1322                this.showStatus("Error occurred while instantiating MetaDataView class - see log for more info");
1323                return;
1324            }
1325        }
1326        catch (ClassNotFoundException ex) {
1327            log.debug("showMetaData(): no suitable MetaDataView class found");
1328            this.showStatus("Unable to find suitable MetaDataView class");
1329            return;
1330        }
1331
1332        log.trace("showMetaData(): finish");
1333    }
1334
1335    public void closeFile(FileFormat theFile) {
1336        if (theFile == null) {
1337            display.beep();
1338            Tools.showError(mainWindow, "Close", "Select a file to close");
1339            return;
1340        }
1341
1342        // Close all the data windows of this file
1343        Shell[] views = display.getShells();
1344        if (views != null) {
1345            for (int i = 0; i < views.length; i++) {
1346                Object shellData = views[i].getData();
1347
1348                if (!(shellData instanceof DataView)) continue;
1349
1350                if ((DataView) shellData != null) {
1351                    HObject obj = ((DataView) shellData).getDataObject();
1352
1353                    if (obj == null || obj.getFileFormat() == null) continue;
1354
1355                    if (obj.getFileFormat().equals(theFile)) {
1356                        views[i].dispose();
1357                        views[i] = null;
1358                    }
1359                }
1360            }
1361        }
1362
1363        int index = url_bar.getSelectionIndex();
1364        if (index >= 0) {
1365            String fName = url_bar.getItem(url_bar.getSelectionIndex());
1366            if (theFile.getFilePath().equals(fName)) {
1367                currentFile = null;
1368                url_bar.setText("");
1369            }
1370        }
1371
1372        try {
1373            treeView.closeFile(theFile);
1374        }
1375        catch (Exception ex) {}
1376
1377        theFile = null;
1378
1379        for (Control control : generalArea.getChildren()) control.dispose();
1380        generalArea.setContent(null);
1381
1382        System.gc();
1383    }
1384
1385    /**
1386     * Write the change of data to the given file.
1387     *
1388     * @param theFile
1389     *           The file to be updated.
1390     */
1391    public void writeDataToFile(FileFormat theFile) {
1392        try {
1393            Shell[] openShells = display.getShells();
1394
1395            if (openShells != null) {
1396                for (int i = 0; i < openShells.length; i++) {
1397                    DataView theView = (DataView) openShells[i].getData();
1398
1399                    if (theView instanceof TableView) {
1400                        TableView tableView = (TableView) theView;
1401                        FileFormat file = tableView.getDataObject().getFileFormat();
1402                        if (file.equals(theFile)) tableView.updateValueInFile();
1403                    }
1404                }
1405            }
1406        }
1407        catch (Exception ex) {
1408            display.beep();
1409            Tools.showError(mainWindow, "Save", ex.getMessage());
1410        }
1411    }
1412
1413    @Override
1414    public void addDataView(DataView dataView) {
1415        if (dataView == null || dataView instanceof MetaDataView) {
1416            return;
1417        }
1418
1419        // Check if the data content is already displayed
1420        Shell[] shellList = display.getShells();
1421        if (shellList != null) {
1422            for (int i = 0; i < shellList.length; i++) {
1423                if (dataView.equals(shellList[i].getData())
1424                        && shellList[i].isVisible()) {
1425                    showWindow(shellList[i]);
1426                    return;
1427                }
1428            }
1429        }
1430
1431        // First window being added
1432        if (shellList.length == 2) {
1433            setEnabled(Arrays.asList(windowMenu.getItems()), true);
1434        }
1435
1436        HObject obj = dataView.getDataObject();
1437        String fullPath = ((obj.getPath() == null) ? "" : obj.getPath())
1438                + ((obj.getName() == null) ? "" : obj.getName());
1439
1440        MenuItem item = new MenuItem(windowMenu, SWT.PUSH);
1441        item.setText(fullPath);
1442        item.addSelectionListener(new SelectionAdapter() {
1443            @Override
1444            public void widgetSelected(SelectionEvent e) {
1445                Shell[] sList = display.getShells();
1446
1447                for (int i = 0; i < sList.length; i++) {
1448                    DataView view = (DataView) sList[i].getData();
1449
1450                    if (view != null) {
1451                        HObject obj = view.getDataObject();
1452
1453                        if (obj.getFullName().equals(((MenuItem) e.widget).getText())) {
1454                            showWindow(sList[i]);
1455                        }
1456                    }
1457                }
1458            }
1459        });
1460
1461        mainWindow.setCursor(null);
1462    }
1463
1464    @Override
1465    public void removeDataView(DataView dataView) {
1466        if (mainWindow.isDisposed()) return;
1467
1468        HObject obj = dataView.getDataObject();
1469        if (obj == null) return;
1470
1471        MenuItem[] items = windowMenu.getItems();
1472        for (int i = 0; i < items.length; i++) {
1473            if(items[i].getText().equals(obj.getFullName())) {
1474                items[i].dispose();
1475            }
1476        }
1477
1478        // Last window being closed
1479        if (display.getShells().length == 2) {
1480            for (MenuItem item : windowMenu.getItems()) item.setEnabled(false);
1481        }
1482    }
1483
1484    @Override
1485    public DataView getDataView(HObject dataObject) {
1486        Shell[] openShells = display.getShells();
1487        DataView view = null;
1488        HObject currentObj = null;
1489        FileFormat currentFile = null;
1490
1491        for (int i = 0; i < openShells.length; i++) {
1492            view = (DataView) openShells[i].getData();
1493
1494            if (view != null) {
1495                currentObj = view.getDataObject();
1496                if (currentObj == null) continue;
1497
1498                currentFile = currentObj.getFileFormat();
1499
1500                if (currentObj.equals(dataObject) && currentFile.equals(dataObject.getFileFormat()))
1501                    return view;
1502            }
1503        }
1504
1505        return null;
1506    }
1507
1508    /**
1509     * Set the testing state that determines if HDFView
1510     * is being executed for GUI testing.
1511     *
1512     * @param testing
1513     *           Provides SWTBot native dialog compatibility
1514     *           workarounds if set to true.
1515     */
1516    public void setTestState(boolean testing) {
1517        isTesting = testing;
1518        currentDir = rootDir;
1519    }
1520
1521    public boolean getTestState() {
1522        return isTesting;
1523    }
1524
1525    /**
1526     * Set default UI fonts.
1527     */
1528    private void updateFont(Font font) {
1529        if (currentFont != null) currentFont.dispose();
1530
1531        currentFont = font;
1532
1533        mainWindow.setFont(font);
1534        recentFilesButton.setFont(font);
1535        url_bar.setFont(font);
1536        clearTextButton.setFont(font);
1537        status.setFont(font);
1538
1539        // On certain platforms the url_bar items don't update their size after
1540        // a font change. Removing and replacing them fixes this.
1541        for (String item : url_bar.getItems()) {
1542            url_bar.remove(item);
1543            url_bar.add(item);
1544        }
1545
1546        if (treeView.getSelectedFile() != null) {
1547            url_bar.select(0);
1548        }
1549
1550        if (treeView instanceof DefaultTreeView)
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                try {
1695                    treeView.openFile(filename, accessMode);
1696                }
1697                catch (Throwable ex) {
1698                    try {
1699                        treeView.openFile(filename, FileFormat.READ);
1700                    }
1701                    catch (Throwable ex2) {
1702                        display.beep();
1703                        url_bar.deselectAll();
1704                        Tools.showError(mainWindow, "Open", "Failed to open file " + filename + "\n" + ex2);
1705                        currentFile = null;
1706                    }
1707                }
1708            }
1709
1710            try {
1711                url_bar.remove(filename);
1712            }
1713            catch (Exception ex) {}
1714
1715            url_bar.add(filename, 0);
1716            url_bar.select(0);
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}