    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.TreeView;
    016
    017import java.io.BufferedInputStream;
    018import java.io.BufferedOutputStream;
    019import java.io.File;
    020import java.io.FileInputStream;
    021import java.io.FileNotFoundException;
    022import java.io.FileOutputStream;
    023import java.io.Serializable;
    024import java.lang.reflect.Method;
    025import java.util.ArrayList;
    026import java.util.Arrays;
    027import java.util.BitSet;
    028import java.util.Enumeration;
    029import java.util.HashMap;
    030import java.util.Iterator;
    031import java.util.LinkedList;
    032import java.util.List;
    033import java.util.Queue;
    034
    035import org.slf4j.Logger;
    036import org.slf4j.LoggerFactory;
    037
    038import org.eclipse.swt.SWT;
    039import org.eclipse.swt.events.DisposeEvent;
    040import org.eclipse.swt.events.DisposeListener;
    041import org.eclipse.swt.events.KeyAdapter;
    042import org.eclipse.swt.events.KeyEvent;
    043import org.eclipse.swt.events.MenuAdapter;
    044import org.eclipse.swt.events.MenuDetectEvent;
    045import org.eclipse.swt.events.MenuDetectListener;
    046import org.eclipse.swt.events.MenuEvent;
    047import org.eclipse.swt.events.MouseAdapter;
    048import org.eclipse.swt.events.MouseEvent;
    049import org.eclipse.swt.events.SelectionAdapter;
    050import org.eclipse.swt.events.SelectionEvent;
    051import org.eclipse.swt.graphics.Font;
    052import org.eclipse.swt.graphics.Image;
    053import org.eclipse.swt.graphics.Point;
    054import org.eclipse.swt.graphics.Rectangle;
    055import org.eclipse.swt.layout.GridData;
    056import org.eclipse.swt.layout.GridLayout;
    057import org.eclipse.swt.widgets.Button;
    058import org.eclipse.swt.widgets.Combo;
    059import org.eclipse.swt.widgets.Composite;
    060import org.eclipse.swt.widgets.Dialog;
    061import org.eclipse.swt.widgets.Display;
    062import org.eclipse.swt.widgets.Event;
    063import org.eclipse.swt.widgets.FileDialog;
    064import org.eclipse.swt.widgets.Label;
    065import org.eclipse.swt.widgets.Listener;
    066import org.eclipse.swt.widgets.Menu;
    067import org.eclipse.swt.widgets.MenuItem;
    068import org.eclipse.swt.widgets.Shell;
    069import org.eclipse.swt.widgets.Tree;
    070import org.eclipse.swt.widgets.TreeItem;
    071
    072import hdf.hdf5lib.HDF5Constants;
    073
    074import hdf.object.CompoundDS;
    075import hdf.object.DataFormat;
    076import hdf.object.Dataset;
    077import hdf.object.Datatype;
    078import hdf.object.FileFormat;
    079import hdf.object.Group;
    080import hdf.object.HObject;
    081import hdf.object.MetaDataContainer;
    082import hdf.object.ScalarDS;
    083
    084import hdf.view.DefaultFileFilter;
    085import hdf.view.HDFView;
    086import hdf.view.Tools;
    087import hdf.view.ViewProperties;
    088import hdf.view.ViewProperties.DATA_VIEW_KEY;
    089import hdf.view.ViewProperties.DataViewType;
    090import hdf.view.DataView.DataView;
    091import hdf.view.DataView.DataViewFactory;
    092import hdf.view.DataView.DataViewFactoryProducer;
    093import hdf.view.DataView.DataViewManager;
    094import hdf.view.MetaDataView.MetaDataView;
    095import hdf.view.dialog.DataOptionDialog;
    096import hdf.view.dialog.InputDialog;
    097import hdf.view.dialog.NewCompoundDatasetDialog;
    098import hdf.view.dialog.NewDatasetDialog;
    099import hdf.view.dialog.NewDatatypeDialog;
    100import hdf.view.dialog.NewGroupDialog;
    101import hdf.view.dialog.NewImageDialog;
    102import hdf.view.dialog.NewLinkDialog;
    103
    104/**
    105 * TreeView defines APIs for opening files and displaying the file structure in
    106 * a tree structure.
    107 *
    108 * TreeView uses folders and leaf items to represent groups and data objects in
    109 * the file. You can expand or collapse folders to navigate data objects in the
    110 * file.
    111 *
    112 * From the TreeView, you can open data content or metadata of the selected object.
    113 * You can select object(s) to delete or add new objects to the file.
    114 *
    115 * @author Jordan T. Henderson
    116 * @version 2.4 12//2015
    117 */
    118public class DefaultTreeView implements TreeView {
    119
    120    private static final Logger log = LoggerFactory.getLogger(DefaultTreeView.class);
    121
    122    private Shell                         shell;
    123
    124    private Font                          curFont;
    125
    126    /** The owner of this TreeView */
    127    private DataViewManager               viewer;
    128
    129    /** Thread to load TableView Data in the background */
    130    private LoadDataThread                loadDataThread;
    131
    132    /**
    133     * The tree which holds file structures.
    134     */
    135    private final Tree                    tree;
    136
    137    /** The currently selected tree item */
    138    private TreeItem                      selectedItem = null;
    139
    140    /** The list of current selected objects for copying */
    141    private TreeItem[]                    objectsToCopy = null;
    142
    143    private TreeItem[]                    currentSelectionsForMove = null;
    144
    145    /** The currently selected object */
    146    private HObject                       selectedObject;
    147
    148    /** The currently selected file */
    149    private FileFormat                    selectedFile;
    150
    151    /** Maintains a list of TreeItems in the tree in breadth-first order
    152     * to prevent many calls of getAllItemsBreadthFirst.
    153     */
    154    //private ArrayList<TreeItem>         breadthFirstItems = null;
    155
    156    /** A list of currently open files */
    157    private final List<FileFormat>        fileList = new ArrayList<>();
    158
    159    /** A list of editing GUI components */
    160    private List<MenuItem>                editGUIs = new ArrayList<>();
    161
    162    /**
    163     * The popup menu used to display user choice of actions on data object.
    164     */
    165    private final Menu                    popupMenu;
    166
    167    private Menu                          newObjectMenu;
    168    private Menu                          exportDatasetMenu;
    169
    170    private MenuItem                      openVirtualFilesMenuItem;
    171    private MenuItem                      addDatasetMenuItem;
    172    private MenuItem                      exportDatasetMenuItem;
    173    private MenuItem                      addTableMenuItem;
    174    private MenuItem                      addDatatypeMenuItem;
    175    private MenuItem                      addLinkMenuItem;
    176    private MenuItem                      setLibVerBoundsItem;
    177    private MenuItem                      changeIndexItem;
    178
    179    /** Keep Image instances to prevent many calls to ViewProperties.getTypeIcon() */
    180    private Image h4Icon = ViewProperties.getH4Icon();
    181    private Image h4IconR = ViewProperties.getH4IconR();
    182    private Image h5Icon = ViewProperties.getH5Icon();
    183    private Image h5IconR = ViewProperties.getH5IconR();
    184    private Image nc3Icon = ViewProperties.getNC3Icon();
    185    private Image nc3IconR = ViewProperties.getNC3IconR();
    186    private Image imageIcon = ViewProperties.getImageIcon();
    187    private Image imageIconA = ViewProperties.getImageIconA();
    188    private Image textIcon = ViewProperties.getTextIcon();
    189    private Image textIconA = ViewProperties.getTextIconA();
    190    private Image datasetIcon = ViewProperties.getDatasetIcon();
    191    private Image datasetIconA = ViewProperties.getDatasetIconA();
    192    private Image tableIcon = ViewProperties.getTableIcon();
    193    private Image tableIconA = ViewProperties.getTableIconA();
    194    private Image datatypeIcon = ViewProperties.getDatatypeIcon();
    195    private Image datatypeIconA = ViewProperties.getDatatypeIconA();
    196    private Image folderCloseIcon = ViewProperties.getFoldercloseIcon();
    197    private Image folderCloseIconA = ViewProperties.getFoldercloseIconA();
    198    private Image folderOpenIcon = ViewProperties.getFolderopenIcon();
    199    private Image folderOpenIconA = ViewProperties.getFolderopenIconA();
    200    private Image questionIcon = ViewProperties.getQuestionIcon();
    201
    202    /** Flag to indicate if the dataset is displayed as default */
    203    private boolean                       isDefaultDisplay = true;
    204
    205    /** Flag to indicate if TreeItems are being moved */
    206    private boolean                       moveFlag = false;
    207
    208    private boolean                       isApplyBitmaskOnly  = false;
    209
    210    private int                           binaryOrder;
    211
    212    private String                        currentSearchPhrase = null;
    213
    214    /** Used to open a File using a temporary indexing type and order */
    215    private int                           tempIdxType = -1;
    216    private int                           tempIdxOrder = -1;
    217
    218    private enum OBJECT_TYPE {GROUP, DATASET, IMAGE, TABLE, DATATYPE, LINK};
    219
    220    /**
    221     * Create a visual component for opening files and displaying the file
    222     * structure in a tree structure.
    223     *
    224     * @param parent
    225     *        the parent component
    226     * @param theView
    227     *        the associated data view manager
    228     */
    229    public DefaultTreeView(Composite parent, DataViewManager theView) {
    230        viewer = theView;
    231        shell = parent.getShell();
    232
    233        try {
    234            curFont = new Font(
    235                    Display.getCurrent(),
    236                    ViewProperties.getFontType(),
    237                    ViewProperties.getFontSize(),
    238                    SWT.NORMAL);
    239        }
    240        catch (Exception ex) {
    241            curFont = null;
    242        }
    243
    244        // Initialize the Tree
    245        tree = new Tree(parent, SWT.MULTI | SWT.VIRTUAL);
    246        tree.setSize(tree.computeSize(SWT.DEFAULT, SWT.DEFAULT));
    247        tree.setFont(curFont);
    248
    249        // Create the context menu for the Tree
    250        popupMenu = createPopupMenu();
    251        tree.setMenu(popupMenu);
    252
    253        // Handle tree key events
    254        tree.addKeyListener(new KeyAdapter() {
    255            @Override
    256            public void keyReleased(KeyEvent e) {
    257                int key = e.keyCode;
    258
    259                if (key == SWT.ARROW_DOWN || key == SWT.ARROW_UP || key == SWT.KEYPAD_2 || key == SWT.KEYPAD_8) {
    260                    TreeItem[] selectedItems = tree.getSelection();
    261                    TreeItem theItem = selectedItems[0];
    262
    263                    if(theItem.equals(selectedItem)) return;
    264
    265                    selectedItem = theItem;
    266                    selectedObject = ((HObject) (selectedItem.getData()));
    267                    FileFormat theFile = selectedObject.getFileFormat();
    268                    if ((theFile != null) && !theFile.equals(selectedFile)) {
    269                        // A different file is selected, handle only one file at a time
    270                        selectedFile = theFile;
    271                        tree.deselectAll();
    272                    }
    273
    274                    ((HDFView) viewer).showMetaData(selectedObject);
    275                }
    276                else if (key == SWT.ARROW_LEFT || key == SWT.KEYPAD_4) {
    277                    if(selectedObject instanceof Group) {
    278                        selectedItem.setExpanded(false);
    279
    280                        Event collapse = new Event();
    281                        collapse.item = selectedItem;
    282
    283                        tree.notifyListeners(SWT.Collapse, collapse);
    284                    }
    285                }
    286                else if (key == SWT.ARROW_RIGHT || key == SWT.KEYPAD_6) {
    287                    if(selectedObject instanceof Group) {
    288                        selectedItem.setExpanded(true);
    289
    290                        Event expand = new Event();
    291                        expand.item = selectedItem;
    292
    293                        tree.notifyListeners(SWT.Expand, expand);
    294                    }
    295                }
    296            }
    297        });
    298
    299        /**
    300         * If user presses Enter on a TreeItem, expand/collapse the item if it
    301         * is a group, or try to show the data content if it is a data object.
    302         */
    303        tree.addListener(SWT.Traverse, new Listener() {
    304            @Override
    305            public void handleEvent(Event event) {
    306                if (event.detail != SWT.TRAVERSE_RETURN)
    307                    return;
    308
    309                TreeItem item = selectedItem;
    310                if(item == null)
    311                    return;
    312
    313                final HObject obj = (HObject) item.getData();
    314                if(obj == null)
    315                    return;
    316
    317                if(obj instanceof Group) {
    318                    boolean isExpanded = item.getExpanded();
    319
    320                    item.setExpanded(!isExpanded);
    321
    322                    Event expand = new Event();
    323                    expand.item = item;
    324
    325                    if(isExpanded)
    326                        tree.notifyListeners(SWT.Collapse, expand);
    327                    else
    328                        tree.notifyListeners(SWT.Expand, expand);
    329                }
    330                else {
    331                    if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) {
    332                        try {
    333                            loadDataThread = new LoadDataThread();
    334                            loadDataThread.start();
    335                        }
    336                        catch (Exception err) {
    337                            shell.getDisplay().beep();
    338                            Tools.showError(shell, "Select", err.getMessage());
    339                        }
    340                    }
    341                    else {
    342                        Tools.showInformation(shell, "Open", "No data to display in an object with a NULL dataspace.");
    343                    }
    344                }
    345            }
    346        });
    347
    348        /**
    349         * Handle mouse clicks on data objects in the tree view. A right mouse-click
    350         * to show the popup menu for user choice. A double left-mouse-click to
    351         * display the data content. A single left-mouse-click to select the current
    352         * data object.
    353         */
    354        tree.addMouseListener(new MouseAdapter() {
    355            // Double click opens data content of selected data object
    356            @Override
    357            public void mouseDoubleClick(MouseEvent e) {
    358                isDefaultDisplay = true;
    359
    360                try {
    361                    if(!(selectedObject instanceof Group)) {
    362                        if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) {
    363                            loadDataThread = new LoadDataThread();
    364                            loadDataThread.start();
    365                        }
    366                        else {
    367                            Tools.showInformation(shell, "Open",
    368                                    "No data to display in an object with a NULL dataspace.");
    369                        }
    370                    }
    371                    else {
    372                        boolean isExpanded = selectedItem.getExpanded();
    373
    374                        selectedItem.setExpanded(!isExpanded);
    375
    376                        Event expand = new Event();
    377                        expand.item = selectedItem;
    378
    379                        if(isExpanded)
    380                            tree.notifyListeners(SWT.Collapse, expand);
    381                        else
    382                            tree.notifyListeners(SWT.Expand, expand);
    383                    }
    384                }
    385                catch (Exception ex) {
    386                    log.trace("defaultDisplay showDataContent failed: {}", ex.getMessage());
    387                    ex.printStackTrace();
    388                }
    389            }
    390
    391            // When a mouse release is detected, attempt to set the selected item
    392            // and object to the TreeItem under the pointer
    393            @Override
    394            public void mouseUp(MouseEvent e) {
    395                // Make sure user clicked on a TreeItem
    396                TreeItem theItem = tree.getItem(new Point(e.x, e.y));
    397
    398                if (theItem == null) {
    399                    tree.deselectAll();
    400                    selectedItem = null;
    401                    selectedObject = null;
    402                    selectedFile = null;
    403
    404                    // Clear any information shown in the object info panel
    405                    ((HDFView) viewer).showMetaData(null);
    406
    407                    return;
    408                }
    409
    410                if (theItem.equals(selectedItem))
    411                    return;
    412
    413                FileFormat theFile = null;
    414
    415                selectedItem = theItem;
    416
    417                try {
    418                    selectedObject = (HObject) selectedItem.getData();
    419                }
    420                catch(NullPointerException ex) {
    421                    viewer.showError("Object " + selectedItem.getText() + " had no associated data.");
    422                    return;
    423                }
    424
    425                try {
    426                    theFile = selectedObject.getFileFormat();
    427                }
    428                catch(NullPointerException ex) {
    429                    viewer.showError("Error retrieving FileFormat of HObject " + selectedObject.getName() + ".");
    430                    return;
    431                }
    432
    433                if ((theFile != null) && !theFile.equals(selectedFile)) {
    434                    // A different file is selected, handle only one file at a time
    435                    selectedFile = theFile;
    436                }
    437
    438                // Set this file to the most recently selected file in the recent files bar
    439                Combo recentFilesCombo = ((HDFView) viewer).getUrlBar();
    440                String filename = selectedFile.getAbsolutePath();
    441
    442                try {
    443                    recentFilesCombo.remove(filename);
    444                }
    445                catch (Exception ex) {}
    446
    447                // first entry is always the workdir
    448                recentFilesCombo.add(filename, 1);
    449                recentFilesCombo.select(1);
    450
    451                ((HDFView) viewer).showMetaData(selectedObject);
    452            }
    453        });
    454
    455        // Show context menu only if user has selected a data object
    456        tree.addMenuDetectListener(new MenuDetectListener() {
    457            @Override
    458            public void menuDetected(MenuDetectEvent e) {
    459                Display display = Display.getDefault();
    460
    461                Point pt = display.map(null, tree, new Point(e.x, e.y));
    462                TreeItem item = tree.getItem(pt);
    463                if(item == null) { e.doit = false; return; }
    464
    465                FileFormat theFile = null;
    466
    467                selectedItem = item;
    468
    469                log.trace("tree.addMenuDetectListener(): selectedItem={}", selectedItem.getText());
    470                try {
    471                    selectedObject = (HObject) selectedItem.getData();
    472                }
    473                catch(NullPointerException ex) {
    474                    viewer.showError("Object " + selectedItem.getText() + " had no associated data.");
    475                    return;
    476                }
    477
    478                try {
    479                    theFile = selectedObject.getFileFormat();
    480                }
    481                catch(NullPointerException ex) {
    482                    viewer.showError("Error retrieving FileFormat of HObject " + selectedObject.getName() + ".");
    483                    return;
    484                }
    485
    486                if ((theFile != null) && !theFile.equals(selectedFile)) {
    487                    // A different file is selected, handle only one file at a time
    488                    selectedFile = theFile;
    489                    //tree.deselectAll();
    490                    //tree.setSelection(selPath);
    491                    log.trace("tree.addMenuDetectListener(): selectedFile={}", selectedFile.getAbsolutePath());
    492                }
    493
    494                ((HDFView) viewer).showMetaData(selectedObject);
    495
    496                popupMenu.setLocation(display.map(tree, null, pt));
    497                popupMenu.setVisible(true);
    498            }
    499        });
    500
    501        tree.addListener(SWT.Expand, new Listener() {
    502            @Override
    503            public void handleEvent(Event event) {
    504                TreeItem item = (TreeItem) event.item;
    505                Object obj = item.getData();
    506
    507                if (!(obj instanceof Group))
    508                    return;
    509
    510                Group theGroup = (Group) item.getData();
    511
    512                if(theGroup.isRoot())
    513                    return;
    514
    515                // Prevent graphical issues from happening by stopping
    516                // tree from redrawing until all the items are created
    517                tree.setRedraw(false);
    518
    519                if(item.getItemCount() > 0)
    520                    item.setImage(theGroup.hasAttribute() ? folderOpenIconA : folderOpenIcon);
    521
    522                // Process any remaining SetData events and then allow
    523                // the tree to redraw once all are finished
    524                //                while(tree.getDisplay().readAndDispatch());
    525
    526                tree.setRedraw(true);
    527            }
    528        });
    529
    530        tree.addListener(SWT.Collapse, new Listener() {
    531            @Override
    532            public void handleEvent(Event event) {
    533                TreeItem item = (TreeItem) event.item;
    534                Object obj = item.getData();
    535
    536                if (!(obj instanceof Group))
    537                    return;
    538
    539                Group theGroup = (Group) item.getData();
    540
    541                if(theGroup.isRoot())
    542                    return;
    543
    544                item.setImage(theGroup.hasAttribute() ? folderCloseIconA : folderCloseIcon);
    545            }
    546        });
    547
    548        // When groups are expanded, populate TreeItems corresponding to file objects
    549        // on demand.
    550        tree.addListener(SWT.SetData, new Listener() {
    551            @Override
    552            public void handleEvent(Event event) {
    553                TreeItem item = (TreeItem) event.item;
    554                TreeItem parentItem = item.getParentItem();
    555
    556                int position = parentItem.indexOf(item);
    557                HObject obj = ((Group) parentItem.getData()).getMember(position);
    558
    559                item.setData(obj);
    560                item.setFont(curFont);
    561                item.setText(obj.getName());
    562                item.setImage(getObjectTypeImage(obj));
    563
    564                if(obj instanceof Group)
    565                    item.setItemCount(((Group) obj).getMemberList().size());
    566            }
    567        });
    568
    569        tree.addDisposeListener(new DisposeListener() {
    570            @Override
    571            public void widgetDisposed(DisposeEvent e) {
    572                if (curFont != null)
    573                    curFont.dispose();
    574            }
    575        });
    576    }
    577
    578    /** Creates a popup menu for a right mouse click on a data object */
    579    private Menu createPopupMenu() {
    580        Menu menu = new Menu(tree);
    581        MenuItem item;
    582
    583        item = new MenuItem(menu, SWT.PUSH);
    584        item.setText("&Open");
    585        item.addSelectionListener(new SelectionAdapter() {
    586            @Override
    587            public void widgetSelected(SelectionEvent e) {
    588                isDefaultDisplay = true;
    589
    590                try {
    591                    if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) {
    592                        loadDataThread = new LoadDataThread();
    593                        loadDataThread.start();
    594                    }
    595                    else {
    596                        Tools.showInformation(shell, "Open", "No data to display in an object with a NULL dataspace.");
    597                    }
    598                }
    599                catch (Exception err) {
    600                    shell.getDisplay().beep();
    601                    Tools.showError(shell, "Open", err.getMessage());
    602                }
    603            }
    604        });
    605
    606        item = new MenuItem(menu, SWT.PUSH);
    607        item.setText("Open &As");
    608        item.addSelectionListener(new SelectionAdapter() {
    609            @Override
    610            public void widgetSelected(SelectionEvent e) {
    611                isDefaultDisplay = false;
    612
    613                try {
    614                    if ((selectedObject instanceof Dataset) && !((Dataset) selectedObject).isNULL()) {
    615                        loadDataThread = new LoadDataThread();
    616                        loadDataThread.start();
    617                    }
    618                    else {
    619                        Tools.showInformation(shell, "Open", "No data to display in an object with a NULL dataspace.");
    620                    }
    621                }
    622                catch (Exception err) {
    623                    shell.getDisplay().beep();
    624                    err.printStackTrace();
    625                    Tools.showError(shell, "Open", err.getMessage());
    626                }
    627            }
    628        });
    629
    630        openVirtualFilesMenuItem = new MenuItem(menu, SWT.PUSH);
    631        openVirtualFilesMenuItem.setText("Open Source Fi&les");
    632        openVirtualFilesMenuItem.addSelectionListener(new SelectionAdapter() {
    633            @Override
    634            public void widgetSelected(SelectionEvent e) {
    635                isDefaultDisplay = false;
    636
    637                log.trace("createPopupMenu(): selectedObject={}", selectedObject);
    638                // If dataset is virtual - open source files. Only for HDF5
    639                if (selectedObject != null) {
    640                    log.trace("createPopupMenu(): selectedObject={} is dataset instance-{} of type H5 {}", selectedObject, selectedObject instanceof Dataset, selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)));
    641                    if (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)) &&
    642                            (selectedObject instanceof Dataset)) {
    643                        Dataset dataset = (Dataset) selectedObject;
    644                        boolean isVirtual = dataset.isVirtual();
    645                        log.trace("createPopupMenu(): isVirtual={}", isVirtual);
    646                        if(isVirtual) {
    647                            for(int ndx=0; ndx<dataset.getVirtualMaps(); ndx++) {
    648                                try {
    649                                    String theFile = selectedFile.getParentFile().getAbsolutePath() + File.separator + dataset.getVirtualFilename(ndx);
    650                                    openFile(theFile, FileFormat.WRITE);
    651                                }
    652                                catch (Exception ex) {
    653                                    shell.getDisplay().beep();
    654                                    ex.printStackTrace();
    655                                    Tools.showError(shell, "Open", ex.getMessage() + "\n" + dataset.getVirtualFilename(ndx));
    656                                }
    657                                log.trace("createPopupMenu(): virtualNameList[{}]={}", ndx, dataset.getVirtualFilename(ndx));
    658                            }
    659                        }
    660                    }
    661                }
    662            }
    663        });
    664
    665        new MenuItem(menu, SWT.SEPARATOR);
    666
    667        MenuItem newObjectMenuItem = new MenuItem(menu, SWT.CASCADE);
    668        newObjectMenuItem.setText("New");
    669        editGUIs.add(newObjectMenuItem);
    670
    671        new MenuItem(menu, SWT.SEPARATOR);
    672
    673        item = new MenuItem(menu, SWT.PUSH);
    674        item.setText("Cu&t");
    675        item.addSelectionListener(new SelectionAdapter() {
    676            @Override
    677            public void widgetSelected(SelectionEvent e) {
    678                moveObject();
    679            }
    680        });
    681        editGUIs.add(item);
    682
    683        item = new MenuItem(menu, SWT.PUSH);
    684        item.setText("&Copy");
    685        item.addSelectionListener(new SelectionAdapter() {
    686            @Override
    687            public void widgetSelected(SelectionEvent e) {
    688                copyObject();
    689            }
    690        });
    691
    692        item = new MenuItem(menu, SWT.PUSH);
    693        item.setText("&Paste");
    694        item.addSelectionListener(new SelectionAdapter() {
    695            @Override
    696            public void widgetSelected(SelectionEvent e) {
    697                pasteObject();
    698            }
    699        });
    700        editGUIs.add(item);
    701
    702        item = new MenuItem(menu, SWT.PUSH);
    703        item.setText("&Delete");
    704        item.addSelectionListener(new SelectionAdapter() {
    705            @Override
    706            public void widgetSelected(SelectionEvent e) {
    707                cutObject();
    708            }
    709        });
    710        editGUIs.add(item);
    711
    712        exportDatasetMenuItem = new MenuItem(menu, SWT.CASCADE);
    713        exportDatasetMenuItem.setText("Export Dataset");
    714
    715        new MenuItem(menu, SWT.SEPARATOR);
    716
    717        item = new MenuItem(menu, SWT.PUSH);
    718        item.setText("&Save to");
    719        item.addSelectionListener(new SelectionAdapter() {
    720            @Override
    721            public void widgetSelected(SelectionEvent e) {
    722                TreeItem[] selectedItems = tree.getSelection();
    723
    724                if (selectedItems.length <= 0) return;
    725
    726                for (int i = 0; i < selectedItems.length; i++) {
    727                    if (((HObject) selectedItems[i].getData() instanceof Group)
    728                            && ((Group) selectedItems[i].getData()).isRoot()) {
    729                        shell.getDisplay().beep();
    730                        Tools.showError(shell, "Save", "Cannot save the root group.\nUse \"Save As\" from file menu to save the whole file");
    731                        return;
    732                    }
    733                }
    734
    735                String filetype = FileFormat.FILE_TYPE_HDF4;
    736                if (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)))
    737                    filetype = FileFormat.FILE_TYPE_HDF5;
    738                else if (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3)))
    739                    filetype = FileFormat.FILE_TYPE_NC3;
    740
    741                String currentDir = selectedObject.getFileFormat().getParent();
    742
    743                if (currentDir != null)
    744                    currentDir += File.separator;
    745                else
    746                    currentDir = "";
    747
    748                FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
    749
    750                DefaultFileFilter filter = null;
    751
    752                if (filetype.equals(FileFormat.FILE_TYPE_HDF4)) {
    753                    fChooser.setFileName(Tools.checkNewFile(currentDir, ".hdf").getName());
    754                    filter = DefaultFileFilter.getFileFilterHDF4();
    755                }
    756                else if (filetype.equals(FileFormat.FILE_TYPE_NC3)) {
    757                    fChooser.setFileName(Tools.checkNewFile(currentDir, ".nc").getName());
    758                    filter = DefaultFileFilter.getFileFilterNetCDF3();
    759                }
    760                else {
    761                    fChooser.setFileName(Tools.checkNewFile(currentDir, ".h5").getName());
    762                    filter = DefaultFileFilter.getFileFilterHDF5();
    763                }
    764
    765                fChooser.setFilterExtensions(new String[] {filter.getExtensions()});
    766                fChooser.setFilterNames(new String[] {filter.getDescription()});
    767                fChooser.setFilterIndex(0);
    768
    769                String filename = fChooser.open();
    770
    771                if(filename == null)
    772                    return;
    773
    774                try {
    775                    Tools.createNewFile(filename, currentDir, filetype, fileList);
    776                }
    777                catch (Exception ex) {
    778                    Tools.showError(shell, "Save", ex.getMessage());
    779                }
    780
    781                FileFormat dstFile = null;
    782
    783                try {
    784                    dstFile = openFile(filename, FileFormat.WRITE);
    785                }
    786                catch (Exception ex) {
    787                    shell.getDisplay().beep();
    788                    Tools.showError(shell, "Save", ex.getMessage() + "\n" + filename);
    789                }
    790                if (dstFile != null)
    791                    pasteObject(selectedItems, findTreeItem(dstFile.getRootObject()), dstFile);
    792            }
    793        });
    794
    795        item = new MenuItem(menu, SWT.PUSH);
    796        item.setText("&Rename");
    797        item.addSelectionListener(new SelectionAdapter() {
    798            @Override
    799            public void widgetSelected(SelectionEvent e) {
    800                renameObject();
    801            }
    802        });
    803        editGUIs.add(item);
    804
    805        new MenuItem(menu, SWT.SEPARATOR);
    806
    807        changeIndexItem = new MenuItem(menu, SWT.PUSH);
    808        changeIndexItem.setText("Change file indexing");
    809        changeIndexItem.addSelectionListener(new SelectionAdapter() {
    810            @Override
    811            public void widgetSelected(SelectionEvent e) {
    812                ChangeIndexingDialog dialog = new ChangeIndexingDialog(shell, SWT.NONE, selectedFile);
    813                dialog.open();
    814                if (dialog.isReloadFile()) {
    815                    try {
    816                        selectedFile.setIndexType(dialog.getIndexType());
    817                    }
    818                    catch (Exception ex) {
    819                        log.debug("ChangeIndexingDialog(): setIndexType failed: ", ex);
    820                    }
    821                    try {
    822                        selectedFile.setIndexOrder(dialog.getIndexOrder());
    823                    }
    824                    catch (Exception ex) {
    825                        log.debug("ChangeIndexingDialog(): setIndexOrder failed: ", ex);
    826                    }
    827
    828                    try {
    829                        reopenFile(selectedFile, -1);
    830                    }
    831                    catch (Exception ex) {
    832                        log.debug("reload file {} failure after indexing change: ", selectedFile.getAbsolutePath(), ex);
    833                        Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + " after changing indexing: " + ex.getMessage());
    834                    }
    835                }
    836            }
    837        });
    838
    839        new MenuItem(menu, SWT.SEPARATOR);
    840
    841        item = new MenuItem(menu, SWT.PUSH);
    842        item.setText("&Find");
    843        item.addSelectionListener(new SelectionAdapter() {
    844            @Override
    845            public void widgetSelected(SelectionEvent e) {
    846                String findStr = currentSearchPhrase;
    847                if (findStr == null)
    848                    findStr = "";
    849
    850                findStr = (new InputDialog(shell, "Find Object by Name",
    851                        "Find (e.g. O3Quality, O3*, or *Quality):", findStr)).open();
    852
    853                if (findStr != null && findStr.length() > 0)
    854                    currentSearchPhrase = findStr;
    855
    856                find(currentSearchPhrase, selectedItem);
    857            }
    858        });
    859
    860        new MenuItem(menu, SWT.SEPARATOR);
    861
    862        item = new MenuItem(menu, SWT.PUSH);
    863        item.setText("Expand All");
    864        item.addSelectionListener(new SelectionAdapter() {
    865            @Override
    866            public void widgetSelected(SelectionEvent e) {
    867                if(selectedItem != null)
    868                    recursiveExpand(selectedItem, true);
    869            }
    870        });
    871
    872        item = new MenuItem(menu, SWT.PUSH);
    873        item.setText("Collapse All");
    874        item.addSelectionListener(new SelectionAdapter() {
    875            @Override
    876            public void widgetSelected(SelectionEvent e) {
    877                if(selectedItem != null)
    878                    recursiveExpand(selectedItem, false);
    879            }
    880        });
    881
    882        new MenuItem(menu, SWT.SEPARATOR);
    883
    884        item = new MenuItem(menu, SWT.PUSH);
    885        item.setText("Close Fil&e");
    886        item.addSelectionListener(new SelectionAdapter() {
    887            @Override
    888            public void widgetSelected(SelectionEvent e) {
    889                try {
    890                    ((HDFView) viewer).closeFile(selectedFile);
    891                }
    892                catch (Exception ex) {
    893                    Tools.showError(shell, "Close", ex.getMessage());
    894                }
    895            }
    896        });
    897
    898        item = new MenuItem(menu, SWT.PUSH);
    899        item.setText("&Reload File");
    900        item.addSelectionListener(new SelectionAdapter() {
    901            @Override
    902            public void widgetSelected(SelectionEvent e) {
    903                try {
    904                    reopenFile(selectedFile, -1);
    905                }
    906                catch (Exception ex) {
    907                    log.debug("reload file {} failure: ", selectedFile.getAbsolutePath(), ex);
    908                    Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + ": " + ex.getMessage());
    909                }
    910            }
    911        });
    912
    913        item = new MenuItem(menu, SWT.CASCADE);
    914        item.setText("Reload File As");
    915
    916        Menu reloadFileMenu = new Menu(item);
    917        item.setMenu(reloadFileMenu);
    918
    919        item = new MenuItem(reloadFileMenu, SWT.PUSH);
    920        item.setText("Read-Only");
    921        item.addSelectionListener(new SelectionAdapter() {
    922            @Override
    923            public void widgetSelected(SelectionEvent e) {
    924                try {
    925                    reopenFile(selectedFile, FileFormat.READ);
    926                }
    927                catch (Exception ex) {
    928                    log.debug("reload file {} as read-only failure: ", selectedFile.getAbsolutePath(), ex);
    929                    Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + " read-only: " + ex.getMessage());
    930                }
    931            }
    932        });
    933
    934        item = new MenuItem(reloadFileMenu, SWT.PUSH);
    935        item.setText("SWMR Read-Only");
    936        item.addSelectionListener(new SelectionAdapter() {
    937            @Override
    938            public void widgetSelected(SelectionEvent e) {
    939                try {
    940                    reopenFile(selectedFile, FileFormat.READ | FileFormat.MULTIREAD);
    941                }
    942                catch (Exception ex) {
    943                    log.debug("reload file {} as SWMR read-only failure: ", selectedFile.getAbsolutePath(), ex);
    944                    Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + " SWMR read-only: " + ex.getMessage());
    945                }
    946            }
    947        });
    948
    949        item = new MenuItem(reloadFileMenu, SWT.PUSH);
    950        item.setText("Read/Write");
    951        item.addSelectionListener(new SelectionAdapter() {
    952            @Override
    953            public void widgetSelected(SelectionEvent e) {
    954                try {
    955                    reopenFile(selectedFile, FileFormat.WRITE);
    956                }
    957                catch (Exception ex) {
    958                    log.debug("reload file {} as read/write failure: ", selectedFile.getAbsolutePath(), ex);
    959                    Tools.showError(shell, "File reload error", "Error reloading file " + selectedFile.getAbsolutePath() + " read/write: " + ex.getMessage());
    960                }
    961            }
    962        });
    963
    964        new MenuItem(menu, SWT.SEPARATOR);
    965
    966        setLibVerBoundsItem = new MenuItem(menu, SWT.NONE);
    967        setLibVerBoundsItem.setText("Set Lib version bounds");
    968        setLibVerBoundsItem.addSelectionListener(new SelectionAdapter() {
    969            @Override
    970            public void widgetSelected(SelectionEvent e) {
    971                new ChangeLibVersionDialog(shell, SWT.NONE).open();
    972            }
    973        });
    974
    975
    976        // Add new object menu
    977        newObjectMenu = new Menu(menu);
    978        newObjectMenuItem.setMenu(newObjectMenu);
    979
    980        item = new MenuItem(newObjectMenu, SWT.PUSH);
    981        item.setText("Group");
    982        item.setImage(ViewProperties.getFoldercloseIcon());
    983        item.addSelectionListener(new SelectionAdapter() {
    984            @Override
    985            public void widgetSelected(SelectionEvent e) {
    986                addNewObject(OBJECT_TYPE.GROUP);
    987            }
    988        });
    989        editGUIs.add(item);
    990
    991        addDatasetMenuItem = new MenuItem(newObjectMenu, SWT.PUSH);
    992        addDatasetMenuItem.setText("Dataset");
    993        addDatasetMenuItem.setImage(ViewProperties.getDatasetIcon());
    994        addDatasetMenuItem.addSelectionListener(new SelectionAdapter() {
    995            @Override
    996            public void widgetSelected(SelectionEvent e) {
    997                addNewObject(OBJECT_TYPE.DATASET);
    998            }
    999        });
    1000        editGUIs.add(addDatasetMenuItem);
    1001
    1002        item = new MenuItem(newObjectMenu, SWT.PUSH);
    1003        item.setText("Image");
    1004        item.setImage(ViewProperties.getImageIcon());
    1005        item.addSelectionListener(new SelectionAdapter() {
    1006            @Override
    1007            public void widgetSelected(SelectionEvent e) {
    1008                addNewObject(OBJECT_TYPE.IMAGE);
    1009            }
    1010        });
    1011        editGUIs.add(item);
    1012
    1013        addTableMenuItem = new MenuItem(newObjectMenu, SWT.PUSH);
    1014        addTableMenuItem.setText("Compound DS");
    1015        addTableMenuItem.setImage(ViewProperties.getTableIcon());
    1016        addTableMenuItem.addSelectionListener(new SelectionAdapter() {
    1017            @Override
    1018            public void widgetSelected(SelectionEvent e) {
    1019                addNewObject(OBJECT_TYPE.TABLE);
    1020            }
    1021        });
    1022        editGUIs.add(addTableMenuItem);
    1023
    1024        addDatatypeMenuItem = new MenuItem(newObjectMenu, SWT.PUSH);
    1025        addDatatypeMenuItem.setText("Datatype");
    1026        addDatatypeMenuItem.setImage(ViewProperties.getDatatypeIcon());
    1027        addDatatypeMenuItem.addSelectionListener(new SelectionAdapter() {
    1028            @Override
    1029            public void widgetSelected(SelectionEvent e) {
    1030                addNewObject(OBJECT_TYPE.DATATYPE);
    1031            }
    1032        });
    1033        editGUIs.add(addDatatypeMenuItem);
    1034
    1035        addLinkMenuItem = new MenuItem(newObjectMenu, SWT.PUSH);
    1036        addLinkMenuItem.setText("Link");
    1037        addLinkMenuItem.setImage(ViewProperties.getLinkIcon());
    1038        addLinkMenuItem.addSelectionListener(new SelectionAdapter() {
    1039            @Override
    1040            public void widgetSelected(SelectionEvent e) {
    1041                addNewObject(OBJECT_TYPE.LINK);
    1042            }
    1043        });
    1044        editGUIs.add(addLinkMenuItem);
    1045
    1046
    1047        // Add export dataset menu
    1048        exportDatasetMenu = new Menu(menu);
    1049        exportDatasetMenuItem.setMenu(exportDatasetMenu);
    1050
    1051        item = new MenuItem(exportDatasetMenu, SWT.PUSH);
    1052        item.setText("Export Data to Text File");
    1053        item.addSelectionListener(new SelectionAdapter() {
    1054            @Override
    1055            public void widgetSelected(SelectionEvent e) {
    1056                binaryOrder = 99;
    1057
    1058                try {
    1059                    saveDataAsFile();
    1060                }
    1061                catch (Exception ex) {
    1062                    shell.getDisplay().beep();
    1063                    Tools.showError(shell, "Export Dataset", ex.getMessage());
    1064                }
    1065            }
    1066        });
    1067
    1068        item = new MenuItem(exportDatasetMenu, SWT.PUSH);
    1069        item.setText("Export Data as Native Order");
    1070        item.addSelectionListener(new SelectionAdapter() {
    1071            @Override
    1072            public void widgetSelected(SelectionEvent e) {
    1073                binaryOrder = 1;
    1074
    1075                try {
    1076                    saveDataAsFile();
    1077                }
    1078                catch (Exception ex) {
    1079                    shell.getDisplay().beep();
    1080                    Tools.showError(shell, "Export Dataset", ex.getMessage());
    1081                }
    1082            }
    1083        });
    1084
    1085        item = new MenuItem(exportDatasetMenu, SWT.PUSH);
    1086        item.setText("Export Data as Little Endian");
    1087        item.addSelectionListener(new SelectionAdapter() {
    1088            @Override
    1089            public void widgetSelected(SelectionEvent e) {
    1090                binaryOrder = 2;
    1091
    1092                try {
    1093                    saveDataAsFile();
    1094                }
    1095                catch (Exception ex) {
    1096                    shell.getDisplay().beep();
    1097                    Tools.showError(shell, "Export Dataset", ex.getMessage());
    1098                }
    1099            }
    1100        });
    1101
    1102        item = new MenuItem(exportDatasetMenu, SWT.PUSH);
    1103        item.setText("Export Data as Big Endian");
    1104        item.addSelectionListener(new SelectionAdapter() {
    1105            @Override
    1106            public void widgetSelected(SelectionEvent e) {
    1107                binaryOrder = 3;
    1108
    1109                try {
    1110                    saveDataAsFile();
    1111                }
    1112                catch (Exception ex) {
    1113                    shell.getDisplay().beep();
    1114                    Tools.showError(shell, "Export Dataset", ex.getMessage());
    1115                }
    1116            }
    1117        });
    1118
    1119        // Add listener to dynamically enable/disable menu items based
    1120        // on selection in tree
    1121        menu.addMenuListener(new MenuAdapter() {
    1122            @Override
    1123            public void menuShown(MenuEvent e) {
    1124                if (selectedItem == null || selectedObject == null || selectedFile == null) return;
    1125
    1126                boolean isReadOnly = selectedObject.getFileFormat().isReadOnly();
    1127                boolean isWritable = !isReadOnly;
    1128
    1129                setEnabled(editGUIs, isWritable);
    1130
    1131                if (selectedObject instanceof Group) {
    1132                    boolean state = !(((Group) selectedObject).isRoot());
    1133
    1134                    popupMenu.getItem(0).setEnabled(false); // "Open" menuitem
    1135                    popupMenu.getItem(1).setEnabled(false); // "Open as" menuitem
    1136                    popupMenu.getItem(2).setEnabled(false); // "Open Source Files" menuitem
    1137                    popupMenu.getItem(6).setEnabled(
    1138                            (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)))
    1139                            && state && isWritable); // "Cut" menuitem
    1140                    popupMenu.getItem(7).setEnabled(state); // "Copy" menuitem
    1141                    popupMenu.getItem(9).setEnabled(state && isWritable); // "Delete" menuitem
    1142                    popupMenu.getItem(10).setEnabled(false); // "Export Dataset" menuitem
    1143                    popupMenu.getItem(12).setEnabled(state && isWritable); // "Save to" menuitem
    1144                    popupMenu.getItem(13).setEnabled(state && isWritable); // "Rename" menuitem
    1145                }
    1146                else {
    1147                    popupMenu.getItem(0).setEnabled(true); // "Open" menuitem
    1148                    popupMenu.getItem(1).setEnabled(true); // "Open as" menuitem
    1149                    popupMenu.getItem(2).setEnabled(
    1150                            (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)))); // "Open Source Files" menuitem
    1151                    popupMenu.getItem(6).setEnabled(
    1152                            (selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5)))
    1153                            && isWritable); // "Cut" menuitem
    1154                    popupMenu.getItem(7).setEnabled(true); // "Copy" menuitem
    1155                    popupMenu.getItem(9).setEnabled(isWritable); // "Delete" menuitem
    1156                    popupMenu.getItem(10).setEnabled(true); // "Export Dataset" menuitem
    1157                    popupMenu.getItem(12).setEnabled(true); // "Save to" menuitem
    1158                    popupMenu.getItem(13).setEnabled(isWritable); // "Rename" menuitem
    1159                }
    1160
    1161                // Adding table is only supported by HDF5
    1162                if ((selectedFile != null) && selectedFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
    1163                    //openVirtualFilesMenuItem.setEnabled(true); // Should be moved to createPopupMenu() since swt doesn't support MenuItem.setVisible
    1164
    1165                    boolean state = false;
    1166                    if ((selectedObject instanceof Group)) {
    1167                        state = (((Group) selectedObject).isRoot());
    1168                        setLibVerBoundsItem.setEnabled(isWritable && state);
    1169                    }
    1170                    else {
    1171                        setLibVerBoundsItem.setEnabled(false);
    1172                    }
    1173
    1174                    changeIndexItem.setEnabled(state);
    1175                }
    1176                else {
    1177                    addTableMenuItem.setEnabled(false);
    1178                    addDatatypeMenuItem.setEnabled(false);
    1179                    addLinkMenuItem.setEnabled(false);
    1180                    //openVirtualFilesMenuItem.setEnabled(false);
    1181                    setLibVerBoundsItem.setEnabled(false);
    1182                    changeIndexItem.setEnabled(false);
    1183                }
    1184
    1185                // Export table is only supported by HDF5
    1186                if ((selectedObject != null) && selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
    1187                    if ((selectedObject instanceof Dataset)) {
    1188                        Dataset dataset = (Dataset) selectedObject;
    1189                        if ((dataset instanceof ScalarDS))
    1190                            exportDatasetMenuItem.setEnabled(true);
    1191                        openVirtualFilesMenuItem.setEnabled(true);
    1192                    }
    1193                    else {
    1194                        exportDatasetMenuItem.setEnabled(false);
    1195                        openVirtualFilesMenuItem.setEnabled(false);
    1196                    }
    1197                }
    1198                else {
    1199                    exportDatasetMenuItem.setEnabled(false);
    1200                    openVirtualFilesMenuItem.setEnabled(false);
    1201                }
    1202            }
    1203        });
    1204
    1205        return menu;
    1206    }
    1207
    1208    /**
    1209     * Creates a dialog for the user to select a type of new
    1210     * object to be added to the TreeView, then passes the
    1211     * result of the dialog on to addObject(HObject newObject, Group parentGroup)
    1212     *
    1213     * @param type
    1214     *          The type (GROUP, DATASET, IMAGE, TABLE, DATATYPE, LINK) of object to add.
    1215     */
    1216    private void addNewObject(OBJECT_TYPE type) {
    1217        if ((selectedObject == null) || (selectedItem == null)) return;
    1218
    1219        TreeItem parentItem = null;
    1220        if(selectedObject instanceof Group)
    1221            parentItem = selectedItem;
    1222        else
    1223            parentItem = selectedItem.getParentItem();
    1224
    1225        // Find the root item of the selected file
    1226        TreeItem rootItem = selectedItem;
    1227        while(rootItem.getParentItem() != null)
    1228            rootItem = rootItem.getParentItem();
    1229
    1230        HObject obj = null;
    1231
    1232        switch(type) {
    1233        case GROUP:
    1234            NewGroupDialog groupDialog = new NewGroupDialog(shell, (Group) parentItem.getData(),
    1235                    breadthFirstUserObjects(rootItem));
    1236            groupDialog.open();
    1237            obj = groupDialog.getObject();
    1238            parentItem = findTreeItem(groupDialog.getParentGroup());
    1239            break;
    1240        case DATASET:
    1241            NewDatasetDialog datasetDialog = new NewDatasetDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem));
    1242            datasetDialog.open();
    1243            obj = datasetDialog.getObject();
    1244            parentItem = findTreeItem(datasetDialog.getParentGroup());
    1245            break;
    1246        case IMAGE:
    1247            NewImageDialog imageDialog = new NewImageDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem));
    1248            imageDialog.open();
    1249            obj = imageDialog.getObject();
    1250            parentItem = findTreeItem(imageDialog.getParentGroup());
    1251            break;
    1252        case TABLE:
    1253            NewCompoundDatasetDialog tableDialog = new NewCompoundDatasetDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem));
    1254            tableDialog.open();
    1255            obj = tableDialog.getObject();
    1256            parentItem = findTreeItem(tableDialog.getParentGroup());
    1257            break;
    1258        case DATATYPE:
    1259            NewDatatypeDialog datatypeDialog = new NewDatatypeDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem));
    1260            datatypeDialog.open();
    1261            obj = datatypeDialog.getObject();
    1262            parentItem = findTreeItem(datatypeDialog.getParentGroup());
    1263            break;
    1264        case LINK:
    1265            NewLinkDialog linkDialog = new NewLinkDialog(shell, (Group) parentItem.getData(), breadthFirstUserObjects(rootItem), getCurrentFiles());
    1266            linkDialog.open();
    1267            obj = linkDialog.getObject();
    1268            parentItem = findTreeItem(linkDialog.getParentGroup());
    1269            break;
    1270        }
    1271
    1272        if (obj == null)
    1273            return;
    1274
    1275        try {
    1276            this.insertObject(obj, parentItem);
    1277        }
    1278        catch (Exception ex) {
    1279            shell.getDisplay().beep();
    1280            Tools.showError(shell, "Create", ex.getMessage());
    1281        }
    1282    }
    1283
    1284    /**
    1285     * Adds an already created HObject to the tree under the
    1286     * TreeItem containing the specified parent group.
    1287     *
    1288     * @param obj
    1289     *            the object to add.
    1290     * @param parentGroup
    1291     *            the parent group to add the object to.
    1292     */
    1293    @Override
    1294    public TreeItem addObject(HObject obj, Group parentGroup) {
    1295        if ((obj == null) || (parentGroup == null))
    1296            return null;
    1297
    1298        return insertObject(obj, findTreeItem(parentGroup));
    1299    }
    1300
    1301    /**
    1302     * Insert an object into the tree as the last object
    1303     * under parent item pobj.
    1304     *
    1305     * @param obj
    1306     *            the object to insert.
    1307     * @param pobj
    1308     *            the parent TreeItem to insert the new object under.
    1309     *            If null, inserts the object at the end of the Tree.
    1310     *
    1311     * @return the newly created TreeItem
    1312     */
    1313    private TreeItem insertObject(HObject obj, TreeItem pobj) {
    1314        if ((obj == null))
    1315            return null;
    1316
    1317        TreeItem item;
    1318
    1319        if(pobj != null) {
    1320            item = new TreeItem(pobj, SWT.NONE, pobj.getItemCount());
    1321            item.setFont(curFont);
    1322            item.setText(obj.getName());
    1323        }
    1324        else {
    1325            // Parent object was null, insert at end of tree as root object
    1326            item = new TreeItem(tree, SWT.NONE, tree.getItemCount());
    1327            item.setFont(curFont);
    1328            item.setText(obj.getFileFormat().getName());
    1329        }
    1330
    1331        item.setData(obj);
    1332        item.setImage(getObjectTypeImage(obj));
    1333
    1334        return item;
    1335    }
    1336
    1337    /** Move selected objects */
    1338    private void moveObject() {
    1339        objectsToCopy = tree.getSelection();
    1340        moveFlag = true;
    1341        currentSelectionsForMove = tree.getSelection();
    1342    }
    1343
    1344    /** Copy selected objects */
    1345    private void copyObject() {
    1346        if (moveFlag)
    1347            if(!Tools.showConfirm(shell, "Copy object", "Do you want to copy all the selected object(s) instead of move?"))
    1348                return;
    1349        moveFlag = false;
    1350        currentSelectionsForMove = null;
    1351        objectsToCopy = tree.getSelection();
    1352    }
    1353
    1354    /** Delete selected objects */
    1355    private void cutObject() {
    1356        if (moveFlag)
    1357            if(!Tools.showConfirm(shell, "Delete object", "Do you want to delete all the selected object(s) instead of move?"))
    1358                return;
    1359        moveFlag = false;
    1360        currentSelectionsForMove = null;
    1361        objectsToCopy = tree.getSelection();
    1362        removeSelectedObjects();
    1363    }
    1364
    1365    /** Paste selected objects */
    1366    private void pasteObject() {
    1367        if (moveFlag) {
    1368            HObject theObj = null;
    1369            for (int i = 0; i < currentSelectionsForMove.length; i++) {
    1370                TreeItem currentItem = currentSelectionsForMove[i];
    1371                theObj = (HObject) currentItem.getData();
    1372
    1373                if (isObjectOpen(theObj)) {
    1374                    shell.getDisplay().beep();
    1375                    Tools.showError(shell, "Move Objects", "Cannot move the selected object: " + theObj
    1376                            + "\nThe dataset or dataset in the group is in use."
    1377                            + "\n\nPlease close the dataset(s) and try again.\n");
    1378
    1379                    moveFlag = false;
    1380                    currentSelectionsForMove = null;
    1381                    objectsToCopy = null;
    1382                    return;
    1383                }
    1384            }
    1385        }
    1386
    1387        TreeItem pitem = selectedItem;
    1388
    1389        if ((objectsToCopy == null) || (objectsToCopy.length <= 0) || (pitem == null))
    1390            return;
    1391
    1392        FileFormat srcFile = ((HObject) objectsToCopy[0].getData()).getFileFormat();
    1393        FileFormat dstFile = getSelectedFile();
    1394        FileFormat h5file = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5);
    1395        FileFormat h4file = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4);
    1396        FileFormat ncfile = FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3);
    1397
    1398        if (srcFile == null) {
    1399            shell.getDisplay().beep();
    1400            Tools.showError(shell, "Copy", "Source file is null.");
    1401            return;
    1402        }
    1403        else if (dstFile == null) {
    1404            shell.getDisplay().beep();
    1405            Tools.showError(shell, "Copy", "Destination file is null.");
    1406            return;
    1407        }
    1408        else if (srcFile.isThisType(h4file) && dstFile.isThisType(h5file)) {
    1409            shell.getDisplay().beep();
    1410            Tools.showError(shell, "Copy", "Unsupported operation: cannot copy HDF4 object to HDF5 file");
    1411            return;
    1412        }
    1413        else if (srcFile.isThisType(h5file) && dstFile.isThisType(h4file)) {
    1414            shell.getDisplay().beep();
    1415            Tools.showError(shell, "Copy", "Unsupported operation: cannot copy HDF5 object to HDF4 file");
    1416            return;
    1417        }
    1418        else if (srcFile.isThisType(ncfile) && dstFile.isThisType(ncfile)) {
    1419            shell.getDisplay().beep();
    1420            Tools.showError(shell, "Copy", "Unsupported operation: cannot copy NetCDF3 objects");
    1421            return;
    1422        }
    1423
    1424        if (moveFlag) {
    1425            if (srcFile != dstFile) {
    1426                shell.getDisplay().beep();
    1427                Tools.showError(shell, "Move", "Cannot move the selected object to different file");
    1428                moveFlag = false;
    1429                currentSelectionsForMove = null;
    1430                objectsToCopy = null;
    1431                return;
    1432            }
    1433        }
    1434
    1435        /*
    1436        if (pitem.getParentItem() != null) {
    1437            pitem = pitem.getParentItem();
    1438        }*/
    1439
    1440        Group pgroup = (Group) pitem.getData();
    1441        String fullPath = pgroup.getPath() + pgroup.getName();
    1442        if (pgroup.isRoot()) {
    1443            fullPath = HObject.SEPARATOR;
    1444        }
    1445
    1446        String msg = "";
    1447        if (srcFile.isThisType(h4file))
    1448            msg = "WARNING: object can not be deleted after it is copied.\n\n";
    1449
    1450        msg += "Do you want to copy the selected object(s) to \nGroup: " + fullPath + "\nFile: "
    1451                + dstFile.getFilePath();
    1452
    1453        if (moveFlag) {
    1454            String moveMsg = "Do you want to move the selected object(s) to \nGroup: " + fullPath + "\nFile: "
    1455                    + dstFile.getFilePath();
    1456            if(!Tools.showConfirm(shell, "Move Object", moveMsg))
    1457                return;
    1458        }
    1459        else {
    1460            if(!Tools.showConfirm(shell, "Copy object", msg))
    1461                return;
    1462        }
    1463
    1464        pasteObject(objectsToCopy, pitem, dstFile);
    1465
    1466        if (moveFlag) {
    1467            removeSelectedObjects();
    1468            moveFlag = false;
    1469            currentSelectionsForMove = null;
    1470            objectsToCopy = null;
    1471        }
    1472    }
    1473
    1474    /** Paste selected objects */
    1475    private void pasteObject(TreeItem[] objList, TreeItem pobj, FileFormat dstFile) {
    1476        if ((objList == null) || (objList.length <= 0) || (pobj == null)) return;
    1477
    1478        FileFormat srcFile = ((HObject) objList[0].getData()).getFileFormat();
    1479        Group pgroup = (Group) pobj.getData();
    1480
    1481        HObject theObj = null;
    1482        for (int i = 0; i < objList.length; i++) {
    1483            theObj = (HObject) objList[i].getData();
    1484
    1485            if ((theObj instanceof Group) && ((Group) theObj).isRoot()) {
    1486                shell.getDisplay().beep();
    1487                Tools.showError(shell, "Paste", "Unsupported operation: cannot copy the root group");
    1488                return;
    1489            }
    1490
    1491            // Check if it creates infinite loop
    1492            Group pg = pgroup;
    1493            while (!pg.isRoot()) {
    1494                if (theObj.equals(pg)) {
    1495                    shell.getDisplay().beep();
    1496                    Tools.showError(shell, "Paste", "Unsupported operation: cannot copy a group to itself.");
    1497                    return;
    1498                }
    1499                pg = pg.getParent();
    1500            }
    1501
    1502            try {
    1503                log.trace("pasteObject(...): dstFile.copy({}, {}, null)", theObj, pgroup);
    1504
    1505                HObject newObj = null;
    1506                if((newObj = srcFile.copy(theObj, pgroup, null)) != null) {
    1507                    // Add the node to the tree
    1508                    TreeItem newItem = insertObject(newObj, pobj);
    1509
    1510                    // If this is a group, add its first level child items
    1511                    if(newObj instanceof Group) {
    1512                        Iterator<HObject> children = ((Group) newObj).getMemberList().iterator();
    1513                        while(children.hasNext())
    1514                            insertObject(children.next(), newItem);
    1515                    }
    1516                }
    1517            }
    1518            catch (Exception ex) {
    1519                shell.getDisplay().beep();
    1520                Tools.showError(shell, "Paste", ex.getMessage());
    1521            }
    1522        } // (int i = 0; i < objList.length; i++)
    1523    }
    1524
    1525    /**
    1526     * Rename the currently selected object.
    1527     */
    1528    private void renameObject() {
    1529        if (moveFlag) {
    1530            if(!Tools.showConfirm(shell,  "Rename object", "Do you want to rename all the selected object(s) instead of move?"))
    1531                return;
    1532        }
    1533        moveFlag = false;
    1534        currentSelectionsForMove = null;
    1535
    1536        if (selectedObject == null)
    1537            return;
    1538
    1539        if ((selectedObject instanceof Group) && ((Group) selectedObject).isRoot()) {
    1540            shell.getDisplay().beep();
    1541            Tools.showError(shell, "Rename", "Cannot rename the root.");
    1542            return;
    1543        }
    1544
    1545        boolean isH4 = selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4));
    1546        if (isH4) {
    1547            shell.getDisplay().beep();
    1548            Tools.showError(shell, "Rename", "Cannot rename HDF4 object.");
    1549            return;
    1550        }
    1551
    1552        boolean isN3 = selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3));
    1553        if (isN3) {
    1554            shell.getDisplay().beep();
    1555            Tools.showError(shell, "Rename", "Cannot rename NetCDF3 object.");
    1556            return;
    1557        }
    1558
    1559        String oldName = selectedObject.getName();
    1560        String newName = (new InputDialog(shell, "Rename Object",
    1561                "Rename \"" + oldName + "\" to:", oldName)).open();
    1562
    1563        if (newName == null)
    1564            return;
    1565
    1566        newName = newName.trim();
    1567        if ((newName == null) || (newName.length() == 0) || newName.equals(oldName))
    1568            return;
    1569
    1570        try {
    1571            selectedObject.setName(newName);
    1572        }
    1573        catch (Exception ex) {
    1574            shell.getDisplay().beep();
    1575            Tools.showError(shell, "Rename Object", ex.getMessage());
    1576        }
    1577
    1578        selectedItem.setText(newName);
    1579    }
    1580
    1581    private void removeSelectedObjects() {
    1582        FileFormat theFile = getSelectedFile();
    1583        if (theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4))) {
    1584            shell.getDisplay().beep();
    1585            Tools.showError(shell, "Remove object", "Unsupported operation: cannot delete HDF4 object.");
    1586            return;
    1587        }
    1588        if (theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) {
    1589            shell.getDisplay().beep();
    1590            Tools.showError(shell, "Remove object", "Unsupported operation: cannot delete NetCDF3 object.");
    1591            return;
    1592        }
    1593
    1594        TreeItem[] currentSelections = tree.getSelection();
    1595
    1596        if (moveFlag)
    1597            currentSelections = currentSelectionsForMove;
    1598        if ((currentSelections == null) || (currentSelections.length <= 0))
    1599            return;
    1600
    1601        if (!moveFlag) {
    1602            if(!Tools.showConfirm(shell, "Remove object", "Do you want to remove all the selected object(s) ?"))
    1603                return;
    1604        }
    1605
    1606        HObject theObj = null;
    1607        for (int i = 0; i < currentSelections.length; i++) {
    1608            TreeItem currentItem = currentSelections[i];
    1609            theObj = (HObject) currentItem.getData();
    1610
    1611            // Cannot delete a root object
    1612            if (theObj instanceof Group && ((Group) theObj).isRoot()) {
    1613                shell.getDisplay().beep();
    1614                Tools.showError(shell, "Delete Objects", "Unsupported operation: cannot delete the file root.");
    1615                return;
    1616            }
    1617
    1618            if (!moveFlag) {
    1619                if (isObjectOpen(theObj)) {
    1620                    shell.getDisplay().beep();
    1621                    Tools.showError(shell, "Delete Objects", "Cannot delete the selected object: " + theObj
    1622                            + "\nThe dataset or dataset in the group is in use."
    1623                            + "\n\nPlease close the dataset(s) and try again.\n");
    1624                    continue;
    1625                }
    1626            }
    1627
    1628            try {
    1629                theFile.delete(theObj);
    1630            }
    1631            catch (Exception ex) {
    1632                shell.getDisplay().beep();
    1633                Tools.showError(shell, "Delete Objects", ex.getMessage());
    1634                continue;
    1635            }
    1636
    1637            // When a TreeItem is disposed, it should be removed from its parent
    1638            // items member list to prevent a bug when copying and deleting
    1639            // groups/datasets
    1640            ((Group) currentItem.getParentItem().getData()).removeFromMemberList(theObj);
    1641
    1642            if (currentItem.equals(selectedItem)) {
    1643                selectedItem = null;
    1644                selectedObject = null;
    1645                selectedFile = null;
    1646            }
    1647
    1648            currentItem.dispose();
    1649        } // (int i=0; i < currentSelections.length; i++)
    1650    }
    1651
    1652    /**
    1653     * Populates the TreeView with TreeItems corresponding to
    1654     * the top-level user objects in the specified file. The rest
    1655     * of the user objects in the file are populated as TreeItems
    1656     * on demand when the user expands groups.
    1657     *
    1658     * @return the root TreeItem created in the Tree corresponding
    1659     * to the file object.
    1660     */
    1661    private TreeItem populateTree(FileFormat theFile) {
    1662        if (theFile == null) {
    1663            shell.getDisplay().beep();
    1664            Tools.showError(shell, "Open File", "Error opening file");
    1665            log.debug("Error populating tree, File object was null.");
    1666            return null;
    1667        }
    1668        else if ((theFile.getFID() < 0) || (theFile.getRootObject() == null)) {
    1669            if (theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) {
    1670                log.trace("populateTree(): FileID={} Null Root={}", theFile.getFID(), (theFile.getRootObject() == null));
    1671            }
    1672            //TODO: Update FitsFile and NC2File to have a fid other than -1
    1673            // so this check isn't needed
    1674            if (theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4)) ||
    1675                    //theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3)) ||
    1676                    theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
    1677                shell.getDisplay().beep();
    1678                Tools.showError(shell, "Open File", "Error opening file " + theFile.getName());
    1679                log.debug("Error populating tree for {}, File ID was wrong or File root object was null.", theFile.getFilePath());
    1680                return null;
    1681            }
    1682        }
    1683
    1684        TreeItem rootItem = null;
    1685
    1686        try {
    1687            rootItem = insertObject(theFile.getRootObject(), null);
    1688            if (rootItem != null) {
    1689                Iterator<HObject> it = ((Group) rootItem.getData()).getMemberList().iterator();
    1690                while (it.hasNext()) {
    1691                    TreeItem newItem = null;
    1692                    HObject obj = it.next();
    1693
    1694                    newItem = insertObject(obj, rootItem);
    1695
    1696                    // Tell SWT how many members this group has so they can
    1697                    // be populated when the group is expanded
    1698                    if (obj instanceof Group) {
    1699                        newItem.setItemCount(((Group) obj).getMemberList().size());
    1700                        log.debug("populateTree(): group members size {}:", ((Group) obj).getMemberList().size());
    1701                    }
    1702                }
    1703            }
    1704        }
    1705        catch (Exception ex) {
    1706            log.debug("populateTree(): Error populating Tree with members of file {}:", theFile.getFilePath(), ex);
    1707            if (rootItem != null)
    1708                rootItem.dispose();
    1709            shell.getDisplay().beep();
    1710            Tools.showError(shell, "Open File", "Error opening file " + theFile.getName() + "\n\n" + ex.getMessage());
    1711            return null;
    1712        }
    1713
    1714        return rootItem;
    1715    }
    1716
    1717    /**
    1718     * Recursively expand/collapse a given selected TreeItem.
    1719     *
    1720     * @param item the selected tree item
    1721     * @param expand
    1722     *            Expands the TreeItem and its children if true.
    1723     *            Collapse the TreeItem and its children if false.
    1724     */
    1725    //TODO: some groups dont get expanded right, likely due to SetData not being
    1726    // able to catch up with a large number of expanding
    1727    private void recursiveExpand(TreeItem item, boolean expand) {
    1728        if(item == null || !(item.getData() instanceof Group))
    1729            return;
    1730
    1731        TreeItem[] toExpand = item.getItems();
    1732
    1733        item.setExpanded(expand);
    1734
    1735        // Make sure the TreeItem's icon gets set appropriately by
    1736        // notifying its Expand or Collapse listener
    1737        Event event = new Event();
    1738        event.item = item;
    1739        tree.notifyListeners(expand ? SWT.Expand : SWT.Collapse, event);
    1740
    1741        // All SetData events for this group must be processed before any
    1742        // child groups can be expanded, otherwise their data will be
    1743        // null
    1744        while(tree.getDisplay().readAndDispatch());
    1745
    1746        for(int i = 0; i < toExpand.length; i++)
    1747            recursiveExpand(toExpand[i], expand);
    1748    }
    1749
    1750    /**
    1751     * Gets the Image to set on the TreeItem for the specified HObject,
    1752     * based on the type of HObject it is.
    1753     *
    1754     * @param obj
    1755     *
    1756     * @return the image for the specified HObject
    1757     */
    1758    private Image getObjectTypeImage(HObject obj) {
    1759        if (obj == null)
    1760            return null;
    1761
    1762        // Should be safe to cast to a MetaDataContainer here because the
    1763        // TreeView should never be able to select an object that does
    1764        // not implement the MetaDataContainer interface
    1765        boolean hasAttribute = ((MetaDataContainer) obj).hasAttribute();
    1766
    1767        if(obj instanceof Dataset) {
    1768            if (obj instanceof ScalarDS) {
    1769                ScalarDS sd = (ScalarDS) obj;
    1770                Datatype dt = sd.getDatatype();
    1771
    1772                if (sd.isImage()) {
    1773                    if (hasAttribute)
    1774                        return imageIconA;
    1775                    else
    1776                        return imageIcon;
    1777                }
    1778                else if ((dt != null) && dt.isText()) {
    1779                    if (hasAttribute)
    1780                        return textIconA;
    1781                    else
    1782                        return textIcon;
    1783                }
    1784                else {
    1785                    if (hasAttribute)
    1786                        return datasetIconA;
    1787                    else
    1788                        return datasetIcon;
    1789                }
    1790            }
    1791            else if (obj instanceof CompoundDS) {
    1792                if (hasAttribute)
    1793                    return tableIconA;
    1794                else
    1795                    return tableIcon;
    1796            }
    1797        }
    1798        else if(obj instanceof Group) {
    1799            if(((Group) obj).isRoot()) {
    1800                FileFormat theFile = obj.getFileFormat();
    1801
    1802                if(theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3))) {
    1803                    if(theFile.isReadOnly())
    1804                        return nc3IconR;
    1805                    else
    1806                        return nc3Icon;
    1807                }
    1808                else if(theFile.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4))) {
    1809                    if(theFile.isReadOnly())
    1810                        return h4IconR;
    1811                    else
    1812                        return h4Icon;
    1813                }
    1814                else {
    1815                    if(theFile.isReadOnly())
    1816                        return h5IconR;
    1817                    else
    1818                        return h5Icon;
    1819                }
    1820            }
    1821            else {
    1822                if(hasAttribute)
    1823                    return folderCloseIconA;
    1824                else
    1825                    return folderCloseIcon;
    1826            }
    1827        }
    1828        else if(obj instanceof Datatype) {
    1829            if(hasAttribute)
    1830                return datatypeIconA;
    1831            else
    1832                return datatypeIcon;
    1833        }
    1834
    1835        return questionIcon;
    1836    }
    1837
    1838    /**
    1839     * Checks if a file is already open.
    1840     *
    1841     * @param filename the file to query
    1842     *
    1843     * @return true if the file is open
    1844     */
    1845    private boolean isFileOpen(String filename) {
    1846        boolean isOpen = false;
    1847
    1848        // Find the file by matching its file name from the list of open files
    1849        FileFormat theFile = null;
    1850        Iterator<FileFormat> iterator = fileList.iterator();
    1851        while (iterator.hasNext()) {
    1852            theFile = iterator.next();
    1853            if (theFile.getFilePath().equals(filename)) {
    1854                isOpen = true;
    1855                break;
    1856            }
    1857        }
    1858
    1859        return isOpen;
    1860    }
    1861
    1862    /**
    1863     * Checks if an object is already open.
    1864     */
    1865    private boolean isObjectOpen(HObject obj) {
    1866        boolean isOpen = false;
    1867
    1868        if (obj instanceof Group) {
    1869            Group g = (Group) obj;
    1870            List<?> members = g.getMemberList();
    1871            if ((members == null) || members.isEmpty()) {
    1872                return false;
    1873            }
    1874            else {
    1875                int n = members.size();
    1876                for (int i = 0; i < n; i++) {
    1877                    HObject theObj = (HObject) members.get(i);
    1878                    isOpen = (viewer.getDataView(theObj) != null);
    1879                    if (isOpen)
    1880                        break;
    1881                }
    1882            }
    1883        }
    1884        else {
    1885            return (viewer.getDataView(obj) != null);
    1886        }
    1887
    1888        return isOpen;
    1889    }
    1890
    1891    /**
    1892     * Returns a list that lists all TreeItems in the
    1893     * current Tree that are children of the specified
    1894     * TreeItem in a breadth-first manner.
    1895     *
    1896     * @param the current Tree item
    1897     *
    1898     * @return list of TreeItems
    1899     */
    1900    private ArrayList<TreeItem> getItemsBreadthFirst(TreeItem item) {
    1901        if (item == null)
    1902            return null;
    1903
    1904        ArrayList<TreeItem> allItems = new ArrayList<>();
    1905        Queue<TreeItem> currentChildren = new LinkedList<>();
    1906        TreeItem currentItem = item;
    1907
    1908        // Add all root items in the Tree to a Queue
    1909        currentChildren.addAll(Arrays.asList(currentItem.getItems()));
    1910
    1911        // For every item in the queue, remove it from the head of the queue,
    1912        // add it to the list of all items, then add all of its possible children
    1913        // TreeItems to the end of the queue. This produces a breadth-first
    1914        // ordering of the Tree's TreeItems.
    1915        while(!currentChildren.isEmpty()) {
    1916            currentItem = currentChildren.remove();
    1917            allItems.add(currentItem);
    1918
    1919            if(currentItem.getItemCount() <= 0)
    1920                continue;
    1921
    1922            currentChildren.addAll(Arrays.asList(currentItem.getItems()));
    1923        }
    1924
    1925        return allItems;
    1926    }
    1927
    1928    /**
    1929     * Returns a list of all user objects that traverses the subtree rooted at
    1930     * this item in breadth-first order..
    1931     *
    1932     * @param item
    1933     *            the item to start with.
    1934     */
    1935    private final List<Object> breadthFirstUserObjects(TreeItem item) {
    1936        if (item == null) return null;
    1937
    1938        ArrayList<Object> list = new ArrayList<>();
    1939        list.add(item.getData()); // Add this item to the list first
    1940
    1941        Iterator<TreeItem> it = getItemsBreadthFirst(item).iterator();
    1942        TreeItem theItem = null;
    1943
    1944        while (it.hasNext()) {
    1945            theItem = it.next();
    1946            list.add(theItem.getData());
    1947        }
    1948
    1949        return list;
    1950    }
    1951
    1952    /**
    1953     * Find first object that is matched by name under the specified
    1954     * TreeItem.
    1955     *
    1956     * @param objName
    1957     *            -- the object name.
    1958     * @return the object if found, otherwise, returns null.
    1959     */
    1960    private final HObject find(String objName, TreeItem parentItem) {
    1961        if (objName == null || objName.length() <= 0 || parentItem == null) return null;
    1962
    1963        HObject retObj = null;
    1964        boolean isFound = false;
    1965        boolean isPrefix = false;
    1966        boolean isSuffix = false;
    1967        boolean isContain = false;
    1968
    1969        if (objName.equals("*"))
    1970            return null;
    1971
    1972        if (objName.startsWith("*")) {
    1973            isSuffix = true;
    1974            objName = objName.substring(1, objName.length());
    1975        }
    1976
    1977        if (objName.endsWith("*")) {
    1978            isPrefix = true;
    1979            objName = objName.substring(0, objName.length() - 1);
    1980        }
    1981
    1982        if (isPrefix && isSuffix) {
    1983            isContain = true;
    1984            isPrefix = isSuffix = false;
    1985        }
    1986
    1987        if (objName == null || objName.length() <= 0)
    1988            return null;
    1989
    1990        HObject obj = null;
    1991        String theName = null;
    1992        TreeItem theItem = null;
    1993        Iterator<TreeItem> it = getItemsBreadthFirst(parentItem).iterator();
    1994        while (it.hasNext()) {
    1995            theItem = it.next();
    1996            obj = (HObject) theItem.getData();
    1997            if (obj != null && (theName = obj.getName()) != null) {
    1998                if (isPrefix)
    1999                    isFound = theName.startsWith(objName);
    2000                else if (isSuffix)
    2001                    isFound = theName.endsWith(objName);
    2002                else if (isContain)
    2003                    isFound = theName.contains(objName);
    2004                else
    2005                    isFound = theName.equals(objName);
    2006
    2007                if (isFound) {
    2008                    retObj = obj;
    2009                    break;
    2010                }
    2011            }
    2012        }
    2013
    2014        if (retObj != null) {
    2015            tree.deselectAll();
    2016            tree.setSelection(theItem);
    2017            tree.showItem(theItem);
    2018        }
    2019
    2020        return retObj;
    2021    }
    2022
    2023    /**
    2024     * Save the current file into a new HDF4 file. Since HDF4 does not
    2025     * support packing, the source file is copied into the new file with
    2026     * the exact same content.
    2027     */
    2028    private final void saveAsHDF4(FileFormat srcFile) {
    2029        if (srcFile == null) {
    2030            shell.getDisplay().beep();
    2031            Tools.showError(shell, "Save", "Select a file to save.");
    2032            return;
    2033        }
    2034
    2035        HObject root = srcFile.getRootObject();
    2036        if (root == null) {
    2037            shell.getDisplay().beep();
    2038            Tools.showError(shell, "Save", "The file is empty.");
    2039            return;
    2040        }
    2041
    2042        String currentDir = srcFile.getParent();
    2043
    2044        if (currentDir != null)
    2045            currentDir += File.separator;
    2046        else
    2047            currentDir = "";
    2048
    2049        String filename = null;
    2050        if (((HDFView) viewer).getTestState()) {
    2051            filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open();
    2052        }
    2053        else {
    2054            FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
    2055            fChooser.setFileName(Tools.checkNewFile(currentDir, ".hdf").getName());
    2056
    2057            DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF4();
    2058            fChooser.setFilterExtensions(new String[] {filter.getExtensions()});
    2059            fChooser.setFilterNames(new String[] {filter.getDescription()});
    2060            fChooser.setFilterIndex(0);
    2061
    2062            filename = fChooser.open();
    2063        }
    2064        if(filename == null)
    2065            return;
    2066
    2067        try {
    2068            Tools.createNewFile(filename, currentDir, FileFormat.FILE_TYPE_HDF4, fileList);
    2069        }
    2070        catch (Exception ex) {
    2071            Tools.showError(shell, "Save", ex.getMessage());
    2072        }
    2073
    2074        // Since cannot pack hdf4, simply copy the whole physical file
    2075        int length = 0;
    2076        int bsize = 512;
    2077        byte[] buffer;
    2078
    2079        try (BufferedInputStream bi = new BufferedInputStream(new FileInputStream(srcFile.getFilePath()))) {
    2080            try (BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(filename))) {
    2081                buffer = new byte[bsize];
    2082                try {
    2083                    length = bi.read(buffer, 0, bsize);
    2084                }
    2085                catch (Exception ex) {
    2086                    length = 0;
    2087                }
    2088                while (length > 0) {
    2089                    try {
    2090                        bo.write(buffer, 0, length);
    2091                        length = bi.read(buffer, 0, bsize);
    2092                    }
    2093                    catch (Exception ex) {
    2094                        length = 0;
    2095                    }
    2096                }
    2097
    2098                try {
    2099                    bo.flush();
    2100                }
    2101                catch (Exception ex) {
    2102                    log.debug("Output file:", ex);
    2103                }
    2104            }
    2105            catch (Exception ex) {
    2106                shell.getDisplay().beep();
    2107                Tools.showError(shell, "Save", ex.getMessage());
    2108                return;
    2109            }
    2110        }
    2111        catch (Exception ex) {
    2112            shell.getDisplay().beep();
    2113            Tools.showError(shell, "Save", ex.getMessage() + "\n" + filename);
    2114            return;
    2115        }
    2116
    2117        try {
    2118            openFile(filename, FileFormat.WRITE);
    2119        }
    2120        catch (Exception ex) {
    2121            shell.getDisplay().beep();
    2122            Tools.showError(shell, "Save", ex.getMessage() + "\n" + filename);
    2123        }
    2124    }
    2125
    2126    /**
    2127     * Copy the current file into a new HDF5 file. The new file does not include the
    2128     * inaccessible objects. Values of reference dataset are not updated in the
    2129     * new file.
    2130     */
    2131    private void saveAsHDF5(FileFormat srcFile) {
    2132        if (srcFile == null) {
    2133            shell.getDisplay().beep();
    2134            Tools.showError(shell, "Save", "Select a file to save.");
    2135            return;
    2136        }
    2137
    2138        HObject root = srcFile.getRootObject();
    2139        if (root == null) {
    2140            shell.getDisplay().beep();
    2141            Tools.showError(shell, "Save", "The file is empty.");
    2142            return;
    2143        }
    2144
    2145        String currentDir = srcFile.getParent();
    2146
    2147        if (currentDir != null)
    2148            currentDir += File.separator;
    2149        else
    2150            currentDir = "";
    2151
    2152        String filename = null;
    2153        if (((HDFView) viewer).getTestState()) {
    2154            filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open();
    2155        }
    2156        else {
    2157            FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
    2158            fChooser.setFileName(Tools.checkNewFile(currentDir, ".h5").getName());
    2159
    2160            DefaultFileFilter filter = DefaultFileFilter.getFileFilterHDF5();
    2161            fChooser.setFilterExtensions(new String[] {filter.getExtensions()});
    2162            fChooser.setFilterNames(new String[] {filter.getDescription()});
    2163            fChooser.setFilterIndex(0);
    2164
    2165            filename = fChooser.open();
    2166        }
    2167        if(filename == null)
    2168            return;
    2169
    2170        try {
    2171            Tools.createNewFile(filename, currentDir, FileFormat.FILE_TYPE_HDF5, fileList);
    2172        }
    2173        catch (Exception ex) {
    2174            Tools.showError(shell, "Save", ex.getMessage());
    2175        }
    2176
    2177        TreeItem rootItem = findTreeItem(root);
    2178        int n = rootItem.getItemCount();
    2179        ArrayList<TreeItem> objList = new ArrayList<>(n);
    2180
    2181        try {
    2182            for (int i = 0; i < n; i++) objList.add(rootItem.getItem(i));
    2183        }
    2184        catch (Exception ex) {
    2185            log.debug("saveAsHDF5() objList add failure: ", ex);
    2186        }
    2187
    2188        FileFormat newFile = null;
    2189        try {
    2190            newFile = openFile(filename, FileFormat.WRITE);
    2191        }
    2192        catch (Exception ex) {
    2193            shell.getDisplay().beep();
    2194            Tools.showError(shell, "Save", ex.getMessage() + "\n" + filename);
    2195            return;
    2196        }
    2197
    2198        if (newFile == null)
    2199            return;
    2200
    2201        HObject pitem = newFile.getRootObject();
    2202
    2203        pasteObject(objList.toArray(new TreeItem[0]), findTreeItem(pitem), newFile);
    2204        objList.clear();
    2205
    2206        Group srcGroup = (Group) root;
    2207        Group dstGroup = (Group) newFile.getRootObject();
    2208        Object[] parameter = new Object[2];
    2209        Class<?> classHOjbect = null;
    2210        Class<?>[] parameterClass = new Class[2];
    2211        Method method = null;
    2212
    2213        // Copy attributes of the root group
    2214        try {
    2215            parameter[0] = srcGroup;
    2216            parameter[1] = dstGroup;
    2217            classHOjbect = Class.forName("hdf.object.HObject");
    2218            parameterClass[0] = parameterClass[1] = classHOjbect;
    2219            method = newFile.getClass().getMethod("copyAttributes", parameterClass);
    2220            method.invoke(newFile, parameter);
    2221        }
    2222        catch (Exception ex) {
    2223            shell.getDisplay().beep();
    2224            Tools.showError(shell, "Save", ex.getMessage());
    2225        }
    2226
    2227        // Update reference datasets
    2228        parameter[0] = srcGroup.getFileFormat();
    2229        parameter[1] = newFile;
    2230        parameterClass[0] = parameterClass[1] = parameter[0].getClass();
    2231        try {
    2232            method = newFile.getClass().getMethod("updateReferenceDataset", parameterClass);
    2233            method.invoke(newFile, parameter);
    2234        }
    2235        catch (Exception ex) {
    2236            shell.getDisplay().beep();
    2237            Tools.showError(shell, "Save", ex.getMessage());
    2238        }
    2239    }
    2240
    2241    /** Save data as file.
    2242     *
    2243     * @throws Exception if a failure occurred
    2244     */
    2245    private void saveDataAsFile() throws Exception {
    2246        if (!(selectedObject instanceof Dataset) || (selectedObject == null) || (selectedItem == null))
    2247            return;
    2248
    2249        File chosenFile = null;
    2250        String filename = null;
    2251        Dataset dataset = (Dataset) selectedObject;
    2252        String currentDir = dataset.getFile().substring(0, dataset.getFile().lastIndexOf(File.separator));
    2253        String msgtext = null;
    2254        if(binaryOrder == 99)
    2255            msgtext = "Save Dataset Data To Text File --- " + dataset.getName();
    2256        else
    2257            msgtext = "Save Current Data To Binary File --- " + dataset.getName();
    2258        if (((HDFView) viewer).getTestState()) {
    2259            filename = currentDir + File.separator + new InputDialog(shell, msgtext, "").open();
    2260        }
    2261        else {
    2262            FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
    2263            fChooser.setFilterPath(currentDir);
    2264
    2265            DefaultFileFilter filter = null;
    2266
    2267            if (binaryOrder == 99) {
    2268                fChooser.setText(msgtext);
    2269                fChooser.setFileName(dataset.getName() + ".txt");
    2270                filter = DefaultFileFilter.getFileFilterText();
    2271            }
    2272            else {
    2273                fChooser.setText(msgtext);
    2274                fChooser.setFileName(dataset.getName() + ".bin");
    2275                filter = DefaultFileFilter.getFileFilterBinary();
    2276            }
    2277
    2278            fChooser.setFilterExtensions(new String[] {"*", filter.getExtensions()});
    2279            fChooser.setFilterNames(new String[] {"All Files", filter.getDescription()});
    2280            fChooser.setFilterIndex(1);
    2281
    2282            filename = fChooser.open();
    2283        }
    2284        if(filename == null)
    2285            return;
    2286
    2287        // Check if the file is in use
    2288        List<?> saveFileList = viewer.getTreeView().getCurrentFiles();
    2289        if (saveFileList != null) {
    2290            FileFormat theFile = null;
    2291            Iterator<?> iterator = saveFileList.iterator();
    2292            while (iterator.hasNext()) {
    2293                theFile = (FileFormat) iterator.next();
    2294                if (theFile.getFilePath().equals(filename)) {
    2295                    shell.getDisplay().beep();
    2296                    Tools.showError(shell, "Export Dataset", "Unable to save data to file \"" + filename + "\". \nThe file is being used.");
    2297                    return;
    2298                }
    2299            }
    2300        }
    2301
    2302        chosenFile = new File(filename);
    2303
    2304        if (chosenFile.exists()) {
    2305            if(!Tools.showConfirm(shell, "Export Dataset", "File exists. Do you want to replace it?"))
    2306                return;
    2307        }
    2308
    2309        boolean isH4 = selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4));
    2310        if (isH4) {
    2311            shell.getDisplay().beep();
    2312            Tools.showError(shell, "Save", "Cannot export HDF4 object.");
    2313            return;
    2314        }
    2315
    2316        boolean isN3 = selectedObject.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3));
    2317        if (isN3) {
    2318            shell.getDisplay().beep();
    2319            Tools.showError(shell, "Save", "Cannot export netCDF3 object.");
    2320            return;
    2321        }
    2322
    2323        try {
    2324            selectedObject.getFileFormat().exportDataset(filename, dataset, binaryOrder);
    2325            viewer.showStatus("Data saved to: " + filename);
    2326        }
    2327        catch (Exception ex) {
    2328            shell.getDisplay().beep();
    2329            Tools.showError(shell, "Save", "Unable to export dataset: " + ex.getMessage());
    2330        }
    2331    }
    2332
    2333    /** enable/disable GUI components */
    2334    private static void setEnabled(List<MenuItem> list, boolean b) {
    2335        if (list == null)
    2336            return;
    2337
    2338        Iterator<MenuItem> it = list.iterator();
    2339        while (it.hasNext())
    2340            it.next().setEnabled(b);
    2341    }
    2342
    2343    /**
    2344     * Opens a file and retrieves the file structure of the file. It also can be
    2345     * used to create a new file by setting the accessID to FileFormat.CREATE.
    2346     *
    2347     * Subclasses must implement this function to take appropriate steps to open
    2348     * a file.
    2349     *
    2350     * @param filename
    2351     *            the name of the file to open.
    2352     * @param accessID
    2353     *            identifier for the file access. Valid value of accessID is:
    2354     *            <ul>
    2355     *            <li>FileFormat.READ --- allow read-only access to file.</li>
    2356     *            <li>FileFormat.WRITE --- allow read and write access to file.</li>
    2357     *            <li>FileFormat.CREATE --- create a new file.</li>
    2358     *            </ul>
    2359     *
    2360     * @return the FileFormat of this file if successful; otherwise returns null.
    2361     *
    2362     * @throws Exception if a failure occurred
    2363     */
    2364    @Override
    2365    public FileFormat openFile(String filename, int accessID) throws Exception {
    2366        log.trace("openFile: {},{}", filename, accessID);
    2367        FileFormat fileFormat = null;
    2368        boolean isSWMRFile = (FileFormat.MULTIREAD == (accessID & FileFormat.MULTIREAD));
    2369        log.trace("openFile: isSWMRFile={}", isSWMRFile);
    2370        boolean isNewFile = (FileFormat.OPEN_NEW == (accessID & FileFormat.OPEN_NEW));
    2371        if (isNewFile)
    2372            accessID = accessID - FileFormat.OPEN_NEW; //strip OPEN_NEW
    2373
    2374        if (isFileOpen(filename)) {
    2375            viewer.showStatus("File is in use.");
    2376            return null;
    2377        }
    2378
    2379        File tmpFile = new File(filename);
    2380        if (!tmpFile.exists())
    2381            throw new FileNotFoundException("File does not exist.");
    2382
    2383        if (!tmpFile.canWrite() && !isSWMRFile)
    2384            accessID = FileFormat.READ;
    2385
    2386        Enumeration<?> keys = FileFormat.getFileFormatKeys();
    2387
    2388        String theKey = null;
    2389        while (keys.hasMoreElements()) {
    2390            theKey = (String) keys.nextElement();
    2391            if (theKey.equals(FileFormat.FILE_TYPE_HDF4)) {
    2392                log.trace("openFile: {} FILE_TYPE_HDF4", filename);
    2393                try {
    2394                    FileFormat h4format = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4);
    2395                    if ((h4format != null) && h4format.isThisType(filename)) {
    2396                        fileFormat = h4format.createInstance(filename, accessID);
    2397                        break;
    2398                    }
    2399                }
    2400                catch (UnsatisfiedLinkError e) {
    2401                    log.debug("openFile({}): HDF4 library link error:", filename, e);
    2402                    viewer.showError("Unable to open file '" + filename + "': HDF4 library linking error");
    2403                }
    2404                catch (Exception err) {
    2405                    log.debug("openFile: Error retrieving the file structure of {}:", filename, err);
    2406                }
    2407                continue;
    2408            }
    2409            else if (theKey.equals(FileFormat.FILE_TYPE_HDF5)) {
    2410                log.trace("openFile: {} FILE_TYPE_HDF5", filename);
    2411                try {
    2412                    FileFormat h5format = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5);
    2413                    if ((h5format != null) && h5format.isThisType(filename)) {
    2414                        fileFormat = h5format.createInstance(filename, accessID);
    2415                        break;
    2416                    }
    2417                }
    2418                catch (UnsatisfiedLinkError e) {
    2419                    log.debug("openFile({}): HDF5 library link error:", filename, e);
    2420                    viewer.showError("Unable to open file '" + filename + "': HDF5 library linking error");
    2421                }
    2422                catch (Exception err) {
    2423                    log.debug("openFile: Error retrieving the file structure of {}:", filename, err);
    2424                }
    2425                continue;
    2426            }
    2427            else if (theKey.equals(FileFormat.FILE_TYPE_NC3)) {
    2428                log.trace("openFile: {} FILE_TYPE_NC3", filename);
    2429                try {
    2430                    FileFormat nc3format = FileFormat.getFileFormat(FileFormat.FILE_TYPE_NC3);
    2431                    if ((nc3format != null) && nc3format.isThisType(filename)) {
    2432                        fileFormat = nc3format.createInstance(filename, accessID);
    2433                        break;
    2434                    }
    2435                }
    2436                catch (UnsatisfiedLinkError e) {
    2437                    log.debug("openFile({}): NetCDF3 library link error:", filename, e);
    2438                    viewer.showError("Unable to open file '" + filename + "': NetCDF3 library linking error");
    2439                }
    2440                catch (Exception err) {
    2441                    log.debug("openFile: Error retrieving the file structure of {}:", filename, err);
    2442                }
    2443                continue;
    2444            }
    2445            else {
    2446                log.trace("openFile: {} Other", filename);
    2447                try {
    2448                    FileFormat theformat = FileFormat.getFileFormat(theKey);
    2449                    if (theformat.isThisType(filename)) {
    2450                        fileFormat = theformat.createInstance(filename, accessID);
    2451                        break;
    2452                    }
    2453                }
    2454                catch (Exception err) {
    2455                    log.debug("openFile: Error retrieving the file structure of {}:", filename, err);
    2456                }
    2457            }
    2458        }
    2459
    2460        if (fileFormat == null)
    2461            throw new java.io.IOException("Unsupported fileformat - " + filename);
    2462
    2463        if (fileFormat.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
    2464            if (tempIdxType >= 0) {
    2465                fileFormat.setIndexType(tempIdxType);
    2466
    2467                // Reset the temporary index type
    2468                tempIdxType = -1;
    2469            }
    2470            else
    2471                fileFormat.setIndexType(fileFormat.getIndexType(ViewProperties.getIndexType()));
    2472
    2473            if (tempIdxOrder >= 0) {
    2474                fileFormat.setIndexOrder(tempIdxOrder);
    2475
    2476                // Reset the temporary index order
    2477                tempIdxOrder = -1;
    2478            }
    2479            else
    2480                fileFormat.setIndexOrder(fileFormat.getIndexOrder(ViewProperties.getIndexOrder()));
    2481        }
    2482
    2483        return initFile(fileFormat);
    2484    }
    2485
    2486    /**
    2487     * Initializes a FileFormat object by opening it and populating the file tree structure.
    2488     *
    2489     * @param fileFormat
    2490     *            the file to open with an existing FileFormat instance.
    2491     *
    2492     * @return the initialized FileFormat of this file if successful; otherwise returns null.
    2493     *
    2494     * @throws Exception
    2495     *             if a failure occurred
    2496     */
    2497    private FileFormat initFile(FileFormat fileFormat) throws Exception {
    2498        log.trace("initFile[{}] - start", fileFormat.getAbsolutePath());
    2499
    2500        TreeItem fileRoot = null;
    2501
    2502        shell.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_WAIT));
    2503
    2504        try {
    2505            fileFormat.setMaxMembers(ViewProperties.getMaxMembers());
    2506            fileFormat.setStartMembers(ViewProperties.getStartMembers());
    2507
    2508            fileFormat.open();
    2509
    2510            fileRoot = populateTree(fileFormat);
    2511
    2512            if (fileRoot != null) {
    2513                /* Expand top level items of root object */
    2514                int currentRowCount = tree.getItemCount();
    2515                if (currentRowCount > 0)
    2516                    tree.getItem(currentRowCount - 1).setExpanded(true);
    2517
    2518                fileList.add(fileFormat);
    2519            }
    2520
    2521            tree.setItemCount(fileList.size());
    2522
    2523            log.trace("initFile[{}] - fileList items={}", fileFormat.getAbsolutePath(), fileList.size());
    2524        }
    2525        catch (Exception ex) {
    2526            log.debug("initFile: FileFormat init error:", ex);
    2527            fileFormat = null;
    2528        }
    2529        finally {
    2530            shell.setCursor(null);
    2531        }
    2532
    2533        return fileFormat;
    2534    }
    2535
    2536    @Override
    2537    public FileFormat reopenFile(FileFormat fileFormat, int newFileAccessMode) throws Exception {
    2538        String fileFormatName = fileFormat.getAbsolutePath();
    2539
    2540        // Make sure to reload the file using the file's current indexing options
    2541        tempIdxType = fileFormat.getIndexType(null);
    2542        tempIdxOrder = fileFormat.getIndexOrder(null);
    2543
    2544        closeFile(fileFormat);
    2545        ((HDFView) viewer).showMetaData(null);
    2546
    2547        if (newFileAccessMode < 0) {
    2548            if (ViewProperties.isReadOnly())
    2549                return openFile(fileFormatName, FileFormat.READ);
    2550            else if (ViewProperties.isReadSWMR())
    2551                return openFile(fileFormatName, FileFormat.READ | FileFormat.MULTIREAD);
    2552            else
    2553                return openFile(fileFormatName, FileFormat.WRITE);
    2554        }
    2555        else
    2556            return openFile(fileFormatName, newFileAccessMode);
    2557    }
    2558
    2559    /**
    2560     * Close a file
    2561     *
    2562     * @param file
    2563     *            the file to close
    2564     *
    2565     * @throws Exception if a failure occurred
    2566     */
    2567    @Override
    2568    public void closeFile(FileFormat file) throws Exception {
    2569        if (file == null) return;
    2570
    2571        // Find the file item in the tree and remove it
    2572        FileFormat theFile = null;
    2573        TreeItem[] openFiles = tree.getItems(); // Returns the top-level items of the tree
    2574
    2575        for (int i = 0; i < openFiles.length; i++) {
    2576            theFile = ((Group) openFiles[i].getData()).getFileFormat();
    2577
    2578            if (theFile.equals(file)) {
    2579                // Remove TreeItem from the view
    2580                openFiles[i].dispose();
    2581                log.trace("dispose({}):", theFile.getFilePath());
    2582
    2583                try {
    2584                    theFile.close();
    2585                }
    2586                catch (Exception ex) {
    2587                    log.debug("closeFile({}):", theFile.getFilePath(), ex);
    2588                }
    2589
    2590                fileList.remove(theFile);
    2591                if (theFile.equals(selectedFile)) {
    2592                    selectedFile = null;
    2593                    selectedObject = null;
    2594                    selectedItem = null;
    2595                }
    2596
    2597                break;
    2598            }
    2599        }
    2600    }
    2601
    2602    /**
    2603     * Save a file
    2604     *
    2605     * @param file
    2606     *            the file to save
    2607     *
    2608     * @throws Exception if a failure occurred
    2609     */
    2610    @Override
    2611    public void saveFile(FileFormat file) throws Exception {
    2612        if (file == null) {
    2613            shell.getDisplay().beep();
    2614            Tools.showError(shell, "Save", "Select a file to save.");
    2615            return;
    2616        }
    2617
    2618        boolean isH4 = file.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4));
    2619        boolean isH5 = file.isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
    2620
    2621        if (!(isH4 || isH5)) {
    2622            shell.getDisplay().beep();
    2623            Tools.showError(shell, "Save", "Saving file is not supported for this file type");
    2624            return;
    2625        }
    2626
    2627        // Write the change of the data into the file before saving the file
    2628        ((HDFView) viewer).writeDataToFile(file);
    2629
    2630        if (isH5)
    2631            saveAsHDF5(file);
    2632        else if (isH4)
    2633            saveAsHDF4(file);
    2634    }
    2635
    2636    /**
    2637     * Returns the tree item that contains the given data object.
    2638     */
    2639    @Override
    2640    public TreeItem findTreeItem(HObject obj) {
    2641        if (obj == null)
    2642            return null;
    2643
    2644        if (obj.getFileFormat().getRootObject() == null)
    2645            return null;
    2646
    2647        // Locate the item's file root to save on search time in large files
    2648        TreeItem[] fileRoots = tree.getItems();
    2649        TreeItem rootItem = null;
    2650        HObject rootObject = null;
    2651        for (int i = 0; i < fileRoots.length; i++) {
    2652            rootItem = fileRoots[i];
    2653            rootObject = (HObject) rootItem.getData();
    2654
    2655            if (rootObject == null)
    2656                continue;
    2657
    2658            if (rootObject.getFileFormat().equals(obj.getFileFormat())) {
    2659                // If the object being looked for is a file root, return
    2660                // this found TreeItem
    2661                if (obj instanceof Group && ((Group) obj).isRoot())
    2662                    return rootItem;
    2663
    2664                // Else the file root for the object being looked for has
    2665                // been found, continue the search through only this TreeItem's
    2666                // members
    2667                break;
    2668            }
    2669        }
    2670
    2671        TreeItem theItem = null;
    2672        HObject theObj = null;
    2673        List<TreeItem> breadthFirstItems = getItemsBreadthFirst(rootItem);
    2674
    2675        if (breadthFirstItems != null) {
    2676            Iterator<TreeItem> it = getItemsBreadthFirst(rootItem).iterator();
    2677
    2678            while (it.hasNext()) {
    2679                theItem = it.next();
    2680                theObj = (HObject) theItem.getData();
    2681
    2682                if (theObj == null)
    2683                    continue;
    2684
    2685                if (theObj.equals(obj))
    2686                    return theItem;
    2687            }
    2688        }
    2689
    2690        return null;
    2691    }
    2692
    2693    /**
    2694     * change the display option.
    2695     */
    2696    @Override
    2697    public void setDefaultDisplayMode(boolean displaymode) {
    2698        isDefaultDisplay = displaymode;
    2699    }
    2700
    2701    /**
    2702     * Gets the selected file. When multiple files are open, we need to know
    2703     * which file is currently selected.
    2704     *
    2705     * @return the FileFormat of the currently selected file.
    2706     */
    2707    @Override
    2708    public FileFormat getSelectedFile() {
    2709        return selectedFile;
    2710    }
    2711
    2712    /**
    2713     * @return the currently selected object in the tree.
    2714     */
    2715    @Override
    2716    public HObject getCurrentObject() {
    2717        return selectedObject;
    2718    }
    2719
    2720    /**
    2721     * @return the Tree which holds the file structure.
    2722     */
    2723    @Override
    2724    public Tree getTree() {
    2725        return tree;
    2726    }
    2727
    2728    /**
    2729     * @return the list of currently open files.
    2730     */
    2731    @Override
    2732    public List<FileFormat> getCurrentFiles() {
    2733        return fileList;
    2734    }
    2735
    2736    /**
    2737     * Display the content of a data object.
    2738     *
    2739     * @param dataObject
    2740     *            the data object
    2741     *
    2742     * @return the DataView that displays the data content
    2743     *
    2744     * @throws Exception if a failure occurred
    2745     */
    2746    @Override
    2747    public DataView showDataContent(HObject dataObject) throws Exception {
    2748        /* Can only display objects with data */
    2749        if ((dataObject == null) || !(dataObject instanceof DataFormat))
    2750            return null;
    2751
    2752        log.trace("showDataContent({}): start", dataObject.getName());
    2753
    2754        /* Set up the default display properties passed to the DataView instance */
    2755        DataView theView = null;
    2756        DataFormat d = (DataFormat) dataObject;
    2757        HashMap<DATA_VIEW_KEY, Serializable> map = new HashMap<>(8);
    2758
    2759        if (!d.isInited())
    2760            d.init();
    2761
    2762        boolean isImage = ((d instanceof ScalarDS) && ((ScalarDS) d).isImage());
    2763        boolean isDisplayTypeChar = false;
    2764        boolean isTransposed = false;
    2765        boolean isIndexBase1 = ViewProperties.isIndexBase1();
    2766        BitSet bitmask = null;
    2767        String dataViewName = null;
    2768
    2769        if (isDefaultDisplay) { /* Displaying a data object using the default display options */
    2770            DataView existingView = viewer.getDataView((HObject) d);
    2771
    2772            /*
    2773             * Check to make sure this data object isn't already opened in an existing
    2774             * DataView. If it is, just bring that DataView to focus.
    2775             */
    2776            if (existingView != null) {
    2777                Shell[] shells = Display.getDefault().getShells();
    2778
    2779                if (shells.length >= 1) {
    2780                    for (int i = 0; i < shells.length; i++) {
    2781                        DataView view = (DataView) shells[i].getData();
    2782
    2783                        if (view != null) {
    2784                            if (view.equals(existingView)) {
    2785                                shells[i].forceActive();
    2786
    2787                                log.trace("showDataContent(): found existing DataView for data object {}", dataObject.getName());
    2788
    2789                                return view;
    2790                            }
    2791                        }
    2792                    }
    2793                }
    2794            }
    2795        }
    2796        else { /* Open Dialog to allow user to choose different data display options */
    2797            DataOptionDialog dialog = new DataOptionDialog(shell, d);
    2798            dialog.open();
    2799
    2800            if (dialog.isCancelled())
    2801                return null;
    2802
    2803            isImage = dialog.isImageDisplay();
    2804            isDisplayTypeChar = dialog.isDisplayTypeChar();
    2805            dataViewName = dialog.getDataViewName();
    2806            isTransposed = dialog.isTransposed();
    2807            bitmask = dialog.getBitmask();
    2808            isIndexBase1 = dialog.isIndexBase1();
    2809            isApplyBitmaskOnly = dialog.isApplyBitmaskOnly();
    2810        }
    2811
    2812        map.put(ViewProperties.DATA_VIEW_KEY.OBJECT, dataObject);
    2813        map.put(ViewProperties.DATA_VIEW_KEY.VIEW_NAME, dataViewName);
    2814        map.put(ViewProperties.DATA_VIEW_KEY.CHAR, isDisplayTypeChar);
    2815        map.put(ViewProperties.DATA_VIEW_KEY.TRANSPOSED, isTransposed);
    2816        map.put(ViewProperties.DATA_VIEW_KEY.INDEXBASE1, isIndexBase1);
    2817        map.put(ViewProperties.DATA_VIEW_KEY.BITMASK, bitmask);
    2818        if (isApplyBitmaskOnly)
    2819            map.put(ViewProperties.DATA_VIEW_KEY.BITMASKOP, ViewProperties.BITMASK_OP.AND);
    2820
    2821        log.trace(
    2822                "showDataContent(): object={} dataViewName={} isDisplayTypeChar={} isTransposed={} isIndexBase1={} bitmask={}",
    2823                dataObject, dataViewName, isDisplayTypeChar, isTransposed, isIndexBase1, bitmask);
    2824
    2825        shell.setCursor(Display.getCurrent().getSystemCursor(SWT.CURSOR_WAIT));
    2826
    2827        if (isImage) {
    2828            DataViewFactory imageViewFactory = null;
    2829            try {
    2830                imageViewFactory = DataViewFactoryProducer.getFactory(DataViewType.IMAGE);
    2831            }
    2832            catch (Exception ex) {
    2833                log.debug("showDataContent(): error occurred while instantiating ImageView factory class", ex);
    2834                viewer.showError("Error occurred while instantiating ImageView factory class");
    2835                return null;
    2836            }
    2837
    2838            if (imageViewFactory == null) {
    2839                log.debug("showDataContent(): ImageView factory is null");
    2840                return null;
    2841            }
    2842
    2843            try {
    2844                theView = imageViewFactory.getImageView(viewer, map);
    2845
    2846                if (theView == null) {
    2847                    log.debug("showDataContent(): error occurred while instantiating ImageView class");
    2848                    viewer.showError("Error occurred while instantiating ImageView class");
    2849                    Tools.showError(shell, "Show Data", "Error occurred while instantiating ImageView class");
    2850                }
    2851            }
    2852            catch (ClassNotFoundException ex) {
    2853                log.debug("showDataContent(): no suitable ImageView class found");
    2854                viewer.showError("Unable to find suitable ImageView class for object '" + dataObject.getName() + "'");
    2855                Tools.showError(shell, "Show Data", "Unable to find suitable ImageView class for object '" + dataObject.getName() + "'");
    2856                theView = null;
    2857            }
    2858        }
    2859        else {
    2860            DataViewFactory tableViewFactory = null;
    2861            try {
    2862                tableViewFactory = DataViewFactoryProducer.getFactory(DataViewType.TABLE);
    2863            }
    2864            catch (Exception ex) {
    2865                log.debug("showDataContent(): error occurred while instantiating TableView factory class", ex);
    2866                viewer.showError("Error occurred while instantiating TableView factory class");
    2867                return null;
    2868            }
    2869
    2870            if (tableViewFactory == null) {
    2871                log.debug("showDataContent(): TableView factory is null");
    2872                return null;
    2873            }
    2874
    2875            try {
    2876                theView = tableViewFactory.getTableView(viewer, map);
    2877
    2878                if (theView == null) {
    2879                    log.debug("showDataContent(): error occurred while instantiating TableView class");
    2880                    viewer.showError("Error occurred while instantiating TableView class");
    2881                    Tools.showError(shell, "Show Data", "Error occurred while instantiating TableView class");
    2882                }
    2883            }
    2884            catch (ClassNotFoundException ex) {
    2885                log.debug("showDataContent(): no suitable TableView class found");
    2886                viewer.showError("Unable to find suitable TableView class for object '" + dataObject.getName() + "'");
    2887                Tools.showError(shell, "Show Data", "Unable to find suitable TableView class for object '" + dataObject.getName() + "'");
    2888                theView = null;
    2889            }
    2890        }
    2891
    2892        if (!shell.isDisposed())
    2893            shell.setCursor(null);
    2894
    2895        return theView;
    2896    }
    2897
    2898    /**
    2899     * Updates the current font.
    2900     *
    2901     * @param font
    2902     *           the new font
    2903     */
    2904    public void updateFont(Font font) {
    2905        if (curFont != null)
    2906            curFont.dispose();
    2907
    2908        log.trace("updateFont():");
    2909        curFont = font;
    2910
    2911        tree.setFont(font);
    2912        tree.pack();
    2913        tree.requestLayout();
    2914    }
    2915
    2916    /**
    2917     * Updates the icon for the TreeItem representing the given HObject. Used
    2918     * to change the icon after a status update, such as adding an attribute to
    2919     * an object.
    2920     *
    2921     * @param obj
    2922     *           the object to update the icon for
    2923     */
    2924    public void updateItemIcon(HObject obj) {
    2925        if (obj == null) {
    2926            log.debug("updateItemIcon(): object is null");
    2927            return;
    2928        }
    2929
    2930        TreeItem theItem = findTreeItem(obj);
    2931
    2932        if (theItem == null) {
    2933            log.debug("updateItemIcon(): could not find TreeItem for HObject");
    2934        }
    2935        else {
    2936            if (obj instanceof Group && !(((Group) obj).isRoot())) {
    2937                if (theItem.getExpanded()) {
    2938                    if (((MetaDataContainer) obj).hasAttribute())
    2939                        theItem.setImage(folderOpenIconA);
    2940                    else
    2941                        theItem.setImage(folderOpenIcon);
    2942                }
    2943                else {
    2944                    if (((MetaDataContainer) obj).hasAttribute())
    2945                        theItem.setImage(folderCloseIconA);
    2946                    else
    2947                        theItem.setImage(folderCloseIcon);
    2948                }
    2949            }
    2950            else {
    2951                theItem.setImage(getObjectTypeImage(obj));
    2952            }
    2953        }
    2954    }
    2955
    2956    /**
    2957     * ChangeIndexingDialog displays file index options.
    2958     */
    2959    private class ChangeIndexingDialog extends Dialog
    2960    {
    2961        private Button checkIndexByName;
    2962        private Button checkIndexIncrements;
    2963        private Button checkIndexNative;
    2964
    2965        private boolean reloadFile;
    2966
    2967        private FileFormat selectedFile;
    2968        private int indexType;
    2969        private int indexOrder;
    2970
    2971        private ChangeIndexingDialog(Shell parent, int style, FileFormat viewSelectedFile) {
    2972            super(parent, style);
    2973
    2974            selectedFile = viewSelectedFile;
    2975            try {
    2976                indexType = selectedFile.getIndexType(null);
    2977            }
    2978            catch (Exception ex) {
    2979                log.debug("ChangeIndexingDialog(): getIndexType failed: ", ex);
    2980            }
    2981            try {
    2982                indexOrder = selectedFile.getIndexOrder(null);
    2983            }
    2984            catch (Exception ex) {
    2985                log.debug("ChangeIndexingDialog(): getIndexOrder failed: ", ex);
    2986            }
    2987            reloadFile = false;
    2988        }
    2989
    2990        private void setIndexOptions() {
    2991            try {
    2992                if (checkIndexByName.getSelection())
    2993                    indexType = selectedFile.getIndexType("H5_INDEX_NAME");
    2994                else
    2995                    indexType = selectedFile.getIndexType("H5_INDEX_CRT_ORDER");
    2996            }
    2997            catch (Exception ex) {
    2998                log.debug("setIndexOptions(): getIndexType failed: ", ex);
    2999            }
    3000
    3001            try {
    3002                if (checkIndexIncrements.getSelection())
    3003                    indexOrder = selectedFile.getIndexOrder("H5_ITER_INC");
    3004                else if (checkIndexNative.getSelection())
    3005                    indexOrder = selectedFile.getIndexOrder("H5_ITER_NATIVE");
    3006                else
    3007                    indexOrder = selectedFile.getIndexOrder("H5_ITER_DEC");
    3008            }
    3009            catch (Exception ex) {
    3010                log.debug("setIndexOptions(): getIndexOrder failed: ", ex);
    3011            }
    3012
    3013            reloadFile = true;
    3014        }
    3015
    3016        /** @return the current value of the index type. */
    3017        public int getIndexType() {
    3018            return indexType;
    3019        }
    3020
    3021        /** @return the current value of the index order. */
    3022        public int getIndexOrder() {
    3023            return indexOrder;
    3024        }
    3025
    3026        /** @return the current value of the reloadFile. */
    3027        public boolean isReloadFile() {
    3028            return reloadFile;
    3029        }
    3030
    3031        /** open the ChangeIndexingDialog for setting the indexing values. */
    3032        public void open() {
    3033            Shell parent = getParent();
    3034            final Shell openShell = new Shell(parent, SWT.DIALOG_TRIM | SWT.APPLICATION_MODAL);
    3035            openShell.setFont(curFont);
    3036            openShell.setText("Indexing options");
    3037            openShell.setImages(ViewProperties.getHdfIcons());
    3038            openShell.setLayout(new GridLayout(1, true));
    3039
    3040            // Create main content region
    3041            Composite content = new Composite(openShell, SWT.NONE);
    3042            content.setLayout(new GridLayout(1, true));
    3043            content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    3044
    3045            org.eclipse.swt.widgets.Group indexingTypeGroup = new org.eclipse.swt.widgets.Group(content, SWT.NONE);
    3046            indexingTypeGroup.setFont(curFont);
    3047            indexingTypeGroup.setText("Indexing Type");
    3048            indexingTypeGroup.setLayout(new GridLayout(2, true));
    3049            indexingTypeGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    3050
    3051            int initIndexType = 0;
    3052            try {
    3053                initIndexType = selectedFile.getIndexType("H5_INDEX_NAME");
    3054            }
    3055            catch (Exception ex) {
    3056                log.debug("open(): getIndexType failed: ", ex);
    3057            }
    3058            checkIndexByName = new Button(indexingTypeGroup, SWT.RADIO);
    3059            checkIndexByName.setFont(curFont);
    3060            checkIndexByName.setText("By Name");
    3061            checkIndexByName.setSelection((indexType) == initIndexType);
    3062            checkIndexByName.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
    3063
    3064            try {
    3065                initIndexType = selectedFile.getIndexType("H5_INDEX_CRT_ORDER");
    3066            }
    3067            catch (Exception ex) {
    3068                log.debug("open(): getIndexType failed: ", ex);
    3069            }
    3070            Button byOrder = new Button(indexingTypeGroup, SWT.RADIO);
    3071            byOrder.setFont(curFont);
    3072            byOrder.setText("By Creation Order");
    3073            byOrder.setSelection((indexType) == initIndexType);
    3074            byOrder.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
    3075
    3076            org.eclipse.swt.widgets.Group indexingOrderGroup = new org.eclipse.swt.widgets.Group(content, SWT.NONE);
    3077            indexingOrderGroup.setFont(curFont);
    3078            indexingOrderGroup.setText("Indexing Order");
    3079            indexingOrderGroup.setLayout(new GridLayout(3, true));
    3080            indexingOrderGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    3081
    3082            int initIndexOrder = 0;
    3083            try {
    3084                initIndexOrder = selectedFile.getIndexOrder("H5_ITER_INC");
    3085            }
    3086            catch (Exception ex) {
    3087                log.debug("open(): getIndexOrder failed: ", ex);
    3088            }
    3089            checkIndexIncrements = new Button(indexingOrderGroup, SWT.RADIO);
    3090            checkIndexIncrements.setFont(curFont);
    3091            checkIndexIncrements.setText("Increments");
    3092            checkIndexIncrements.setSelection((indexOrder) == initIndexOrder);
    3093            checkIndexIncrements.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
    3094
    3095            try {
    3096                initIndexOrder = selectedFile.getIndexOrder("H5_ITER_DEC");
    3097            }
    3098            catch (Exception ex) {
    3099                log.debug("open(): getIndexOrder failed: ", ex);
    3100            }
    3101            Button decrements = new Button(indexingOrderGroup, SWT.RADIO);
    3102            decrements.setFont(curFont);
    3103            decrements.setText("Decrements");
    3104            decrements.setSelection((indexOrder) == initIndexOrder);
    3105            decrements.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
    3106
    3107            try {
    3108                initIndexOrder = selectedFile.getIndexOrder("H5_ITER_NATIVE");
    3109            }
    3110            catch (Exception ex) {
    3111                log.debug("open(): getIndexOrder failed: ", ex);
    3112            }
    3113            checkIndexNative = new Button(indexingOrderGroup, SWT.RADIO);
    3114            checkIndexNative.setFont(curFont);
    3115            checkIndexNative.setText("Native");
    3116            checkIndexNative.setSelection((indexOrder) == initIndexOrder);
    3117            checkIndexNative.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
    3118
    3119
    3120            // Create Ok/Cancel button region
    3121            Composite buttonComposite = new Composite(openShell, SWT.NONE);
    3122            buttonComposite.setLayout(new GridLayout(2, true));
    3123            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    3124
    3125            Button okButton = new Button(buttonComposite, SWT.PUSH);
    3126            okButton.setFont(curFont);
    3127            okButton.setText("   &Reload File   ");
    3128            okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
    3129            okButton.addSelectionListener(new SelectionAdapter() {
    3130                @Override
    3131                public void widgetSelected(SelectionEvent e) {
    3132                    setIndexOptions();
    3133                    openShell.dispose();
    3134                }
    3135            });
    3136
    3137            Button cancelButton = new Button(buttonComposite, SWT.PUSH);
    3138            cancelButton.setFont(curFont);
    3139            cancelButton.setText("     &Cancel     ");
    3140            cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
    3141            cancelButton.addSelectionListener(new SelectionAdapter() {
    3142                @Override
    3143                public void widgetSelected(SelectionEvent e) {
    3144                    reloadFile = false;
    3145                    openShell.dispose();
    3146                }
    3147            });
    3148
    3149            openShell.pack();
    3150
    3151            Point minimumSize = openShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    3152
    3153            openShell.setMinimumSize(minimumSize);
    3154
    3155            Rectangle parentBounds = parent.getBounds();
    3156            Point shellSize = openShell.getSize();
    3157            openShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
    3158                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
    3159
    3160            openShell.open();
    3161
    3162            Display display = parent.getDisplay();
    3163            while (!openShell.isDisposed())
    3164                if (!display.readAndDispatch())
    3165                    display.sleep();
    3166        }
    3167    }
    3168
    3169    private class ChangeLibVersionDialog extends Dialog
    3170    {
    3171        public ChangeLibVersionDialog(Shell parent, int style) {
    3172            super(parent, style);
    3173        }
    3174
    3175        public void open() {
    3176            Shell parent = getParent();
    3177            final Shell openShell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
    3178            openShell.setFont(curFont);
    3179            openShell.setText("Set the library version bounds: ");
    3180            openShell.setImages(ViewProperties.getHdfIcons());
    3181            openShell.setLayout(new GridLayout(1, true));
    3182
    3183            String[] lowValues = { "Earliest", "V18", "V110", "V112", "V114", "Latest" };
    3184            int[] lowConstants = { HDF5Constants.H5F_LIBVER_EARLIEST, HDF5Constants.H5F_LIBVER_V18, HDF5Constants.H5F_LIBVER_V110, HDF5Constants.H5F_LIBVER_V112, HDF5Constants.H5F_LIBVER_V114, HDF5Constants.H5F_LIBVER_LATEST };
    3185            String[] highValues = { "V18", "V110", "V112", "V114", "Latest" };
    3186            int[] highConstants = { HDF5Constants.H5F_LIBVER_V18, HDF5Constants.H5F_LIBVER_V110, HDF5Constants.H5F_LIBVER_V112, HDF5Constants.H5F_LIBVER_V114, HDF5Constants.H5F_LIBVER_LATEST };
    3187
    3188            // Try to retrieve the existing version bounds
    3189            int[] current = null;
    3190            try {
    3191                current = selectedObject.getFileFormat().getLibBounds();
    3192            }
    3193            catch (Exception err) {
    3194                openShell.getDisplay().beep();
    3195                Tools.showError(openShell, "Version bounds", "Error when getting lib version bounds, using default");
    3196                current = new int[]{HDF5Constants.H5F_LIBVER_EARLIEST, HDF5Constants.H5F_LIBVER_LATEST};
    3197            }
    3198            int lowidx = 0;
    3199            for(int i = 0; i < lowConstants.length; i++) {
    3200                if(lowConstants[i] == current[0]){
    3201                    lowidx = i;
    3202                    break;
    3203                }
    3204            }
    3205            int highidx = 0;
    3206            for(int i = 0; i < highConstants.length; i++) {
    3207                if(highConstants[i] == current[1]){
    3208                    highidx = i;
    3209                    break;
    3210                }
    3211            }
    3212
    3213            // Dummy label
    3214            new Label(openShell, SWT.LEFT);
    3215
    3216            Label label = new Label(openShell, SWT.LEFT);
    3217            label.setFont(curFont);
    3218            label.setText("Earliest Version: ");
    3219
    3220            final Combo earliestCombo = new Combo(openShell, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY);
    3221            earliestCombo.setFont(curFont);
    3222            earliestCombo.setItems(lowValues);
    3223            earliestCombo.select(lowidx);
    3224            earliestCombo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    3225
    3226            label = new Label(openShell, SWT.LEFT);
    3227            label.setFont(curFont);
    3228            label.setText("Latest Version: ");
    3229
    3230            final Combo latestCombo = new Combo(openShell, SWT.SINGLE | SWT.BORDER | SWT.READ_ONLY);
    3231            latestCombo.setFont(curFont);
    3232            latestCombo.setItems(highValues);
    3233            latestCombo.select(highidx);
    3234            latestCombo.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    3235
    3236            // Dummy label to consume remain space after resizing
    3237            new Label(openShell, SWT.LEFT).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    3238
    3239            // Create Ok/Cancel button region
    3240            Composite buttonComposite = new Composite(openShell, SWT.NONE);
    3241            buttonComposite.setLayout(new GridLayout(2, true));
    3242            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    3243
    3244            Button okButton = new Button(buttonComposite, SWT.PUSH);
    3245            okButton.setFont(curFont);
    3246            okButton.setText("   &OK   ");
    3247            okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
    3248            okButton.addSelectionListener(new SelectionAdapter() {
    3249                @Override
    3250                public void widgetSelected(SelectionEvent e) {
    3251                    try {
    3252                        selectedObject.getFileFormat().setLibBounds(earliestCombo.getItem(earliestCombo.getSelectionIndex()), latestCombo.getItem(latestCombo.getSelectionIndex()));
    3253                    }
    3254                    catch (Exception err) {
    3255                        openShell.getDisplay().beep();
    3256                        Tools.showError(openShell, "Version bounds", "Error when setting lib version bounds");
    3257                        return;
    3258                    }
    3259
    3260                    openShell.dispose();
    3261
    3262                    ((HDFView) viewer).showMetaData(selectedObject);
    3263                }
    3264            });
    3265
    3266            Button cancelButton = new Button(buttonComposite, SWT.PUSH);
    3267            cancelButton.setFont(curFont);
    3268            cancelButton.setText(" &Cancel ");
    3269            cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
    3270            cancelButton.addSelectionListener(new SelectionAdapter() {
    3271                @Override
    3272                public void widgetSelected(SelectionEvent e) {
    3273                    openShell.dispose();
    3274                }
    3275            });
    3276
    3277            openShell.pack();
    3278
    3279            Point minimumSize = openShell.computeSize(SWT.DEFAULT, SWT.DEFAULT);
    3280
    3281            openShell.setMinimumSize(minimumSize);
    3282            openShell.setSize(minimumSize.x + 50, minimumSize.y);
    3283
    3284            Rectangle parentBounds = parent.getBounds();
    3285            Point shellSize = openShell.getSize();
    3286            openShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
    3287                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
    3288
    3289            openShell.open();
    3290
    3291            Display display = parent.getDisplay();
    3292            while (!openShell.isDisposed())
    3293                if (!display.readAndDispatch())
    3294                    display.sleep();
    3295        }
    3296    }
    3297
    3298    private class LoadDataThread extends Thread
    3299    {
    3300        LoadDataThread() {
    3301            super();
    3302            setDaemon(true);
    3303        }
    3304
    3305        @Override
    3306        public void run() {
    3307            try {
    3308                Display.getDefault().syncExec(new Runnable() {
    3309                    @Override
    3310                    public void run() {
    3311                        try {
    3312                            showDataContent(selectedObject);
    3313                        }
    3314                        catch (Exception ex) {
    3315                            log.debug("showDataContent failure: ", ex);
    3316                        }
    3317                    }
    3318                });
    3319            }
    3320            catch (Exception e) {
    3321                log.debug("showDataContent loading manually interrupted");
    3322            }
    3323        }
    3324    }
    3325}