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