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