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