home *** CD-ROM | disk | FTP | other *** search
- /* Pinger: a count-down ('kitchen') timer. */
- /* ------------------------------------------------------------------ */
- /* This is a sample stand-alone Java application with frame, menus, */
- /* and a custom component that shows how to use images and graphics. */
- /* This version needs Java 1.1 (or later) to run. */
- /* */
- /* When the timer ends, an audio clip is played. You'll only hear */
- /* this if: */
- /* -- you have a sound card or equivalent installed and working. */
- /* -- you have speakers, headphone, etc. attached to the sound */
- /* card, in range of your hearing, and switched on if need be. */
- /* -- the file Pinger.au is available (this can be any sound clip */
- /* in .au format, 0.5 seconds or less; a default is supplied). */
- /* You can specify the name of an alternative soundclip file when the */
- /* pinger is started. For example: java Pinger myclip.au */
- /* ------------------------------------------------------------------ */
- /* For the latest version, go to http://www2.hursley.ibm.com/netrexx */
- /* ------------------------------------------------------------------ */
- /* Mike Cowlishaw -- April 1996 - October 1997 */
-
- options binary -- optional, for speed
- import sun.audio. -- for sound clip player
-
- /* ------------------------------------------------------------------ */
- /* Pinger -- a stand-alone application for the Java Virtual Machine */
- /* ------------------------------------------------------------------ */
- class Pinger extends Frame-
- adapter implements WindowListener, ActionListener
-
- properties constant
- defaultclip='Pinger.au' -- default soundclip file
-
- properties private
- -- active dialogs --
- aboutdialog=Dialog
- helpdialog =Dialog
-
- -- menu selection objects --
- menuhelp=MenuItem("Help")
- menuabout=MenuItem("About")
-
- /* The 'main' method is called when this class is started as an application */
- /* The optional argument is the name of the sound clip file to use */
- method main(s=String[]) static
- Pinger("Pinger", Rexx(s)) -- make a Pinger, with title
-
- /* The constructor/initializer for Pinger */
- method Pinger(title, soundclip)
- super(title) -- pass title to Frame
- addWindowListener(this) -- we want window events
-
- -- setup the logical structure of the frame
- createmenus -- set up menubar
- setLayout(BorderLayout()) -- we must have a layout
- timer=PingerComponent() -- make new timer
- add("Center", timer) -- add new timer image
- this.pack -- recalculate the frame
-
- -- position centre-screen
- sizex=200; sizey=175
- screen=Toolkit.getDefaultToolkit.getScreenSize
- setBounds((screen.width-sizex)%2,(screen.height-sizey)%2, sizex, sizey)
-
- this.show -- make us visible
- timer.prime(soundclip) -- prime the audio system
-
- /* Create menus and menu bar */
- method createmenus
- bar=MenuBar() -- create a MenuBar
- -- Help drop-down menu --
- help=Menu("Help") -- create a Menu
- help.add(menuhelp)
- -- help.addSeparator -- [how to add a separator]
- help.add(menuabout)
- bar.add(help) -- add the menu to the MenuBar
- setMenuBar(bar) -- add the menubar to the Frame
- -- say we want the MenuItem events to come to us
- menuhelp.addActionListener(this)
- menuabout.addActionListener(this)
-
- /* Method for handling Button and MenuItem events */
- method actionPerformed(a=ActionEvent)
- source=a.getSource -- the sender object
- if source<=MenuItem then -- .. from a MenuItem
- select label menuitems
- when source=menuhelp then do
- if helpdialog=null then
- helpdialog=PingerDialog(this, PingerDialog.HELP)
- helpdialog.show
- end
- when source=menuabout then do
- -- aboutdialog varies, so we always construct a new one
- aboutdialog=PingerDialog(this, PingerDialog.ABOUT)
- aboutdialog.show
- end
- end menuitems
-
- /* windowClosing -- called when the window is closed.
- We need to handle this to end the program. */
- method windowClosing(e=WindowEvent)
- exit
-
- /* ------------------------------------------------------------------ */
- /* PingerTime -- an object that holds a time, initially 00:00 */
- /* ------------------------------------------------------------------ */
- /* As an object, it can be protected for safe multiple-thread access. */
- class PingerTime
- properties public -- we're just a data receptacle
- mm=0 -- minutes
- ss=0 -- seconds
-
- /* ------------------------------------------------------------------ */
- /* PingerComponent -- a custom Pinger component */
- /* ------------------------------------------------------------------ */
- class PingerComponent extends Canvas- -- Canvas is a drawing area
- adapter implements MouseListener, MouseMotionListener
-
- properties constant
- minmm=0; maxmm=99 -- bounds
- minss=0; maxss=59 -- ..
-
- properties private
- -- Timing properties
- current=PingerTime() -- current time [minutes and seconds]
- timer =PingerTimer -- main timer, null unless started
- spinner=PingerSpinner -- spin-button, non-null if spinning
-
- -- Sound stuff
- bleeper=PingerBleeper -- a bleeper object
-
- -- Drawing and layout properties
- shadow=Image -- shadow image
- width=0; height=0 -- current picture dimensions
- draw=Graphics -- 'context' where we can draw
- background=Color.yellow -- guess what
-
- badsize =boolean 1 -- too small
- resizing=boolean 0 -- we are preparing new shadow
- spinbutw=0 -- spin button width
- spinbuth=10 -- spin button height [fixed]
- controlw=0 -- control button width
- controlh=24 -- control button height [fixed]
- gap=5 -- margin [fixed]
- timerect =Rectangle -- where time will go
- timefont ="TimesRoman" -- font face for time
- timepoints=0 -- pointsize for time
- timeweight=Font.BOLD -- weight for time
- timecol =Color.black -- color for time
- timedi =boolean -- time needs redraw
- buttons =6 -- number of buttons
- but=Rectangle[buttons] -- button rectangles
- -- 0/1 are Ups; 2/3 are Downs
- -- 4/5 are reset/start
- bcol=Color[buttons] -- button colors
- btext=String[buttons] -- button labels
- ben=boolean[buttons] -- button enabled
- bup=boolean[buttons] -- button up
- bdi=boolean[buttons] -- button dirty (needs redraw)
- over=-1 -- button we are over [-1 is none]
-
- /* Construct the component */
- method PingerComponent
- super()
- addMouseListener(this) -- we want mouse events ..
- addMouseMotionListener(this) -- .. and mouse movements
-
- /* update -- called in reponse to repaint() */
- -- We update our off-screen image here, to avoid embarrassment to the
- -- AWT caused by asynchronous calls to paint()
- method update(g=Graphics)
- -- redraw areas that have changed since last repaint. We draw into
- -- an off-screen image, later copied to the screen in a single call.
- loop i=0 to 5 -- redraw dirty buttons
- if bdi[i] then do
- drawbutton(i)
- bdi[i]=0
- end
- end i
- if timedi then do
- drawtime(timerect) -- redraw the time
- timedi=0
- end
- paint(g)
-
- /* paint -- called when the window needs to be resized or redrawn */
- method paint(g=Graphics)
- if resizing then return -- ignore paints while recalculating
- if shadow=null | width<>getSize.width | height<>getSize.height then do
- resizing=1
- newsize
- return
- end
- g.drawImage(shadow, 0, 0, this) -- copy to screen
-
- /* newsize -- here when a new size detected */
- method newsize
- width=getSize.width; height=getSize.height
-
- -- The very first time that we get here, there won't be an existing
- -- Image, so we have to create it. It cannot be set up in advance,
- -- as there's no physical context earlier.
- shadow=createImage(width, height) -- need new image
- draw=shadow.getGraphics -- for graphics
- if height<gap*6+spinbuth*2+controlh*3/2 | width<50
- then do label toosmall
- badsize=1
- text="Window too small"
- draw.setColor(Color.white) -- warner
- draw.fillRect(0, 0, width-1, height-1)
- draw.setFont(Font("Helvetica", Font.PLAIN, 10))
- fm=draw.getFontMetrics -- find out about font
- w=fm.stringWidth(text) -- actual width
- x=(width-w)%2 -- offset
- y=(height-fm.getHeight)%2 -- raw offset
- y=y+fm.getAscent+fm.getLeading -- adjust for whitespace
- draw.setColor(Color.black)
- draw.drawString(text, x, y)
- end toosmall
- else do label bigenough
- badsize=0
- draw.setColor(background) -- ok
- draw.fillRect(0, 0, width-1, height-1)
- spinbutw=(width-gap-gap)*4%9 -- button sizes
- controlw=(width-gap*3)%2
-
- -- Calculate button positions
- but[0]=Rectangle(gap, gap, spinbutw, spinbuth)
- but[1]=Rectangle(width-gap-spinbutw, gap, spinbutw, spinbuth)
- lowspins=height-gap*3-1-controlh-spinbuth
- but[2]=Rectangle(gap, lowspins, spinbutw, spinbuth)
- but[3]=Rectangle(width-gap-spinbutw, lowspins, spinbutw, spinbuth)
- lowconts=height-gap-controlh
- but[4]=Rectangle(gap, lowconts, controlw, controlh)
- but[5]=Rectangle(width-gap-controlw, lowconts, controlw, controlh)
-
- -- Calculate time position and pointsize
- timerect=Rectangle(gap, gap*2+spinbuth,-
- width-gap*2, height-controlh-spinbuth*2-gap*6-1)
- -- use 24 point for measuring, as an exact font almost certainly there
- draw.setFont(Font(timefont, timeweight, 24)) -- measure font
- fm=draw.getFontMetrics -- get metrics
- x0=fm.stringWidth('0')
- xc=fm.stringWidth(':')
- y0=fm.getAscent
- xpoints=timerect.width*24/(x0*4+xc*3)
- ypoints=timerect.height*24/y0
- if xpoints<ypoints then timepoints=int xpoints
- else timepoints=int ypoints
-
- -- Draw separator
- draw.setColor(Color.lightGray)
- sepy=lowspins+gap+spinbuth+1
- draw.drawLine(0, sepy, width-1, sepy)
- end bigenough
-
- -- Set default state for buttons --
- loop i=0 to buttons-1
- select
- when i<4 then do
- btext[i]=""; bcol[i]=Color.orange.darker
- end
- when i=4 then do
- btext[i]="Reset"; bcol[i]=Color.cyan.darker
- end
- when i=5 then do
- btext[i]="Start"; bcol[i]=Color.cyan.darker
- end
- end
- bup[i]=1; ben[i]=1; bdi[i]=1
- end
-
- -- Update the time and buttons
- resizing=0 -- OK to use shadow, now
- newtime -- this will refresh the display
-
- /* Draw the time */
- method drawtime(where=Rectangle)
- if badsize then return
- draw.setColor(background)
- draw.fillRect(where.x, where.y, where.width-1, where.height-1)
- draw.setFont(Font(timefont, timeweight, timepoints ))
- fm=draw.getFontMetrics -- find out about font
- y=(where.height-fm.getHeight)%2 -- raw offset
- y=y+fm.getAscent+fm.getLeading -- adjust for whitespace
-
- do protect current -- snapshot
- mmtext=String current.mm
- sstext=String current.ss
- end
- if sstext.length=1 then sstext='0'sstext
- ctext=':'
- draw.setColor(timecol)
- -- centre the colon
- cw=fm.stringWidth(ctext)
- cx=(where.width-cw)%2
- draw.drawString(ctext, where.x+cx, where.y+y)
- -- right-align minutes
- mw=fm.stringWidth(mmtext)
- mx=spinbutw-mw -- shouldn't really use spinbutw
- draw.drawString(mmtext, where.x+mx, where.y+y)
- -- left-align seconds
- -- sw=fm.stringWidth(sstext)
- sx=where.width-spinbutw
- draw.drawString(sstext, where.x+sx, where.y+y)
-
- /* Draw a button, up or down, filling with colour */
- method drawbutton(b=int)
- if badsize then return
- text=btext[b]; where=but[b]
- if ben[b] then do -- enabled
- col=bcol[b]; up=bup[b]
- if over=b then col=col.brighter -- highlight if active
- end
- else do -- not enabled
- col=Color.gray; up=1
- end
- draw.setColor(col)
- draw.fillRect(where.x, where.y, where.width-1, where.height-1)
- draw.setColor(Color.gray)
- -- remember, draw3DRect in JDK 1.0 draws too large
- draw.draw3DRect(where.x, where.y, where.width-1, where.height-1, up)
- draw.draw3DRect(where.x+1, where.y+1, where.width-3, where.height-3, up)
- draw.setColor(Color.gray.darker)
- draw.drawLine(where.x, where.y, where.x+1, where.y+1) -- add definition
- if text.length=0 then return
- -- have some text to add
- draw.setFont(Font("Helvetica", Font.PLAIN, 15)) -- choose font
- fm=draw.getFontMetrics -- find out about font
- w=fm.stringWidth(text) -- actual width
- x=(where.width-w)%2 -- offset
- if x<0 then return -- won't fit
- y=(where.height-fm.getHeight)%2 -- raw offset
- y=y+fm.getAscent+fm.getLeading -- adjust for whitespace
- if y<0 then return -- won't fit
- draw.setColor(Color.black)
- draw.drawString(text, where.x+x, where.y+y)
-
- /* Returns number of button we are over, or -1 if none */
- method hit(m=MouseEvent) returns int
- if badsize | m=null then return -1 -- no buttons or no event
- x=m.getX; y=m.getY
- loop b=0 to buttons-1
- if x>=but[b].x
- then if x<but[b].x+but[b].width
- then if y>=but[b].y
- then if y<but[b].y+but[b].height then return b
- end b
- return -1
-
- /* The mouse is moving over our image */
- method mouseMoved(m=MouseEvent)
- new=hit(m)
- if new=over then return -- no change
- /* change of button (or move off button) */
- if spinner<>null then do; spinner.halt; spinner=null; end
- old=over; over=new
- -- indicate button state change
- if old >=0 then do; bup[old]=1; bdi[old]=1; end
- if over>=0 then do; bup[over]=1; bdi[over]=1; end
- this.repaint
-
- /* Drag is treated just like Move (redraws button if leaves button) */
- method mouseDragged(m=MouseEvent)
- mouseMoved(m)
-
- /* mouseReleased redraws the button, if we're over one */
- method mouseReleased(m=MouseEvent)
- if spinner<>null then do; spinner.halt; spinner=null; end
- new=hit(m)
- if new=-1 then return -- not over a button
- over=new
- bup[over]=1; bdi[over]=1
- this.repaint
-
- /* mousePressed takes an action then redraws the button, if we're over one */
- method mousePressed(m=MouseEvent)
- new=hit(m)
- if new=-1 then return -- not over a button
- over=new
- bup[over]=0; bdi[over]=1
- if spinner<>null then do; spinner.halt; spinner=null; end -- just in case
-
- -- [from here, all paths should initiate a repaint eventually]
- if \ben[over] then this.repaint -- not enabled, just repaint
- else select label action -- enabled, so take action
- when over=0 then spinner=PingerSpinner(this, 1, +1)
- when over=1 then spinner=PingerSpinner(this, 0, +1)
- when over=2 then spinner=PingerSpinner(this, 1, -1)
- when over=3 then spinner=PingerSpinner(this, 0, -1)
- when over=4 then do -- reset
- if timer<>null then stoptimer -- stop the timer
- do protect current
- current.mm=0; current.ss=0 -- back to zero
- -- [or could simply make a new PingerTime object]
- end
- newtime
- end
- when over=5 then do
- if timer<>null then stoptimer -- Stop or Beep state
- else timer=PingerTimer(this) -- Start state
- settextStart
- this.repaint
- end
- end action
-
- /* mouseEntered and mouseExited call our mouseMoved to ensure known state */
- method mouseEntered(m=MouseEvent); mouseMoved(m)
- method mouseExited(m=MouseEvent); mouseMoved(m)
-
- /* Increase or decrease minutes and seconds numbers
- Arg1 is increment (+1 or -1)
- returns 1 if result is 00:00
- */
- method bumpmm(inc=int) returns boolean
- do protect current
- current.mm=current.mm+inc
- if current.mm>maxmm then current.mm=maxmm
- else if current.mm<minmm then current.mm=minmm
- end
- return newtime
-
- method bumpss(inc=int) returns boolean
- do protect current
- current.ss=current.ss+inc
- if current.ss>maxss then /* carry */ do
- current.ss=minss
- current.mm=current.mm+1
- if current.mm>maxmm then do; current.mm=maxmm; current.ss=maxss; end
- end
- if current.ss<minss then /* borrow */ do
- current.ss=maxss
- current.mm=current.mm-1
- if current.mm<minmm then do; current.mm=minmm; current.ss=minss; end
- end
- end
- return newtime
-
- /* we have a new time -- update buttons, etc.
- returns 1 if the current time is 00:00 */
- method newtime returns boolean
- -- set buttons enablement and state
- ben[0]=1; ben[1]=1
- do protect current
- if current.mm=maxmm then do
- ben[0]=0
- if current.ss=maxss then ben[1]=0
- end
- atzero=current.mm=0 & current.ss=0
- end
- ben[2]=\atzero; ben[3]=ben[2]
- ben[4]=\atzero | timer<>null; ben[5]=ben[4]
- settextStart
- loop i=0 to 5; bdi[i]=1; end -- redraw all buttons
- timedi=1 -- redraw the time
- this.repaint -- update display
- return atzero
-
- /* set the text for button 5 */
- method settextStart
- do protect current
- atzero=current.mm=0 & current.ss=0 -- take safe snapshot
- end
- if atzero then do
- if timer=null then btext[5]="Time?"
- else btext[5]="Beep!"
- end
- else /* nonzero */ do
- if timer=null then btext[5]="Start"
- else btext[5]="Stop"
- end
- bdi[5]=1 -- button needs redraw
-
- /* prime -- called to initialize the sound system */
- method prime(filename)
- if filename='' then filename=Pinger.defaultclip
- bleeper=PingerBleeper(filename) -- make the bleeper object
-
- /* ping -- sound the bleeper once */
- method ping
- bleeper.bleep
-
- /* stoptimer -- called when the timer is to be stopped */
- method stoptimer
- -- closing: stop the timer last, because we may have been called from it
- savetimer=timer; timer=null
- newtime -- update display
- if savetimer<>null then do; savetimer.halt; savetimer=null; end
- return
-
- /* ------------------------------------------------------------------ */
- /* PingerSpinner -- a class for the spinner Thread */
- /* ------------------------------------------------------------------ */
- class PingerSpinner extends Thread
- count=0 -- ticks
- owner=PingerComponent -- who we work for
- mins=boolean -- 1 if a minutes spinner
- inc=int -- up or down
- spin=boolean 1 -- spin allowed
-
- properties constant
- slow=400 -- initial delay time [ms]
- fast=30 -- fastest delay time [ms]
- over=6 -- how many ticks to accelerate over
-
- /* Construct with
- Arg1 is parent object
- Arg2 is 1=minute, 0=seconds
- Arg3 is increment (+1 or -1)
- */
- method PingerSpinner(newowner=PingerComponent, newmins=boolean, newinc=int)
- owner=newowner; mins=newmins; inc=newinc
- this.start
-
- /* This runs so long as the button is held down (after which halt is
- set), bumping either minutes or seconds, at intervals */
- method run
- loop until \spin -- always bump once
- if mins then owner.bumpmm(inc)
- else owner.bumpss(inc)
- count=count+1
- if count<=over then wait=fast+(over+1-count)*(slow-fast)%over
- else wait=fast
- sleep(wait)
- catch InterruptedException
- return
- end
-
- /* This method is called to request that the spin stop. We use this
- pending request, as a direct call to stop could leave the call to
- owner unfinished. */
- method halt
- spin=0
-
- /* ------------------------------------------------------------------ */
- /* PingerTimer -- a main pinger timer Thread */
- /* ------------------------------------------------------------------ */
- /* This thread must update the pinger in real time, so it keeps */
- /* an eye on 'wall-clock' time so the timer cannot drift. Any delays */
- /* will be corrected as soon as the thread gets control. */
- /* */
- /* When done, it notifies its owner with calls to the 'ping' method, */
- /* for each bleep, and then to the 'stoptimer' method. */
-
- class PingerTimer extends Thread
- owner=PingerComponent -- who we work for
- started=System.currentTimeMillis -- timestamp of when we were born
- down=boolean 1 -- count down while 1
-
- /* Construct with
- Arg1 is parent object
- */
- method PingerTimer(newowner=PingerComponent)
- owner=newowner
- this.setPriority(Thread.MAX_PRIORITY) -- time matters, here
- this.start -- off we go
-
- /* This runs until stopped, or we reach 0 */
- method run
- loop millisecs=started+1000 by 1000 -- when next tick
- -- calculate how long to next second
- wait=millisecs-System.currentTimeMillis
- if wait<=0 then iterate -- badly behind
- sleep(wait)
- if \down then leave -- halt request
- if owner.bumpss(-1) then leave -- decrement and quit if 0
- catch InterruptedException
- return
- end
-
- if down then loop for 3
- -- reach here iff countdown not halted
- owner.ping
- sleep(600)
- catch InterruptedException
- nop
- end
-
- owner.stoptimer -- definitely done
-
- /* This method is called to request that the count stop. We use this
- pending request, as a direct call to stop could leave the bumpss
- call to owner unfinished. */
- method halt
- down=0
-
- /* ------------------------------------------------------------------ */
- /* PingerBleeper -- the sound maker */
- /* ------------------------------------------------------------------ */
- class PingerBleeper
- soundfile=File -- File of The Sound
- okfile=boolean 0 -- 1 if the file is good
- input=InputStream -- .. as a stream
- audio=AudioStream -- .. as audio
-
- /* Constructor.
- Arg1 is beep file (.au) to play
- */
- method PingerBleeper(filename)
- -- first ensure that the file exists
- soundfile=File(filename)
- if \soundfile.exists | \soundfile.isFile then do
- say 'The audio file "'filename'" could not be found'
- return
- end
- okfile=1 -- file is plausible
-
- -- now prime the audio subsystem by sending it a 0 byte. This loads
- -- the audio software so the final bleeps will be prompt (starting
- -- up the audio subsystem can take seconds on some platforms).
- do
- input=ByteArrayInputStream([byte 0])
- -- next lines prime the audio subsystem, by trying to play an
- -- invalid stream
- audio=AudioStream(input)
- AudioPlayer.player.start(audio)
- catch IOException
- -- we expect to get here
- end
-
- /* bleep once */
- method bleep
- if \okfile then return -- no good file to play
- do
- -- set the input stream each time ('rewind')
- input=FileInputStream(soundfile)
- audio=AudioStream(input)
- catch IOException
- say "Could not play file '"soundfile"'"
- okfile=0 -- don't even try next time
- end
- AudioPlayer.player.start(audio)
-
- /* ------------------------------------------------------------------ */
- /* PingerDialog -- tell about us */
- /* ------------------------------------------------------------------ */
- class PingerDialog extends Dialog adapter implements WindowListener
- owner=Pinger -- who we work for
- form=int -- form of text
-
- -- Drawing and layout properties
- shadow=Image -- shadow image
- d=Graphics -- where we can draw
- width=0; height=0 -- current picture dimensions
-
- properties constant
- HELP=0 -- build Help text
- ABOUT=1 -- build About text
-
- /* Construct a general dialog for Pinger */
- method PingerDialog(newowner=Pinger, newform=int)
- super(newowner, title(newform), 1)
- owner=newowner; form=newform
- addWindowListener(this) -- we want window events
-
- -- position centre-screen
- sizex=400; sizey=300 -- TV shape
- screen=Toolkit.getDefaultToolkit.getScreenSize
- this.addNotify -- ensure peer exists
- setBounds((screen.width-sizex)%2,(screen.height-sizey)%2, sizex, sizey)
- setResizable(0) -- fixed size panel, please
- -- display (show) will be triggered by creator
-
- /* provide a title string, on demand */
- method title(titleform=int) static returns String
- if titleform=HELP then return "Help for Pinger"
- return "About Pinger"
-
- /* update -- overridden, because we set background */
- method update(g=Graphics); paint(g)
-
- /* paint -- called when the window needs to be redrawn */
- method paint(g=Graphics)
- -- we do not need to protect the image, here, as there should be
- -- only one. However, a race condition is possible, so we check for
- -- an uninitialized 'shadow'.
- if width<>getSize.width | height<>getSize.height then newsize
- else if shadow<>null then g.drawImage(shadow, 0, 0, this)
-
- /* newsize -- here when a new size detected; should be called once */
- method newsize
- /* make a new image to draw in, if needed */
- width=getSize.width; height=getSize.height
- shadow=this.createImage(width, height) -- make image
- d=shadow.getGraphics -- context to draw
- d.setColor(Color.white) -- background
- d.fillRect(0, 0, width, height) -- ..
-
- d.setFont(Font("TimesRoman", Font.PLAIN, 20)) -- measure font
- fm=d.getFontMetrics -- get metrics
- h=fm.getHeight+2 -- +2 pels leading
- y=(height-h*5)%3 -- offset
-
- -- Add some text, etc. Any graphics allowed, here.
- if form=ABOUT then do
- secs=int Date().getTime//7000
- d.setColor(Color.getHSBColor(secs/6999, 1, 0.5))
- d.drawString("Simple 'Pinger' application", 20, y+h)
- d.drawString("For the NetRexx source, see:", 20, y+h*2)
- d.drawString(" http://www2.hursley.ibm.com/netrexx/", 20, y+h*3)
- d.drawString("Mike Cowlishaw, 1996-1997 ", 20, y+h*4)
- end
- else /* Help */ do
- d.setColor(Color.blue.darker)
- d.drawString("This is a 'kitchen timer' application", 20, y+h)
- d.drawString("Set the time using the unmarked buttons.", 20, y+h*2)
- d.drawString("Press 'Start' to start countdown.", 20, y+h*3)
- d.drawString("Press 'Reset' to zero timer.", 20, y+h*4)
- end
- d.setFont(Font("Helvetica", Font.PLAIN, 12))
- d.setColor(Color.black)
- d.drawString("Compiled with" version, 20, y+h*5)
-
- this.repaint
-
- /* windowClosing -- called when the window is closed.
- We need to handle this to end the program. */
- method windowClosing(e=WindowEvent)
- owner.requestFocus
- this.dispose()
- return
-
-