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