summaryrefslogtreecommitdiff
path: root/projects/net.wotonomy.ui.swing/src/main/java/net/wotonomy/ui/swing/components/KeyDelayTimer.java
blob: 31bdb70003cc596ec2ed12502641ac0c7b5b4b8d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
Wotonomy: OpenStep design patterns for pure Java applications.
Copyright (C) 2000 Blacksmith, Inc.

This library 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 2.1 of the License, or (at your option) any later version.

This library 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.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see http://www.gnu.org
*/

package net.wotonomy.ui.swing.components;

import java.awt.AWTEventMulticaster;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.Timer;

/**
 * KeyDelayTimer is a utility that listens for KeyEvents from one or more
 * components. After receiving a KeyEvents the timer will broadcast an action
 * event if a specified time interval passes without a subsequent KeyEvent.<BR>
 * <BR>
 *
 * This utility is useful for implementing any kind of auto-complete feature in
 * a user interface.
 *
 * @author michael@mpowers.net
 * @author $Author: cgruber $
 * @version $Revision: 904 $ $Date: 2006-02-18 18:19:05 -0500 (Sat, 18 Feb 2006)
 *          $
 */
public class KeyDelayTimer implements ActionListener, KeyListener {
	// delay timer for keypress-sensitve events
	protected Timer keyTimer = null;
	protected Component lastFieldTouched = null;
	protected long timeLastFieldTouched = 0;
	protected int interval = 400; // adjust as needed

	// for action multicasting
	protected ActionListener actionListener = null;

	/**
	 * Default constructor.
	 */
	public KeyDelayTimer() {
		keyTimer = new Timer(interval, this);
	}

	/**
	 * Convenience constructor.
	 * 
	 * @param listener An action listener to be notified of delay events.
	 */
	public KeyDelayTimer(ActionListener listener) {
		this();
		addActionListener(listener);
	}

	/**
	 * Returns the last component that generated a KeyEvent.
	 * 
	 * @return The component that sent the most recent KeyEvent.
	 */
	public Component getComponent() {
		return lastFieldTouched;
	}

	/**
	 * Returns the number of milliseconds before an ActionEvent is generated. The
	 * default is 400.
	 * 
	 * @return The current delay interval in milliseconds.
	 */
	public int getInterval() {
		return interval;
	}

	/**
	 * Sets the number of milliseconds before an ActionEvent will be generated after
	 * a KeyEvent is received.
	 * 
	 * @param millis The new delay interval in milliseconds.
	 */
	public void setInterval(int millis) {
		interval = millis;
		keyTimer.setDelay(interval / 2);
	}

	// interface KeyListener

	public void keyTyped(KeyEvent e) {
	}

	public void keyPressed(KeyEvent e) {
	}

	/**
	 * Receives key events from one or more components. Records the component and
	 * the time this event was received, then starts the timer.
	 * 
	 * @param e The key event in question.
	 */
	public void keyReleased(KeyEvent e) { // handles keystrokes in the textfields (except ENTER and ESCAPE)
		if ((Character.isLetterOrDigit(e.getKeyChar())) || (e.getKeyCode() == KeyEvent.VK_SPACE)
				|| (e.getKeyCode() == KeyEvent.VK_DELETE) || (e.getKeyCode() == KeyEvent.VK_BACK_SPACE)) {
			this.lastFieldTouched = e.getComponent();
			this.timeLastFieldTouched = System.currentTimeMillis();
			this.keyTimer.start();
			return;
		}
	}

	// interface ActionListener

	/**
	 * Receives ActionEvents from the internal timer. If the interval has passed
	 * without another KeyEvent, an ActionEvent is broadcast, with the name of this
	 * class as the ActionCommand, and the internal timer is stopped.
	 * 
	 * @param e The action event in question.
	 */
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() == keyTimer) {
			if (System.currentTimeMillis() - this.timeLastFieldTouched > interval) {
				this.keyTimer.stop();
				broadcastEvent(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, this.getClass().getName()));
			}
			return;
		}
	}

	// Action Multicast methods

	/**
	 * Adds an action listener to the list that will be notified by button events
	 * and changes in button state.
	 * 
	 * @param l An action listener to be notified.
	 */
	public void addActionListener(ActionListener l) {
		actionListener = AWTEventMulticaster.add(actionListener, l);
	}

	/**
	 * Removes an action listener from the list that will be notified by button
	 * events and changes in button state.
	 * 
	 * @param l An action listener to be removed.
	 */
	public void removeActionListener(ActionListener l) {
		actionListener = AWTEventMulticaster.remove(actionListener, l);
	}

	/**
	 * Notifies all registered action listeners of a pending Action Event.
	 * 
	 * @param e An action event to be broadcast.
	 */
	protected void broadcastEvent(ActionEvent e) {
		if (actionListener != null) {
			actionListener.actionPerformed(e);
		}
	}

}