    001/*****************************************************************************
    002 * Copyright by The HDF Group.                                               *
    003 * All rights reserved.                                                      *
    004 *                                                                           *
    005 * This file is part of the HDF Java Products distribution.                  *
    006 * The full copyright notice, including terms governing use, modification,   *
    007 * and redistribution, is contained in the COPYING file, which can be found  *
    008 * at the root of the source code distribution tree,                         *
    009 * or in https://www.hdfgroup.org/licenses.                                  *
    010 * If you do not have access to either file, you may request a copy from     *
    011 * help@hdfgroup.org.                                                        *
    012 ****************************************************************************/
    013
    014package hdf.view.TableView;
    015
    016import java.awt.Toolkit;
    017import java.awt.datatransfer.Clipboard;
    018import java.awt.datatransfer.DataFlavor;
    019import java.awt.datatransfer.StringSelection;
    020import java.io.BufferedReader;
    021import java.io.BufferedWriter;
    022import java.io.DataOutputStream;
    023import java.io.File;
    024import java.io.FileNotFoundException;
    025import java.io.FileOutputStream;
    026import java.io.FileReader;
    027import java.io.FileWriter;
    028import java.io.IOException;
    029import java.io.PrintWriter;
    030import java.lang.reflect.Array;
    031import java.lang.reflect.Constructor;
    032import java.nio.ByteOrder;
    033import java.text.DecimalFormat;
    034import java.text.NumberFormat;
    035import java.util.ArrayList;
    036import java.util.BitSet;
    037import java.util.HashMap;
    038import java.util.Iterator;
    039import java.util.LinkedHashSet;
    040import java.util.List;
    041import java.util.Set;
    042import java.util.StringTokenizer;
    043
    044import org.slf4j.Logger;
    045import org.slf4j.LoggerFactory;
    046
    047import org.eclipse.nebula.widgets.nattable.NatTable;
    048import org.eclipse.nebula.widgets.nattable.command.StructuralRefreshCommand;
    049import org.eclipse.nebula.widgets.nattable.command.VisualRefreshCommand;
    050import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
    051import org.eclipse.nebula.widgets.nattable.config.AbstractUiBindingConfiguration;
    052import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
    053import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
    054import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
    055import org.eclipse.nebula.widgets.nattable.coordinate.Range;
    056import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
    057import org.eclipse.nebula.widgets.nattable.data.validate.DataValidator;
    058import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
    059import org.eclipse.nebula.widgets.nattable.edit.action.KeyEditAction;
    060import org.eclipse.nebula.widgets.nattable.edit.action.MouseEditAction;
    061import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration;
    062import org.eclipse.nebula.widgets.nattable.edit.config.DialogErrorHandling;
    063import org.eclipse.nebula.widgets.nattable.grid.command.ClientAreaResizeCommand;
    064import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
    065import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
    066import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
    067import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
    068import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
    069import org.eclipse.nebula.widgets.nattable.layer.ILayer;
    070import org.eclipse.nebula.widgets.nattable.layer.IUniqueIndexLayer;
    071import org.eclipse.nebula.widgets.nattable.layer.config.DefaultColumnHeaderLayerConfiguration;
    072import org.eclipse.nebula.widgets.nattable.layer.config.DefaultColumnHeaderStyleConfiguration;
    073import org.eclipse.nebula.widgets.nattable.layer.config.DefaultRowHeaderLayerConfiguration;
    074import org.eclipse.nebula.widgets.nattable.layer.config.DefaultRowHeaderStyleConfiguration;
    075import org.eclipse.nebula.widgets.nattable.painter.cell.TextPainter;
    076import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.BeveledBorderDecorator;
    077import org.eclipse.nebula.widgets.nattable.painter.cell.decorator.LineBorderDecorator;
    078import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
    079import org.eclipse.nebula.widgets.nattable.selection.command.SelectAllCommand;
    080import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
    081import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
    082import org.eclipse.nebula.widgets.nattable.style.HorizontalAlignmentEnum;
    083import org.eclipse.nebula.widgets.nattable.style.Style;
    084import org.eclipse.nebula.widgets.nattable.ui.action.IMouseAction;
    085import org.eclipse.nebula.widgets.nattable.ui.binding.UiBindingRegistry;
    086import org.eclipse.nebula.widgets.nattable.ui.matcher.CellEditorMouseEventMatcher;
    087import org.eclipse.nebula.widgets.nattable.ui.matcher.LetterOrDigitKeyEventMatcher;
    088import org.eclipse.nebula.widgets.nattable.ui.matcher.MouseEventMatcher;
    089import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuAction;
    090import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
    091import org.eclipse.nebula.widgets.nattable.viewport.command.ShowRowInViewportCommand;
    092import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
    093
    094import org.eclipse.swt.SWT;
    095import org.eclipse.swt.custom.SashForm;
    096import org.eclipse.swt.custom.ScrolledComposite;
    097import org.eclipse.swt.events.DisposeEvent;
    098import org.eclipse.swt.events.DisposeListener;
    099import org.eclipse.swt.events.MouseEvent;
    100import org.eclipse.swt.events.SelectionAdapter;
    101import org.eclipse.swt.events.SelectionEvent;
    102import org.eclipse.swt.events.TraverseEvent;
    103import org.eclipse.swt.events.TraverseListener;
    104import org.eclipse.swt.graphics.Color;
    105import org.eclipse.swt.graphics.Font;
    106import org.eclipse.swt.graphics.Point;
    107import org.eclipse.swt.graphics.Rectangle;
    108import org.eclipse.swt.layout.FillLayout;
    109import org.eclipse.swt.layout.GridData;
    110import org.eclipse.swt.layout.GridLayout;
    111import org.eclipse.swt.widgets.Button;
    112import org.eclipse.swt.widgets.Combo;
    113import org.eclipse.swt.widgets.Composite;
    114import org.eclipse.swt.widgets.Dialog;
    115import org.eclipse.swt.widgets.Display;
    116import org.eclipse.swt.widgets.FileDialog;
    117import org.eclipse.swt.widgets.Label;
    118import org.eclipse.swt.widgets.Menu;
    119import org.eclipse.swt.widgets.MenuItem;
    120import org.eclipse.swt.widgets.Shell;
    121import org.eclipse.swt.widgets.Text;
    122import org.eclipse.swt.widgets.ToolBar;
    123import org.eclipse.swt.widgets.ToolItem;
    124
    125import hdf.hdf5lib.HDF5Constants;
    126
    127import hdf.object.CompoundDS;
    128import hdf.object.DataFormat;
    129import hdf.object.Dataset;
    130import hdf.object.Datatype;
    131import hdf.object.FileFormat;
    132import hdf.object.Group;
    133import hdf.object.HObject;
    134import hdf.object.ScalarDS;
    135
    136import hdf.object.h5.H5Datatype;
    137import hdf.object.h5.H5ReferenceType;
    138
    139import hdf.view.Chart;
    140import hdf.view.DefaultFileFilter;
    141import hdf.view.HDFView;
    142import hdf.view.Tools;
    143import hdf.view.ViewProperties;
    144import hdf.view.ViewProperties.BITMASK_OP;
    145import hdf.view.DataView.DataViewManager;
    146import hdf.view.TableView.DataDisplayConverterFactory.HDFDisplayConverter;
    147import hdf.view.TableView.DataProviderFactory.HDFDataProvider;
    148import hdf.view.TreeView.TreeView;
    149import hdf.view.dialog.InputDialog;
    150import hdf.view.dialog.MathConversionDialog;
    151import hdf.view.dialog.NewDatasetDialog;
    152
    153/**
    154 * DefaultBaseTableView serves as the base class for a DataView that displays
    155 * HDF data in a tabular format. This class is used for internal bookkeeping and
    156 * as a place to store higher-level data manipulation functions, whereas its
    157 * subclasses are responsible for setting up the actual GUI components.
    158 *
    159 * @author jhenderson
    160 * @version 1.0 4/13/2018
    161 */
    162public abstract class DefaultBaseTableView implements TableView
    163{
    164
    165    private static final Logger   log = LoggerFactory.getLogger(DefaultBaseTableView.class);
    166
    167    private final Display                   display = Display.getDefault();
    168    /** The reference to the display shell used */
    169    protected final Shell                   shell;
    170    /** The current font */
    171    protected Font                          curFont;
    172
    173    /** The main HDFView */
    174    protected final DataViewManager         viewer;
    175
    176    /** The reference to the NAT table used */
    177    protected NatTable                      dataTable;
    178
    179    /** The data object to be displayed in the Table */
    180    protected final DataFormat              dataObject;
    181
    182    /** The data value of the data object */
    183    protected Object                        dataValue;
    184
    185    /** The value used for fill */
    186    protected Object                        fillValue;
    187
    188    /** the valid types of tableviews */
    189    protected enum ViewType {
    190        /** The data view is of type spreadsheet */
    191        TABLE,
    192        /** The data view is of type image */
    193        IMAGE
    194    };
    195
    196    /** The type of view */
    197    protected      ViewType                 viewType = ViewType.TABLE;
    198
    199    /** Changed to use normalized scientific notation (1 is less than coefficient is less than 10). */
    200    protected final DecimalFormat           scientificFormat = new DecimalFormat("0.0###E0###");
    201    /** custom format pattern */
    202    protected DecimalFormat                 customFormat     = new DecimalFormat("###.#####");
    203    /** the normal format to be used for numbers */
    204    protected final NumberFormat            normalFormat     = null;
    205    /** the format to be used for numbers */
    206    protected NumberFormat                  numberFormat     = normalFormat;
    207
    208    /** Used for bitmask operations on data */
    209    protected BitSet                        bitmask = null;
    210    /** Used for the type of bitmask operation */
    211    protected BITMASK_OP                    bitmaskOP = BITMASK_OP.EXTRACT;
    212
    213    /** Fields to keep track of which 'frame' of 3 dimensional data is being displayed */
    214    private Text                            frameField;
    215    private long                            curDataFrame = 0;
    216    private long                            maxDataFrame = 1;
    217
    218    /** The index base used for display row and column numbers of data */
    219    protected int                           indexBase = 0;
    220
    221    /** size of default data length */
    222    protected int                           fixedDataLength = -1;
    223
    224    /** default binary order */
    225    protected int                           binaryOrder;
    226
    227    /** status if file is read only */
    228    protected boolean                       isReadOnly = false;
    229
    230    /** status if the enums are to display converted */
    231    protected boolean                       isEnumConverted = false;
    232
    233    /** status if the display type is a char */
    234    protected boolean                       isDisplayTypeChar;
    235
    236    /** status if the data is transposed */
    237    protected boolean                       isDataTransposed;
    238
    239    /** reference status */
    240    protected boolean                       isRegRef = false, isObjRef = false, isStdRef = false;
    241    /** show data as status */
    242    protected boolean                       showAsHex = false, showAsBin = false;
    243
    244    /** Keep references to the selection layers for ease of access */
    245    protected SelectionLayer                selectionLayer;
    246    /** Keep references to the data layers for ease of access */
    247    protected DataLayer                     dataLayer;
    248
    249    /** reference to the data provider for the row */
    250    protected IDataProvider                 rowHeaderDataProvider;
    251    /** reference to the data provider for the column */
    252    protected IDataProvider                 columnHeaderDataProvider;
    253
    254    /** reference to the data provider */
    255    protected HDFDataProvider               dataProvider;
    256    /** reference to the display converter */
    257    protected HDFDisplayConverter           dataDisplayConverter;
    258
    259    /**
    260     * Global variables for GUI components on the default to show data
    261     */
    262    /** Checkbox menu item for Fixed Data Length default*/
    263    protected MenuItem                      checkFixedDataLength = null;
    264    /** Checkbox menu item for Custom Notation default*/
    265    protected MenuItem                      checkCustomNotation = null;
    266    /** Checkbox menu item for Scientific Notation default */
    267    protected MenuItem                      checkScientificNotation = null;
    268    /** Checkbox menu item for hex default */
    269    protected MenuItem                      checkHex = null;
    270    /** Checkbox menu item for binary default */
    271    protected MenuItem                      checkBin = null;
    272    /** Checkbox menu item for enum default*/
    273    protected MenuItem                      checkEnum = null;
    274
    275    /** Labeled Group to display the index base */
    276    protected org.eclipse.swt.widgets.Group indexBaseGroup;
    277
    278    /** Text field to display the value of the currently selected table cell */
    279    protected Text                          cellValueField;
    280
    281    /** Label to indicate the current cell location */
    282    protected Label                         cellLabel;
    283
    284
    285    /**
    286     * Constructs a base TableView with no additional data properties.
    287     *
    288     * @param theView
    289     *            the main HDFView.
    290     */
    291    public DefaultBaseTableView(DataViewManager theView) {
    292        this(theView, null);
    293    }
    294
    295    /**
    296     * Constructs a base TableView with the specified data properties.
    297     *
    298     * @param theView
    299     *            the main HDFView.
    300     *
    301     * @param dataPropertiesMap
    302     *            the properties on how to show the data. The map is used to allow
    303     *            applications to pass properties on how to display the data, such
    304     *            as: transposing data, showing data as characters, applying a
    305     *            bitmask, and etc. Predefined keys are listed at
    306     *            ViewProperties.DATA_VIEW_KEY.
    307     */
    308    @SuppressWarnings("rawtypes")
    309    public DefaultBaseTableView(DataViewManager theView, HashMap dataPropertiesMap) {
    310        shell = new Shell(display, SWT.SHELL_TRIM);
    311
    312        shell.setData(this);
    313
    314        shell.setLayout(new GridLayout(1, true));
    315
    316        /*
    317         * When the table is closed, make sure to prompt the user about saving their
    318         * changes, then do any pending cleanup work.
    319         */
    320        shell.addDisposeListener(new DisposeListener() {
    321            @Override
    322            public void widgetDisposed(DisposeEvent e) {
    323                if (dataProvider != null) {
    324                    if (dataProvider.getIsValueChanged() && !isReadOnly) {
    325                        if (Tools.showConfirm(shell, "Changes Detected", "\"" + ((HObject) dataObject).getName()
    326                                + "\" has changed.\nDo you want to save the changes?"))
    327                            updateValueInFile();
    328                        else
    329                            dataObject.clearData();
    330                    }
    331                }
    332
    333                dataValue = null;
    334                dataTable = null;
    335
    336                if (curFont != null)
    337                    curFont.dispose();
    338
    339                viewer.removeDataView(DefaultBaseTableView.this);
    340            }
    341        });
    342
    343        /* Grab the current font to be used for all GUI components */
    344        try {
    345            curFont = new Font(display, ViewProperties.getFontType(), ViewProperties.getFontSize(), SWT.NORMAL);
    346        }
    347        catch (Exception ex) {
    348            curFont = null;
    349        }
    350
    351        viewer = theView;
    352
    353        /* Retrieve any display properties passed in via the HashMap parameter */
    354        HObject hObject = null;
    355
    356        if (ViewProperties.isIndexBase1())
    357            indexBase = 1;
    358
    359        if (dataPropertiesMap != null) {
    360            hObject = (HObject) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.OBJECT);
    361
    362            bitmask = (BitSet) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.BITMASK);
    363            bitmaskOP = (BITMASK_OP) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.BITMASKOP);
    364
    365            Boolean b = (Boolean) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.CHAR);
    366            if (b != null) isDisplayTypeChar = b.booleanValue();
    367
    368            b = (Boolean) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.TRANSPOSED);
    369            if (b != null) isDataTransposed = b.booleanValue();
    370
    371            b = (Boolean) dataPropertiesMap.get(ViewProperties.DATA_VIEW_KEY.INDEXBASE1);
    372            if (b != null) {
    373                if (b.booleanValue())
    374                    indexBase = 1;
    375                else
    376                    indexBase = 0;
    377            }
    378        }
    379
    380        if (hObject == null)
    381            hObject = viewer.getTreeView().getCurrentObject();
    382
    383        /* Only edit objects which actually contain editable data */
    384        if ((hObject == null) || !(hObject instanceof DataFormat)) {
    385            log.debug("data object is null or not an instanceof DataFormat");
    386            dataObject = null;
    387            shell.dispose();
    388            return;
    389        }
    390
    391        dataObject = (DataFormat) hObject;
    392        if (((HObject) dataObject).getFileFormat() == null) {
    393            log.debug("DataFormat object cannot access FileFormat");
    394            shell.dispose();
    395            return;
    396        }
    397
    398        isReadOnly = ((HObject) dataObject).getFileFormat().isReadOnly();
    399
    400        if (((HObject) dataObject).getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4))
    401                && (dataObject instanceof CompoundDS)) {
    402            /* Cannot edit HDF4 VData */
    403            isReadOnly = true;
    404        }
    405
    406        /* Disable edit feature for SZIP compression when encode is not enabled */
    407        if (!isReadOnly) {
    408            String compression = dataObject.getCompression();
    409            if ((compression != null) && compression.startsWith("SZIP")) {
    410                if (!compression.endsWith("ENCODE_ENABLED"))
    411                    isReadOnly = true;
    412            }
    413        }
    414
    415        log.trace("dataObject({}) isReadOnly={}", dataObject, isReadOnly);
    416
    417        long[] dims = dataObject.getDims();
    418        long tsize = 1;
    419
    420        if (dims == null) {
    421            log.debug("data object has null dimensions");
    422            viewer.showError("Error: Data object '" + ((HObject) dataObject).getName() + "' has null dimensions.");
    423            shell.dispose();
    424            Tools.showError(display.getActiveShell(), "Error", "Could not open data object '" + ((HObject) dataObject).getName()
    425                    + "'. Data object has null dimensions.");
    426            return;
    427        }
    428
    429        for (int i = 0; i < dims.length; i++)
    430            tsize *= dims[i];
    431
    432        log.trace("Data object Size={} Height={} Width={}", tsize, dataObject.getHeight(), dataObject.getWidth());
    433
    434        if (dataObject.getHeight() <= 0 || dataObject.getWidth() <= 0 || tsize <= 0) {
    435            log.debug("data object has dimension of size 0");
    436            viewer.showError("Error: Data object '" + ((HObject) dataObject).getName() + "' has dimension of size 0.");
    437            shell.dispose();
    438            Tools.showError(display.getActiveShell(), "Error", "Could not open data object '" + ((HObject) dataObject).getName()
    439                    + "'. Data object has dimension of size 0.");
    440            return;
    441        }
    442
    443        /*
    444         * Determine whether the data is to be displayed as characters and whether or
    445         * not enum data is to be converted.
    446         */
    447        Datatype dtype = dataObject.getDatatype();
    448
    449        log.trace("Data object getDatatypeClass()={}", dtype.getDatatypeClass());
    450        isDisplayTypeChar = (isDisplayTypeChar
    451                && (dtype.getDatatypeSize() == 1 || (dtype.isArray() && dtype.getDatatypeBase().isChar())));
    452
    453        isEnumConverted = ViewProperties.isConvertEnum();
    454
    455        log.trace("Data object isDisplayTypeChar={} isEnumConverted={}", isDisplayTypeChar, isEnumConverted);
    456
    457        if (dtype.isRef()) {
    458            if (((HObject) dataObject).getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5))) {
    459                isStdRef = ((H5Datatype)dtype).isStdRef();
    460                isRegRef = ((H5Datatype)dtype).isRegRef();
    461                isObjRef = ((H5Datatype)dtype).isRefObj();
    462            }
    463        }
    464
    465
    466        // Setup subset information
    467        int space_type = dataObject.getSpaceType();
    468        int rank = dataObject.getRank();
    469        int[] selectedIndex = dataObject.getSelectedIndex();
    470        long[] count = dataObject.getSelectedDims();
    471        long[] stride = dataObject.getStride();
    472        long[] start = dataObject.getStartDims();
    473        int n = Math.min(3, rank);
    474
    475        if (rank > 2) {
    476            curDataFrame = start[selectedIndex[2]] + indexBase;
    477            maxDataFrame = (indexBase == 1) ? dims[selectedIndex[2]] : dims[selectedIndex[2]] - 1;
    478        }
    479
    480        /* Create the toolbar area that contains useful shortcuts */
    481        ToolBar toolBar = createToolbar(shell);
    482        toolBar.setSize(shell.getSize().x, 30);
    483        toolBar.setLocation(0, 0);
    484
    485        /*
    486         * Create the group that contains the text fields for displaying the value and
    487         * location of the current cell, as well as the index base.
    488         */
    489        indexBaseGroup = new org.eclipse.swt.widgets.Group(shell, SWT.SHADOW_ETCHED_OUT);
    490        indexBaseGroup.setFont(curFont);
    491        indexBaseGroup.setText(indexBase + "-based");
    492        indexBaseGroup.setLayout(new GridLayout(1, true));
    493        indexBaseGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    494
    495        SashForm content = new SashForm(indexBaseGroup, SWT.VERTICAL);
    496        content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    497        content.setSashWidth(10);
    498
    499        SashForm cellValueComposite = new SashForm(content, SWT.HORIZONTAL);
    500        cellValueComposite.setSashWidth(8);
    501
    502        cellLabel = new Label(cellValueComposite, SWT.RIGHT | SWT.BORDER);
    503        cellLabel.setAlignment(SWT.CENTER);
    504        cellLabel.setFont(curFont);
    505
    506        final ScrolledComposite cellValueFieldScroller = new ScrolledComposite(cellValueComposite, SWT.V_SCROLL | SWT.H_SCROLL);
    507        cellValueFieldScroller.setLayout(new FillLayout());
    508
    509        cellValueField = new Text(cellValueFieldScroller, SWT.MULTI | SWT.BORDER | SWT.WRAP);
    510        cellValueField.setEditable(false);
    511        cellValueField.setBackground(new Color(display, 255, 255, 240));
    512        cellValueField.setEnabled(false);
    513        cellValueField.setFont(curFont);
    514
    515        cellValueFieldScroller.setContent(cellValueField);
    516        cellValueFieldScroller.setExpandHorizontal(true);
    517        cellValueFieldScroller.setExpandVertical(true);
    518        cellValueFieldScroller.setMinSize(cellValueField.computeSize(SWT.DEFAULT, SWT.DEFAULT));
    519
    520        cellValueComposite.setWeights(new int[] { 1, 5 });
    521
    522        /* Make sure that the Dataset's data value is accessible for conditionally adding GUI components */
    523        try {
    524            loadData(dataObject);
    525            if (isStdRef) {
    526                if (dataObject.getRank() > 2)
    527                    ((H5ReferenceType)dtype).setRefSize((int)dataObject.getWidth() * (int)dataObject.getWidth());
    528                ((H5ReferenceType)dtype).setData(dataValue);
    529            }
    530        }
    531        catch (Exception ex) {
    532            log.debug("loadData(): data not loaded: ", ex);
    533            viewer.showError("Error: unable to load table data");
    534            shell.dispose();
    535            Tools.showError(display.getActiveShell(), "Open", "An error occurred while loading data for the table:\n\n" + ex.getMessage());
    536            return;
    537        }
    538
    539        /* Create the Shell's MenuBar */
    540        shell.setMenuBar(createMenuBar(shell));
    541
    542        /*
    543         * Set the default selection on the "Show Hexadecimal/Show Binary", etc. MenuItems.
    544         * This step must be done after the menu bar has actually been created.
    545         */
    546        if (dataObject.getDatatype().isBitField() || dataObject.getDatatype().isOpaque()) {
    547            showAsHex = true;
    548            checkHex.setSelection(true);
    549            checkScientificNotation.setSelection(false);
    550            checkCustomNotation.setSelection(false);
    551            checkBin.setSelection(false);
    552            showAsBin = false;
    553            numberFormat = normalFormat;
    554        }
    555
    556        /*
    557         * Set the default selection on the "Show Enum", etc. MenuItems.
    558         * This step must be done after the menu bar has actually been created.
    559         */
    560        if (dataObject.getDatatype().isEnum()) {
    561            checkEnum.setSelection(isEnumConverted);
    562            checkScientificNotation.setSelection(false);
    563            checkCustomNotation.setSelection(false);
    564            checkBin.setSelection(false);
    565            checkHex.setSelection(false);
    566            showAsBin = false;
    567            showAsHex = false;
    568            numberFormat = normalFormat;
    569        }
    570
    571        /* Create the actual NatTable */
    572        log.debug("table creation {}", ((HObject) dataObject).getName());
    573        try {
    574            dataTable = createTable(content, dataObject);
    575            if (dataTable == null) {
    576                log.debug("table creation for object {} failed", ((HObject) dataObject).getName());
    577                viewer.showError("Creating table for object '" + ((HObject) dataObject).getName() + "' failed.");
    578                shell.dispose();
    579                Tools.showError(display.getActiveShell(), "Open", "Failed to create Table object");
    580                return;
    581            }
    582        }
    583        catch (UnsupportedOperationException ex) {
    584            log.debug("Subclass does not implement createTable()");
    585            shell.dispose();
    586            return;
    587        }
    588
    589        /*
    590         * Set the default data display conversion settings.
    591         */
    592        updateDataConversionSettings();
    593
    594        dataTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    595
    596        /*
    597         * Set the Shell's title using the object path and name
    598         */
    599        StringBuilder sb = new StringBuilder(hObject.getName());
    600
    601        if (((HObject) dataObject).getFileFormat() != null) {
    602            sb.append("  at  ")
    603            .append(hObject.getPath())
    604            .append("  [")
    605            .append(((HObject) dataObject).getFileFormat().getName())
    606            .append("  in  ")
    607            .append(((HObject) dataObject).getFileFormat().getParent())
    608            .append("]");
    609        }
    610
    611        shell.setText(sb.toString());
    612
    613        /*
    614         * Append subsetting information and show this as a status message in the
    615         * HDFView main window
    616         */
    617        sb.append(" [ dims");
    618        sb.append(selectedIndex[0]);
    619        for (int i = 1; i < n; i++) {
    620            sb.append("x");
    621            sb.append(selectedIndex[i]);
    622        }
    623        sb.append(", start");
    624        sb.append(start[selectedIndex[0]]);
    625        for (int i = 1; i < n; i++) {
    626            sb.append("x");
    627            sb.append(start[selectedIndex[i]]);
    628        }
    629        sb.append(", count");
    630        sb.append(count[selectedIndex[0]]);
    631        for (int i = 1; i < n; i++) {
    632            sb.append("x");
    633            sb.append(count[selectedIndex[i]]);
    634        }
    635        sb.append(", stride");
    636        sb.append(stride[selectedIndex[0]]);
    637        for (int i = 1; i < n; i++) {
    638            sb.append("x");
    639            sb.append(stride[selectedIndex[i]]);
    640        }
    641        sb.append(" ] ");
    642
    643        if (log.isTraceEnabled())
    644            log.trace("subset={}", sb);
    645
    646        viewer.showStatus(sb.toString());
    647
    648        indexBaseGroup.pack();
    649
    650        content.setWeights(new int[] { 1, 12 });
    651
    652        shell.pack();
    653
    654        int width = 700 + (ViewProperties.getFontSize() - 12) * 15;
    655        int height = 500 + (ViewProperties.getFontSize() - 12) * 10;
    656        shell.setSize(width, height);
    657    }
    658
    659    /**
    660     * Creates the toolbar for the Shell.
    661     */
    662    private ToolBar createToolbar(final Shell shell) {
    663        ToolBar toolbar = new ToolBar(shell, SWT.HORIZONTAL | SWT.RIGHT | SWT.BORDER);
    664        toolbar.setFont(curFont);
    665        toolbar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    666
    667        // Chart button
    668        ToolItem item = new ToolItem(toolbar, SWT.PUSH);
    669        item.setImage(ViewProperties.getChartIcon());
    670        item.setToolTipText("Line Plot");
    671        item.addSelectionListener(new SelectionAdapter() {
    672            @Override
    673            public void widgetSelected(SelectionEvent e) {
    674                showLineplot();
    675            }
    676        });
    677
    678        if (dataObject.getRank() > 2) {
    679            new ToolItem(toolbar, SWT.SEPARATOR).setWidth(20);
    680
    681            // First frame button
    682            item = new ToolItem(toolbar, SWT.PUSH);
    683            item.setImage(ViewProperties.getFirstIcon());
    684            item.setToolTipText("First Frame");
    685            item.addSelectionListener(new SelectionAdapter() {
    686                @Override
    687                public void widgetSelected(SelectionEvent e) {
    688                    firstFrame();
    689                }
    690            });
    691
    692            // Previous frame button
    693            item = new ToolItem(toolbar, SWT.PUSH);
    694            item.setImage(ViewProperties.getPreviousIcon());
    695            item.setToolTipText("Previous Frame");
    696            item.addSelectionListener(new SelectionAdapter() {
    697                @Override
    698                public void widgetSelected(SelectionEvent e) {
    699                    previousFrame();
    700                }
    701            });
    702
    703            ToolItem separator = new ToolItem(toolbar, SWT.SEPARATOR);
    704
    705            frameField = new Text(toolbar, SWT.SINGLE | SWT.BORDER | SWT.CENTER);
    706            frameField.setFont(curFont);
    707            frameField.setText(String.valueOf(curDataFrame));
    708            frameField.addTraverseListener(new TraverseListener() {
    709                @Override
    710                public void keyTraversed(TraverseEvent e) {
    711                    if (e.detail == SWT.TRAVERSE_RETURN) {
    712                        try {
    713                            int frame = 0;
    714
    715                            try {
    716                                frame = Integer.parseInt(frameField.getText().trim()) - indexBase;
    717                            }
    718                            catch (Exception ex) {
    719                                frame = -1;
    720                            }
    721
    722                            gotoFrame(frame);
    723                        }
    724                        catch (Exception ex) {
    725                            log.debug("Frame change failure: ", ex);
    726                        }
    727                    }
    728                }
    729            });
    730
    731            frameField.pack();
    732
    733            separator.setWidth(frameField.getSize().x + 30);
    734            separator.setControl(frameField);
    735
    736            separator = new ToolItem(toolbar, SWT.SEPARATOR);
    737
    738            Text maxFrameText = new Text(toolbar, SWT.SINGLE | SWT.BORDER | SWT.CENTER);
    739            maxFrameText.setFont(curFont);
    740            maxFrameText.setText(String.valueOf(maxDataFrame));
    741            maxFrameText.setEditable(false);
    742            maxFrameText.setEnabled(false);
    743
    744            maxFrameText.pack();
    745
    746            separator.setWidth(maxFrameText.getSize().x + 30);
    747            separator.setControl(maxFrameText);
    748
    749            new ToolItem(toolbar, SWT.SEPARATOR).setWidth(10);
    750
    751            // Next frame button
    752            item = new ToolItem(toolbar, SWT.PUSH);
    753            item.setImage(ViewProperties.getNextIcon());
    754            item.setToolTipText("Next Frame");
    755            item.addSelectionListener(new SelectionAdapter() {
    756                @Override
    757                public void widgetSelected(SelectionEvent e) {
    758                    nextFrame();
    759                }
    760            });
    761
    762            // Last frame button
    763            item = new ToolItem(toolbar, SWT.PUSH);
    764            item.setImage(ViewProperties.getLastIcon());
    765            item.setToolTipText("Last Frame");
    766            item.addSelectionListener(new SelectionAdapter() {
    767                @Override
    768                public void widgetSelected(SelectionEvent e) {
    769                    lastFrame();
    770                }
    771            });
    772        }
    773
    774        return toolbar;
    775    }
    776
    777    /**
    778     * Creates the menubar for the Shell.
    779     *
    780     * @param theShell
    781     *    the reference to the display shell
    782     *
    783     * @return the newly created menu
    784     */
    785    protected Menu createMenuBar(final Shell theShell) {
    786        Menu menuBar = new Menu(theShell, SWT.BAR);
    787        boolean isEditable = !isReadOnly;
    788
    789        MenuItem tableMenuItem = new MenuItem(menuBar, SWT.CASCADE);
    790        tableMenuItem.setText("&Table");
    791
    792        Menu tableMenu = new Menu(theShell, SWT.DROP_DOWN);
    793        tableMenuItem.setMenu(tableMenu);
    794
    795        MenuItem item = new MenuItem(tableMenu, SWT.PUSH);
    796        item.setText("Select All");
    797        item.setAccelerator(SWT.CTRL | 'A');
    798        item.addSelectionListener(new SelectionAdapter() {
    799            @Override
    800            public void widgetSelected(SelectionEvent e) {
    801                try {
    802                    dataTable.doCommand(new SelectAllCommand());
    803                }
    804                catch (Exception ex) {
    805                    theShell.getDisplay().beep();
    806                    Tools.showError(theShell, "Select", ex.getMessage());
    807                }
    808            }
    809        });
    810
    811        item = new MenuItem(tableMenu, SWT.PUSH);
    812        item.setText("Copy");
    813        item.setAccelerator(SWT.CTRL | 'C');
    814        item.addSelectionListener(new SelectionAdapter() {
    815            @Override
    816            public void widgetSelected(SelectionEvent e) {
    817                copyData();
    818            }
    819        });
    820
    821        item = new MenuItem(tableMenu, SWT.PUSH);
    822        item.setText("Paste");
    823        item.setAccelerator(SWT.CTRL | 'V');
    824        item.setEnabled(isEditable);
    825        item.addSelectionListener(new SelectionAdapter() {
    826            @Override
    827            public void widgetSelected(SelectionEvent e) {
    828                pasteData();
    829            }
    830        });
    831
    832        new MenuItem(tableMenu, SWT.SEPARATOR);
    833
    834        item = new MenuItem(tableMenu, SWT.PUSH);
    835        item.setText("Copy to New Dataset");
    836        item.setEnabled(isEditable && (dataObject instanceof ScalarDS));
    837        item.addSelectionListener(new SelectionAdapter() {
    838            @Override
    839            public void widgetSelected(SelectionEvent e) {
    840                if ((selectionLayer.getSelectedColumnPositions().length <= 0)
    841                        || (selectionLayer.getSelectedRowCount() <= 0)) {
    842                    Tools.showInformation(theShell, "Copy", "Select table cells to write.");
    843                    return;
    844                }
    845
    846                TreeView treeView = viewer.getTreeView();
    847                Group pGroup = (Group) (treeView.findTreeItem((HObject) dataObject).getParentItem().getData());
    848                HObject root = ((HObject) dataObject).getFileFormat().getRootObject();
    849
    850                if (root == null) return;
    851
    852                ArrayList<HObject> list = new ArrayList<>(((HObject) dataObject).getFileFormat().getNumberOfMembers() + 5);
    853                Iterator<HObject> it = ((Group) root).depthFirstMemberList().iterator();
    854
    855                while (it.hasNext())
    856                    list.add(it.next());
    857                list.add(root);
    858
    859                NewDatasetDialog dialog = new NewDatasetDialog(theShell, pGroup, list, DefaultBaseTableView.this);
    860                dialog.open();
    861
    862                HObject obj = dialog.getObject();
    863                if (obj != null) {
    864                    Group pgroup = dialog.getParentGroup();
    865                    try {
    866                        treeView.addObject(obj, pgroup);
    867                    }
    868                    catch (Exception ex) {
    869                        log.debug("Write selection to dataset:", ex);
    870                    }
    871                }
    872
    873                list.clear();
    874            }
    875        });
    876
    877        item = new MenuItem(tableMenu, SWT.PUSH);
    878        item.setText("Save Changes to File");
    879        item.setAccelerator(SWT.CTRL | 'U');
    880        item.setEnabled(isEditable);
    881        item.addSelectionListener(new SelectionAdapter() {
    882            @Override
    883            public void widgetSelected(SelectionEvent e) {
    884                try {
    885                    updateValueInFile();
    886                }
    887                catch (Exception ex) {
    888                    theShell.getDisplay().beep();
    889                    Tools.showError(theShell, "Save", ex.getMessage());
    890                }
    891            }
    892        });
    893
    894        new MenuItem(tableMenu, SWT.SEPARATOR);
    895
    896        item = new MenuItem(tableMenu, SWT.PUSH);
    897        item.setText("Show Lineplot");
    898        item.addSelectionListener(new SelectionAdapter() {
    899            @Override
    900            public void widgetSelected(SelectionEvent e) {
    901                showLineplot();
    902            }
    903        });
    904
    905        item = new MenuItem(tableMenu, SWT.PUSH);
    906        item.setText("Show Statistics");
    907        item.addSelectionListener(new SelectionAdapter() {
    908            @Override
    909            public void widgetSelected(SelectionEvent e) {
    910                try {
    911                    Object theData = getSelectedData();
    912
    913                    if (dataObject instanceof CompoundDS) {
    914                        int cols = selectionLayer.getFullySelectedColumnPositions().length;
    915                        if (cols != 1) {
    916                            Tools.showError(theShell, "Statistics", "Please select one column at a time for compound dataset.");
    917                            return;
    918                        }
    919                    }
    920                    else if (theData == null) {
    921                        theData = dataValue;
    922                    }
    923
    924                    double[] minmax = new double[2];
    925                    double[] stat = new double[2];
    926
    927                    Tools.findMinMax(theData, minmax, fillValue);
    928                    if (Tools.computeStatistics(theData, stat, fillValue) > 0) {
    929                        String stats = "Min                      = " + minmax[0] + "\nMax                      = "
    930                                + minmax[1] + "\nMean                     = " + stat[0] + "\nStandard deviation = "
    931                                + stat[1];
    932                        Tools.showInformation(theShell, "Statistics", stats);
    933                    }
    934
    935                    System.gc();
    936                }
    937                catch (Exception ex) {
    938                    theShell.getDisplay().beep();
    939                    Tools.showError(shell, "Statistics", ex.getMessage());
    940                }
    941            }
    942        });
    943
    944        new MenuItem(tableMenu, SWT.SEPARATOR);
    945
    946        item = new MenuItem(tableMenu, SWT.PUSH);
    947        item.setText("Math Conversion");
    948        item.setEnabled(isEditable);
    949        item.addSelectionListener(new SelectionAdapter() {
    950            @Override
    951            public void widgetSelected(SelectionEvent e) {
    952                try {
    953                    mathConversion();
    954                }
    955                catch (Exception ex) {
    956                    shell.getDisplay().beep();
    957                    Tools.showError(theShell, "Convert", ex.getMessage());
    958                }
    959            }
    960        });
    961
    962        new MenuItem(tableMenu, SWT.SEPARATOR);
    963
    964        item = new MenuItem(tableMenu, SWT.PUSH);
    965        item.setText("Close");
    966        item.addSelectionListener(new SelectionAdapter() {
    967            @Override
    968            public void widgetSelected(SelectionEvent e) {
    969                theShell.dispose();
    970            }
    971        });
    972
    973        /********************************************************************
    974         *                                                                  *
    975         * Set up MenuItems for refreshing the TableView                    *
    976         *                                                                  *
    977         ********************************************************************/
    978        item = new MenuItem(tableMenu, SWT.PUSH);
    979        item.setText("Start Timer");
    980        item.addSelectionListener(new SelectionAdapter() {
    981            @Override
    982            public void widgetSelected(SelectionEvent e) {
    983                viewer.executeTimer(true);
    984            }
    985        });
    986
    987        item = new MenuItem(tableMenu, SWT.PUSH);
    988        item.setText("Stop Timer");
    989        item.addSelectionListener(new SelectionAdapter() {
    990            @Override
    991            public void widgetSelected(SelectionEvent e) {
    992                viewer.executeTimer(false);
    993            }
    994        });
    995
    996
    997        /********************************************************************
    998         *                                                                  *
    999         * Set up MenuItems for Importing/Exporting Data from the TableView *
    1000         *                                                                  *
    1001         ********************************************************************/
    1002        MenuItem importExportMenuItem = new MenuItem(menuBar, SWT.CASCADE);
    1003        importExportMenuItem.setText("&Import/Export Data");
    1004
    1005        Menu importExportMenu = new Menu(theShell, SWT.DROP_DOWN);
    1006        importExportMenuItem.setMenu(importExportMenu);
    1007
    1008        item = new MenuItem(importExportMenu, SWT.CASCADE);
    1009        item.setText("Export Data to");
    1010
    1011        Menu exportMenu = new Menu(item);
    1012        item.setMenu(exportMenu);
    1013
    1014        item = new MenuItem(exportMenu, SWT.PUSH);
    1015        item.setText("Text File");
    1016        item.addSelectionListener(new SelectionAdapter() {
    1017            @Override
    1018            public void widgetSelected(SelectionEvent e) {
    1019                try {
    1020                    saveAsText();
    1021                }
    1022                catch (Exception ex) {
    1023                    theShell.getDisplay().beep();
    1024                    Tools.showError(theShell, "Save", ex.getMessage());
    1025                }
    1026            }
    1027        });
    1028
    1029        item = new MenuItem(importExportMenu, SWT.CASCADE);
    1030        item.setText("Import Data from");
    1031
    1032        Menu importMenu = new Menu(item);
    1033        item.setMenu(importMenu);
    1034
    1035        item = new MenuItem(importMenu, SWT.PUSH);
    1036        item.setText("Text File");
    1037        item.setEnabled(!isReadOnly);
    1038        item.addSelectionListener(new SelectionAdapter() {
    1039            @Override
    1040            public void widgetSelected(SelectionEvent e) {
    1041                String currentDir = ((HObject) dataObject).getFileFormat().getParent();
    1042
    1043                String filename = null;
    1044                if (((HDFView) viewer).getTestState()) {
    1045                    filename = currentDir + File.separator + new InputDialog(theShell, "Enter a file name", "").open();
    1046                }
    1047                else {
    1048                    FileDialog fChooser = new FileDialog(theShell, SWT.OPEN);
    1049                    fChooser.setFilterPath(currentDir);
    1050
    1051                    DefaultFileFilter filter = DefaultFileFilter.getFileFilterText();
    1052                    fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
    1053                    fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
    1054                    fChooser.setFilterIndex(1);
    1055
    1056                    filename = fChooser.open();
    1057                }
    1058
    1059                if (filename == null)
    1060                    return;
    1061
    1062                File chosenFile = new File(filename);
    1063                if (!chosenFile.exists()) {
    1064                    Tools.showError(theShell, "Import Data From Text File", "Data import error: " + filename + " does not exist.");
    1065                    return;
    1066                }
    1067
    1068                if (!Tools.showConfirm(theShell, "Import Data From Text File", "Do you want to paste selected data?"))
    1069                    return;
    1070
    1071                importTextData(chosenFile.getAbsolutePath());
    1072            }
    1073        });
    1074
    1075        return menuBar;
    1076    }
    1077
    1078    /**
    1079     * Loads the data buffer of an object.
    1080     *
    1081     * @param dataObject
    1082     *        the object that has the buffer for the data.
    1083     *
    1084     * @throws Exception if a failure occurred
    1085     */
    1086    protected void loadData(DataFormat dataObject) throws Exception {
    1087        if (!dataObject.isInited()) {
    1088            try {
    1089                dataObject.init();
    1090            }
    1091            catch (Exception ex) {
    1092                dataValue = null;
    1093                log.debug("loadData(): ", ex);
    1094                throw ex;
    1095            }
    1096        }
    1097
    1098        // use lazy convert for large number of strings
    1099        if (dataObject.getHeight() > 10000 && dataObject instanceof CompoundDS) {
    1100            ((CompoundDS) dataObject).setConvertByteToString(false);
    1101        }
    1102
    1103        // Make sure entire dataset is not loaded when looking at 3D
    1104        // datasets using the default display mode (double clicking the
    1105        // data object)
    1106        if (dataObject.getRank() > 2)
    1107            dataObject.getSelectedDims()[dataObject.getSelectedIndex()[2]] = 1;
    1108
    1109        dataValue = null;
    1110        try {
    1111            log.trace("loadData(): call getData()");
    1112            dataValue = dataObject.getData();
    1113        }
    1114        catch (Exception ex) {
    1115            dataValue = null;
    1116            log.debug("loadData(): ", ex);
    1117            throw ex;
    1118        }
    1119    }
    1120
    1121    /**
    1122     * Create a data table for a data object.
    1123     *
    1124     * @param parent
    1125     *            the parent object this table will be associated with.
    1126     * @param dataObject
    1127     *            the data object this table will be associated with.
    1128     *
    1129     * @return the newly created data table
    1130     */
    1131    protected abstract NatTable createTable(Composite parent, DataFormat dataObject);
    1132
    1133    /**
    1134     * Show the object reference data.
    1135     *
    1136     * @param ref
    1137     *            the identifer for the object reference.
    1138     */
    1139    protected abstract void showObjRefData(byte[] ref);
    1140
    1141    /**
    1142     * Show the region reference data.
    1143     *
    1144     * @param reg
    1145     *            the identifier for the region reference.
    1146     */
    1147    protected abstract void showRegRefData(byte[] reg);
    1148
    1149    /**
    1150     * Show the standard reference data.
    1151     *
    1152     * @param reg
    1153     *            the identifier for the standard reference.
    1154     */
    1155    protected abstract void showStdRefData(byte[] reg);
    1156
    1157    /**
    1158     * Get the data editing rule for the object.
    1159     *
    1160     * @param dataObject
    1161     *        the data object
    1162     *
    1163     * @return the rule
    1164     */
    1165    protected abstract IEditableRule getDataEditingRule(DataFormat dataObject);
    1166
    1167    /**
    1168     * Update the display converters.
    1169     */
    1170    protected void updateDataConversionSettings() {
    1171        if (dataDisplayConverter != null) {
    1172            dataDisplayConverter.setShowAsHex(showAsHex);
    1173            dataDisplayConverter.setShowAsBin(showAsBin);
    1174            dataDisplayConverter.setNumberFormat(numberFormat);
    1175            dataDisplayConverter.setConvertEnum(isEnumConverted);
    1176        }
    1177    }
    1178
    1179    /**
    1180     * Update dataset's value in file. The changes will go to the file.
    1181     */
    1182    @Override
    1183    public void updateValueInFile() {
    1184
    1185        if (isReadOnly || !dataProvider.getIsValueChanged() || showAsBin || showAsHex) {
    1186            log.debug("updateValueInFile(): file not updated; read-only or unchanged data or displayed as hex or binary");
    1187            return;
    1188        }
    1189
    1190        try {
    1191            dataObject.write();
    1192        }
    1193        catch (Exception ex) {
    1194            shell.getDisplay().beep();
    1195            Tools.showError(shell, "Update", ex.getMessage());
    1196            log.debug("updateValueInFile(): ", ex);
    1197            return;
    1198        }
    1199
    1200        dataProvider.setIsValueChanged(false);
    1201    }
    1202
    1203    @Override
    1204    public HObject getDataObject() {
    1205        return (HObject) dataObject;
    1206    }
    1207
    1208    @Override
    1209    public Object getTable() {
    1210        return dataTable;
    1211    }
    1212
    1213    @Override
    1214    public int getSelectedRowCount() {
    1215        return selectionLayer.getSelectedRowCount();
    1216    }
    1217
    1218    @Override
    1219    public int getSelectedColumnCount() {
    1220        return selectionLayer.getSelectedColumnPositions().length;
    1221    }
    1222
    1223    /** @return the selection layer */
    1224    public SelectionLayer getSelectionLayer() {
    1225        return selectionLayer;
    1226    }
    1227
    1228    /** @return the data layer */
    1229    public DataLayer getDataLayer() {
    1230        return dataLayer;
    1231    }
    1232
    1233    /** refresh the data table */
    1234    @Override
    1235    public void refreshDataTable() {
    1236        log.trace("refreshDataTable()");
    1237
    1238        shell.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
    1239        dataValue = dataObject.refreshData();
    1240        shell.setCursor(null);
    1241
    1242        long[] dims = dataObject.getDims();
    1243        log.trace("refreshDataTable() dims:{}", dims);
    1244        dataProvider.updateDataBuffer(dataValue);
    1245        ((RowHeaderDataProvider)rowHeaderDataProvider).updateRows(dataObject);
    1246        log.trace("refreshDataTable(): rows={} : cols={}", dataProvider.getRowCount(), dataProvider.getColumnCount());
    1247
    1248        dataTable.doCommand(new StructuralRefreshCommand());
    1249        final ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
    1250        dataTable.doCommand(new ShowRowInViewportCommand(dataProvider.getRowCount()-1));
    1251        log.trace("refreshDataTable() finish");
    1252    }
    1253
    1254    // Flip to previous 'frame' of Table data
    1255    private void previousFrame() {
    1256        // Only valid operation if data object has 3 or more dimensions
    1257        if (dataObject.getRank() < 3)
    1258            return;
    1259
    1260        long[] start = dataObject.getStartDims();
    1261        int[] selectedIndex = dataObject.getSelectedIndex();
    1262        long curFrame = start[selectedIndex[2]];
    1263
    1264        if (curFrame == 0)
    1265            return; // Current frame is the first frame
    1266
    1267        gotoFrame(curFrame - 1);
    1268    }
    1269
    1270    // Flip to next 'frame' of Table data
    1271    private void nextFrame() {
    1272        // Only valid operation if data object has 3 or more dimensions
    1273        if (dataObject.getRank() < 3)
    1274            return;
    1275
    1276        long[] start = dataObject.getStartDims();
    1277        int[] selectedIndex = dataObject.getSelectedIndex();
    1278        long[] dims = dataObject.getDims();
    1279        long curFrame = start[selectedIndex[2]];
    1280
    1281        if (curFrame == dims[selectedIndex[2]] - 1)
    1282            return; // Current frame is the last frame
    1283
    1284        gotoFrame(curFrame + 1);
    1285    }
    1286
    1287    // Flip to the first 'frame' of Table data
    1288    private void firstFrame() {
    1289        // Only valid operation if data object has 3 or more dimensions
    1290        if (dataObject.getRank() < 3)
    1291            return;
    1292
    1293        long[] start = dataObject.getStartDims();
    1294        int[] selectedIndex = dataObject.getSelectedIndex();
    1295        long curFrame = start[selectedIndex[2]];
    1296
    1297        if (curFrame == 0)
    1298            return; // Current frame is the first frame
    1299
    1300        gotoFrame(0);
    1301    }
    1302
    1303    // Flip to the last 'frame' of Table data
    1304    private void lastFrame() {
    1305        // Only valid operation if data object has 3 or more dimensions
    1306        if (dataObject.getRank() < 3)
    1307            return;
    1308
    1309        long[] start = dataObject.getStartDims();
    1310        int[] selectedIndex = dataObject.getSelectedIndex();
    1311        long[] dims = dataObject.getDims();
    1312        long curFrame = start[selectedIndex[2]];
    1313
    1314        if (curFrame == dims[selectedIndex[2]] - 1)
    1315            return; // Current page is the last page
    1316
    1317        gotoFrame(dims[selectedIndex[2]] - 1);
    1318    }
    1319
    1320    // Flip to the specified 'frame' of Table data
    1321    private void gotoFrame(long idx) {
    1322        // Only valid operation if data object has 3 or more dimensions
    1323        if (dataObject.getRank() < 3 || idx == (curDataFrame - indexBase))
    1324            return;
    1325
    1326        // Make sure to save any changes to this frame of data before changing frames
    1327        if (dataProvider.getIsValueChanged())
    1328            updateValueInFile();
    1329
    1330        long[] start = dataObject.getStartDims();
    1331        int[] selectedIndex = dataObject.getSelectedIndex();
    1332        long[] dims = dataObject.getDims();
    1333
    1334        // Do a bit of frame index validation
    1335        if ((idx < 0) || (idx >= dims[selectedIndex[2]])) {
    1336            shell.getDisplay().beep();
    1337            Tools.showError(shell, "Select",
    1338                    "Frame number must be between " + indexBase + " and " + (dims[selectedIndex[2]] - 1 + indexBase));
    1339            return;
    1340        }
    1341
    1342        start[selectedIndex[2]] = idx;
    1343        curDataFrame = idx + indexBase;
    1344        frameField.setText(String.valueOf(curDataFrame));
    1345
    1346        dataObject.clearData();
    1347
    1348        shell.setCursor(display.getSystemCursor(SWT.CURSOR_WAIT));
    1349
    1350        try {
    1351            dataValue = dataObject.getData();
    1352
    1353            /*
    1354             * TODO: Converting data from unsigned C integers to Java integers
    1355             *       is currently unsupported for Compound Datasets.
    1356             */
    1357            if (!(dataObject instanceof CompoundDS))
    1358                dataObject.convertFromUnsignedC();
    1359
    1360            dataValue = dataObject.getData();
    1361        }
    1362        catch (Exception ex) {
    1363            shell.getDisplay().beep();
    1364            Tools.showError(shell, "Error loading data", "Dataset getData: " + ex.getMessage());
    1365            log.debug("gotoFrame(): ", ex);
    1366            dataValue = null;
    1367        }
    1368        finally {
    1369            shell.setCursor(null);
    1370        }
    1371
    1372        dataProvider.updateDataBuffer(dataValue);
    1373
    1374        dataTable.doCommand(new VisualRefreshCommand());
    1375    }
    1376
    1377    /**
    1378     * Copy data from the spreadsheet to the system clipboard.
    1379     */
    1380    private void copyData() {
    1381        StringBuilder sb = new StringBuilder();
    1382
    1383        Rectangle selection = selectionLayer.getLastSelectedRegion();
    1384        if (selection == null) {
    1385            Tools.showError(shell, "Copy", "Select data to copy.");
    1386            return;
    1387        }
    1388
    1389        int r0 = selectionLayer.getLastSelectedRegion().y; // starting row
    1390        int c0 = selectionLayer.getLastSelectedRegion().x; // starting column
    1391
    1392        if ((r0 < 0) || (c0 < 0))
    1393            return;
    1394
    1395        int nr = selectionLayer.getSelectedRowCount();
    1396        int nc = selectionLayer.getSelectedColumnPositions().length;
    1397        int r1 = r0 + nr; // finish row
    1398        int c1 = c0 + nc; // finishing column
    1399
    1400        try {
    1401            for (int i = r0; i < r1; i++) {
    1402                sb.append(selectionLayer.getDataValueByPosition(c0, i).toString());
    1403                for (int j = c0 + 1; j < c1; j++)
    1404                    sb.append("\t").append(selectionLayer.getDataValueByPosition(j, i).toString());
    1405                sb.append("\n");
    1406            }
    1407        }
    1408        catch (java.lang.OutOfMemoryError err) {
    1409            shell.getDisplay().beep();
    1410            Tools.showError(shell, "Copy",
    1411                    "Copying data to system clipboard failed. \nUse \"export/import data\" for copying/pasting large data.");
    1412            return;
    1413        }
    1414
    1415        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
    1416        StringSelection contents = new StringSelection(sb.toString());
    1417        cb.setContents(contents, null);
    1418    }
    1419
    1420    /**
    1421     * Paste data from the system clipboard to the spreadsheet.
    1422     */
    1423    private void pasteData() {
    1424        if (!Tools.showConfirm(shell, "Clipboard Data", "Do you want to paste selected data?"))
    1425            return;
    1426
    1427        int cols = selectionLayer.getPreferredColumnCount();
    1428        int rows = selectionLayer.getPreferredRowCount();
    1429        int r0 = 0;
    1430        int c0 = 0;
    1431
    1432        Rectangle selection = selectionLayer.getLastSelectedRegion();
    1433        if (selection != null) {
    1434            r0 = selection.y;
    1435            c0 = selection.x;
    1436        }
    1437
    1438        if (c0 < 0)
    1439            c0 = 0;
    1440        if (r0 < 0)
    1441            r0 = 0;
    1442        int r = r0;
    1443        int c = c0;
    1444
    1445        Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
    1446        String line = "";
    1447        try {
    1448            String s = (String) cb.getData(DataFlavor.stringFlavor);
    1449
    1450            StringTokenizer st = new StringTokenizer(s, "\n");
    1451            // read line by line
    1452            while (st.hasMoreTokens() && (r < rows)) {
    1453                line = st.nextToken();
    1454
    1455                if (fixedDataLength < 1) {
    1456                    // separate by delimiter
    1457                    StringTokenizer lt = new StringTokenizer(line, "\t");
    1458                    while (lt.hasMoreTokens() && (c < cols)) {
    1459                        try {
    1460                            dataProvider.setDataValue(c, r, lt.nextToken());
    1461                        }
    1462                        catch (Exception ex) {
    1463                            continue;
    1464                        }
    1465                        c++;
    1466                    }
    1467                    r = r + 1;
    1468                    c = c0;
    1469                }
    1470                else {
    1471                    // the data has fixed length
    1472                    int n = line.length();
    1473                    String theVal;
    1474                    for (int i = 0; i < n; i = i + fixedDataLength) {
    1475                        try {
    1476                            theVal = line.substring(i, i + fixedDataLength);
    1477                            dataProvider.setDataValue(c, r, theVal);
    1478                        }
    1479                        catch (Exception ex) {
    1480                            continue;
    1481                        }
    1482                        c++;
    1483                    }
    1484                }
    1485            }
    1486        }
    1487        catch (Exception ex) {
    1488            shell.getDisplay().beep();
    1489            Tools.showError(shell, "Paste", ex.getMessage());
    1490        }
    1491    }
    1492
    1493    /**
    1494     * Save data as text.
    1495     *
    1496     * @throws Exception
    1497     *             if a failure occurred
    1498     */
    1499    protected void saveAsText() throws Exception {
    1500        String currentDir = ((HObject) dataObject).getFileFormat().getParent();
    1501
    1502        String filename = null;
    1503        if (((HDFView) viewer).getTestState()) {
    1504            filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open();
    1505        }
    1506        else {
    1507            FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
    1508            fChooser.setFilterPath(currentDir);
    1509
    1510            DefaultFileFilter filter = DefaultFileFilter.getFileFilterText();
    1511            fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
    1512            fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
    1513            fChooser.setFilterIndex(1);
    1514            fChooser.setText("Save Current Data To Text File --- " + ((HObject) dataObject).getName());
    1515
    1516            filename = fChooser.open();
    1517        }
    1518        if (filename == null)
    1519            return;
    1520
    1521        File chosenFile = new File(filename);
    1522        String fname = chosenFile.getAbsolutePath();
    1523
    1524        log.trace("saveAsText: file={}", fname);
    1525
    1526        // Check if the file is in use and prompt for overwrite
    1527        if (chosenFile.exists()) {
    1528            List<?> fileList = viewer.getTreeView().getCurrentFiles();
    1529            if (fileList != null) {
    1530                FileFormat theFile = null;
    1531                Iterator<?> iterator = fileList.iterator();
    1532                while (iterator.hasNext()) {
    1533                    theFile = (FileFormat) iterator.next();
    1534                    if (theFile.getFilePath().equals(fname)) {
    1535                        shell.getDisplay().beep();
    1536                        Tools.showError(shell, "Save",
    1537                                "Unable to save data to file \"" + fname + "\". \nThe file is being used.");
    1538                        return;
    1539                    }
    1540                }
    1541            }
    1542
    1543            if (!Tools.showConfirm(shell, "Save", "File exists. Do you want to replace it?"))
    1544                return;
    1545        }
    1546
    1547        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(chosenFile)));
    1548
    1549        String delName = ViewProperties.getDataDelimiter();
    1550        String delimiter = "";
    1551
    1552        // delimiter must include a tab to be consistent with copy/paste for
    1553        // compound fields
    1554        if (dataObject instanceof CompoundDS)
    1555            delimiter = "\t";
    1556
    1557        if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_TAB))
    1558            delimiter = "\t";
    1559        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SPACE))
    1560            delimiter = " " + delimiter;
    1561        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COMMA))
    1562            delimiter = "," + delimiter;
    1563        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COLON))
    1564            delimiter = ":" + delimiter;
    1565        else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SEMI_COLON))
    1566            delimiter = ";" + delimiter;
    1567
    1568        int cols = selectionLayer.getPreferredColumnCount();
    1569        int rows = selectionLayer.getPreferredRowCount();
    1570
    1571        for (int i = 0; i < rows; i++) {
    1572            out.print(selectionLayer.getDataValueByPosition(0, i));
    1573            for (int j = 1; j < cols; j++) {
    1574                out.print(delimiter);
    1575                out.print(selectionLayer.getDataValueByPosition(j, i));
    1576            }
    1577            out.println();
    1578        }
    1579
    1580        out.flush();
    1581        out.close();
    1582
    1583        viewer.showStatus("Data saved to: " + fname);
    1584    }
    1585
    1586    /**
    1587     * Save data as binary.
    1588     *
    1589     * @throws Exception
    1590     *             if a failure occurred
    1591     */
    1592    protected void saveAsBinary() throws Exception {
    1593        String currentDir = ((HObject) dataObject).getFileFormat().getParent();
    1594
    1595        String filename = null;
    1596        if (((HDFView) viewer).getTestState()) {
    1597            filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open();
    1598        }
    1599        else {
    1600            FileDialog fChooser = new FileDialog(shell, SWT.SAVE);
    1601            fChooser.setFilterPath(currentDir);
    1602
    1603            DefaultFileFilter filter = DefaultFileFilter.getFileFilterBinary();
    1604            fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
    1605            fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
    1606            fChooser.setFilterIndex(1);
    1607            fChooser.setText("Save Current Data To Binary File --- " + ((HObject) dataObject).getName());
    1608
    1609            filename = fChooser.open();
    1610        }
    1611        if (filename == null)
    1612            return;
    1613
    1614        File chosenFile = new File(filename);
    1615        String fname = chosenFile.getAbsolutePath();
    1616
    1617        log.trace("saveAsBinary: file={}", fname);
    1618
    1619        // Check if the file is in use and prompt for overwrite
    1620        if (chosenFile.exists()) {
    1621            List<?> fileList = viewer.getTreeView().getCurrentFiles();
    1622            if (fileList != null) {
    1623                FileFormat theFile = null;
    1624                Iterator<?> iterator = fileList.iterator();
    1625                while (iterator.hasNext()) {
    1626                    theFile = (FileFormat) iterator.next();
    1627                    if (theFile.getFilePath().equals(fname)) {
    1628                        shell.getDisplay().beep();
    1629                        Tools.showError(shell, "Save",
    1630                                "Unable to save data to file \"" + fname + "\". \nThe file is being used.");
    1631                        return;
    1632                    }
    1633                }
    1634            }
    1635
    1636            if (!Tools.showConfirm(shell, "Save", "File exists. Do you want to replace it?"))
    1637                return;
    1638        }
    1639
    1640        try (DataOutputStream out = new DataOutputStream(new FileOutputStream(chosenFile))) {
    1641            if (dataObject instanceof ScalarDS) {
    1642                ((ScalarDS) dataObject).convertToUnsignedC();
    1643                Object data = dataObject.getData();
    1644                ByteOrder bo = ByteOrder.nativeOrder();
    1645
    1646                if (binaryOrder == 1)
    1647                    bo = ByteOrder.nativeOrder();
    1648                else if (binaryOrder == 2)
    1649                    bo = ByteOrder.LITTLE_ENDIAN;
    1650                else if (binaryOrder == 3)
    1651                    bo = ByteOrder.BIG_ENDIAN;
    1652
    1653                Tools.saveAsBinary(out, data, bo);
    1654
    1655                viewer.showStatus("Data saved to: " + fname);
    1656            }
    1657            else
    1658                viewer.showError("Data not saved - not a ScalarDS");
    1659        }
    1660    }
    1661
    1662    /**
    1663     * Import data values from text file.
    1664     *
    1665     * @param fname
    1666     *            the file to import text from
    1667     */
    1668    protected void importTextData(String fname) {
    1669        int cols = selectionLayer.getPreferredColumnCount();
    1670        int rows = selectionLayer.getPreferredRowCount();
    1671        int r0;
    1672        int c0;
    1673
    1674        Rectangle lastSelection = selectionLayer.getLastSelectedRegion();
    1675        if (lastSelection != null) {
    1676            r0 = lastSelection.y;
    1677            c0 = lastSelection.x;
    1678
    1679            if (c0 < 0)
    1680                c0 = 0;
    1681            if (r0 < 0)
    1682                r0 = 0;
    1683        }
    1684        else {
    1685            r0 = 0;
    1686            c0 = 0;
    1687        }
    1688
    1689        // Start at the first column for compound datasets
    1690        if (dataObject instanceof CompoundDS)
    1691            c0 = 0;
    1692
    1693        String importLine = null;
    1694        StringTokenizer tokenizer1 = null;
    1695        try (BufferedReader in = new BufferedReader(new FileReader(fname))) {
    1696            try {
    1697                importLine = in.readLine();
    1698            }
    1699            catch (FileNotFoundException ex) {
    1700                log.debug("import data values from text file {}:", fname, ex);
    1701                return;
    1702            }
    1703            catch (IOException ex) {
    1704                log.debug("read text file {}:", fname, ex);
    1705                return;
    1706            }
    1707
    1708            String delName = ViewProperties.getDataDelimiter();
    1709            String delimiter = "";
    1710
    1711            if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_TAB))
    1712                delimiter = "\t";
    1713            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SPACE))
    1714                delimiter = " " + delimiter;
    1715            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COMMA))
    1716                delimiter = ",";
    1717            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_COLON))
    1718                delimiter = ":";
    1719            else if (delName.equalsIgnoreCase(ViewProperties.DELIMITER_SEMI_COLON))
    1720                delimiter = ";";
    1721            String token = null;
    1722            int r = r0;
    1723            int c = c0;
    1724            while ((importLine != null) && (r < rows)) {
    1725                if (fixedDataLength > 0) {
    1726                    // the data has fixed length
    1727                    int n = importLine.length();
    1728                    String theVal;
    1729                    for (int i = 0; i < n; i = i + fixedDataLength) {
    1730                        try {
    1731                            theVal = importLine.substring(i, i + fixedDataLength);
    1732                            dataProvider.setDataValue(c, r, theVal);
    1733                        }
    1734                        catch (Exception ex) {
    1735                            continue;
    1736                        }
    1737                        c++;
    1738                    }
    1739                }
    1740                else {
    1741                    try {
    1742                        tokenizer1 = new StringTokenizer(importLine, delimiter);
    1743                        while (tokenizer1.hasMoreTokens() && (c < cols)) {
    1744                            token = tokenizer1.nextToken();
    1745                            StringTokenizer tokenizer2 = new StringTokenizer(token);
    1746                            if (tokenizer2.hasMoreTokens()) {
    1747                                while (tokenizer2.hasMoreTokens() && (c < cols)) {
    1748                                    dataProvider.setDataValue(c, r, tokenizer2.nextToken());
    1749                                    c++;
    1750                                }
    1751                            }
    1752                            else
    1753                                c++;
    1754                        }
    1755                    }
    1756                    catch (Exception ex) {
    1757                        Tools.showError(shell, "Import", ex.getMessage());
    1758                        return;
    1759                    }
    1760                }
    1761
    1762                try {
    1763                    importLine = in.readLine();
    1764                }
    1765                catch (IOException ex) {
    1766                    log.debug("read text file {}:", fname, ex);
    1767                    importLine = null;
    1768                }
    1769
    1770                // Start at the first column for compound datasets
    1771                if (dataObject instanceof CompoundDS)
    1772                    c = 0;
    1773                else
    1774                    c = c0;
    1775
    1776                r++;
    1777            } // ((line != null) && (r < rows))
    1778        }
    1779        catch (IOException ex) {
    1780            log.debug("import text file {}:", fname, ex);
    1781        }
    1782    }
    1783
    1784    /**
    1785     * Import data values from binary file.
    1786     */
    1787    protected void importBinaryData() {
    1788        String currentDir = ((HObject) dataObject).getFileFormat().getParent();
    1789
    1790        String filename = null;
    1791        if (((HDFView) viewer).getTestState()) {
    1792            filename = currentDir + File.separator + new InputDialog(shell, "Enter a file name", "").open();
    1793        }
    1794        else {
    1795            FileDialog fChooser = new FileDialog(shell, SWT.OPEN);
    1796            fChooser.setFilterPath(currentDir);
    1797
    1798            DefaultFileFilter filter = DefaultFileFilter.getFileFilterBinary();
    1799            fChooser.setFilterExtensions(new String[] { "*", filter.getExtensions() });
    1800            fChooser.setFilterNames(new String[] { "All Files", filter.getDescription() });
    1801            fChooser.setFilterIndex(1);
    1802
    1803            filename = fChooser.open();
    1804        }
    1805
    1806        if (filename == null)
    1807            return;
    1808
    1809        File chosenFile = new File(filename);
    1810        if (!chosenFile.exists()) {
    1811            Tools.showError(shell, "Import Data from Binary File", "Data import error: " + chosenFile.getName() + " does not exist.");
    1812            return;
    1813        }
    1814
    1815        if (!Tools.showConfirm(shell, "Import Data from Binary File", "Do you want to paste selected data?"))
    1816            return;
    1817
    1818        ByteOrder bo = ByteOrder.nativeOrder();
    1819        if (binaryOrder == 1)
    1820            bo = ByteOrder.nativeOrder();
    1821        else if (binaryOrder == 2)
    1822            bo = ByteOrder.LITTLE_ENDIAN;
    1823        else if (binaryOrder == 3)
    1824            bo = ByteOrder.BIG_ENDIAN;
    1825
    1826        try {
    1827            if (Tools.getBinaryDataFromFile(dataValue, chosenFile.getAbsolutePath(), bo))
    1828                dataProvider.setIsValueChanged(true);
    1829
    1830            dataTable.doCommand(new StructuralRefreshCommand());
    1831        }
    1832        catch (Exception ex) {
    1833            log.debug("importBinaryData():", ex);
    1834        }
    1835        catch (OutOfMemoryError e) {
    1836            log.debug("importBinaryData(): Out of memory");
    1837        }
    1838    }
    1839
    1840    /**
    1841     * Convert selected data based on predefined math functions.
    1842     */
    1843    private void mathConversion() throws Exception {
    1844        if (isReadOnly) {
    1845            log.debug("mathConversion(): can't convert read-only data");
    1846            return;
    1847        }
    1848
    1849        int cols = selectionLayer.getSelectedColumnPositions().length;
    1850        if ((dataObject instanceof CompoundDS) && (cols > 1)) {
    1851            shell.getDisplay().beep();
    1852            Tools.showError(shell, "Convert", "Please select one column at a time for math conversion" + "for compound dataset.");
    1853            log.debug("mathConversion(): more than one column selected for CompoundDS");
    1854            return;
    1855        }
    1856
    1857        Object theData = getSelectedData();
    1858        if (theData == null) {
    1859            shell.getDisplay().beep();
    1860            Tools.showError(shell, "Convert", "No data is selected.");
    1861            log.debug("mathConversion(): no data selected");
    1862            return;
    1863        }
    1864
    1865        MathConversionDialog dialog = new MathConversionDialog(shell, theData);
    1866        dialog.open();
    1867
    1868        if (dialog.isConverted()) {
    1869            if (dataObject instanceof CompoundDS) {
    1870                Object colData = null;
    1871                try {
    1872                    colData = ((List<?>) dataObject.getData()).get(selectionLayer.getSelectedColumnPositions()[0]);
    1873                }
    1874                catch (Exception ex) {
    1875                    log.debug("mathConversion(): ", ex);
    1876                }
    1877
    1878                if (colData != null) {
    1879                    int size = Array.getLength(theData);
    1880                    System.arraycopy(theData, 0, colData, 0, size);
    1881                }
    1882            }
    1883            else {
    1884                int rows = selectionLayer.getSelectedRowCount();
    1885
    1886                // Since NatTable returns the selected row positions as a Set<Range>, convert
    1887                // this to
    1888                // an Integer[]
    1889                Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
    1890                Set<Integer> selectedRowPos = new LinkedHashSet<>();
    1891                Iterator<Range> i1 = rowPositions.iterator();
    1892                while (i1.hasNext())
    1893                    selectedRowPos.addAll(i1.next().getMembers());
    1894
    1895                int r0 = selectedRowPos.toArray(new Integer[0])[0];
    1896                int c0 = selectionLayer.getSelectedColumnPositions()[0];
    1897
    1898                int w = dataTable.getPreferredColumnCount() - 1;
    1899                int idxSrc = 0;
    1900                int idxDst = 0;
    1901
    1902                for (int i = 0; i < rows; i++) {
    1903                    idxDst = (r0 + i) * w + c0;
    1904                    System.arraycopy(theData, idxSrc, dataValue, idxDst, cols);
    1905                    idxSrc += cols;
    1906                }
    1907            }
    1908
    1909            System.gc();
    1910
    1911            dataProvider.setIsValueChanged(true);
    1912        }
    1913    }
    1914
    1915    private void showLineplot() {
    1916        // Since NatTable returns the selected row positions as a Set<Range>, convert
    1917        // this to
    1918        // an Integer[]
    1919        Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
    1920        Set<Integer> selectedRowPos = new LinkedHashSet<>();
    1921        Iterator<Range> i1 = rowPositions.iterator();
    1922        while (i1.hasNext()) {
    1923            selectedRowPos.addAll(i1.next().getMembers());
    1924        }
    1925
    1926        Integer[] rows = selectedRowPos.toArray(new Integer[0]);
    1927        int[] cols = selectionLayer.getSelectedColumnPositions();
    1928
    1929        if ((rows == null) || (cols == null) || (rows.length <= 0) || (cols.length <= 0)) {
    1930            shell.getDisplay().beep();
    1931            Tools.showError(shell, "Select", "Select rows/columns to draw line plot.");
    1932            return;
    1933        }
    1934
    1935        int nrow = dataTable.getPreferredRowCount() - 1;
    1936        int ncol = dataTable.getPreferredColumnCount() - 1;
    1937
    1938        log.trace("DefaultTableView showLineplot: {} - {}", nrow, ncol);
    1939        LinePlotOption lpo = new LinePlotOption(shell, SWT.NONE, nrow, ncol);
    1940        lpo.open();
    1941
    1942        int plotType = lpo.getPlotBy();
    1943        if (plotType == LinePlotOption.NO_PLOT)
    1944            return;
    1945
    1946        boolean isRowPlot = (plotType == LinePlotOption.ROW_PLOT);
    1947        int xIndex = lpo.getXindex();
    1948
    1949        // figure out to plot data by row or by column
    1950        // Plot data by rows if all columns are selected and part of
    1951        // rows are selected, otherwise plot data by column
    1952        double[][] data = null;
    1953        int nLines = 0;
    1954        String title = "Lineplot - " + ((HObject) dataObject).getPath() + ((HObject) dataObject).getName();
    1955        String[] lineLabels = null;
    1956        double[] yRange = { Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY };
    1957        double[] xData = null;
    1958
    1959        if (isRowPlot) {
    1960            title += " - by row";
    1961            nLines = rows.length;
    1962            if (nLines > 10) {
    1963                shell.getDisplay().beep();
    1964                nLines = 10;
    1965                Tools.showWarning(shell, "Select",
    1966                        "More than 10 rows are selected.\n" + "The first 10 rows will be displayed.");
    1967            }
    1968            lineLabels = new String[nLines];
    1969            data = new double[nLines][cols.length];
    1970
    1971            double value = 0.0;
    1972            for (int i = 0; i < nLines; i++) {
    1973                lineLabels[i] = String.valueOf(rows[i] + indexBase);
    1974                for (int j = 0; j < cols.length; j++) {
    1975                    data[i][j] = 0;
    1976                    try {
    1977                        value = Double.parseDouble(selectionLayer.getDataValueByPosition(cols[j], rows[i]).toString());
    1978                        data[i][j] = value;
    1979                        yRange[0] = Math.min(yRange[0], value);
    1980                        yRange[1] = Math.max(yRange[1], value);
    1981                    }
    1982                    catch (NumberFormatException ex) {
    1983                        log.debug("rows[{}]:", i, ex);
    1984                    }
    1985                }
    1986            }
    1987
    1988            if (xIndex >= 0) {
    1989                xData = new double[cols.length];
    1990                for (int j = 0; j < cols.length; j++) {
    1991                    xData[j] = 0;
    1992                    try {
    1993                        value = Double.parseDouble(selectionLayer.getDataValueByPosition(cols[j], xIndex).toString());
    1994                        xData[j] = value;
    1995                    }
    1996                    catch (NumberFormatException ex) {
    1997                        log.debug("xIndex of {}:", xIndex, ex);
    1998                    }
    1999                }
    2000            }
    2001        }
    2002        else {
    2003            title += " - by column";
    2004            nLines = cols.length;
    2005            if (nLines > 10) {
    2006                shell.getDisplay().beep();
    2007                nLines = 10;
    2008                Tools.showWarning(shell, "Select",
    2009                        "More than 10 columns are selected.\n" + "The first 10 columns will be displayed.");
    2010            }
    2011            lineLabels = new String[nLines];
    2012            data = new double[nLines][rows.length];
    2013            double value = 0.0;
    2014            for (int j = 0; j < nLines; j++) {
    2015                lineLabels[j] = columnHeaderDataProvider.getDataValue(cols[j] + indexBase, 0).toString();
    2016                for (int i = 0; i < rows.length; i++) {
    2017                    data[j][i] = 0;
    2018                    try {
    2019                        value = Double.parseDouble(selectionLayer.getDataValueByPosition(cols[j], rows[i]).toString());
    2020                        data[j][i] = value;
    2021                        yRange[0] = Math.min(yRange[0], value);
    2022                        yRange[1] = Math.max(yRange[1], value);
    2023                    }
    2024                    catch (NumberFormatException ex) {
    2025                        log.debug("cols[{}]:", j, ex);
    2026                    }
    2027                }
    2028            }
    2029
    2030            if (xIndex >= 0) {
    2031                xData = new double[rows.length];
    2032                for (int j = 0; j < rows.length; j++) {
    2033                    xData[j] = 0;
    2034                    try {
    2035                        value = Double.parseDouble(selectionLayer.getDataValueByPosition(xIndex, rows[j]).toString());
    2036                        xData[j] = value;
    2037                    }
    2038                    catch (NumberFormatException ex) {
    2039                        log.debug("xIndex of {}:", xIndex, ex);
    2040                    }
    2041                }
    2042            }
    2043        }
    2044
    2045        int n = removeInvalidPlotData(data, xData, yRange);
    2046        if (n < data[0].length) {
    2047            double[][] dataNew = new double[data.length][n];
    2048            for (int i = 0; i < data.length; i++)
    2049                System.arraycopy(data[i], 0, dataNew[i], 0, n);
    2050
    2051            data = dataNew;
    2052
    2053            if (xData != null) {
    2054                double[] xDataNew = new double[n];
    2055                System.arraycopy(xData, 0, xDataNew, 0, n);
    2056                xData = xDataNew;
    2057            }
    2058        }
    2059
    2060        // allow to draw a flat line: all values are the same
    2061        if (yRange[0] == yRange[1]) {
    2062            yRange[1] += 1;
    2063            yRange[0] -= 1;
    2064        }
    2065        else if (yRange[0] > yRange[1]) {
    2066            shell.getDisplay().beep();
    2067            Tools.showError(shell, "Select", "Cannot show line plot for the selected data. \n" + "Please check the data range: ("
    2068                    + yRange[0] + ", " + yRange[1] + ").");
    2069            return;
    2070        }
    2071        if (xData == null) { // use array index and length for x data range
    2072            xData = new double[2];
    2073            xData[0] = indexBase; // 1- or zero-based
    2074            xData[1] = data[0].length + (double) indexBase - 1; // maximum index
    2075        }
    2076
    2077        Chart cv = new Chart(shell, title, Chart.LINEPLOT, data, xData, yRange);
    2078        cv.setLineLabels(lineLabels);
    2079
    2080        String cname = dataValue.getClass().getName();
    2081        char dname = cname.charAt(cname.lastIndexOf('[') + 1);
    2082        if ((dname == 'B') || (dname == 'S') || (dname == 'I') || (dname == 'J'))
    2083            cv.setTypeToInteger();
    2084
    2085        cv.open();
    2086    }
    2087
    2088    /**
    2089     * Remove values of NaN, INF from the array.
    2090     *
    2091     * @param data
    2092     *            the data array
    2093     * @param xData
    2094     *            the x-axis data points
    2095     * @param yRange
    2096     *            the range of data values
    2097     *
    2098     * @return number of data points in the plot data if successful; otherwise,
    2099     *         returns false.
    2100     */
    2101    private int removeInvalidPlotData(double[][] data, double[] xData, double[] yRange) {
    2102        int idx = 0;
    2103        boolean hasInvalid = false;
    2104
    2105        if (data == null || yRange == null)
    2106            return -1;
    2107
    2108        yRange[0] = Double.POSITIVE_INFINITY;
    2109        yRange[1] = Double.NEGATIVE_INFINITY;
    2110
    2111        for (int i = 0; i < data[0].length; i++) {
    2112            hasInvalid = false;
    2113
    2114            for (int j = 0; j < data.length; j++) {
    2115                hasInvalid = Tools.isNaNINF(data[j][i]);
    2116                if (xData != null)
    2117                    hasInvalid = hasInvalid || Tools.isNaNINF(xData[i]);
    2118
    2119                if (hasInvalid)
    2120                    break;
    2121                else {
    2122                    data[j][idx] = data[j][i];
    2123                    if (xData != null)
    2124                        xData[idx] = xData[i];
    2125                    yRange[0] = Math.min(yRange[0], data[j][idx]);
    2126                    yRange[1] = Math.max(yRange[1], data[j][idx]);
    2127                }
    2128            }
    2129
    2130            if (!hasInvalid)
    2131                idx++;
    2132        }
    2133
    2134        return idx;
    2135    }
    2136
    2137    /**
    2138     * An implementation of a GridLayer with support for column grouping and with
    2139     * editing triggered by a double click instead of a single click.
    2140     */
    2141    protected class EditingGridLayer extends GridLayer
    2142    {
    2143        /** Create the Grid Layer with editing triggered by a
    2144         *  double click instead of a single click.
    2145         *
    2146         * @param bodyLayer
    2147         *        the body layer
    2148         * @param columnHeaderLayer
    2149         *        the Column Header layer
    2150         * @param rowHeaderLayer
    2151         *        the Row Header layer
    2152         * @param cornerLayer
    2153         *        the Corner Layer
    2154         */
    2155        public EditingGridLayer(ILayer bodyLayer, ILayer columnHeaderLayer, ILayer rowHeaderLayer, ILayer cornerLayer) {
    2156            super(bodyLayer, columnHeaderLayer, rowHeaderLayer, cornerLayer, false);
    2157
    2158            // Left-align cells, change font for rendering cell text
    2159            // and add cell data display converter for displaying as
    2160            // Hexadecimal, Binary, etc.
    2161            this.addConfiguration(new AbstractRegistryConfiguration() {
    2162                @Override
    2163                public void configureRegistry(IConfigRegistry configRegistry) {
    2164                    Style cellStyle = new Style();
    2165
    2166                    cellStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.LEFT);
    2167                    cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,
    2168                            Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
    2169
    2170                    if (curFont != null)
    2171                        cellStyle.setAttributeValue(CellStyleAttributes.FONT, curFont);
    2172                    else
    2173                        cellStyle.setAttributeValue(CellStyleAttributes.FONT, Display.getDefault().getSystemFont());
    2174
    2175                    configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle,
    2176                            DisplayMode.NORMAL, GridRegion.BODY);
    2177
    2178                    configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle,
    2179                            DisplayMode.SELECT, GridRegion.BODY);
    2180
    2181                    // Add data display conversion capability
    2182                    try {
    2183                        dataDisplayConverter = DataDisplayConverterFactory.getDataDisplayConverter(dataObject);
    2184
    2185                        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
    2186                                dataDisplayConverter, DisplayMode.NORMAL, GridRegion.BODY);
    2187                    }
    2188                    catch (Exception ex) {
    2189                        log.debug("EditingGridLayer: failed to retrieve a DataDisplayConverter: ", ex);
    2190                        dataDisplayConverter = null;
    2191                    }
    2192                }
    2193            });
    2194
    2195            if (isStdRef || isRegRef || isObjRef) {
    2196                // Show data pointed to by reference on double click
    2197                this.addConfiguration(new AbstractUiBindingConfiguration() {
    2198                    @Override
    2199                    public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
    2200                        uiBindingRegistry.registerDoubleClickBinding(new MouseEventMatcher(), new IMouseAction() {
    2201                            @Override
    2202                            public void run(NatTable table, MouseEvent event) {
    2203                                if (!(isStdRef || isRegRef || isObjRef))
    2204                                    return;
    2205
    2206                                viewType = ViewType.TABLE;
    2207
    2208                                Object theData = null;
    2209                                try {
    2210                                    theData = ((Dataset) getDataObject()).getData();
    2211                                }
    2212                                catch (Exception ex) {
    2213                                    log.debug("show reference data: ", ex);
    2214                                    theData = null;
    2215                                    Tools.showError(shell, "Select", ex.getMessage());
    2216                                }
    2217
    2218                                if (theData == null) {
    2219                                    shell.getDisplay().beep();
    2220                                    Tools.showError(shell, "Select", "No data selected.");
    2221                                    return;
    2222                                }
    2223
    2224                                // Since NatTable returns the selected row positions as a Set<Range>, convert
    2225                                // this to an Integer[]
    2226                                Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
    2227                                Set<Integer> selectedRowPos = new LinkedHashSet<>();
    2228                                Iterator<Range> i1 = rowPositions.iterator();
    2229                                while (i1.hasNext()) {
    2230                                    selectedRowPos.addAll(i1.next().getMembers());
    2231                                }
    2232
    2233                                Integer[] selectedRows = selectedRowPos.toArray(new Integer[0]);
    2234                                if (selectedRows == null || selectedRows.length <= 0) {
    2235                                    log.debug("show reference data: no data selected");
    2236                                    Tools.showError(shell, "Select", "No data selected.");
    2237                                    return;
    2238                                }
    2239                                int len = Array.getLength(selectedRows);
    2240                                for (int i = 0; i < len; i++) {
    2241                                    byte[] rElements = null;
    2242                                    if (theData instanceof ArrayList)
    2243                                        rElements = (byte[]) ((ArrayList) theData).get(selectedRows[i]);
    2244                                    else
    2245                                        rElements = (byte[]) theData;
    2246
    2247                                    if (isStdRef)
    2248                                        showStdRefData(rElements);
    2249                                    else if (isRegRef)
    2250                                        showRegRefData(rElements);
    2251                                    else if (isObjRef)
    2252                                        showObjRefData(rElements);
    2253                                }
    2254                            }
    2255                        });
    2256                    }
    2257                });
    2258            }
    2259            else {
    2260                // Add default bindings for editing
    2261                this.addConfiguration(new DefaultEditConfiguration());
    2262
    2263                // Register cell editing rules with the table and add
    2264                // data validation
    2265                this.addConfiguration(new AbstractRegistryConfiguration() {
    2266                    @Override
    2267                    public void configureRegistry(IConfigRegistry configRegistry) {
    2268                        IEditableRule editingRule = getDataEditingRule(dataObject);
    2269                        if (editingRule != null) {
    2270                            // Register cell editing rules with table
    2271                            configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
    2272                                    editingRule, DisplayMode.EDIT);
    2273                        }
    2274
    2275                        // Add data validator and validation error handler
    2276                        DataValidator validator = null;
    2277                        try {
    2278                            validator = DataValidatorFactory.getDataValidator(dataObject);
    2279                        }
    2280                        catch (Exception ex) {
    2281                            log.debug("EditingGridLayer: no DataValidator retrieved, data editing will be disabled");
    2282                        }
    2283
    2284                        if (validator != null) {
    2285                            configRegistry.registerConfigAttribute(EditConfigAttributes.DATA_VALIDATOR, validator,
    2286                                    DisplayMode.EDIT, GridRegion.BODY);
    2287                        }
    2288
    2289                        configRegistry.registerConfigAttribute(EditConfigAttributes.VALIDATION_ERROR_HANDLER,
    2290                                new DialogErrorHandling(), DisplayMode.EDIT, GridRegion.BODY);
    2291                    }
    2292                });
    2293
    2294                // Change cell editing to be on double click rather than single click
    2295                // and allow editing of cells by pressing keys as well
    2296                this.addConfiguration(new AbstractUiBindingConfiguration() {
    2297                    @Override
    2298                    public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
    2299                        uiBindingRegistry.registerFirstKeyBinding(new LetterOrDigitKeyEventMatcher(), new KeyEditAction());
    2300                        uiBindingRegistry.registerFirstDoubleClickBinding(
    2301                                new CellEditorMouseEventMatcher(), new MouseEditAction());
    2302                    }
    2303                });
    2304            }
    2305        }
    2306    }
    2307
    2308    /**
    2309     * An implementation of the table's Row Header which adapts to the current font.
    2310     */
    2311    protected class RowHeader extends RowHeaderLayer
    2312    {
    2313        /** Create the RowHeader which adapts to the current font.
    2314         *
    2315         * @param baseLayer
    2316         *        the base layer
    2317         * @param verticalLayerDependency
    2318         *        the vertical layer dependency
    2319         * @param selectionLayer
    2320         *        the selection layer
    2321         */
    2322        public RowHeader(IUniqueIndexLayer baseLayer, ILayer verticalLayerDependency, SelectionLayer selectionLayer) {
    2323            super(baseLayer, verticalLayerDependency, selectionLayer);
    2324
    2325            this.addConfiguration(new DefaultRowHeaderLayerConfiguration() {
    2326                @Override
    2327                public void addRowHeaderStyleConfig() {
    2328                    this.addConfiguration(new DefaultRowHeaderStyleConfiguration() {
    2329                        {
    2330                            this.cellPainter = new LineBorderDecorator(new TextPainter(false, true, 2, true));
    2331                            this.bgColor = Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
    2332                            this.font = (curFont == null) ? Display.getDefault().getSystemFont() : curFont;
    2333                        }
    2334                    });
    2335                }
    2336            });
    2337        }
    2338    }
    2339
    2340    /**
    2341     * Custom Row Header data provider to set row indices based on Index Base for
    2342     * both Scalar Datasets and Compound Datasets.
    2343     */
    2344    protected class RowHeaderDataProvider implements IDataProvider
    2345    {
    2346        private int    rank;
    2347        private int    space_type;
    2348        private long[] dims;
    2349        private long[] startArray;
    2350        private long[] strideArray;
    2351        private int[]  selectedIndex;
    2352
    2353        /** the start value. */
    2354        protected int  start;
    2355        /** the stride value. */
    2356        protected int  stride;
    2357
    2358        private int    nrows;
    2359
    2360        /** Create the Row Header data provider to set row indices based on Index Base for
    2361         *  both Scalar Datasets and Compound Datasets.
    2362         *
    2363         * @param theDataObject
    2364         *        the data object
    2365         */
    2366        public RowHeaderDataProvider(DataFormat theDataObject) {
    2367            this.space_type = theDataObject.getSpaceType();
    2368            this.rank = theDataObject.getRank();
    2369            this.dims = theDataObject.getSelectedDims();
    2370            this.startArray = theDataObject.getStartDims();
    2371            this.strideArray = theDataObject.getStride();
    2372            this.selectedIndex = theDataObject.getSelectedIndex();
    2373
    2374            if (rank > 1)
    2375                this.nrows = (int) theDataObject.getHeight();
    2376            else
    2377                this.nrows = (int) dims[0];
    2378
    2379            start = (int) startArray[selectedIndex[0]];
    2380            stride = (int) strideArray[selectedIndex[0]];
    2381        }
    2382
    2383
    2384        /** Update the Row Header data provider to set row indices based on Index Base for
    2385         *  both Scalar Datasets and Compound Datasets.
    2386         *
    2387         * @param theDataObject
    2388         *        the data object
    2389         */
    2390        public void updateRows(DataFormat theDataObject) {
    2391            this.rank = theDataObject.getRank();
    2392            this.dims = theDataObject.getSelectedDims();
    2393            this.selectedIndex = theDataObject.getSelectedIndex();
    2394
    2395            if (rank > 1)
    2396                this.nrows = (int) theDataObject.getHeight();
    2397            else
    2398                this.nrows = (int) dims[0];
    2399        }
    2400
    2401        @Override
    2402        public int getColumnCount() {
    2403            return 1;
    2404        }
    2405
    2406        @Override
    2407        public int getRowCount() {
    2408            return nrows;
    2409        }
    2410
    2411        @Override
    2412        public Object getDataValue(int columnIndex, int rowIndex) {
    2413            return String.valueOf(start + indexBase + (rowIndex * stride));
    2414        }
    2415
    2416        @Override
    2417        public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
    2418            // Intentional
    2419        }
    2420    }
    2421
    2422    /**
    2423     * An implementation of the table's Column Header which adapts to the current
    2424     * font.
    2425     */
    2426    protected class ColumnHeader extends ColumnHeaderLayer
    2427    {
    2428        /** Create the ColumnHeader which adapts to the current font.
    2429         *
    2430         * @param baseLayer
    2431         *        the base layer
    2432         * @param horizontalLayerDependency
    2433         *        the horizontal layer dependency
    2434         * @param selectionLayer
    2435         *        the selection layer
    2436         */
    2437        public ColumnHeader(IUniqueIndexLayer baseLayer, ILayer horizontalLayerDependency,
    2438                SelectionLayer selectionLayer) {
    2439            super(baseLayer, horizontalLayerDependency, selectionLayer);
    2440
    2441            this.addConfiguration(new DefaultColumnHeaderLayerConfiguration() {
    2442                @Override
    2443                public void addColumnHeaderStyleConfig() {
    2444                    this.addConfiguration(new DefaultColumnHeaderStyleConfiguration() {
    2445                        {
    2446                            this.cellPainter = new BeveledBorderDecorator(new TextPainter(false, true, 2, true));
    2447                            this.bgColor = Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW);
    2448                            this.font = (curFont == null) ? Display.getDefault().getSystemFont() : curFont;
    2449                        }
    2450                    });
    2451                }
    2452            });
    2453        }
    2454    }
    2455
    2456    /** Context-menu for dealing with region and object references */
    2457    protected class RefContextMenu extends AbstractUiBindingConfiguration
    2458    {
    2459        private final Menu contextMenu;
    2460
    2461        /** Create the Context-menu for dealing with region and object references.
    2462         *
    2463         * @param table
    2464         *        the NatTable object
    2465         */
    2466        public RefContextMenu(NatTable table) {
    2467            this.contextMenu = createMenu(table).build();
    2468        }
    2469
    2470        private void showRefTable() {
    2471            log.trace("show reference data: Show data as {}", viewType);
    2472
    2473            Object theData = getSelectedData();
    2474            if (theData == null) {
    2475                shell.getDisplay().beep();
    2476                Tools.showError(shell, "Select", "No data selected.");
    2477                return;
    2478            }
    2479            if (!(theData instanceof byte[]) && !(theData instanceof ArrayList)) {
    2480                shell.getDisplay().beep();
    2481                Tools.showError(shell, "Select", "Data selected is not a reference.");
    2482                return;
    2483            }
    2484            log.trace("show reference data: Data is {}", theData);
    2485
    2486            // Since NatTable returns the selected row positions as a Set<Range>, convert
    2487            // this to an Integer[]
    2488            Set<Range> rowPositions = selectionLayer.getSelectedRowPositions();
    2489            Set<Integer> selectedRowPos = new LinkedHashSet<>();
    2490            Iterator<Range> i1 = rowPositions.iterator();
    2491            while (i1.hasNext())
    2492                selectedRowPos.addAll(i1.next().getMembers());
    2493
    2494            Integer[] selectedRows = selectedRowPos.toArray(new Integer[0]);
    2495            int[] selectedCols = selectionLayer.getSelectedColumnPositions();
    2496            if (selectedRows == null || selectedRows.length <= 0) {
    2497                shell.getDisplay().beep();
    2498                Tools.showError(shell, "Select", "No data selected.");
    2499                log.trace("show reference data: Show data as {}: selectedRows is empty", viewType);
    2500                return;
    2501            }
    2502
    2503            int len = Array.getLength(selectedRows) * Array.getLength(selectedCols);
    2504            log.trace("show reference data: Show data as {}: len={}", viewType, len);
    2505            if (len > 1) {
    2506                shell.getDisplay().beep();
    2507                Tools.showError(shell, "Select", "Reference selection must be one cell.");
    2508                log.trace("show reference data: Show data as {}: Too much data", viewType);
    2509                return;
    2510            }
    2511
    2512            for (int i = 0; i < len; i++) {
    2513                byte[] rElements = null;
    2514                if (theData instanceof ArrayList)
    2515                    rElements = (byte[]) ((ArrayList) theData).get(i);
    2516                else
    2517                    rElements = (byte[]) theData;
    2518
    2519                if (rElements.length == HDF5Constants.H5R_DSET_REG_REF_BUF_SIZE) {
    2520                    showRegRefData(rElements);
    2521                }
    2522                else if (rElements.length == HDF5Constants.H5R_OBJ_REF_BUF_SIZE) {
    2523                    showObjRefData(rElements);
    2524                }
    2525                else {
    2526                    showStdRefData(rElements);
    2527                }
    2528            }
    2529        }
    2530
    2531        private PopupMenuBuilder createMenu(NatTable table) {
    2532            Menu menu = new Menu(table);
    2533
    2534            MenuItem item = new MenuItem(menu, SWT.PUSH);
    2535            item.setText("Show As &Table");
    2536            item.addSelectionListener(new SelectionAdapter() {
    2537                @Override
    2538                public void widgetSelected(SelectionEvent e) {
    2539                    viewType = ViewType.TABLE;
    2540                    showRefTable();
    2541                }
    2542            });
    2543
    2544            item = new MenuItem(menu, SWT.PUSH);
    2545            item.setText("Show As &Image");
    2546            item.addSelectionListener(new SelectionAdapter() {
    2547                @Override
    2548                public void widgetSelected(SelectionEvent e) {
    2549                    viewType = ViewType.IMAGE;
    2550                    showRefTable();
    2551                }
    2552            });
    2553
    2554            return new PopupMenuBuilder(table, menu);
    2555        }
    2556
    2557        @Override
    2558        public void configureUiBindings(UiBindingRegistry uiBindingRegistry) {
    2559            uiBindingRegistry.registerMouseDownBinding(
    2560                    new MouseEventMatcher(SWT.NONE, GridRegion.BODY, MouseEventMatcher.RIGHT_BUTTON),
    2561                    new PopupMenuAction(this.contextMenu));
    2562        }
    2563    }
    2564
    2565    private class LinePlotOption extends Dialog
    2566    {
    2567        private Shell linePlotOptionShell;
    2568
    2569        private Button rowButton, colButton;
    2570
    2571        private Combo rowBox, colBox;
    2572
    2573        public static final int NO_PLOT = -1;
    2574        public static final int ROW_PLOT = 0;
    2575        public static final int COLUMN_PLOT = 1;
    2576
    2577        private int nrow, ncol;
    2578
    2579        private int idx_xaxis = -1;
    2580        private int plotType = -1;
    2581
    2582        public LinePlotOption(Shell parent, int style, int nrow, int ncol) {
    2583            super(parent, style);
    2584
    2585            this.nrow = nrow;
    2586            this.ncol = ncol;
    2587        }
    2588
    2589        public void open() {
    2590            Shell parent = getParent();
    2591            linePlotOptionShell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
    2592            linePlotOptionShell.setFont(curFont);
    2593            linePlotOptionShell.setText("Line Plot Options -- " + ((HObject) dataObject).getName());
    2594            linePlotOptionShell.setImages(ViewProperties.getHdfIcons());
    2595            linePlotOptionShell.setLayout(new GridLayout(1, true));
    2596
    2597            Label label = new Label(linePlotOptionShell, SWT.RIGHT);
    2598            label.setFont(curFont);
    2599            label.setText("Select Line Plot Options:");
    2600
    2601            Composite content = new Composite(linePlotOptionShell, SWT.BORDER);
    2602            content.setLayout(new GridLayout(3, false));
    2603            content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    2604
    2605            label = new Label(content, SWT.RIGHT);
    2606            label.setFont(curFont);
    2607            label.setText(" Series in:");
    2608
    2609            colButton = new Button(content, SWT.RADIO);
    2610            colButton.setFont(curFont);
    2611            colButton.setText("Column");
    2612            colButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
    2613            colButton.addSelectionListener(new SelectionAdapter() {
    2614                @Override
    2615                public void widgetSelected(SelectionEvent e) {
    2616                    colBox.setEnabled(true);
    2617                    rowBox.setEnabled(false);
    2618                }
    2619            });
    2620
    2621            rowButton = new Button(content, SWT.RADIO);
    2622            rowButton.setFont(curFont);
    2623            rowButton.setText("Row");
    2624            rowButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, false, false));
    2625            rowButton.addSelectionListener(new SelectionAdapter() {
    2626                @Override
    2627                public void widgetSelected(SelectionEvent e) {
    2628                    rowBox.setEnabled(true);
    2629                    colBox.setEnabled(false);
    2630                }
    2631            });
    2632
    2633            label = new Label(content, SWT.RIGHT);
    2634            label.setFont(curFont);
    2635            label.setText(" For abscissa use:");
    2636
    2637            long[] startArray = dataObject.getStartDims();
    2638            long[] strideArray = dataObject.getStride();
    2639            int[] selectedIndex = dataObject.getSelectedIndex();
    2640            int start = (int) startArray[selectedIndex[0]];
    2641            int stride = (int) strideArray[selectedIndex[0]];
    2642
    2643            colBox = new Combo(content, SWT.SINGLE | SWT.READ_ONLY);
    2644            colBox.setFont(curFont);
    2645            GridData colBoxData = new GridData(SWT.FILL, SWT.FILL, true, false);
    2646            colBoxData.minimumWidth = 100;
    2647            colBox.setLayoutData(colBoxData);
    2648
    2649            colBox.add("array index");
    2650
    2651            for (int i = 0; i < ncol; i++)
    2652                colBox.add("column " + columnHeaderDataProvider.getDataValue(i, 0));
    2653
    2654            rowBox = new Combo(content, SWT.SINGLE | SWT.READ_ONLY);
    2655            rowBox.setFont(curFont);
    2656            GridData rowBoxData = new GridData(SWT.FILL, SWT.FILL, true, false);
    2657            rowBoxData.minimumWidth = 100;
    2658            rowBox.setLayoutData(rowBoxData);
    2659
    2660            rowBox.add("array index");
    2661
    2662            for (int i = 0; i < nrow; i++)
    2663                rowBox.add("row " + (start + indexBase + i * stride));
    2664
    2665            // Create Ok/Cancel button region
    2666            Composite buttonComposite = new Composite(linePlotOptionShell, SWT.NONE);
    2667            buttonComposite.setLayout(new GridLayout(2, true));
    2668            buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
    2669
    2670            Button okButton = new Button(buttonComposite, SWT.PUSH);
    2671            okButton.setFont(curFont);
    2672            okButton.setText("   &OK   ");
    2673            okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
    2674            okButton.addSelectionListener(new SelectionAdapter() {
    2675                @Override
    2676                public void widgetSelected(SelectionEvent e) {
    2677                    if (colButton.getSelection()) {
    2678                        idx_xaxis = colBox.getSelectionIndex() - 1;
    2679                        plotType = COLUMN_PLOT;
    2680                    }
    2681                    else {
    2682                        idx_xaxis = rowBox.getSelectionIndex() - 1;
    2683                        plotType = ROW_PLOT;
    2684                    }
    2685
    2686                    linePlotOptionShell.dispose();
    2687                }
    2688            });
    2689
    2690            Button cancelButton = new Button(buttonComposite, SWT.PUSH);
    2691            cancelButton.setFont(curFont);
    2692            cancelButton.setText(" &Cancel ");
    2693            cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
    2694            cancelButton.addSelectionListener(new SelectionAdapter() {
    2695                @Override
    2696                public void widgetSelected(SelectionEvent e) {
    2697                    plotType = NO_PLOT;
    2698                    linePlotOptionShell.dispose();
    2699                }
    2700            });
    2701
    2702            colButton.setSelection(true);
    2703            rowButton.setSelection(false);
    2704
    2705            colBox.select(0);
    2706            rowBox.select(0);
    2707
    2708            colBox.setEnabled(colButton.getSelection());
    2709            rowBox.setEnabled(rowButton.getSelection());
    2710
    2711            linePlotOptionShell.pack();
    2712
    2713            linePlotOptionShell.setMinimumSize(linePlotOptionShell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
    2714
    2715            Rectangle parentBounds = parent.getBounds();
    2716            Point shellSize = linePlotOptionShell.getSize();
    2717            linePlotOptionShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
    2718                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
    2719
    2720            linePlotOptionShell.open();
    2721
    2722            Display display = parent.getDisplay();
    2723            while (!linePlotOptionShell.isDisposed())
    2724                if (!display.readAndDispatch()) display.sleep();
    2725        }
    2726
    2727        int getXindex() {
    2728            return idx_xaxis;
    2729        }
    2730
    2731        int getPlotBy() {
    2732            return plotType;
    2733        }
    2734    }
    2735}