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