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}