/**
* GmailAssistant 1.1 (2008-03-16)
* Copyright 2008 Zach Scrivena
* zachscrivena@gmail.com
* http://gmailassistant.sourceforge.net/
*
* Notifier for multiple Gmail accounts.
*
* TERMS AND CONDITIONS:
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* as published by the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gmailassistant;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.Timer;
import java.util.TimerTask;
/**
* Blink the keyboard num-lock LED.
*/
class KeyboardLedBlinker
{
/** interval between keyboard num-lock LED blinking */
private static final long BLINK_INTERVAL_MILLISECONDS = 200L;
/** number of blinking iterations when performing a test */
private static final int TEST_ITERATIONS = 2;
/** timer refresh interval */
private static final long TIMER_REFRESH_INTERVAL_MILLISECONDS = BLINK_INTERVAL_MILLISECONDS;
/** timer for blinking the keyboard num-lock LED */
private final Timer timer;
/** is the timer cancelled? */
private volatile boolean timerCancelled = false;
/** queued actions to be performed by the timer */
private final Queue<ActionType> actions = new ArrayDeque<ActionType>();
/**
* Constructor.
*/
KeyboardLedBlinker()
{
/***************************
* INITIALIZE TIMER THREAD *
***************************/
this.timer = new Timer("Keyboard-Blinker-Timer", true);
this.timer.schedule(new TimerTask()
{
/** toolkit for accessing num-lock state */
private final Toolkit toolkit = Toolkit.getDefaultToolkit();
/** robot for pressing num-lock key */
private Robot robot = null;
/** current num-lock state */
private boolean state;
/** saved num-lock state, to be restored */
private boolean savedState;
/** is the blinker active? */
private boolean blinking = false;
/**
* Set the num-lock state to the specified state.
*
* @param state
* desired num-lock state
*/
private void setNumLockState(
final boolean state)
{
try
{
this.toolkit.setLockingKeyState(
KeyEvent.VK_NUM_LOCK,
state);
}
catch (Exception e)
{
if (this.robot == null)
{
try
{
this.robot = new Robot();
}
catch (Exception ee)
{
/* ignore */
}
}
if (this.robot != null)
{
this.robot.keyPress(KeyEvent.VK_NUM_LOCK);
this.robot.keyRelease(KeyEvent.VK_NUM_LOCK);
}
}
}
@Override
public void run()
{
ActionType action;
synchronized (KeyboardLedBlinker.this.actions)
{
action = KeyboardLedBlinker.this.actions.poll();
}
if (action == ActionType.START)
{
if (!this.blinking)
{
try
{
this.savedState = this.toolkit.getLockingKeyState(KeyEvent.VK_NUM_LOCK);
}
catch (Exception e)
{
this.savedState = false;
}
this.state = this.savedState;
this.blinking = true;
}
}
else if (action == ActionType.STOP)
{
if (this.blinking)
{
setNumLockState(this.savedState);
this.blinking = false;
}
}
else if (action == ActionType.TEST)
{
for (int i = 0; i < KeyboardLedBlinker.TEST_ITERATIONS; i++)
{
setNumLockState(true);
try
{
Thread.sleep(KeyboardLedBlinker.BLINK_INTERVAL_MILLISECONDS);
}
catch (InterruptedException e)
{
/* ignore */
}
setNumLockState(false);
try
{
Thread.sleep(KeyboardLedBlinker.BLINK_INTERVAL_MILLISECONDS);
}
catch (InterruptedException e)
{
/* ignore */
}
}
}
else if (action == ActionType.TERMINATE)
{
KeyboardLedBlinker.this.timer.cancel();
KeyboardLedBlinker.this.timerCancelled = true;
}
if (this.blinking)
{
this.state = !this.state;
setNumLockState(this.state);
}
}
}, 0, KeyboardLedBlinker.BLINK_INTERVAL_MILLISECONDS);
}
/**
* Start the blinker, if it has not already been started.
*/
void start()
{
synchronized (this.actions)
{
this.actions.add(ActionType.START);
}
}
/**
* Stop the blinker, if it is blinking.
*/
void stop()
{
synchronized (this.actions)
{
this.actions.add(ActionType.STOP);
}
}
/**
* Test the blinker.
*/
void test()
{
synchronized (this.actions)
{
this.actions.add(ActionType.TEST);
}
}
/**
* Stop the blinker now, if it is blinking.
*/
void cancelAll()
{
synchronized (this.actions)
{
this.actions.clear();
this.actions.add(ActionType.STOP);
}
}
/**
* Terminate the timer thread.
* This method blocks until the thread is cancelled.
*/
void terminate()
{
synchronized (this.actions)
{
this.actions.clear();
}
do
{
synchronized (this.actions)
{
this.actions.add(ActionType.STOP);
this.actions.add(ActionType.TERMINATE);
}
if (this.timerCancelled)
{
return;
}
try
{
Thread.sleep(KeyboardLedBlinker.TIMER_REFRESH_INTERVAL_MILLISECONDS / 10);
}
catch (Exception e)
{
/* ignore */
}
}
while (true);
}
/*****************
* INNER CLASSES *
*****************/
/**
* Types of action.
*/
private enum ActionType
{
START,
STOP,
TEST,
TERMINATE
}
}