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