My program visually demonstrates a sequential version of the well known QuickSort algorithm, with two new visual demonstrations: (I) a parallel version of QuickSort, implemented using low level Thread API and SwingUtilities, and (II) a parallel version of QuickSort, implemented using SwingWorker API.
I am trying to have a facility to restart the program after a successful run. Currently, the buttons are disabled when the sorting operation starts, which is correct, but they never re-enable and so I was wondering if there is a way to enable all the buttons after the successful run? Some of the code is as follows:
// http://www.java2s.com/Code/Java/Collections-Data-Structure/Animationforquicksort.htm
// http://www.sorting-algorithms.com/quick-sort
import java.lang.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.util.*;
import javax.swing.*;
import java.util.concurrent.atomic.*;
public class QuickSortVisualizer implements Runnable {
    static int LENGTH = 32;
    static int LEFT = 500;
    static int RIGHT = 500;
    static int SWAP = 1000;
    int[] Values;
    AtomicInteger WorkerThreads = new AtomicInteger();
    public static void main(String[] args) {
        try {
            if (args.length == 0) {             
                LENGTH = 32;
                LEFT = 500;
                RIGHT = 500;
                SWAP = 1000;
            } else if (args.length == 4) { 
                //dw about this
            } else {
                throw new Exception("incorrect command-line argument count"); 
            }
            System.err.format("... LENGTH=%d LEFT=%d RIGHT=%d SWAP=%d%n", LENGTH, LEFT, RIGHT, SWAP);
            SwingUtilities.invokeAndWait(new QuickSortVisualizer());
            System.err.format("... GUI started%n");
        } catch (Exception ex) {
            System.err.format("*** %s%n", ex.getMessage());
        }
    }
    JButton BoredButton;
    JButton WorkerButtonSequential;
    JButton WorkerButtonThreads;
    JButton WorkerButtonSwingWorkers;
    SorterPanel MySortPanel;
    JLabel StatusBar;
    public void run() {
        JFrame frame = new JFrame();
        frame.setTitle("My Quick Sort Visualizer");
        Font font = new Font("Monospaced", Font.BOLD, 18);
        BoredButton = new JButton("I am bored");
        BoredButton.setFont(font);
        BoredButton.setPreferredSize(new Dimension(180, 30));
        BoredButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                BoredButton_Click();
            }
        });
        WorkerButtonSequential = new JButton("QS Sequential");
        WorkerButtonSequential.setFont(font);
        WorkerButtonSequential.setPreferredSize(new Dimension(185, 30));
        WorkerButtonSequential.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                WorkerButtonSequential_Click();
            }
        });
        WorkerButtonThreads = new JButton("QS Threads");
        WorkerButtonThreads.setFont(font);
        WorkerButtonThreads.setPreferredSize(new Dimension(185, 30));
        WorkerButtonThreads.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                WorkerButtonThreads_Click();
            }
        });
        WorkerButtonSwingWorkers = new JButton("QS SwingWorkers");
        WorkerButtonSwingWorkers.setFont(font);
        WorkerButtonSwingWorkers.setPreferredSize(new Dimension(200, 30));
        WorkerButtonSwingWorkers.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                WorkerButtonSwingWorkers_Click();
            }
        });
        JPanel strip = new JPanel(new FlowLayout(FlowLayout.CENTER));
        strip.add(BoredButton);
        strip.add(WorkerButtonSequential);
        strip.add(WorkerButtonThreads);
        strip.add(WorkerButtonSwingWorkers);
        frame.getContentPane().add(strip, BorderLayout.NORTH);
        StatusBar = new JLabel();
        StatusBar.setFont(font);
        StatusBar.setPreferredSize(new Dimension(800, 20));
        frame.getContentPane().add(StatusBar, BorderLayout.SOUTH);
        MySortPanel = new SorterPanel();
        frame.getContentPane().add(MySortPanel, BorderLayout.CENTER);
        frame.getRootPane().setDefaultButton(BoredButton);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(800, 400);
        frame.setResizable(true);
        frame.setVisible(true);
    }
    public void BoredButton_Click() {
        String text = Calendar.getInstance().getTime().toString();
        StatusBar.setText(text);
        System.err.format("... now %s%n", text);
    }
    public void WorkerButtonSequential_Click() {
        WorkerButtonSequential.setEnabled(false);
        WorkerButtonThreads.setEnabled(false);
        WorkerButtonSwingWorkers.setEnabled(false);
        System.err.format("... sequential%n");
        QSSequential();
    }
    public void WorkerButtonThreads_Click() {
        WorkerButtonSequential.setEnabled(false);
        WorkerButtonThreads.setEnabled(false);
        WorkerButtonSwingWorkers.setEnabled(false);
        int processors = Runtime.getRuntime().availableProcessors();
        int threshold = processors * 2;
        System.err.format("... parallel threads: processors=%d threshold=%d%n", processors, threshold);
        QSThreads(threshold);
    }
    public void WorkerButtonSwingWorkers_Click() {
        WorkerButtonSequential.setEnabled(false);
        WorkerButtonThreads.setEnabled(false);
        WorkerButtonSwingWorkers.setEnabled(false);
        int processors = Runtime.getRuntime().availableProcessors();
        int threshold = processors * 2;
        System.err.format("... parallel swingworkers: processors=%d threshold=%d%n", processors, threshold);
        QSSwingWorkers(threshold);
    }
    void QSInit() {
        Values = new int[LENGTH];
        for (int i = 0; i < Values.length; i++) {
            Values[i] = (int)Math.round(Math.random() * (MySortPanel.getHeight()-10));
        }
        print("... initial values");                
        MySortPanel.setValues(Values);      
    }
    void QSSequential() {
        QSInit();
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                QuickSortSequential qss = new QuickSortSequential(Values, 0, Values.length - 1, MySortPanel);
                System.err.format("... started%n");
                qss.run();
                DoneAll();
            }
        });
    }
    void QSThreads(int threshold) {
        QSInit();
        QuickSortThread qst = new QuickSortThread(Values, 0, Values.length - 1, threshold, MySortPanel);
        WorkerThreads.set(0);
        incWorkerThreads();
        System.err.format("... started%n");
        qst.start();
    }
    void QSSwingWorkers(int threshold) {
        QSInit();
        QuickSortWorker qsw = new QuickSortWorker(Values, 0, Values.length - 1, threshold, MySortPanel);
        WorkerThreads.set(0);
        incWorkerThreads();
        System.err.format("... started%n");
        qsw.execute();      
    }
    void print(String caption) {
        System.err.format("%s%n", caption);
        for (int i=0; i<Values.length; i++) {
            System.err.format(" %d:%d", i, Values[i]);
        }
        System.err.format("%n");
    }
    void incWorkerThreads() {
        int w = WorkerThreads.incrementAndGet();
        System.err.format("... workers=%d%n", w);
    }
    void decWorkerThreads() {
        int w = WorkerThreads.decrementAndGet();
        System.err.format("... workers=%d%n", w);
        if (w <= 0) DoneAll();
    }
    void DoneAll() {
        print("... sorted values");                 
        WorkerButtonSequential.setEnabled(true);    
        WorkerButtonThreads.setEnabled(true);       
        WorkerButtonSwingWorkers.setEnabled(true);      
        System.err.format("%n");
    }
    // === SorterPanel
    /* colour codes
    pivot : YELLOW
    left item : GREEN
    right item : BLUE
    left item just before swap : PINK
    right item just before swap : PINK
    left item just after swap : RED
    right item just after swap : RED
    */
    class SorterPanel extends JComponent {
        int[] Values;       
        int width;
        Graphics2D g2;
        Color pen;
        Color back;
        public void setValues(int[] Values) {
            this.Values = Values;
            width = super.getWidth() / Values.length;
            repaint();
        }
        @Override 
        public void paintComponent(Graphics g) {
            if (Values == null) return;
            g2 = (Graphics2D) g;
            pen = Color.BLACK; // g2.getColor();
            back = g2.getBackground();
            for (int i = 0; i < Values.length; i++) {
                g2.draw(new Rectangle2D.Double(width*i+1, 0, width-2, Values[i]));
            }
        }
        public void mark(int i, int value, Color m) {
            g2 = (Graphics2D) super.getGraphics();
            pen = g2.getColor();
            g2.setColor(m);
            //g2.fill(new Rectangle2D.Double(width*i+2, 1, width-4, Values[i]-2));
            g2.fill(new Rectangle2D.Double(width*i+2, 1, width-4, value-2));
            g2.setColor(pen);
        }
        public void unmark(final int i, final int value) {
            mark(i, value, back);
        }
        public void erase(int i, int value) {
            g2 = (Graphics2D) super.getGraphics();
            //g2.clearRect(width*i+1, 0, width-1, Values[i]+1);
            g2.clearRect(width*i+1, 0, width-1, value+1);
        }
        public void redraw(int i, int value) {
            g2 = (Graphics2D) super.getGraphics();
            //g2.draw(new Rectangle2D.Double(width*i+1, 0, width-2, Values[i]));
            g2.draw(new Rectangle2D.Double(width*i+1, 0, width-2, value));
            mark(i, value, back);
        }
    }
    // === QuickSort Sequential
    class QuickSortSequential implements Runnable { 
        int[] array;
        int left;
        int right;
        // === GUI stuff
        SorterPanel sortpan;
        void publish(Runnable gui_update) {
            gui_update.run();
        }
        void mark(final int idx, final Color color) {
            final int value = array[idx];
            publish(new Runnable() { 
                public void run() { 
                    sortpan.mark(idx, value, color); 
                }
            });
        }
        void unmark(final int idx) {
            final int value = array[idx];
            publish(new Runnable() { 
                public void run() { 
                    sortpan.unmark(idx, value); 
                }
            });
        }
        void erase(final int idx) {
            final int value = array[idx];
            publish(new Runnable() { 
                public void run() { 
                    sortpan.erase(idx, value); 
                }
            });
        }
        void redraw(final int idx) {
            final int value = array[idx];
            publish(new Runnable() { 
                public void run() { 
                    sortpan.redraw(idx, value); 
                }
            });
        }
        void sleep(int period) {
            try { 
                Thread.sleep(period); 
            } catch (Exception ex) { 
                System.err.format("%s%n", ex.getMessage()); 
            }                   
        }
        // === stuff
        public QuickSortSequential(final int array[], final int left, final int right, final SorterPanel sortpan) {
            this.array = array;
            this.left = left;
            this.right = right;
            this.sortpan = sortpan;
        }
        public void run() {
            QuickSort();
        }
        // === QuickSort stuff
        void QuickSort() {
            if (left >= right) return;
            final int pivot = Partition();
            if (pivot < 0) return;
            QuickSortSequential lquick = new QuickSortSequential(array, left, pivot-1, sortpan);
            QuickSortSequential rquick = new QuickSortSequential(array, pivot, right, sortpan);
            lquick.run();
            rquick.run();
        }
        int Partition() {
            int leftIdx = left;
            int rightIdx = right;
            int pivotIdx = (left + right) / 2;
            final int pivot = array[pivotIdx]; 
            while (true) {
                if (leftIdx > rightIdx) break;
                mark(pivotIdx, Color.YELLOW);
                mark(leftIdx, Color.GREEN);
                mark(rightIdx, Color.BLUE);
                sleep(LEFT);
                while (true) {
                    if (array[leftIdx] >= pivot) break;
                    else {
                        unmark(leftIdx);
                        leftIdx += 1;
                        mark(pivotIdx, Color.YELLOW);
                        mark(leftIdx, Color.GREEN);
                        sleep(LEFT);
                    }
                }
                while (true) {
                    if (pivot >= array[rightIdx]) break;
                    else {
                        unmark(rightIdx);
                        rightIdx -= 1;
                        mark(pivotIdx, Color.YELLOW);
                        mark(rightIdx, Color.BLUE);
                        sleep(RIGHT);
                    }
                }
                unmark(pivotIdx);
                unmark(leftIdx);
                unmark(rightIdx);
                if (leftIdx <= rightIdx) {                  
                    if (leftIdx < rightIdx) {
                        mark(pivotIdx, Color.YELLOW);
                        mark(leftIdx, Color.PINK);
                        mark(rightIdx, Color.PINK);
                        sleep(SWAP);
                        erase(leftIdx);
                        erase(rightIdx);
                        int temp = array[rightIdx];
                        array[rightIdx] = array[leftIdx];
                        array[leftIdx] = temp;
                        if (pivotIdx == leftIdx) pivotIdx = rightIdx;
                        else if (pivotIdx == rightIdx) pivotIdx = leftIdx;
                        redraw(leftIdx);
                        redraw(rightIdx);
                        mark(pivotIdx, Color.YELLOW);
                        mark(leftIdx, Color.RED);
                        mark(rightIdx, Color.RED);
                        sleep(SWAP);
                    }
                    unmark(pivotIdx);
                    unmark(leftIdx);
                    unmark(rightIdx);
                    leftIdx += 1;
                    rightIdx -= 1;
                }
            }
            return leftIdx;
        }
    }
    // === QuickSort with Threads
    class QuickSortThread extends Thread { 
        int[] array;
        int left;
        int right;
        int threshold;
        // === GUI stuff
        SorterPanel sortpan;
        // === Thread etc stuff
        public QuickSortThread(final int array[], final int left, final int right, final int threshold, final SorterPanel sortpan) {
            this.array = array;
            this.left = left;
            this.right = right;
            this.sortpan = sortpan;
            this.threshold = threshold;
        }
        @Override
        public void run() {
            decWorkerThreads();
        }
    }
    // === QuickSort with SwingWorkers
    class QuickSortWorker extends SwingWorker<Boolean, Runnable> {  
        int[] array;
        int left;
        int right;
        int threshold;
        // === GUI stuff 
        SorterPanel sortpan;
        // === SwingWorker stuff
        public QuickSortWorker(final int array[], final int left, final int right, final int threshold, final SorterPanel sortpan) {
            this.array = array;
            this.left = left;
            this.right = right;
            this.threshold = threshold;
            this.sortpan = sortpan;
        }
        @Override
        public Boolean doInBackground() {
            return true;
        }
        @Override
        public void process(java.util.List<Runnable> gui_updates) {
        }
        @Override
        public void done() {
            decWorkerThreads();
        }
    }
}
 
     
    