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    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.setImage(ViewProperties.getHdfIcon());
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            byte[] palRefs = dataset.getPaletteRefs();
289            if ((palRefs != null) && (palRefs.length > 8))
290                numberOfPalettes = palRefs.length / 8;
291        }
292        for (int i = 1; i < numberOfPalettes; i++) {
293            paletteName = dataset.getPaletteName(i);
294            choicePalette.add(paletteName);
295        }
296        choicePalette.add(PALETTE_GRAY);
297        choicePalette.add(PALETTE_GRAY_WAVE);
298        choicePalette.add(PALETTE_RAINBOW);
299        choicePalette.add(PALETTE_NATURE);
300        choicePalette.add(PALETTE_WAVE);
301        ArrayList<?> plist = (ArrayList<?>) ViewProperties.getPaletteList();
302        int n = plist.size();
303        for (int i = 0; i < n; i++)
304            choicePalette.add((String) plist.get(i));
305
306        choicePalette.select(0);
307
308        Button showValueButton = new Button(paletteComposite, SWT.PUSH);
309        showValueButton.setFont(curFont);
310        showValueButton.setText("Show Values");
311        showValueButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
312        showValueButton.addSelectionListener(new SelectionAdapter() {
313            @Override
314            public void widgetSelected(SelectionEvent e) {
315                if (paletteValueTable == null)
316                    paletteValueTable = new PaletteValueTable(shell, SWT.NONE);
317
318                paletteValueTable.open();
319            }
320        });
321
322        // Add Ok/Cancel/Preview buttons
323        Composite buttonComposite = new Composite(tools, SWT.BORDER);
324        buttonComposite.setLayout(new RowLayout(SWT.HORIZONTAL));
325        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
326
327        Button okButton = new Button(buttonComposite, SWT.PUSH);
328        okButton.setFont(curFont);
329        okButton.setText("   &OK   ");
330        okButton.addSelectionListener(new SelectionAdapter() {
331            @Override
332            public void widgetSelected(SelectionEvent e) {
333                if (isPaletteChanged) {
334                    updatePalette();
335                    isPaletteChanged = false;
336                    imageView.setPalette(palette);
337                    imageView.setImage(currentImage);
338                }
339
340                shell.dispose();
341            }
342        });
343
344        Button cancelButton = new Button(buttonComposite, SWT.PUSH);
345        cancelButton.setFont(curFont);
346        cancelButton.setText(" &Cancel ");
347        cancelButton.addSelectionListener(new SelectionAdapter() {
348            @Override
349            public void widgetSelected(SelectionEvent e) {
350                imageView.setImage(originalImage);
351                shell.dispose();
352            }
353        });
354
355        Button previewButton = new Button(buttonComposite, SWT.PUSH);
356        previewButton.setFont(curFont);
357        previewButton.setText("&Preview");
358        previewButton.addSelectionListener(new SelectionAdapter() {
359            @Override
360            public void widgetSelected(SelectionEvent e) {
361                updatePalette();
362                imageView.setImage(currentImage);
363            }
364        });
365
366        shell.pack();
367
368        shell.addDisposeListener(new DisposeListener() {
369            @Override
370            public void widgetDisposed(DisposeEvent e) {
371                if (curFont != null)
372                    curFont.dispose();
373            }
374        });
375
376        shell.setSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
377
378        Rectangle parentBounds = parent.getBounds();
379        Point shellSize = shell.getSize();
380        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
381                (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
382
383        shell.open();
384    }
385
386    /** @return the data object displayed in this data viewer */
387    @Override
388    public HObject getDataObject() {
389        return dataset;
390    }
391
392    private void updatePalette() {
393        for (int i = 0; i < 256; i++) {
394            palette[0][i] = (byte) paletteData[0][i];
395            palette[1][i] = (byte) paletteData[1][i];
396            palette[2][i] = (byte) paletteData[2][i];
397        }
398
399        IndexColorModel colorModel = new IndexColorModel(
400                8,   // bits - the number of bits each pixel occupies
401                256, // size - the size of the color component arrays
402                palette[0],  // r - the array of red color components
403                palette[1],  // g - the array of green color components
404                palette[2]); // b - the array of blue color components
405
406        long w = dataset.getWidth();
407        long h = dataset.getHeight();
408        MemoryImageSource memoryImageSource = null;
409
410        try {
411            memoryImageSource = (MemoryImageSource) originalImage.getSource();
412        }
413        catch (Exception err) {
414            memoryImageSource = null;
415        }
416
417        if (memoryImageSource == null)
418            memoryImageSource = new MemoryImageSource((int) w, (int) h, colorModel, imageView.getImageByteData(), 0, (int) w);
419        else
420            memoryImageSource.newPixels(imageView.getImageByteData(), colorModel, 0, (int) w);
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    {
428        // Value controlling gap between the sides of the canvas
429        // and the drawn elements
430        private final int gap = 20;
431
432        private int xgap = 0;
433        private int ygap = 0;
434
435        private int plotWidth = 0;
436        private int plotHeight = 0;
437
438        private final int LEGEND_LINE_WIDTH = 10;
439        private final int LEGEND_LINE_GAP = 30;
440
441        // Values controlling the dimensions of the legend,
442        // as well as the gap in between each
443        // element displayed in the legend
444        private int LEGEND_WIDTH = 60;
445        private final int LEGEND_HEIGHT = (5 * LEGEND_LINE_GAP);
446
447        private final int PALETTE_MAX = 255;
448
449        private int dragX0, dragY0; // starting point of mouse drag
450
451        public ChartCanvas(Composite parent, int style) {
452            super(parent, style);
453
454            this.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));
455
456            this.addPaintListener(new PaintListener() {
457                @Override
458                public void paintControl(PaintEvent e) {
459                    // Get the graphics context for this paint event
460                    GC g = e.gc;
461
462                    g.setFont(curFont);
463
464                    Rectangle canvasBounds = getClientArea();
465                    org.eclipse.swt.graphics.Color c = g.getForeground();
466
467                    // Make sure legend width scales with font size
468                    for (int i = 0; i < lineLabels.length; i++) {
469                        int width = g.stringExtent(lineLabels[i]).x;
470                        if (width > (2 * LEGEND_WIDTH / 3) - 10)
471                            LEGEND_WIDTH += width;
472                    }
473
474                    // Calculate maximum width needed to draw the y-axis labels
475                    final int maxYLabelWidth = g.stringExtent(String.valueOf(PALETTE_MAX)).x;
476
477                    // Calculate maximum height needed to draw the x-axis labels
478                    final int maxXLabelHeight = g.stringExtent(String.valueOf(PALETTE_MAX)).y;
479
480                    xgap = maxYLabelWidth + gap;
481                    ygap = getSize().y - maxXLabelHeight - gap - 1;
482                    plotHeight = ygap - gap;
483                    plotWidth = canvasBounds.width - LEGEND_WIDTH - (2 * gap) - xgap;
484
485                    // draw the X axis
486                    g.drawLine(xgap, ygap, xgap + plotWidth, ygap);
487
488                    // draw the Y axis
489                    g.drawLine(xgap, ygap, xgap, gap);
490
491                    // draw X and Y labels: 10 labels for x and y
492                    int dh = plotHeight / 10;
493                    int dw = plotWidth / 10;
494                    int dx = 25;
495                    double dy = 25;
496                    int xp = 2 * gap;
497                    int yp = 0;
498                    int x = 0;
499                    int x0;
500                    int y0;
501                    int x1;
502                    int y1;
503                    double y = 0;
504
505                    // draw X and Y grid labels
506                    String xVal = String.valueOf(x);
507                    String yVal = String.valueOf((int) y);
508                    Point xLabelSize = g.stringExtent(xVal);
509                    Point yLabelSize = g.stringExtent(yVal);
510                    g.drawString(yVal, 0, ygap - yLabelSize.y / 2);
511                    g.drawString(xVal, xgap - xLabelSize.x / 2, canvasBounds.height - xLabelSize.y);
512                    for (int i = 0; i < 10; i++) {
513                        xp += dw;
514                        yp += dh;
515                        x += dx;
516                        y += dy;
517
518                        xVal = String.valueOf(x);
519                        yVal = String.valueOf((int) y);
520                        xLabelSize = g.stringExtent(xVal);
521                        yLabelSize = g.stringExtent(yVal);
522
523                        // Draw tick marks
524                        g.drawLine(xp, ygap, xp, ygap - 5);
525                        g.drawLine(xgap, ygap - yp, xgap + 5, ygap - yp);
526
527                        g.drawString(xVal, xp - (xLabelSize.x / 2), canvasBounds.height - xLabelSize.y);
528                        g.drawString(yVal, 0, ygap - yp - (yLabelSize.y / 2));
529                    }
530
531                    for (int i = 0; i < 3; i++) {
532                        g.setForeground(Display.getCurrent().getSystemColor(lineColors[i]));
533
534                        // set up the line data for drawing one line a time
535                        for (int j = 0; j < 255; j++) {
536                            x0 = xgap + (plotWidth * j / 255);
537                            y0 = ygap - (plotHeight * paletteData[i][j] / 255);
538                            x1 = xgap + (plotWidth * (j + 1) / 255);
539                            y1 = ygap - (plotHeight * (paletteData[i][j + 1]) / 255);
540                            g.drawLine(x0, y0, x1, y1);
541                        }
542
543                        // Draw lines and labels in the legend
544                        x0 = (canvasBounds.width - gap - LEGEND_WIDTH) + (LEGEND_WIDTH / 3);
545                        y0 = gap + LEGEND_LINE_GAP * (i + 1);
546                        g.drawLine(x0, y0, x0 + LEGEND_LINE_WIDTH, y0);
547                        g.drawString(lineLabels[i], x0 + 10, y0 + 3);
548                    }
549
550                    // draw a box on the legend
551                    g.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
552                    g.drawRectangle(canvasBounds.width - LEGEND_WIDTH - gap, gap, LEGEND_WIDTH, LEGEND_HEIGHT);
553
554                    g.setForeground(c); // set the color back to its default
555                }
556            });
557
558            //TODO: editing behavior not quite correct yet
559            this.addMouseMoveListener(new MouseMoveListener() {
560                @Override
561                public void mouseMove(MouseEvent e) {
562                    if ((e.stateMask & SWT.BUTTON1) != 0) {
563                        int x1 = e.x - 40;
564                        if (x1 < 0)
565                            x1 = 0;
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                        else if (checkBlue.getSelection())
576                            lineIdx = 2;
577
578                        int idx = 0;
579                        double b = (double) (y1 - dragY0) / (double) (x1 - dragX0);
580                        double a = dragY0 - b * dragX0;
581                        int i0 = Math.min(dragX0, x1);
582                        int i1 = Math.max(dragX0, x1);
583                        for (int i = i0; i < i1; i++) {
584                            idx = (int) (rx * i);
585                            if (idx > 255)
586                                continue;
587                            double value = 255 - (a + b * i) * ry;
588                            if (value < 0)
589                                value = 0;
590                            else if (value > 255)
591                                value = 255;
592
593                            paletteData[lineIdx][idx] = (int) value;
594                        }
595
596                        chartP.redraw();
597                        isPaletteChanged = true;
598                    }
599                }
600            });
601
602            this.addMouseListener(new MouseAdapter() {
603                @Override
604                public void mouseDown(MouseEvent e) {
605                    // dragX0 = e.x - xgap;
606                    // dragY0 = e.y + gap;
607                    //
608                    //  (dragX0 < 0)
609                    // dragX0 = 0;
610                    //  (dragX0 > xgap + plotWidth)
611                    // dragX0 = xgap + plotWidth;
612                    //  (dragY0 < 0)
613                    // dragY0 = 0;
614                    //  (dragY0 > plotHeight + gap)
615                    // dragY0 = plotHeight + gap;
616                }
617
618                @Override
619                public void mouseUp(MouseEvent e) {
620                    if (paletteValueTable != null)
621                        paletteValueTable.refresh();
622                }
623            });
624        }
625    }
626
627    /** The dialog to show the palette values in spreadsheet. */
628    private class PaletteValueTable extends Dialog
629    {
630        private Display display;
631        private Shell tableShell;
632
633        private Table valueTable;
634
635        private static final String RGBNAME = "Color";
636        private static final String IDXNAME = "Index";
637
638        public PaletteValueTable(Shell parent, int style) {
639            super(parent, style);
640        }
641
642        public void open() {
643            Shell parent = getParent();
644            display = parent.getDisplay();
645
646            tableShell = new Shell(parent, SWT.SHELL_TRIM);
647            tableShell.setFont(curFont);
648            tableShell.setText("");
649            tableShell.setImage(ViewProperties.getHdfIcon());
650            tableShell.setLayout(new GridLayout(1, true));
651
652            Composite content = new Composite(tableShell, SWT.NONE);
653            content.setLayout(new GridLayout(1, true));
654            content.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
655
656            GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
657            data.heightHint = 200;
658            content.setLayoutData(data);
659
660            String[] columnNames = { IDXNAME, "Red", "Green", "Blue", RGBNAME };
661
662            valueTable = new Table(content, SWT.BORDER | SWT.FULL_SELECTION | SWT.V_SCROLL | SWT.NO_SCROLL);
663            valueTable.setHeaderVisible(true);
664            valueTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
665            valueTable.setFont(curFont);
666
667            // Add cell editor for changing cell values in-place
668            valueTable.addListener(SWT.MouseDoubleClick, valueTableCellEditor);
669
670            valueTable.addListener(SWT.Resize, new Listener() {
671                @Override
672                public void handleEvent(Event e) {
673                    int numColumns = valueTable.getColumnCount();
674
675                    for (int i = 0; i < numColumns; i++)
676                        valueTable.getColumn(i).setWidth(valueTable.getClientArea().width / numColumns);
677                }
678            });
679
680            valueTable.addPaintListener(new PaintListener() {
681                @Override
682                public void paintControl(PaintEvent e) {
683                    for (int i = 0; i < valueTable.getItemCount(); i++) {
684                        Color cellColor = new Color(display, paletteData[0][i], paletteData[1][i], paletteData[2][i]);
685
686                        valueTable.getItem(i).setBackground(4, cellColor);
687
688                        cellColor.dispose();
689                    }
690                }
691            });
692
693            for (int i = 0; i < columnNames.length; i++) {
694                TableColumn column = new TableColumn(valueTable, SWT.NONE);
695                column.setText(columnNames[i]);
696                column.setMoveable(false);
697                column.pack();
698            }
699
700            for (int i = 0; i < 256; i++) {
701                TableItem item = new TableItem(valueTable, SWT.NONE);
702                item.setFont(curFont);
703
704                item.setText(new String[] {
705                        String.valueOf(i),
706                        String.valueOf(paletteData[0][i]),
707                        String.valueOf(paletteData[1][i]),
708                        String.valueOf(paletteData[2][i]), null });
709
710                item.setBackground(0, Display.getCurrent().getSystemColor(SWT.COLOR_GRAY));
711            }
712
713            // set cell height for large fonts
714            // int cellRowHeight = Math.max(16, valueTable.getFontMetrics(
715            // valueTable.getFont()).getHeight());
716            // valueTable.setRowHeight(cellRowHeight);
717
718            Button okButton = new Button(tableShell, SWT.PUSH);
719            okButton.setFont(curFont);
720            okButton.setText("   &OK   ");
721            okButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, false));
722            okButton.addSelectionListener(new SelectionAdapter() {
723                @Override
724                public void widgetSelected(SelectionEvent e) {
725                    tableShell.dispose();
726                }
727            });
728
729            tableShell.pack();
730
731            int w = 300 + (ViewProperties.getFontSize() - 12) * 10;
732            int h = 600 + (ViewProperties.getFontSize() - 12) * 15;
733
734            tableShell.setSize(w, h);
735
736            Rectangle parentBounds = parent.getBounds();
737            Point shellSize = tableShell.getSize();
738            tableShell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
739                    (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
740
741            tableShell.open();
742
743            while (!tableShell.isDisposed()) {
744                if (!display.readAndDispatch())
745                    display.sleep();
746            }
747        }
748
749        private void updatePaletteValue(String strValue, int row, int col) {
750            if (strValue == null)
751                return;
752
753            int value = 0;
754
755            try {
756                value = Integer.parseInt(strValue);
757            }
758            catch (Exception ex) {
759                return;
760            }
761
762            if (value < 0 || value > 255) {
763                Tools.showError(tableShell, "Update", "Value is out of range [0, 255]");
764                return;
765            }
766
767            paletteData[col][row] = value;
768            chartP.redraw();
769            isPaletteChanged = true;
770        }
771
772        public void refresh() {
773            if (valueTable != null && !valueTable.isDisposed())
774                valueTable.redraw();
775        }
776
777        private Listener valueTableCellEditor = new Listener() {
778            @Override
779            public void handleEvent(Event event) {
780                final TableEditor editor = new TableEditor(valueTable);
781                editor.horizontalAlignment = SWT.LEFT;
782                editor.grabHorizontal = true;
783
784                Rectangle clientArea = valueTable.getClientArea();
785                Point pt = new Point(event.x, event.y);
786                int index = valueTable.getTopIndex();
787
788                while (index < valueTable.getItemCount()) {
789                    boolean visible = false;
790                    final TableItem item = valueTable.getItem(index);
791
792                    // Only allow editing of RGB values
793                    for (int i = 1; i < valueTable.getColumnCount() - 1; i++) {
794                        Rectangle rect = item.getBounds(i);
795
796                        if (rect.contains(pt)) {
797                            final int column = i;
798                            final int row = index;
799
800                            final Text text = new Text(valueTable, SWT.NONE);
801                            text.setFont(curFont);
802
803                            Listener textListener = new Listener() {
804                                @Override
805                                public void handleEvent(final Event e) {
806                                    switch (e.type) {
807                                        case SWT.FocusOut:
808                                            item.setText(column, text.getText());
809                                            updatePaletteValue(item.getText(column), row, column - 1);
810                                            text.dispose();
811                                            break;
812                                        case SWT.Traverse:
813                                            switch (e.detail) {
814                                                case SWT.TRAVERSE_RETURN:
815                                                    item.setText(column, text.getText());
816                                                    updatePaletteValue(item.getText(column), row, column - 1);
817                                                    break;
818                                                case SWT.TRAVERSE_ESCAPE:
819                                                    text.dispose();
820                                                    e.doit = false;
821                                                    break;
822                                                default:
823                                                    break;
824                                            }
825                                            break;
826                                        default:
827                                            break;
828                                    }
829                                }
830                            };
831
832                            text.addListener(SWT.FocusOut, textListener);
833                            text.addListener(SWT.Traverse, textListener);
834                            editor.setEditor(text, item, i);
835                            text.setText(item.getText(i));
836                            text.selectAll();
837                            text.setFocus();
838                            return;
839                        }
840
841                        if (!visible && rect.intersects(clientArea))
842                            visible = true;
843                    }
844
845                    if (!visible)
846                        return;
847
848                    index++;
849                }
850            }
851        };
852    }
853}