1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * ident	"%Z%%M%	%I%	%E% SMI"
24 *
25 * Copyright (c) 1999-2000 by Sun Microsystems, Inc.
26 * All rights reserved.
27 */
28
29    import java.awt.*;
30    import java.awt.event.*;
31
32    /**
33     * Creates a panel with two buttons (+ and - side by side on it). The
34     * panel registers a DCListener with it that gets notified whenever
35     * these butons are clicked. <bold>The buttons may also be kept continously
36     * pressed for faster increments/decrements.</bold>
37     * <para>
38     * On a single click of the button, the listener is notified to
39     * increment/decrement itself by a small amount. When the button is kept
40     * pressed the following notifications are sent out for larger
41     * increments/decrements. (It is up to the listener to decide the
42     * increment/decrement corresponding to large/small.) Moreover, these
43     * notifications will be sent out much faster if the button is kept
44     * pressed.
45     */
46
47    // The panel waits for a period of BIG_SLEEP_TIME before the faster
48    // increments are sent out. They, in turn, are sent out after
49    // intervals of SMALL_SLEEP_TIME. Therfore, an instance of this class
50    // is associated with 2 timers - a longer one that starts off and then
51    // schedules the shorter one. The shorter one keeps scheduling itself
52    // every time it wakes up.
53
54    public class DCPanel extends Panel {
55
56    private Button plusButton;
57    private Button minusButton;
58
59    private DCListener listener = null;
60
61    private Timer bigTimer;
62    private Timer smallTimer;
63
64    private static int BIG_SLEEP_TIME   = 1000;
65    private static int SMALL_SLEEP_TIME = 100;
66
67    private boolean incrementFlag;
68
69    public DCPanel() {
70
71    setLayout(new GridLayout(1, 2));
72
73    bigTimer     = new BigTimer();
74    smallTimer   = new SmallTimer();
75
76    bigTimer.start();
77    smallTimer.start();
78
79    plusButton = new DCButton("+");
80    minusButton = new DCButton("-");
81
82    add(plusButton);
83    add(minusButton);
84
85    }
86
87    /**
88     * Ensures that this component is not brought into focus by
89     * tabbing. This prevents the tab focus from moving in here instead
90     * of going to a text field.
91     * @return false always.
92     */
93    public boolean isFocusTraversable() {
94    return false;
95    }
96
97    /**
98     * Sets the listener for this tab.
99     * @param listener the DCListener that needs to be notified when the
100     * buttons on this panel are pressed.
101     * @return the old listener
102     */
103    public DCListener setListener(DCListener listener) {
104    DCListener oldListener = this.listener;
105    this.listener = listener;
106    return oldListener;
107    }
108
109    /**
110     * Removes the listener when it no longer need to be notified.
111     * @return the old listener
112     */
113    public DCListener removeListener() {
114    return setListener(null);
115    }
116
117    /**
118     * Kicks the times into action. Is called when a button is pressed.
119     */
120    private void startAction() {
121    bigTimer.request();
122    }
123
124    /**
125     * Stops the timers. Is called when a button is released.
126     */
127    private void stopAction() {
128    smallTimer.cancel();
129    bigTimer.cancel();
130    }
131
132    /**
133     * Notifies the listener about whether to increment or decrement and
134     * by how much.
135     * @param bigFlag true if the listener needs to increment/decrement
136     * by a large amount, false otherwise.
137     */
138    private void informListener(boolean bigFlag) {
139    // System.out.println("DCPanel.informListener: " + bigFlag);
140
141        if (listener != null) {
142
143            if (bigFlag) {
144	    // request a big change
145	    if (incrementFlag)
146	        listener.bigIncrement();
147	    else
148	        listener.bigDecrement();
149            } else {
150	    // request a small change
151	    if (incrementFlag)
152	        listener.increment();
153	    else
154	        listener.decrement();
155            }
156
157        }
158
159    } // informListener
160
161
162    // ***********************************************
163    // 	 I N N E R    C L A S S E S   F O L L O W
164    // ***********************************************
165
166    /**
167     * A timer class since java does not have one.
168     */
169    private abstract class Timer extends Thread {
170    private boolean running = false;
171
172    /**
173     * Sleeps till the timer's services are requested using wait() and
174     * notify(). Then it does its task and goes back to sleep. And
175     * loops forever like this.
176     */
177    public void run() {
178        while (true) {
179	try {
180	  synchronized (this) {
181	    running = false;
182	    // Wait till the timer is required
183	    wait();
184	    running = true;
185	  }
186	  doTask();
187	} catch (InterruptedException e) {}
188        } // while loop
189    } // run method
190
191    protected void doTask() {} // bug in java workshop
192
193    /**
194     * Wakes up the timer.
195     */
196    public synchronized void request() {
197        notify();
198    }
199
200    /**
201     * Cancels the timer if it is running.
202     */
203    public void cancel() {
204        if (running) {
205	interrupt();
206        }
207    }
208
209    }// class Timer
210
211    /**
212     * The first stage of timer - is a longer timer. Wait to see if the
213     * user really wants to amek the increments/decrements go by fast.
214     */
215    private class BigTimer extends Timer {
216
217    /**
218     * Sleep for the long amount of time. Then inform the listener
219     * to have a bigIncrement/bigDecrement. After that, your job is
220     * done, schedule the smaller (faster) timer from this point on.
221     */
222    protected void doTask() {
223        try {
224	sleep(BIG_SLEEP_TIME);
225	informListener(true);
226	smallTimer.request();
227        } catch (InterruptedException e) {
228	informListener(false);
229        }
230    }
231
232    } // class BigTimer
233
234
235    /**
236     * The second stage of timers. This timer keeps rescheduling itself
237     * everytime it wakes up. In between this, it sends a notification
238     * to the listener to do a big Increment/Decrement.
239     */
240    private class SmallTimer extends Timer {
241
242    protected void doTask() {
243        try {
244	// loop forever and keep rescheduling yourself
245	while (true) {
246	  sleep(SMALL_SLEEP_TIME);
247	  informListener(true);
248	    }
249        } catch (InterruptedException e) {}
250    } // doTask method
251
252    } // class SmallTimer
253
254    /**
255     * A mouse listener to detect when a button has been
256     * pressed/released. One instance of this is bound to the plus
257     * button and the other instance to the minus button.
258     */
259    private class DCMouseListener extends MouseAdapter {
260    private boolean plusOrMinus;
261
262    /**
263     * Constructor for DCMouseListener.
264     * @param plusOrMinus true if this is a listener for the plus
265     *     button, false if it is for the minus button.
266     */
267    public DCMouseListener(boolean plusOrMinus) {
268        this.plusOrMinus = plusOrMinus;
269    }
270
271    /**
272     * Kicks in when the mouse is pressed.
273     */
274    public void mousePressed(MouseEvent e) {
275        incrementFlag = plusOrMinus;
276        DCPanel.this.startAction();
277    }
278
279    /**
280     * Kicks in when the mouse is released.
281     */
282    public void mouseReleased(MouseEvent e) {
283        incrementFlag = plusOrMinus;
284        DCPanel.this.stopAction();
285        }
286    }
287
288    /**
289     * The button used by this DCPanel.
290     */
291    private class DCButton extends Button {
292    public DCButton(String text) {
293        super(text);
294        if (text.equals("+"))
295           addMouseListener(new DCMouseListener(true));
296        else
297        addMouseListener(new DCMouseListener(false));
298    }
299
300    /**
301     * Make the button non-focus traversable so that it cannot be
302     * tabbed in to.
303     */
304    public boolean isFocusTraversable() {
305        return false;
306    }
307
308    } // DCButton
309
310
311    /**
312     * Test method for DCPanel class to see appearance.
313     */
314    public static void main(String args[]) {
315    Frame f = new Frame("Testing DCPanel");
316    f.add(new DCPanel());
317    f.setBounds(new Rectangle(100, 100, 100, 100));
318    f.setVisible(true);
319    }
320
321}
322