org.kevinferrare.meijiChokoSolver.gui.SolverPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.kevinferrare.meijiChokoSolver.gui.SolverPanel.java

Source

/*
 * Meiji Choko Solver
 * Copyright 2008 and beyond, Kvin Ferrare.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation; either version 3 of the License, or 
 * (at your option) any later version. 
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */
package org.kevinferrare.meijiChokoSolver.gui;

import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kevinferrare.meijiChokoSolver.gui.SolverGUIOperatingModeJComboBox.SolverGUIOperatingMode;
import org.kevinferrare.meijiChokoSolver.solver.Solver;
import org.kevinferrare.meijiChokoSolver.solver.SolverEventListener;

/**
 * Starts and stops the solver when appropriate and displays it's state depending on
 * how the user chooses it with the <code>SolverGUIOperatingModeJComboBox</code>
 * 
 * @author Kvin Ferrare
 * 
 */
@SuppressWarnings("serial")
class SolverPanel extends RowJPanel implements ActionListener, SolverEventListener {
    private static Log log = LogFactory.getLog(SolverPanel.class);
    private Solver solver;
    private MatrixCanvas canvas;
    private JButton nextButton = new JButton();
    private SolverInfoJPanel solverInfoJPanel;
    private SolverGUIOperatingModeJComboBox solverGUIOperatingModeJComboBox = new SolverGUIOperatingModeJComboBox(
            SolverGUIOperatingMode.PAUSE_AFTER_EACH_SOLUTION);
    private boolean pauseBetweenSolutions;
    private int timeBetweenSolutions;
    private boolean pauseBetweenPlacements;
    private int timeBetweenPlacements;
    private Timer refreshSolverInfosTimer = new Timer(100, this);//will refresh the computation state panel 10 times a second
    private Future<?> solverFuture;//to wait for the solver thread completion

    public SolverPanel(Solver solver) {
        this.solver = solver;
        //register the component to the solver so that the interface gets noticed when something happens
        this.solver.setSolverEventListener(this);

        //layout
        canvas = new MatrixCanvas();
        canvas.setMatrix(solver.getBoard());
        this.add(new TitledJPanel("Board", canvas));
        this.add(createSideBar());

        solverGUIOperatingModeChanged();
        solverInfoJPanel.update();
    }

    /**
     * Creates the components on the right
     * @return
     */
    private JPanel createSideBar() {
        ColumnJPanel panel = new ColumnJPanel();

        panel.add(new TitledJPanel("Current solver controls", createControls()), new Insets(0, 3, 0, 0));

        solverInfoJPanel = new SolverInfoJPanel(solver);
        panel.add(new TitledJPanel("Current solver information", solverInfoJPanel), new Insets(0, 3, 0, 0));

        return panel;
    }

    /**
     * Creates the controls inside the right bar
     * @return
     */
    private JPanel createControls() {
        ColumnJPanel panel = new ColumnJPanel();

        solverGUIOperatingModeJComboBox.addActionListener(this);
        solverGUIOperatingModeJComboBox.setActionCommand("solverGUIOperatingModeChanged");
        panel.add(solverGUIOperatingModeJComboBox);

        nextButton.addActionListener(this);
        nextButton.setActionCommand("next");
        panel.add(nextButton);

        JButton restartButton = new JButton("Restart");
        restartButton.setActionCommand("restartSolver");
        restartButton.addActionListener(this);
        panel.add(restartButton);

        JButton changePaletteButton = new JButton("Change colors");
        changePaletteButton.setActionCommand("changeColors");
        changePaletteButton.addActionListener(this);
        panel.add(changePaletteButton);

        return panel;
    }

    /**
     * Called when the component is shown
     */
    @Override
    public void addNotify() {
        super.addNotify();
        startSolver();
    }

    /**
     * Called when the component is removed
     */
    @Override
    public void removeNotify() {
        super.removeNotify();
        stopSolver();
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        if ("solverGUIOperatingModeChanged".equals(event.getActionCommand())) {
            solverGUIOperatingModeChanged();
        } else if ("next".equals(event.getActionCommand())) {
            continueSolver();
        } else if ("restartSolver".equals(event.getActionCommand())) {
            stopSolver();
            startSolver();
        } else if ("changeColors".equals(event.getActionCommand())) {
            this.canvas.setRandomPalette();
        } else {
            solverInfoJPanel.update();//refresh by timer
        }
    }

    private void startSolver() {
        log.debug("Requesting start");
        solverFuture = Executors.newSingleThreadExecutor().submit(solver);
        refreshSolverInfosTimer.start();
        log.debug("Successfully started");
    }

    private void stopSolver() {
        log.debug("Requesting stop");
        refreshSolverInfosTimer.stop();
        solver.abort();
        continueSolver();//unlock the solver if it was locked waiting for user input
        try {
            solverFuture.get();//wait for the solver to end (should not take long since it's in aborted state)
        } catch (Exception e) {
        }
        solver.reset();//resets the solver for the next run
        log.debug("Successfully stopped");
    }

    private void solverGUIOperatingModeChanged() {
        SolverGUIOperatingMode mode = solverGUIOperatingModeJComboBox.getSelectedSolverGUIOperatingMode();
        //reset
        pauseBetweenSolutions = false;
        timeBetweenSolutions = 0;
        pauseBetweenPlacements = false;
        timeBetweenPlacements = 0;
        switch (mode) {
        case CONTINUOUS:
            break;
        case DELAY_AFTER_EACH_SOLUTION:
            timeBetweenSolutions = 500;
            break;
        case PAUSE_AFTER_EACH_SOLUTION:
            pauseBetweenSolutions = true;
            break;
        case DELAY_AFTER_EACH_PLACEMENT:
            timeBetweenPlacements = 20;
            break;
        case PAUSE_AFTER_EACH_PLACEMENT:
            pauseBetweenPlacements = true;
            break;
        }
        if (!pauseBetweenSolutions && !pauseBetweenPlacements) {
            nextButton.setVisible(false);
            continueSolver();
        }
    }

    /**
     * Pause the current thread for the given delay
     * 
     * @param millis
     *            the delay in milliseconds
     */
    protected void pauseThread(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
        }
    }

    /**
     * Pauses the current thread until the user press the continue button
     */
    protected synchronized void pauseThreadUntilUserAction() {
        try {
            //enable the continue button
            invokeLater(new Runnable() {
                public void run() {
                    updateGuiForWaitForUserAction();
                }
            });
            log.debug("Waiting for user click");
            wait();
            log.debug("The user clicked");
            //disable the continue button
            invokeLater(new Runnable() {
                public void run() {
                    updateGuiForComputing();
                }
            });
        } catch (Exception e) {
        }
    }

    /**
     * Allow the threads waiting for user input to continue
     */
    protected synchronized void continueSolver() {
        notifyAll();
    }

    @Override
    public boolean shouldGetNotifyOfChangesInBoard(Solver solver) {
        return timeBetweenPlacements > 0 || pauseBetweenPlacements;
    }

    @Override
    public void changeInBoard(Solver solver) {
        canvas.repaint();
        solverInfoJPanel.update();
        if (pauseBetweenPlacements) {
            pauseThreadUntilUserAction();
        } else if (timeBetweenPlacements > 0) {
            pauseThread(timeBetweenPlacements);
        }
    }

    @Override
    public void solutionFound(Solver solver) {
        canvas.repaint();
        solverInfoJPanel.update();
        if (pauseBetweenSolutions) {
            pauseThreadUntilUserAction();
        } else if (timeBetweenSolutions > 0) {
            pauseThread(timeBetweenSolutions);
        }
    }

    @Override
    public void allSolutionsFound(Solver solver) {
        //let AWT finish processing the previous switchGuiToComputingMode before re-enabling the button
        refreshSolverInfosTimer.stop();
        invokeLater(new Runnable() {
            public void run() {
                updateGuiForAllSolutionsFound();
            }
        });
    }

    protected void updateGuiForWaitForUserAction() {
        nextButton.requestFocusInWindow();
        nextButton.setVisible(true);
        nextButton.setEnabled(true);
        nextButton.setText("Next");
    }

    protected void updateGuiForComputing() {
        nextButton.setEnabled(false);
        nextButton.setText("Computing...");
    }

    protected void updateGuiForAllSolutionsFound() {
        nextButton.setVisible(true);
        nextButton.setEnabled(false);
        nextButton.setText("No more solutions !");
    }

    private void invokeLater(Runnable runnable) {
        try {
            SwingUtilities.invokeLater(runnable);
        } catch (Exception e) {
        }
    }
}