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            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.setImage(ViewProperties.getHdfIcon());
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}