/* * SimpleSlider.java - Simple proportional slider applet * * Copyright (c) 1996 Chuck McManis, All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL purposes and without * fee is hereby granted provided that this copyright notice * appears in all copies. * * CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE * SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING * BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS * SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT * OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. */ import java.applet.Applet; import java.awt.*; import java.util.Random; import util.comm.*; /** * This class implements a "scrollbar" type control. When the * scrollbar is dragged it outputs a value between 0 and 2000 to * its output datachannel. */ public class SimpleSlider extends Applet implements Runnable { Thread updater = null; DataChannel rr = null; Image altImage = null; Graphics offscreen = null; final static int SLIDER_RESOLUTION = 2000; final static int HORIZONTAL = 1; final static int VERTICAL = 2; int myWidth; int myHeight; int myLength; int myOrientation; int currentPosition = 500; String myChannel; /** * Return a Color value from the parameter list. Colors are * specified using HEX notation as in NetScape. If there is any * error, return the defaultColor instead. */ Color getColor(String name, Color defaultColor) { String x = getParameter(name); if (x != null) { if (x.startsWith("#")) { int z; try { z = Integer.parseInt(x.substring(1), 16); return( new Color( (z >>> 16) & 0xff, (z >>> 8) & 0xff, (z & 0xff))); } catch (NumberFormatException e) { return defaultColor; } } } return defaultColor; } /** * Initialize the applet and parse various parameters. */ public void init() { myOrientation = HORIZONTAL; bgColor = getColor("bgcolor", Color.lightGray); brColor = bgColor.brighter(); dkColor = bgColor.darker(); String x = getParameter("orientation"); if (x != null) { if (x.compareTo("horizontal") == 0) { myOrientation = HORIZONTAL; myHeight = 21; myWidth = size().width; } else if (x.compareTo("vertical") == 0) { myOrientation = VERTICAL; myWidth = 21; myHeight = size().height; } else System.out.println("Bogus orientation."); } else { myWidth = size().width; myHeight = 21; myOrientation = HORIZONTAL; } myChannel = getParameter("datachannel"); setBackground(bgColor); } public void paint(Graphics g) { update(g); } Color bgColor; Color brColor; Color dkColor; Color pad; /** * This is a raised rectangle drawing method that works the way I want * it to work. */ private void rect3D(Graphics g, int x, int y, int w, int h, int thick, boolean b) { h--; w--; g.setColor(b ? dkColor : brColor); for (int i = 0; i < thick; i++) { g.drawLine(x+i, y+h - i, x+w - i, y+h -i); g.drawLine(x+w - i, y+h - i, x+w - i, y+i); } g.setColor(b ? brColor : dkColor); for (int i = 0; i < thick; i++) { g.drawLine(i+x, y+h - i, x+i, y+i); g.drawLine(i+x, y+i, x+w - i, y+i); } } /** Draw a little box (used for the elevator and end caps */ void drawSquare(Graphics g, int x, int y, int size) { g.setColor(bgColor); g.fillRect(x+2, y+2, size, size); rect3D(g, x+3, y+3, size-2, size-2, 2, true); g.setColor(Color.black); } /** * Draw the left end cap on a horizontal slider. */ void leftCap(Graphics g, int x, int y) { drawSquare(g, x, y, capSize); for (int i = 0; i < 5; i++) { g.drawLine(x+8+i, 10 - i, x+8+i, 10+i); } } void rightCap(Graphics g, int x, int y) { drawSquare(g, x, y, capSize); for (int i = 0; i < 5; i++) { g.drawLine(x+9+i, 10 - (4 - i), x+9+i, 10 + (4 - i)); } } void topCap(Graphics g, int x, int y) { drawSquare(g, x, y, capSize); for (int i = 0; i < 5; i++) { g.drawLine(10 - i, y+8+i, 10 + i, y+8+i); } } void bottomCap(Graphics g, int x, int y) { drawSquare(g, x, y, capSize); for (int i = 0; i < 5; i++) { g.drawLine(10 - (4-i), y+8+i, 10 + (4-i), y+8+i); } } int capSize = 17; int elevatorSize = 17; /** * Paint the elevator on to the slider. Note that the computation for * the minimum and maximum positions overlap the end caps by one pixel. * this is done to leave a single pixel line between the cap and the * elevator when fully extended. */ void elevator(Graphics g) { int minPos, maxPos, pos; minPos = capSize - 1; maxPos = ((myOrientation == HORIZONTAL) ? myWidth : myHeight) - capSize - 2 - elevatorSize; pos = (((maxPos - minPos) * currentPosition) / SLIDER_RESOLUTION) + minPos; if (myOrientation == HORIZONTAL) { drawSquare(g, pos, 0, elevatorSize); } else { drawSquare(g, 0, pos, elevatorSize); } } /** * This paints the slider onto the screen. */ public void update(Graphics g) { if (offscreen == null) { altImage = createImage(myWidth, myHeight); offscreen = altImage.getGraphics(); } offscreen.setColor(bgColor); offscreen.fillRect(0, 0, myWidth, myHeight); offscreen.setColor(Color.black); rect3D(offscreen, 0, 0, myWidth, myHeight, 2, false); if (myOrientation == HORIZONTAL) { leftCap(offscreen, 0, 0); rightCap(offscreen, myWidth - 21, 0); } else { topCap(offscreen, 0, 0); bottomCap(offscreen, 0, myHeight - 21); } elevator(offscreen); g.drawImage(altImage, 0, 0, null); } /** * Create a new thread to monitor our own datachannel. */ public void start() { System.out.println("Slider:: start()"); updater = new Thread(this); rr = DataChannel.getChannel(myChannel, updater, 8); updater.start(); } /** * Release the datachannel and stop our outstanding thread. */ public void stop() { rr.releaseChannel(updater); updater = null; } /** * Implement the Runnable interface, this monitors the data * channel and updates the slider as it changes. We do this * so that other producers on the datachannel can update the * slider's position as well. */ public void run() { System.out.println("Slider thread running..."); while (true) { try { currentPosition = ((Integer) rr.getValue()).intValue(); } catch (DataChannelOverrun e) { System.out.println("Slider: OVERRUN!"); } catch (DataChannelShutdown e) { System.out.println("Slider thread shutting down."); return; } catch (DataChannelException ee) { System.out.println("We're dead."); ee.printStackTrace(); } currentPosition = Math.max(0, currentPosition); currentPosition = Math.min(SLIDER_RESOLUTION, currentPosition); repaint(); } } /* * ****** * Slider User Interface Mechanics * ****** */ /** Calculate a new result value given the x and y coordinates of the mouse */ void newPosition(int x, int y) { int loVal, hiVal, midRange, sample; // zero is when the midpoint of the elevator is against the bottom loVal = 18 + (elevatorSize/2); // lowest value hiVal = ((myOrientation == HORIZONTAL) ? myWidth : myHeight) - 18 - (elevatorSize/2); midRange = hiVal - loVal; sample = Math.max(((myOrientation == HORIZONTAL) ? x : y), loVal); sample = Math.min(sample, hiVal); rr.putValue(new Integer(((sample - loVal) * SLIDER_RESOLUTION) / midRange)); } public boolean mouseDown(Event ev, int x, int y) { int result = currentPosition; if (((myOrientation == HORIZONTAL) && ((x <= 20) && (x >= 0))) || (((myOrientation == VERTICAL) && (y <= 20) && (y >= 0)))) { result -= (SLIDER_RESOLUTION * .025); if (result < 0) result = 0; rr.putValue(new Integer(result)); return true; } if (((myOrientation == HORIZONTAL) && ((x >= (myWidth - 20)) && (x <= myWidth))) || (((myOrientation == VERTICAL) && (y >= (myHeight - 20)) && (y <= myHeight)))) { result += (SLIDER_RESOLUTION * .025); if (result > SLIDER_RESOLUTION) result = SLIDER_RESOLUTION; rr.putValue(new Integer(result)); return true; } return true; } public boolean mouseDrag(Event ev, int x, int y) { // ignore if it is inside the end caps if ((myOrientation == HORIZONTAL) && ((y <= myHeight) && (y >= 0)) && ( ((x <= (capSize + 2)) && (x >= 0)) || ((x >= (myWidth - capSize - 2)) && (x <= myWidth)) ) ) { return true; } if ((myOrientation == VERTICAL) && ((x <= myHeight) && (x >= 0)) && ( ((y <= (capSize + 2)) && (y >= 0)) || ((y >= (myHeight - capSize - 2)) && (y <= myHeight)) ) ) { return true; } newPosition(x, y); return true; } }