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