    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.PaletteView;
    016
    017import java.awt.Image;
    018import java.awt.Toolkit;
    019import java.awt.image.IndexColorModel;
    020import java.awt.image.MemoryImageSource;
    021import java.util.ArrayList;
    022import java.util.Vector;
    023
    024import org.eclipse.swt.SWT;
    025import org.eclipse.swt.custom.TableEditor;
    026import org.eclipse.swt.events.DisposeEvent;
    027import org.eclipse.swt.events.DisposeListener;
    028import org.eclipse.swt.events.MouseAdapter;
    029import org.eclipse.swt.events.MouseEvent;
    030import org.eclipse.swt.events.MouseMoveListener;
    031import org.eclipse.swt.events.PaintEvent;
    032import org.eclipse.swt.events.PaintListener;
    033import org.eclipse.swt.events.SelectionAdapter;
    034import org.eclipse.swt.events.SelectionEvent;
    035import org.eclipse.swt.graphics.Color;
    036import org.eclipse.swt.graphics.Font;
    037import org.eclipse.swt.graphics.GC;
    038import org.eclipse.swt.graphics.Point;
    039import org.eclipse.swt.graphics.Rectangle;
    040import org.eclipse.swt.layout.GridData;
    041import org.eclipse.swt.layout.GridLayout;
    042import org.eclipse.swt.layout.RowLayout;
    043import org.eclipse.swt.widgets.Button;
    044import org.eclipse.swt.widgets.Canvas;
    045import org.eclipse.swt.widgets.Combo;
    046import org.eclipse.swt.widgets.Composite;
    047import org.eclipse.swt.widgets.Dialog;
    048import org.eclipse.swt.widgets.Display;
    049import org.eclipse.swt.widgets.Event;
    050import org.eclipse.swt.widgets.Listener;
    051import org.eclipse.swt.widgets.Shell;
    052import org.eclipse.swt.widgets.Table;
    053import org.eclipse.swt.widgets.TableColumn;
    054import org.eclipse.swt.widgets.TableItem;
    055import org.eclipse.swt.widgets.Text;
    056
    057import hdf.object.FileFormat;
    058import hdf.object.HObject;
    059import hdf.object.ScalarDS;
    060import hdf.view.Tools;
    061import hdf.view.ViewProperties;
    062import hdf.view.DataView.DataViewManager;
    063import hdf.view.ImageView.ImageView;
    064
    065/**
    066 * Displays a dialog for viewing and change palettes.
    067 *
    068 * @author Jordan T. Henderson
    069 * @version 2.4 2/27/16
    070 */
    071public class DefaultPaletteView extends Dialog implements PaletteView {
    072
    073    private Shell shell;
    074
    075    private Font curFont;
    076
    077    private ScalarDS dataset;
    078
    079    /** Panel that draws plot of data values. */
    080    private ChartCanvas chartP;
    081    private ImageView imageView;
    082    private PaletteValueTable paletteValueTable;
    083
    084    private Button checkRed;
    085    private Button checkGreen;
    086    private Button checkBlue;
    087
    088    private Combo choicePalette;
    089
    090    private Image originalImage;
    091    private Image currentImage;
    092
    093    private static final int[] lineColors = { SWT.COLOR_RED, SWT.COLOR_GREEN, SWT.COLOR_BLUE };
    094    private static final String[] lineLabels = { "Red", "Green", "Blue" };
    095
    096    private static final String PALETTE_GRAY = "Gray";
    097    private static final String PALETTE_DEFAULT = "Default";
    098    private static final String PALETTE_REVERSE_GRAY = "Reverse Gray";
    099    private static final String PALETTE_GRAY_WAVE = "GrayWave";
    100    private static final String PALETTE_RAINBOW = "Rainbow";
    101    private static final String PALETTE_NATURE = "Nature";
    102    private static final String PALETTE_WAVE = "Wave";
    103
    104    byte[][] palette;
    105    private int numberOfPalettes;
    106    private int[][] paletteData;
    107
    108    boolean isPaletteChanged = false;
    109    private boolean isH5 = false;
    110
    111    public DefaultPaletteView(Shell parent, ImageView theImageView) {
    112        this(parent, null, theImageView);
    113    }
    114
    115    public DefaultPaletteView(Shell parent, DataViewManager theViewer, ImageView theImageView) {
    116        super(parent, SWT.APPLICATION_MODAL);
    117
    118        try {
    119            curFont = new Font(
    120                    Display.getCurrent(),
    121                    ViewProperties.getFontType(),
    122                    ViewProperties.getFontSize(),
    123                    SWT.NORMAL);
    124        }
    125        catch (Exception ex) {
    126            curFont = null;
    127        }
    128
    129        imageView = theImageView;
    130        dataset = (ScalarDS) imageView.getDataObject();
    131
    132        numberOfPalettes = 1;
    133
    134        paletteData = new int[3][256];
    135        byte[][] imagePalette = imageView.getPalette();
    136
    137        int d = 0;
    138        for (int i = 0; i < 3; i++) {
    139            for (int j = 0; j < 256; j++) {
    140                d = imagePalette[i][j];
    141                if (d < 0) {
    142                    d += 256;
    143                }
    144                paletteData[i][j] = d;
    145            }
    146        }
    147
    148        originalImage = currentImage = imageView.getImage();
    149        palette = new byte[3][256];
    150
    151        isH5 = dataset.getFileFormat().isThisType(FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5));
    152
    153        createUI();
    154    }
    155
    156    public void createUI() {
    157        Shell parent = getParent();
    158        shell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
    159        shell.setFont(curFont);
    160        shell.setText("Image Palette for - " + dataset.getPath() + dataset.getName());
    161        shell.setImage(ViewProperties.getHdfIcon());
    162        shell.setLayout(new GridLayout(1, true));
    163
    164        shell.setData(this);
    165
    166        chartP = new ChartCanvas(shell, SWT.DOUBLE_BUFFERED | SWT.BORDER);
    167        GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
    168        data.widthHint = 700 + (ViewProperties.getFontSize() - 12) * 15;
    169        data.heightHint = 500 + (ViewProperties.getFontSize() - 12) * 10;
    170        chartP.setLayoutData(data);
    171
    172        // Create the toolbar composite
    173        Composite tools = new Composite(shell, SWT.NONE);
    174        tools.setLayout(new GridLayout(3, false));
    175        tools.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    176
    177        // Add buttons for changing line colors
    178        Composite rgbComposite = new Composite(tools, SWT.BORDER);
    179        rgbComposite.setLayout(new GridLayout(3, true));
    180        rgbComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    181
    182        checkRed = new Button(rgbComposite, SWT.RADIO);
    183        checkRed.setFont(curFont);
    184        checkRed.setText("Red");
    185        checkRed.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
    186        checkRed.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
    187
    188        checkGreen = new Button(rgbComposite, SWT.RADIO);
    189        checkGreen.setFont(curFont);
    190        checkGreen.setText("Green");
    191        checkGreen.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_GREEN));
    192        checkGreen.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
    193
    194        checkBlue = new Button(rgbComposite, SWT.RADIO);
    195        checkBlue.setFont(curFont);
    196        checkBlue.setText("Blue");
    197        checkBlue.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE));
    198        checkBlue.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
    199
    200        checkRed.setSelection(true);
    201        checkGreen.setSelection(false);
    202        checkBlue.setSelection(false);
    203
    204        // Add controls for selecting palettes and showing values
    205        Composite paletteComposite = new Composite(tools, SWT.BORDER);
    206        paletteComposite.setLayout(new GridLayout(2, false));
    207        paletteComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    208
    209        choicePalette = new Combo(paletteComposite, SWT.SINGLE | SWT.READ_ONLY);
    210        choicePalette.setFont(curFont);
    211        choicePalette.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
    212        choicePalette.addSelectionListener(new SelectionAdapter() {
    213            @Override
    214            public void widgetSelected(SelectionEvent e) {
    215                int idx = choicePalette.getSelectionIndex();
    216                if (idx <= 0) {
    217                    return;
    218                }
    219
    220                byte[][] imagePalette = null;
    221                Object item = choicePalette.getItem(idx);
    222
    223                if (item.equals(PALETTE_DEFAULT)) {
    224                    imagePalette = dataset.getPalette();
    225                }
    226                else if (item.equals(PALETTE_GRAY)) {
    227                    imagePalette = Tools.createGrayPalette();
    228                }
    229                else if (item.equals(PALETTE_REVERSE_GRAY)) {
    230                    imagePalette = Tools.createReverseGrayPalette();
    231                }
    232                else if (item.equals(PALETTE_GRAY_WAVE)) {
    233                    imagePalette = Tools.createGrayWavePalette();
    234                }
    235                else if (item.equals(PALETTE_RAINBOW)) {
    236                    imagePalette = Tools.createRainbowPalette();
    237                }
    238                else if (item.equals(PALETTE_NATURE)) {
    239                    imagePalette = Tools.createNaturePalette();
    240                }
    241                else if (item.equals(PALETTE_WAVE)) {
    242                    imagePalette = Tools.createWavePalette();
    243                }
    244                else if (idx > 0 && idx <= numberOfPalettes) {
    245                    imagePalette = dataset.readPalette(idx - 1);
    246                }
    247                else {
    248                    imagePalette = Tools.readPalette((String) item);
    249                }
    250
    251                if (imagePalette == null) {
    252                    return;
    253                }
    254
    255                int d = 0;
    256                for (int i = 0; i < 3; i++) {
    257                    for (int j = 0; j < 256; j++) {
    258                        d = imagePalette[i][j];
    259                        if (d < 0) {
    260                            d += 256;
    261                        }
    262                        paletteData[i][j] = d;
    263                    }
    264                }
    265
    266                chartP.redraw();
    267                isPaletteChanged = true;
    268            }
    269        });
    270
    271        choicePalette.add("Select palette");
    272
    273        String paletteName = dataset.getPaletteName(0);
    274
    275        if (paletteName != null)
    276            paletteName = paletteName.trim();
    277
    278        if (paletteName != null && paletteName.length() > 0)
    279            choicePalette.add(paletteName);
    280
    281        if (isH5 && (dataset instanceof ScalarDS)) {
    282            byte[] palRefs = dataset.getPaletteRefs();
    283            if ((palRefs != null) && (palRefs.length > 8)) {
    284                numberOfPalettes = palRefs.length / 8;
    285            }
    286        }
    287        for (int i = 1; i < numberOfPalettes; i++) {
    288            paletteName = dataset.getPaletteName(i);
    289            choicePalette.add(paletteName);
    290        }
    291        choicePalette.add(PALETTE_GRAY);
    292        choicePalette.add(PALETTE_GRAY_WAVE);
    293        choicePalette.add(PALETTE_RAINBOW);
    294        choicePalette.add(PALETTE_NATURE);
    295        choicePalette.add(PALETTE_WAVE);
    296        ArrayList<?> plist = (ArrayList<?>) ViewProperties.getPaletteList();
    297        int n = plist.size();
    298        for (int i = 0; i < n; i++)
    299            choicePalette.add((String) plist.get(i));
    300
    301        choicePalette.select(0);
    302
    303        Button showValueButton = new Button(paletteComposite, SWT.PUSH);
    304        showValueButton.setFont(curFont);
    305        showValueButton.setText("Show Values");
    306        showValueButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    307        showValueButton.addSelectionListener(new SelectionAdapter() {
    308            @Override
    309            public void widgetSelected(SelectionEvent e) {
    310                if (paletteValueTable == null) {
    311                    paletteValueTable = new PaletteValueTable(shell, SWT.NONE);
    312                }
    313
    314                paletteValueTable.open();
    315            }
    316        });
    317
    318        // Add Ok/Cancel/Preview buttons
    319        Composite buttonComposite = new Composite(tools, SWT.BORDER);
    320        buttonComposite.setLayout(new RowLayout(SWT.HORIZONTAL));
    321        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
    322
    323        Button okButton = new Button(buttonComposite, SWT.PUSH);
    324        okButton.setFont(curFont);
    325        okButton.setText("   &OK   ");
    326        okButton.addSelectionListener(new SelectionAdapter() {
    327            @Override
    328            public void widgetSelected(SelectionEvent e) {
    329                if (isPaletteChanged) {
    330                    updatePalette();
    331                    isPaletteChanged = false;
    332                    imageView.setPalette(palette);
    333                    imageView.setImage(currentImage);
    334                }
    335
    336                shell.dispose();
    337            }
    338        });
    339
    340        Button cancelButton = new Button(buttonComposite, SWT.PUSH);
    341        cancelButton.setFont(curFont);
    342        cancelButton.setText(" &Cancel ");
    343        cancelButton.addSelectionListener(new SelectionAdapter() {
    344            @Override
    345            public void widgetSelected(SelectionEvent e) {
    346                imageView.setImage(originalImage);
    347                shell.dispose();
    348            }
    349        });
    350
    351        Button previewButton = new Button(buttonComposite, SWT.PUSH);
    352        previewButton.setFont(curFont);
    353        previewButton.setText("&Preview");
    354        previewButton.addSelectionListener(new SelectionAdapter() {
    355            @Override
    356            public void widgetSelected(SelectionEvent e) {
    357                updatePalette();
    358                imageView.setImage(currentImage);
    359            }
    360        });
    361
    362        shell.pack();
    363
    364        shell.addDisposeListener(new DisposeListener() {
    365            @Override
    366            public void widgetDisposed(DisposeEvent e) {
    367                if (curFont != null)
    368                    curFont.dispose();
    369            }
    370        });
    371
    372        shell.setSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
    373
    374        Rectangle parentBounds = parent.getBounds();
    375        Point shellSize = shell.getSize();
    376        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
    377                (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
    378
    379        shell.open();
    380    }
    381
    382    /** @return the data object displayed in this data viewer */
    383    @Override
    384    public HObject getDataObject() {
    385        return dataset;
    386    }
    387
    388    private void updatePalette() {
    389        for (int i = 0; i < 256; i++) {
    390            palette[0][i] = (byte) paletteData[0][i];
    391            palette[1][i] = (byte) paletteData[1][i];
    392            palette[2][i] = (byte) paletteData[2][i];
    393        }
    394
    395        IndexColorModel colorModel = new IndexColorModel(8, // bits - the number
    396                // of bits each
    397                // pixel occupies
    398                256, // size - the size of the color component arrays
    399                palette[0], // r - the array of red color components
    400                palette[1], // g - the array of green color components
    401                palette[2]); // b - the array of blue color components
    402
    403        long w = dataset.getWidth();
    404        long h = dataset.getHeight();
    405        MemoryImageSource memoryImageSource = null;
    406
    407        try {
    408            memoryImageSource = (MemoryImageSource) originalImage.getSource();
    409        }
    410        catch (Exception err) {
    411            memoryImageSource = null;
    412        }
    413
    414        if (memoryImageSource == null) {
    415            memoryImageSource = new MemoryImageSource((int) w, (int) h, colorModel, imageView.getImageByteData(), 0,
    416                    (int) w);
    417        }
    418        else {
    419            memoryImageSource.newPixels(imageView.getImageByteData(), colorModel, 0, (int) w);
    420        }
    421
    422        currentImage = Tools.toBufferedImage(Toolkit.getDefaultToolkit().createImage(memoryImageSource));
    423    }
    424
    425    /** The canvas that paints the data lines. */
    426    private class ChartCanvas extends Canvas {
    427        // Value controlling gap between the sides of the canvas
    428        // and the drawn elements
    429        private final int gap = 20;
    430
    431        private int xgap = 0;
    432        private int ygap = 0;
    433
    434        private int plotWidth = 0;
    435        private int plotHeight = 0;
    436
    437        private final int LEGEND_LINE_WIDTH = 10;
    438        private final int LEGEND_LINE_GAP = 30;
    439
    440        // Values controlling the dimensions of the legend,
    441        // as well as the gap in between each
    442        // element displayed in the legend
    443        private int LEGEND_WIDTH = 60;
    444        private final int LEGEND_HEIGHT = (5 * LEGEND_LINE_GAP);
    445
    446        private final int PALETTE_MAX = 255;
    447
    448        private int dragX0, dragY0; // starting point of mouse drag
    449
    450        public ChartCanvas(Composite parent, int style) {
    451            super(parent, style);
    452
    453            this.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
    454
    455            this.addPaintListener(new PaintListener() {
    456                @Override
    457                public void paintControl(PaintEvent e) {
    458                    // Get the graphics context for this paint event
    459                    GC g = e.gc;
    460
    461                    g.setFont(curFont);
    462
    463                    Rectangle canvasBounds = getClientArea();
    464                    org.eclipse.swt.graphics.Color c = g.getForeground();
    465
    466                    // Make sure legend width scales with font size
    467                    for (int i = 0; i < lineLabels.length; i++) {
    468                        int width = g.stringExtent(lineLabels[i]).x;
    469                        if (width > (2 * LEGEND_WIDTH / 3) - 10)
    470                            LEGEND_WIDTH += width;
    471                    }
    472
    473                    // Calculate maximum width needed to draw the y-axis labels
    474                    final int maxYLabelWidth = g.stringExtent(String.valueOf(PALETTE_MAX)).x;
    475
    476                    // Calculate maximum height needed to draw the x-axis labels
    477                    final int maxXLabelHeight = g.stringExtent(String.valueOf(PALETTE_MAX)).y;
    478
    479                    xgap = maxYLabelWidth + gap;
    480                    ygap = getSize().y - maxXLabelHeight - gap - 1;
    481                    plotHeight = ygap - gap;
    482                    plotWidth = canvasBounds.width - LEGEND_WIDTH - (2 * gap) - xgap;
    483
    484                    // draw the X axis
    485                    g.drawLine(xgap, ygap, xgap + plotWidth, ygap);
    486
    487                    // draw the Y axis
    488                    g.drawLine(xgap, ygap, xgap, gap);
    489
    490                    // draw X and Y labels: 10 labels for x and y
    491                    int dh = plotHeight / 10;
    492                    int dw = plotWidth / 10;
    493                    int dx = 25;
    494                    double dy = 25;
    495                    int xp = 2 * gap;
    496                    int yp = 0;
    497                    int x = 0;
    498                    int x0;
    499                    int y0;
    500                    int x1;
    501                    int y1;
    502                    double y = 0;
    503
    504                    // draw X and Y grid labels
    505                    String xVal = String.valueOf(x);
    506                    String yVal = String.valueOf((int) y);
    507                    Point xLabelSize = g.stringExtent(xVal);
    508                    Point yLabelSize = g.stringExtent(yVal);
    509                    g.drawString(yVal, 0, ygap - yLabelSize.y / 2);
    510                    g.drawString(xVal, xgap - xLabelSize.x / 2, canvasBounds.height - xLabelSize.y);
    511                    for (int i = 0; i < 10; i++) {
    512                        xp += dw;
    513                        yp += dh;
    514                        x += dx;
    515                        y += dy;
    516
    517                        xVal = String.valueOf(x);
    518                        yVal = String.valueOf((int) y);
    519                        xLabelSize = g.stringExtent(xVal);
    520                        yLabelSize = g.stringExtent(yVal);
    521
    522                        // Draw tick marks
    523                        g.drawLine(xp, ygap, xp, ygap - 5);
    524                        g.drawLine(xgap, ygap - yp, xgap + 5, ygap - yp);
    525
    526                        g.drawString(xVal, xp - (xLabelSize.x / 2), canvasBounds.height - xLabelSize.y);
    527                        g.drawString(yVal, 0, ygap - yp - (yLabelSize.y / 2));
    528                    }
    529
    530                    for (int i = 0; i < 3; i++) {
    531                        g.setForeground(Display.getCurrent().getSystemColor(lineColors[i]));
    532
    533                        // set up the line data for drawing one line a time
    534                        for (int j = 0; j < 255; j++) {
    535                            x0 = xgap + (plotWidth * j / 255);
    536                            y0 = ygap - (plotHeight * paletteData[i][j] / 255);
    537                            x1 = xgap + (plotWidth * (j + 1) / 255);
    538                            y1 = ygap - (plotHeight * (paletteData[i][j + 1]) / 255);
    539                            g.drawLine(x0, y0, x1, y1);
    540                        }
    541
    542                        // Draw lines and labels in the legend
    543                        x0 = (canvasBounds.width - gap - LEGEND_WIDTH) + (LEGEND_WIDTH / 3);
    544                        y0 = gap + LEGEND_LINE_GAP * (i + 1);
    545                        g.drawLine(x0, y0, x0 + LEGEND_LINE_WIDTH, y0);
    546                        g.drawString(lineLabels[i], x0 + 10, y0 + 3);
    547                    }
    548
    549                    // draw a box on the legend
    550                    g.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
    551                    g.drawRectangle(canvasBounds.width - LEGEND_WIDTH - gap, gap, LEGEND_WIDTH, LEGEND_HEIGHT);
    552
    553                    g.setForeground(c); // set the color back to its default
    554                }
    555            });
    556
    557            //TODO: editing behavior not quite correct yet
    558            this.addMouseMoveListener(new MouseMoveListener() {
    559                @Override
    560                public void mouseMove(MouseEvent e) {
    561                    if ((e.stateMask & SWT.BUTTON1) != 0) {
    562                        int x1 = e.x - 40;
    563                        if (x1 < 0) {
    564                            x1 = 0;
    565                        }
    566                        int y1 = e.y + 20;
    567
    568                        Point size = chartP.getSize();
    569                        double ry = 255 / (double) size.y;
    570                        double rx = 255 / (double) size.x;
    571
    572                        int lineIdx = 0;
    573                        if (checkGreen.getSelection()) {
    574                            lineIdx = 1;
    575                        }
    576                        else if (checkBlue.getSelection()) {
    577                            lineIdx = 2;
    578                        }
    579
    580                        int idx = 0;
    581                        double b = (double) (y1 - dragY0) / (double) (x1 - dragX0);
    582                        double a = dragY0 - b * dragX0;
    583                        int i0 = Math.min(dragX0, x1);
    584                        int i1 = Math.max(dragX0, x1);
    585                        for (int i = i0; i < i1; i++) {
    586                            idx = (int) (rx * i);
    587                            if (idx > 255) {
    588                                continue;
    589                            }
    590                            double value = 255 - (a + b * i) * ry;
    591                            if (value < 0) {
    592                                value = 0;
    593                            }
    594                            else if (value > 255) {
    595                                value = 255;
    596                            }
    597
    598                            paletteData[lineIdx][idx] = (int) value;
    599                        }
    600
    601                        chartP.redraw();
    602                        isPaletteChanged = true;
    603                    }
    604                }
    605            });
    606
    607            this.addMouseListener(new MouseAdapter() {
    608                @Override
    609                public void mouseDown(MouseEvent e) {
    610                    // dragX0 = e.x - xgap;
    611                    // dragY0 = e.y + gap;
    612                    //
    613                    //  (dragX0 < 0)
    614                    // dragX0 = 0;
    615                    //  (dragX0 > xgap + plotWidth)
    616                    // dragX0 = xgap + plotWidth;
    617                    //  (dragY0 < 0)
    618                    // dragY0 = 0;
    619                    //  (dragY0 > plotHeight + gap)
    620                    // dragY0 = plotHeight + gap;
    621                }
    622
    623                @Override
    624                public void mouseUp(MouseEvent e) {
    625                    if (paletteValueTable != null)
    626                        paletteValueTable.refresh();
    627                }
    628            });
    629        }
    630    }
    631
    632    /** The dialog to show the palette values in spreadsheet. */
    633    private class PaletteValueTable extends Dialog {
    634
    635        private Display display;
    636        private Shell tableShell;
    637
    638        private Table valueTable;
    639
    640        private static final String RGBNAME = "Color";
    641        private static final String IDXNAME = "Index";
    642
    643        public PaletteValueTable(Shell parent, int style) {
    644            super(parent, style);
    645        }
    646
    647        public void open() {
    648            Shell parent = getParent();
    649            display = parent.getDisplay();
    650
    651            tableShell = new Shell(parent, SWT.SHELL_TRIM);
    652            tableShell.setFont(curFont);
    653            tableShell.setText("");
    654            tableShell.setImage(ViewProperties.getHdfIcon());
    655            tableShell.setLayout(new GridLayout(1, true));
    656
    657            Composite content = new Composite(tableShell, SWT.NONE);
    658            content.setLayout(new GridLayout(1, true));
    659            content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    660
    661            GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
    662            data.heightHint = 200;
    663            content.setLayoutData(data);
    664
    665            String[] columnNames = { IDXNAME, "Red", "Green", "Blue", RGBNAME };
    666
    667            valueTable = new Table(content, SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.NO_SCROLL);
    668            valueTable.setHeaderVisible(true);
    669            valueTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
    670            valueTable.setFont(curFont);
    671
    672            // Add cell editor for changing cell values in-place
    673            valueTable.addListener(SWT.MouseDoubleClick, valueTableCellEditor);
    674
    675            valueTable.addListener(SWT.Resize, new Listener() {
    676                @Override
    677                public void handleEvent(Event e) {
    678                    int numColumns = valueTable.getColumnCount();
    679
    680                    for (int i = 0; i < numColumns; i++) {
    681                        valueTable.getColumn(i).setWidth(valueTable.getClientArea().width / numColumns);
    682                    }
    683                }
    684            });
    685
    686            valueTable.addPaintListener(new PaintListener() {
    687                @Override
    688                public void paintControl(PaintEvent e) {
    689                    for (int i = 0; i < valueTable.getItemCount(); i++) {
    690                        Color cellColor = new Color(display, paletteData[0][i], paletteData[1][i], paletteData[2][i]);
    691
    692                        valueTable.getItem(i).setBackground(4, cellColor);
    693
    694                        cellColor.dispose();
    695                    }
    696                }
    697            });
    698
    699            for (int i = 0; i < columnNames.length; i++) {
    700                TableColumn column = new TableColumn(valueTable, SWT.NONE);
    701                column.setText(columnNames[i]);
    702                column.setMoveable(false);
    703                column.pack();
    704            }
    705
    706            for (int i = 0; i < 256; i++) {
    707                TableItem item = new TableItem(valueTable, SWT.NONE);
    708                item.setFont(curFont);
    709
    710                item.setText(new String[] { String.valueOf(i), String.valueOf(paletteData[0][i]),
    711                        String.valueOf(paletteData[1][i]), String.valueOf(paletteData[2][i]), null });
    712
    713                item.setBackground(0, Display.getCurrent().getSystemColor(SWT.COLOR_GRAY));
    714            }
    715
    716            // set cell height for large fonts
    717            // int cellRowHeight = Math.max(16, valueTable.getFontMetrics(
    718            // valueTable.getFont()).getHeight());
    719            // valueTable.setRowHeight(cellRowHeight);
    720
    721            Button okButton = new Button(tableShell, SWT.PUSH);
    722            okButton.setFont(curFont);
    723            okButton.setText("   &OK   ");
    724            okButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false));
    725            okButton.addSelectionListener(new SelectionAdapter() {
    726                @Override
    727                public void widgetSelected(SelectionEvent e) {
    728                    tableShell.dispose();
    729                }
    730            });
    731
    732            tableShell.pack();
    733
    734            int w = 300 + (ViewProperties.getFontSize() - 12) * 10;
    735            int h = 600 + (ViewProperties.getFontSize() - 12) * 15;
    736
    737            tableShell.setSize(w, h);
    738
    739            Rectangle parentBounds = parent.getBounds();
    740            Point shellSize = tableShell.getSize();
    741            tableShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
    742                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
    743
    744            tableShell.open();
    745
    746            while (!tableShell.isDisposed()) {
    747                if (!display.readAndDispatch())
    748                    display.sleep();
    749            }
    750        }
    751
    752        private void updatePaletteValue(String strValue, int row, int col) {
    753            if (strValue == null) {
    754                return;
    755            }
    756
    757            int value = 0;
    758
    759            try {
    760                value = Integer.parseInt(strValue);
    761            }
    762            catch (Exception ex) {
    763                return;
    764            }
    765
    766            if (value < 0 || value > 255) {
    767                Tools.showError(tableShell, "Update", "Value is out of range [0, 255]");
    768                return;
    769            }
    770
    771            paletteData[col][row] = value;
    772            chartP.redraw();
    773            isPaletteChanged = true;
    774        }
    775
    776        public void refresh() {
    777            if (valueTable != null && !valueTable.isDisposed())
    778                valueTable.redraw();
    779        }
    780
    781        private Listener valueTableCellEditor = new Listener() {
    782            @Override
    783            public void handleEvent(Event event) {
    784                final TableEditor editor = new TableEditor(valueTable);
    785                editor.horizontalAlignment = SWT.LEFT;
    786                editor.grabHorizontal = true;
    787
    788                Rectangle clientArea = valueTable.getClientArea();
    789                Point pt = new Point(event.x, event.y);
    790                int index = valueTable.getTopIndex();
    791
    792                while (index < valueTable.getItemCount()) {
    793                    boolean visible = false;
    794                    final TableItem item = valueTable.getItem(index);
    795
    796                    // Only allow editing of RGB values
    797                    for (int i = 1; i < valueTable.getColumnCount() - 1; i++) {
    798                        Rectangle rect = item.getBounds(i);
    799
    800                        if (rect.contains(pt)) {
    801                            final int column = i;
    802                            final int row = index;
    803
    804                            final Text text = new Text(valueTable, SWT.NONE);
    805                            text.setFont(curFont);
    806
    807                            Listener textListener = new Listener() {
    808                                @Override
    809                                public void handleEvent(final Event e) {
    810                                    switch (e.type) {
    811                                        case SWT.FocusOut:
    812                                            item.setText(column, text.getText());
    813                                            updatePaletteValue(item.getText(column), row, column - 1);
    814                                            text.dispose();
    815                                            break;
    816                                        case SWT.Traverse:
    817                                            switch (e.detail) {
    818                                                case SWT.TRAVERSE_RETURN:
    819                                                    item.setText(column, text.getText());
    820                                                    updatePaletteValue(item.getText(column), row, column - 1);
    821                                                    break;
    822                                                case SWT.TRAVERSE_ESCAPE:
    823                                                    text.dispose();
    824                                                    e.doit = false;
    825                                                    break;
    826                                                default:
    827                                                    break;
    828                                            }
    829                                            break;
    830                                        default:
    831                                            break;
    832                                    }
    833                                }
    834                            };
    835
    836                            text.addListener(SWT.FocusOut, textListener);
    837                            text.addListener(SWT.Traverse, textListener);
    838                            editor.setEditor(text, item, i);
    839                            text.setText(item.getText(i));
    840                            text.selectAll();
    841                            text.setFocus();
    842                            return;
    843                        }
    844
    845                        if (!visible && rect.intersects(clientArea)) {
    846                            visible = true;
    847                        }
    848                    }
    849
    850                    if (!visible)
    851                        return;
    852
    853                    index++;
    854                }
    855            }
    856        };
    857    }
    858}