| ThresholdAdjuster |

    package ij.plugin.frame;
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.*;
    import ij.*;
    import ij.plugin.*;
    import ij.process.*;
    import ij.gui.*;
    import ij.measure.*;
    import ij.plugin.frame.Recorder;
    import ij.plugin.filter.Analyzer;

    /** Adjusts the lower and upper threshold levels of the active image. This
        class is multi-threaded to provide a more responsive user interface. */
    public class ThresholdAdjuster extends PlugInFrame implements PlugIn, Measurements,
        Runnable, ActionListener, AdjustmentListener, ItemListener {

        public static final String LOC_KEY = "threshold.loc";
        static final int RED=0, BLACK_AND_WHITE=1, OVER_UNDER=2;
        static final String[] modes = {"Red","Black & White", "Over/Under"};
        static final double defaultMinThreshold = 85;
        static final double defaultMaxThreshold = 170;
        static boolean fill1 = true;
        static boolean fill2 = true;
        static boolean useBW = true;
        static boolean backgroundToNaN = true;
        static Frame instance;
        static int mode = RED;
        ThresholdPlot plot = new ThresholdPlot();
        Thread thread;

        int minValue = -1;
        int maxValue = -1;
        int sliderRange = 256;
        boolean doAutoAdjust,doReset,doApplyLut,doStateChange,doSet;

        Panel panel;
        Button autoB, resetB, applyB, setB;
        int previousImageID;
        int previousImageType;
        double previousMin, previousMax;
        ImageJ ij;
        double minThreshold, maxThreshold;  // 0-255
        Scrollbar minSlider, maxSlider;
        Label label1, label2;
        boolean done;
        boolean invertedLut;
        int lutColor;
        static Choice choice;
        boolean firstActivation;

        public ThresholdAdjuster() {
            super("Threshold");
            if (instance!=null) {
                instance.toFront();
                return;
            }

            WindowManager.addWindow(this);
            instance = this;
            setLutColor(mode);
            IJ.register(PasteController.class);

            ij = IJ.getInstance();
            Font font = new Font("SansSerif", Font.PLAIN, 10);
            GridBagLayout gridbag = new GridBagLayout();
            GridBagConstraints c = new GridBagConstraints();
            setLayout(gridbag);

            // plot
            int y = 0;
            c.gridx = 0;
            c.gridy = y++;
            c.gridwidth = 2;
            c.fill = GridBagConstraints.BOTH;
            c.anchor = GridBagConstraints.CENTER;
            c.insets = new Insets(10, 10, 0, 10);
            add(plot, c);
            plot.addKeyListener(ij);

            // minThreshold slider
            minSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange/3, 1, 0, sliderRange);
            c.gridx = 0;
            c.gridy = y++;
            c.gridwidth = 1;
            c.weightx = IJ.isMacintosh()?90:100;
            c.fill = GridBagConstraints.HORIZONTAL;
            c.insets = new Insets(5, 10, 0, 0);
            add(minSlider, c);
            minSlider.addAdjustmentListener(this);
            minSlider.addKeyListener(ij);
            minSlider.setUnitIncrement(1);
            minSlider.setFocusable(false);

            // minThreshold slider label
            c.gridx = 1;
            c.gridwidth = 1;
            c.weightx = IJ.isMacintosh()?10:0;
            c.insets = new Insets(5, 0, 0, 10);
            label1 = new Label("       ", Label.RIGHT);
            label1.setFont(font);
            add(label1, c);

            // maxThreshold slider
            maxSlider = new Scrollbar(Scrollbar.HORIZONTAL, sliderRange*2/3, 1, 0, sliderRange);
            c.gridx = 0;
            c.gridy = y++;
            c.gridwidth = 1;
            c.weightx = 100;
            c.insets = new Insets(0, 10, 0, 0);
            add(maxSlider, c);
            maxSlider.addAdjustmentListener(this);
            maxSlider.addKeyListener(ij);
            maxSlider.setUnitIncrement(1);
            maxSlider.setFocusable(false);

            // maxThreshold slider label
            c.gridx = 1;
            c.gridwidth = 1;
            c.weightx = 0;
            c.insets = new Insets(0, 0, 0, 10);
            label2 = new Label("       ", Label.RIGHT);
            label2.setFont(font);
            add(label2, c);

            // choice
            choice = new Choice();
            for (int i=0; i<modes.length; i++)
                choice.addItem(modes[i]);
            choice.select(mode);
            choice.addItemListener(this);
            choice.addKeyListener(ij);
            c.gridx = 0;
            c.gridy = y++;
            c.gridwidth = 2;
            c.insets = new Insets(5, 5, 0, 5);
            c.anchor = GridBagConstraints.CENTER;
            c.fill = GridBagConstraints.NONE;
            add(choice, c);

            // buttons
            int trim = IJ.isMacOSX()?11:0;
            panel = new Panel();
            autoB = new TrimmedButton("Auto",trim);
            autoB.addActionListener(this);
            autoB.addKeyListener(ij);
            panel.add(autoB);
            applyB = new TrimmedButton("Apply",trim);
            applyB.addActionListener(this);
            applyB.addKeyListener(ij);
            panel.add(applyB);
            resetB = new TrimmedButton("Reset",trim);
            resetB.addActionListener(this);
            resetB.addKeyListener(ij);
            panel.add(resetB);
            setB = new TrimmedButton("Set",trim);
            setB.addActionListener(this);
            setB.addKeyListener(ij);
            panel.add(setB);
            c.gridx = 0;
            c.gridy = y++;
            c.gridwidth = 2;
            c.insets = new Insets(0, 5, 10, 5);
            add(panel, c);

            addKeyListener(ij);  // ImageJ handles keyboard shortcuts
            pack();
            Point loc = Prefs.getLocation(LOC_KEY);
            if (loc!=null)
                setLocation(loc);
            else
                GUI.center(this);
            firstActivation = true;
            if (IJ.isMacOSX()) setResizable(false);
            show();

            thread = new Thread(this, "ThresholdAdjuster");
            //thread.setPriority(thread.getPriority()-1);
            thread.start();
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp!=null)
                setup(imp);
        }

        public synchronized void adjustmentValueChanged(AdjustmentEvent e) {
            if (e.getSource()==minSlider)
                minValue = minSlider.getValue();
            else
                maxValue = maxSlider.getValue();
            notify();
        }

        public synchronized  void actionPerformed(ActionEvent e) {
            Button b = (Button)e.getSource();
            if (b==null) return;
            if (b==resetB)
                doReset = true;
            else if (b==autoB)
                doAutoAdjust = true;
            else if (b==applyB)
                doApplyLut = true;
            else if (b==setB)
                doSet = true;
            notify();
        }

        void setLutColor(int mode) {
            switch (mode) {
                case RED:
                    lutColor = ImageProcessor.RED_LUT;
                    break;
                case BLACK_AND_WHITE:
                    lutColor = ImageProcessor.BLACK_AND_WHITE_LUT;
                    break;
                case OVER_UNDER:
                    lutColor = ImageProcessor.OVER_UNDER_LUT;
                    break;
            }
        }

        public synchronized void itemStateChanged(ItemEvent e) {
            mode = choice.getSelectedIndex();
            setLutColor(mode);
            doStateChange = true;
            notify();
        }

        ImageProcessor setup(ImagePlus imp) {
            ImageProcessor ip;
            int type = imp.getType();
            if (type==ImagePlus.COLOR_RGB)
                return null;
            ip = imp.getProcessor();
            boolean minMaxChange = false;
            boolean not8Bits = type==ImagePlus.GRAY16 || type==ImagePlus.GRAY32;
            if (not8Bits) {
                if (ip.getMin()==plot.stackMin && ip.getMax()==plot.stackMax)
                    minMaxChange = false;
                else if (ip.getMin()!=previousMin || ip.getMax()!=previousMax) {
                    minMaxChange = true;
                    previousMin = ip.getMin();
                    previousMax = ip.getMax();
                }
            }
            int id = imp.getID();
            if (minMaxChange || id!=previousImageID || type!=previousImageType) {
                //IJ.log(minMaxChange +"  "+ (id!=previousImageID)+"  "+(type!=previousImageType));
                if (not8Bits && minMaxChange) {
                    ip.resetMinAndMax();
                    imp.updateAndDraw();
                }
                invertedLut = imp.isInvertedLut();
                minThreshold = ip.getMinThreshold();
                maxThreshold = ip.getMaxThreshold();
                ImageStatistics stats = plot.setHistogram(imp, false);
                if (minThreshold==ImageProcessor.NO_THRESHOLD)
                    autoSetLevels(ip, stats);
                else {
                    minThreshold = scaleDown(ip, minThreshold);
                    maxThreshold = scaleDown(ip, maxThreshold);
                }
                scaleUpAndSet(ip, minThreshold, maxThreshold);
                updateLabels(imp, ip);
                updatePlot();
                updateScrollBars();
                imp.updateAndDraw();
            }
            previousImageID = id;
            previousImageType = type;
            return ip;
        }

        void autoSetLevels(ImageProcessor ip, ImageStatistics stats) {
            if (stats==null || stats.histogram==null) {
                minThreshold = defaultMinThreshold;
                maxThreshold = defaultMaxThreshold;
                return;
            }
            int threshold = ip.getAutoThreshold(stats.histogram);
            //IJ.log(threshold+"  "+stats.min+"  "+stats.max+"  "+stats.dmode);
            int count1=0, count2=0;
            for (int i=0; i<256; i++) {
                if (i<threshold)
                    count1 += stats.histogram[i];
                else
                    count2 += stats.histogram[i];
            }
            boolean unbalanced = (double)count1/count2>1.25 || (double)count2/count1>1.25;
            //IJ.log(unbalanced+"  "+count1+"  "+count2);
            double lower, upper;
            if (unbalanced) {
                if ((stats.max-stats.dmode)>(stats.dmode-stats.min))
                    {minThreshold=threshold; maxThreshold=255.0;}
                else
                    {minThreshold=0.0; maxThreshold=threshold;}
            } else {
                if (ip.isInvertedLut())
                    {minThreshold=threshold; maxThreshold=255.0;}
                else
                    {minThreshold=0.0; maxThreshold=threshold;}
            }
            if (Recorder.record)
                Recorder.record("setAutoThreshold");
        }

        /** Scales threshold levels in the range 0-255 to the actual levels. */
        void scaleUpAndSet(ImageProcessor ip, double minThreshold, double maxThreshold) {
            if (!(ip instanceof ByteProcessor) && minThreshold!=ImageProcessor.NO_THRESHOLD) {
                double min = ip.getMin();
                double max = ip.getMax();
                if (max>min) {
                    minThreshold = min + (minThreshold/255.0)*(max-min);
                    maxThreshold = min + (maxThreshold/255.0)*(max-min);
                } else
                    minThreshold = maxThreshold = min;
            }
            ip.setThreshold(minThreshold, maxThreshold, lutColor);
        }

        /** Scales a threshold level to the range 0-255. */
        double scaleDown(ImageProcessor ip, double threshold) {
            if (ip instanceof ByteProcessor)
                return threshold;
            double min = ip.getMin();
            double max = ip.getMax();
            if (max>min)
                return ((threshold-min)/(max-min))*255.0;
            else
                return ImageProcessor.NO_THRESHOLD;
        }

        /** Scales a threshold level in the range 0-255 to the actual level. */
        double scaleUp(ImageProcessor ip, double threshold) {
            double min = ip.getMin();
            double max = ip.getMax();
            if (max>min)
                return min + (threshold/255.0)*(max-min);
            else
                return ImageProcessor.NO_THRESHOLD;
        }

        void updatePlot() {
            plot.minThreshold = minThreshold;
            plot.maxThreshold = maxThreshold;
            plot.mode = mode;
            plot.repaint();
        }

        void updateLabels(ImagePlus imp, ImageProcessor ip) {
            double min = ip.getMinThreshold();
            double max = ip.getMaxThreshold();
            if (min==ImageProcessor.NO_THRESHOLD) {
                label1.setText("");
                label2.setText("");
            } else {
                Calibration cal = imp.getCalibration();
                if (cal.calibrated()) {
                    min = cal.getCValue((int)min);
                    max = cal.getCValue((int)max);
                }
                if (((int)min==min && (int)max==max) || (ip instanceof ShortProcessor)) {
                    label1.setText(""+(int)min);
                    label2.setText(""+(int)max);
                } else {
                    label1.setText(""+IJ.d2s(min,2));
                    label2.setText(""+IJ.d2s(max,2));
                }
            }
        }

        void updateScrollBars() {
            minSlider.setValue((int)minThreshold);
            maxSlider.setValue((int)maxThreshold);
        }

        /** Restore image outside non-rectangular roi. */
        void doMasking(ImagePlus imp, ImageProcessor ip) {
            ImageProcessor mask = imp.getMask();
            if (mask!=null)
                ip.reset(mask);
        }

        void adjustMinThreshold(ImagePlus imp, ImageProcessor ip, double value) {
            if (IJ.altKeyDown() || IJ.shiftKeyDown() ) {
                double width = maxThreshold-minThreshold;
                if (width<1.0) width = 1.0;
                minThreshold = value;
                maxThreshold = minThreshold+width;
                if ((minThreshold+width)>255) {
                    minThreshold = 255-width;
                    maxThreshold = minThreshold+width;
                    minSlider.setValue((int)minThreshold);
                }
                maxSlider.setValue((int)maxThreshold);
                scaleUpAndSet(ip, minThreshold, maxThreshold);
                return;
            }
            minThreshold = value;
            if (maxThreshold<minThreshold) {
                maxThreshold = minThreshold;
                maxSlider.setValue((int)maxThreshold);
            }
            scaleUpAndSet(ip, minThreshold, maxThreshold);
        }

        void adjustMaxThreshold(ImagePlus imp, ImageProcessor ip, int cvalue) {
            maxThreshold = cvalue;
            if (minThreshold>maxThreshold) {
                minThreshold = maxThreshold;
                minSlider.setValue((int)minThreshold);
            }
            scaleUpAndSet(ip, minThreshold, maxThreshold);
            IJ.setKeyUp(KeyEvent.VK_ALT);
            IJ.setKeyUp(KeyEvent.VK_SHIFT);
        }

        void reset(ImagePlus imp, ImageProcessor ip) {
            boolean useStackMinAndMax = false;
            if (!(ip instanceof ByteProcessor)) {
                ip.resetMinAndMax();
                useStackMinAndMax = imp.getStackSize()>1 && IJ.altKeyDown();
            }
            ip.resetThreshold();
            plot.setHistogram(imp, useStackMinAndMax);
            updateScrollBars();
            if (Recorder.record)
                Recorder.record("resetThreshold");
        }

        void doSet(ImagePlus imp, ImageProcessor ip) {
            double level1 = ip.getMinThreshold();
            double level2 = ip.getMaxThreshold();
            if (level1==ImageProcessor.NO_THRESHOLD) {
                level1 = scaleUp(ip, defaultMinThreshold);
                level2 = scaleUp(ip, defaultMaxThreshold);
            }
            Calibration cal = imp.getCalibration();
            int digits = (ip instanceof FloatProcessor)||cal.calibrated()?2:0;
            level1 = cal.getCValue(level1);
            level2 = cal.getCValue(level2);
            GenericDialog gd = new GenericDialog("Set Threshold Levels");
            gd.addNumericField("Lower Threshold Level: ", level1, digits);
            gd.addNumericField("Upper Threshold Level: ", level2, digits);
            gd.showDialog();
            if (gd.wasCanceled())
                return;
            level1 = gd.getNextNumber();
            level2 = gd.getNextNumber();
            level1 = cal.getRawValue(level1);
            level2 = cal.getRawValue(level2);
            if (level2<level1)
                level2 = level1;
            double minDisplay = ip.getMin();
            double maxDisplay = ip.getMax();
            ip.resetMinAndMax();
            double minValue = ip.getMin();
            double maxValue = ip.getMax();
            if (level1<minValue) level1 = minValue;
            if (level2>maxValue) level2 = maxValue;
            boolean outOfRange = level1<minDisplay || level2>maxDisplay;
            if (outOfRange)
                plot.setHistogram(imp, false);
            else
                ip.setMinAndMax(minDisplay, maxDisplay);

            minThreshold = scaleDown(ip,level1);
            maxThreshold = scaleDown(ip,level2);
            scaleUpAndSet(ip, minThreshold, maxThreshold);
            updateScrollBars();
            if (Recorder.record) {
                if (imp.getBitDepth()==32)
                    Recorder.record("setThreshold", ip.getMinThreshold(), ip.getMaxThreshold());
                else {
                    int min = (int)ip.getMinThreshold();
                    int max = (int)ip.getMaxThreshold();
                    if (cal.isSigned16Bit()) {
                        min = (int)cal.getCValue(level1);
                        max = (int)cal.getCValue(level2);
                    }
                    Recorder.record("setThreshold", min, max);
                }
            }
        }

        void changeState(ImagePlus imp, ImageProcessor ip) {
            scaleUpAndSet(ip, minThreshold, maxThreshold);
            updateScrollBars();
        }

        void autoThreshold(ImagePlus imp, ImageProcessor ip) {
            ip.resetThreshold();
            previousImageID = 0;
            setup(imp);
        }

        void apply(ImagePlus imp) {
            try {
                if (imp.getBitDepth()==32) {
                    GenericDialog gd = new GenericDialog("NaN Backround");
                    gd.addCheckbox("Set Background Pixels to NaN", backgroundToNaN);
                    gd.showDialog();
                    if (gd.wasCanceled()) {
                        runThresholdCommand();
                        return;
                    }
                    backgroundToNaN = gd.getNextBoolean();
                    if (backgroundToNaN)
                        IJ.run("NaN Background");
                    else
                        runThresholdCommand();
                } else
                    runThresholdCommand();
            } catch (Exception e)
                {/* do nothing */}
            //close();
        }

        void runThresholdCommand() {
            Recorder.recordInMacros = true;
            IJ.run("Convert to Mask");
            Recorder.recordInMacros = false;
        }

        static final int RESET=0, AUTO=1, HIST=2, APPLY=3, STATE_CHANGE=4, MIN_THRESHOLD=5, MAX_THRESHOLD=6, SET=7;

        // Separate thread that does the potentially time-consuming processing
        public void run() {
            while (!done) {
                synchronized(this) {
                    try {wait();}
                    catch(InterruptedException e) {}
                }
                doUpdate();
            }
        }

        void doUpdate() {
            ImagePlus imp;
            ImageProcessor ip;
            int action;
            int min = minValue;
            int max = maxValue;
            if (doReset) action = RESET;
            else if (doAutoAdjust) action = AUTO;
            else if (doApplyLut) action = APPLY;
            else if (doStateChange) action = STATE_CHANGE;
            else if (doSet) action = SET;
            else if (minValue>=0) action = MIN_THRESHOLD;
            else if (maxValue>=0) action = MAX_THRESHOLD;
            else return;
            minValue = -1;
            maxValue = -1;
            doReset = false;
            doAutoAdjust = false;
            doApplyLut = false;
            doStateChange = false;
            doSet = false;
            imp = WindowManager.getCurrentImage();
            if (imp==null) {
                IJ.beep();
                IJ.showStatus("No image");
                return;
            }
            ip = setup(imp);
            if (ip==null) {
                imp.unlock();
                IJ.beep();
                IJ.showStatus("RGB images cannot be thresholded");
                return;
            }
            //IJ.write("setup: "+(imp==null?"null":imp.getTitle()));
            switch (action) {
                case RESET: reset(imp, ip); break;
                case AUTO: autoThreshold(imp, ip); break;
                case APPLY: apply(imp); break;
                case STATE_CHANGE: changeState(imp, ip); break;
                case SET: doSet(imp, ip); break;
                case MIN_THRESHOLD: adjustMinThreshold(imp, ip, min); break;
                case MAX_THRESHOLD: adjustMaxThreshold(imp, ip, max); break;
            }
            updatePlot();
            updateLabels(imp, ip);
            ip.setLutAnimation(true);
            imp.updateAndDraw();
        }

        public void windowClosing(WindowEvent e) {
            close();
            Prefs.saveLocation(LOC_KEY, getLocation());
        }

        /** Overrides close() in PlugInFrame. */
        public void close() {
            super.close();
            instance = null;
            done = true;
            synchronized(this) {
                notify();
            }
        }

        public void windowActivated(WindowEvent e) {
            super.windowActivated(e);
            ImagePlus imp = WindowManager.getCurrentImage();
            if (imp!=null) {
                if (!firstActivation) {
                    previousImageID = 0;
                    setup(imp);
                }
                firstActivation = false;
            }
        }

    } // ThresholdAdjuster class

    class ThresholdPlot extends Canvas implements Measurements, MouseListener {
        static final int WIDTH = 256, HEIGHT=48;
        double minThreshold = 85;
        double maxThreshold = 170;
        int[] histogram;
        Color[] hColors;
        int hmax;
        Image os;
        Graphics osg;
        int mode;
        double stackMin, stackMax;

        public ThresholdPlot() {
            addMouseListener(this);
            setSize(WIDTH+1, HEIGHT+1);
        }

        /** Overrides Component getPreferredSize(). Added to work
            around a bug in Java 1.4.1 on Mac OS X.*/
        public Dimension getPreferredSize() {
            return new Dimension(WIDTH+1, HEIGHT+1);
        }

        ImageStatistics setHistogram(ImagePlus imp, boolean useStackMinAndMax) {
            ImageProcessor ip = imp.getProcessor();
            ImageStatistics stats = null;
            if (!(ip instanceof ByteProcessor)) {
                if (useStackMinAndMax) {
                    stats = new StackStatistics(imp);
                    if (imp.getLocalCalibration().isSigned16Bit())
                        {stats.min += 32768; stats.max += 32768;}
                    stackMin = stats.min;
                    stackMax = stats.max;
                    ip.setMinAndMax(stackMin, stackMax);
                } else
                    stackMin = stackMax = 0.0;
                Calibration cal = imp.getCalibration();
                if (ip instanceof FloatProcessor) {
                    int digits = Math.max(Analyzer.getPrecision(), 2);
                    IJ.showStatus("min="+IJ.d2s(ip.getMin(),digits)+", max="+IJ.d2s(ip.getMax(),digits));
                } else
                    IJ.showStatus("min="+(int)cal.getCValue(ip.getMin())+", max="+(int)cal.getCValue(ip.getMax()));
                ip = ip.convertToByte(true);
                ip.setColorModel(ip.getDefaultColorModel());
            }
            ip.setRoi(imp.getRoi());
            if (stats==null)
                stats = ImageStatistics.getStatistics(ip, AREA+MIN_MAX+MODE, null);
            int maxCount2 = 0;
            histogram = stats.histogram;
            for (int i = 0; i < stats.nBins; i++)
                if ((histogram[i] > maxCount2) && (i != stats.mode))
                    maxCount2 = histogram[i];
            hmax = stats.maxCount;
            if ((hmax>(maxCount2 * 2)) && (maxCount2 != 0)) {
                hmax = (int)(maxCount2 * 1.5);
                histogram[stats.mode] = hmax;
                }
            os = null;

            ColorModel cm = ip.getColorModel();
            if (!(cm instanceof IndexColorModel))
                return null;
            IndexColorModel icm = (IndexColorModel)cm;
            int mapSize = icm.getMapSize();
            if (mapSize!=256)
                return null;
            byte[] r = new byte[256];
            byte[] g = new byte[256];
            byte[] b = new byte[256];
            icm.getReds(r);
            icm.getGreens(g);
            icm.getBlues(b);
            hColors = new Color[256];
            for (int i=0; i<256; i++)
                hColors[i] = new Color(r[i]&255, g[i]&255, b[i]&255);
            return stats;
        }

        public void update(Graphics g) {
            paint(g);
        }

        public void paint(Graphics g) {
            if (g==null) return;
            if (histogram!=null) {
                if (os==null && hmax>0) {
                    os = createImage(WIDTH,HEIGHT);
                    osg = os.getGraphics();
                    osg.setColor(Color.white);
                    osg.fillRect(0, 0, WIDTH, HEIGHT);
                    osg.setColor(Color.gray);
                    for (int i = 0; i < WIDTH; i++) {
                        if (hColors!=null) osg.setColor(hColors[i]);
                        osg.drawLine(i, HEIGHT, i, HEIGHT - ((int)(HEIGHT * histogram[i])/hmax));
                    }
                    osg.dispose();
                }
                if (os==null) return;
                g.drawImage(os, 0, 0, this);
            } else {
                g.setColor(Color.white);
                g.fillRect(0, 0, WIDTH, HEIGHT);
            }
            g.setColor(Color.black);
            g.drawRect(0, 0, WIDTH, HEIGHT);
            if (mode==ThresholdAdjuster.RED)
                g.setColor(Color.red);
            else if (mode==ThresholdAdjuster.OVER_UNDER) {
                g.setColor(Color.blue);
                g.drawRect(1, 1, (int)minThreshold-2, HEIGHT);
                g.drawRect(1, 0, (int)minThreshold-2, 0);
                g.setColor(Color.green);
                g.drawRect((int)maxThreshold+1, 1, WIDTH-(int)maxThreshold, HEIGHT);
                g.drawRect((int)maxThreshold+1, 0, WIDTH-(int)maxThreshold, 0);
                return;
            }
            g.drawRect((int)minThreshold, 1, (int)(maxThreshold-minThreshold), HEIGHT);
            g.drawLine((int)minThreshold, 0, (int)maxThreshold, 0);
         }

        public void mousePressed(MouseEvent e) {}
        public void mouseReleased(MouseEvent e) {}
        public void mouseExited(MouseEvent e) {}
        public void mouseClicked(MouseEvent e) {}
        public void mouseEntered(MouseEvent e) {}

    } // ThresholdPlot class

| ThresholdAdjuster |