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