'===========================================================================
' Subject: ANALOG/DIGITAL CLOCK DISPLAY       Date: 04-01-97 (12:00)       
'  Author: Gary N. Wilkerson, Jr.             Code: QB, QBasic, PDS        
'  Origin: comp.lang.basic.misc             Packet: DATETIME.ABC
'===========================================================================
' Analog/Digital Clock
' by Gary N. Wilkerson Jr.

' Constants
CONST ClockH = 130
CONST ClockV = 120
CONST Left = 1
CONST Right = 3
CONST TRUE = -1
CONST FALSE = NOT TRUE
CONST TwoPI# = 2 * 3.141592654#

' Sub and Function modules
DECLARE SUB OnOff (cond)
DECLARE SUB Box (cond)
DECLARE SUB Center (row, text$)
DECLARE SUB KeyPause ()
DECLARE SUB FuncKeyOn ()
DECLARE SUB FuncKeyOff ()
DECLARE SUB RunClock ()
DECLARE SUB DrawScreen ()
DECLARE SUB ClockBells ()
DECLARE SUB TimeDate (row)
DECLARE SUB GetCurrTime (Hour, Min, Sec, a$)
DECLARE SUB ScreenSaver ()
DECLARE SUB DisplayAlarmStatus ()
DECLARE SUB DisplayChimeStatus ()
DECLARE SUB DisplayClockStatus ()
DECLARE FUNCTION Hplot (Length, ClockPos)
DECLARE FUNCTION Vplot (Length, ClockPos)

' Converts clock face position into Radiens
DEF FnRad (x) = -(x - 30) * TwoPI# / 60

' Set up the Function Key traps
KEY OFF
ON KEY(1) GOSUB SetTime
ON KEY(2) GOSUB SetDate
ON KEY(3) GOSUB SetAlarm
ON KEY(4) GOSUB ToggleAlarm
ON KEY(5) GOSUB ToggleChime
ON KEY(6) GOSUB Switch12or24

' Global variables
DIM SHARED Military, Chime, Alarm, Alarm$, AlarmOn, ScreenSave
Military = FALSE: Chime = TRUE: Alarm = FALSE: Alarm$ = "12:00:00"
AlarmOn = FALSE: ScreenSave = FALSE

' The main routine
    DrawScreen
    FuncKeyOn
    RunClock
    SYSTEM

' Function Key traps
SetTime:
    FuncKeyOff
    RESTORE TimeSet
    Box Left
    LOCATE 13, 26
    LINE INPUT a$   ' Get new time
    a$ = LTRIM$(a$): a$ = RTRIM$(a$)        ' Remove excess spaces
    IF a$ <> "" THEN
        ON ERROR GOTO TimeError
        TIME$ = a$      ' Reset system time
        ON ERROR GOTO 0
    END IF
    DrawScreen
    FuncKeyOn
    RETURN

TimeSet:
DATA Enter new time,"In 24 hour [##:##:##] format"," ",ZZ

TimeError:
    Center 19, "   Invalid Time   "
    KeyPause
    RESUME NEXT

SetDate:
    FuncKeyOff
    RESTORE DateSet
    Box Left
    LOCATE 13, 30
    LINE INPUT a$
    a$ = LTRIM$(a$): a$ = RTRIM$(a$)
    IF a$ <> "" THEN
        ON ERROR GOTO DateError
        DATE$ = a$      ' Reset System Date
        ON ERROR GOTO 0
    END IF
    DrawScreen
    FuncKeyOn
    RETURN

DateSet:
DATA Enter new date,"In ##-##-#### format"," ",ZZ

DateError:
    Center 19, "   Invalid Date   "
    KeyPause
    RESUME NEXT

SetAlarm:
    FuncKeyOff
    RESTORE AlarmSet
    Box Left
    LOCATE 13, 26
    LINE INPUT a$
    a$ = LTRIM$(a$): a$ = RTRIM$(a$)
    IF a$ <> "" THEN
        ' Sets the alarm
        ON ERROR GOTO AlarmError
        b$ = TIME$
        Alarm$ = a$
        TIME$ = a$
        TIME$ = b$
        ON ERROR GOTO 0
    END IF
    ' Set alarm to system format
    a$ = TIME$
    TIME$ = Alarm$
    Alarm$ = TIME$
    TIME$ = a$
    DrawScreen
    FuncKeyOn
    RETURN

AlarmSet:
DATA Enter time for alarm,"In 24 hour [##:##:##] format"," ",ZZ

AlarmError:
    Center 20, "   Invalid Time   "
    KeyPause
    Alarm$ = "12:00:00"
    RESUME NEXT

ToggleAlarm:
    IF AlarmOn THEN AlarmOn = FALSE: RETURN ' If alarm is active, shut it off
    Alarm = NOT Alarm
    IF NOT ScreenSave THEN DisplayAlarmStatus
    RETURN

ToggleChime:
    Chime = NOT Chime
    IF NOT ScreenSave THEN DisplayChimeStatus
    RETURN

Switch12or24:
    Military = NOT Military
    IF NOT ScreenSave THEN DisplayClockStatus
    RETURN

ClockMenu:
DATA [F1] -- Sets Time,[F2] -- Sets Date,[F3] -- Sets Alarm
DATA [F4] -- Toggles Alarm,[F5] -- Toggles Chime,[F6] -- Switch 12/24 hour

' Box
' Draws a box around the DATA for the READ statements until it reaches ZZ
SUB Box (cond)
DIM temp$(5)

u = 0   ' Variable <u> will equal length of longest string in BOX

' Variable <t> will equal the number of strings in BOX
FOR t = 0 TO 15
        READ temp$(t)
        IF temp$(t) = "ZZ" THEN EXIT FOR
        IF LEN(temp$(t)) > u THEN u = LEN(temp$(t))
NEXT t: IF t = 0 THEN EXIT SUB

y = 39 - u / 2: LOCATE 11 - t / 2, y    ' Center the BOX on screen
COLOR 14, 0
PRINT "É"; STRING$(u, 205); "»"         ' Top border of BOX
FOR x = 0 TO t - 1
        a = cond        ' Set variable <a> to current condition
        LOCATE CSRLIN, y
        COLOR 14, 0
        IF temp$(x) = "Í" THEN
                PRINT "Ì"; STRING$(u, 205); "¹"         ' BOX concecrator
        ELSE
                PRINT "º"; SPACE$(u); "º";      ' BOX side borders
                ' First character of string may override condition
                SELECT CASE LEFT$(temp$(x), 1)
                        CASE "®"
                                a = Left: temp$(x) = MID$(temp$(x), 2)
                        CASE ""
                                a = 2: temp$(x) = MID$(temp$(x), 2)
                        CASE "¯"
                                a = Right: temp$(x) = MID$(temp$(x), 2)
                END SELECT
                ' Justify according to condition
                SELECT CASE a
                        CASE Left       ' Left justify
                                LOCATE CSRLIN, y + 1
                        CASE 2          ' Center
                                LOCATE CSRLIN, 40 - LEN(temp$(x)) / 2
                        CASE Right      ' Right justify
                                LOCATE CSRLIN, y + u + 1 - LEN(temp$(x))
                END SELECT
                COLOR 15, 0
                PRINT temp$(x);
                LOCATE CSRLIN + 1, y
        END IF
NEXT x
COLOR 14, 0
PRINT "È"; STRING$(u, 205); "¼"         ' BOX bottom border
COLOR 15, 0
END SUB

' Center
' Centers the text$ at a given row
SUB Center (row, text$)
LOCATE row, 40 - LEN(text$) / 2
PRINT text$
END SUB

'ClockBells
' Sounds the Alarm and Chime at the correct time
SUB ClockBells
' Hourly Chime
IF Chime THEN
        IF RIGHT$(TIME$, 5) = "00:00" THEN
                PLAY "MBo3L8ED+ED+Eo2Bo3DCL2o2A"
        END IF
END IF
IF Alarm THEN IF TIME$ = Alarm$ THEN AlarmOn = 16

' Clock's Alarm
IF AlarmOn THEN
        AlarmOn = AlarmOn - 1
        PLAY "MBo4L16BBBB"
END IF
END SUB

SUB DisplayAlarmStatus
    COLOR 14
    LOCATE 14, 40: PRINT "Alarm"; : OnOff Alarm
END SUB

SUB DisplayChimeStatus
    COLOR 11
    LOCATE 11, 40: IF Chime THEN PRINT "{Hourly Chime}" ELSE PRINT SPACE$(14)
END SUB

SUB DisplayClockStatus
    COLOR 9
    LOCATE 16, 40: PRINT "Time reads at ";
    IF Military THEN PRINT "24";  ELSE PRINT ; "12";
    PRINT " hour mode"
END SUB

' DrawScreen
' Draws the screen with the clock on it
SUB DrawScreen
    'Initialize Screen
    IF ScreenSave THEN CLS : EXIT SUB
    SCREEN 9
    COLOR 15, 0
    CLS

    ' Draw Border
    PRINT "É"; STRING$(78, 205); "»"
    FOR Count = 1 TO 22
        PRINT "º"; TAB(80); "º";
    NEXT Count
    PRINT "È"; STRING$(78, 205); "¼";

    'Draw clock face
    CIRCLE (130, 120), 110, 15, , , .87
    CIRCLE STEP(0, 0), 115, 15, , , .87
    FOR Count = 0 TO 59
        LINE (Hplot(105, Count), Vplot(90, Count))-(Hplot(110, Count), Vplot(95, Count)), 15
        IF Count MOD 5 = 0 THEN
            LINE -(Hplot(100, Count), Vplot(85, Count)), 11
        END IF
    NEXT Count

    COLOR 4
    RESTORE ClockMenu
    FOR Count = 4 TO 9
        LOCATE Count, 40
        READ a$: PRINT a$
    NEXT Count

    DisplayChimeStatus
    COLOR 14
    LOCATE 13, 40: PRINT "Alarm Set At "; Alarm$
    DisplayAlarmStatus
    DisplayClockStatus

    COLOR 13
    Center 20, "Analog / Digital Clock"
    Center 21, "1994 by Gary N. Wilkerson Jr."
    Center 22, "Press [ESC] to quit"
END SUB

' FuncKeyOff
' Turns all the Function Key traps OFF
SUB FuncKeyOff
FOR Count = 1 TO 6
        KEY(Count) OFF
NEXT Count
END SUB

'FuncKeyOn
' Turns all Function Key traps ON
SUB FuncKeyOn
FOR Count = 1 TO 6
        KEY(Count) ON
NEXT Count
END SUB

' GetCurrTime
' Gets the current TIME from the system TIME
SUB GetCurrTime (Hour, Min, Sec, a$)
a$ = TIME$
Min = VAL(MID$(a$, 4, 2)): Sec = VAL(RIGHT$(a$, 2))
Hour = (VAL(LEFT$(a$, 2)) MOD 12) * 5 + (Min / 12)
END SUB

' Hplot
' Converts the hand length and clock face position
' into an X coordinate
FUNCTION Hplot (Length, ClockPos)
Hplot = Length * SIN(FnRad(ClockPos)) + ClockH
END FUNCTION

' KeyPause
' Suspends program activity until key is pressed
SUB KeyPause
Center CSRLIN, "  -->Press any key to continue<--  "
WHILE INKEY$ <> "": WEND        ' Clear keyboard
SLEEP
END SUB

' OnOff
' PRINTs ON if cond is TRUE, OFF is cond is FALSE
SUB OnOff (cond)
PRINT " -- ";
IF cond THEN
        PRINT " [ON]"
ELSE
        PRINT "[OFF]"
END IF
END SUB

' This is the main routine for the
' graphics style analog / digital clock
SUB RunClock
    DO UNTIL INKEY$ = CHR$(27)
        GetCurrTime Hour, Min, Sec, a$

        ' Second Hand
        LINE (Hplot(90, Sec), Vplot(75, Sec))-(ClockH, ClockV), 4
        ' Minute Hand
        LINE -(Hplot(80, Min), Vplot(65, Min)), 14
        ' Hour Hand
        LINE (ClockH, ClockV)-(Hplot(50, Hour), Vplot(35, Hour)), 3

        TimeDate 18
        ClockBells

        ' At a 5 minute interval, activate ScreenSaver
        IF Min MOD 5 = 0 AND Sec = 0 THEN ScreenSaver

        ' Pause for a second to pass
        WHILE a$ = TIME$: WEND

        ' Erase the hands
        LINE (Hplot(90, Sec), Vplot(75, Sec))-(ClockH, ClockV), 0
        LINE -(Hplot(80, Min), Vplot(65, Min)), 0
        LINE (ClockH, ClockV)-(Hplot(50, Hour), Vplot(35, Hour)), 0
    LOOP
END SUB

' ScreenSaver
' Deletes the graphics clock and display a small
' moving text clock to save the screen
SUB ScreenSaver
    ScreenSave = TRUE
    RowPos = 1      'Beginning clock position
    SCREEN 0: COLOR 15, 0: CLS

    DO UNTIL INKEY$ = " "
        GetCurrTime Hour, Min, Sec, a$

        ' At every 2 minute interval, move the text clock
        IF Min MOD 2 = 0 AND Sec = 0 THEN RowPos = (RowPos + 4) MOD 16: CLS

        Center RowPos, "Clock has been erased to preserve screen"
        Center RowPos + 1, "Press <Space Bar> to restore clock"
        TimeDate RowPos + 3

        ClockBells
        WHILE a$ = TIME$: WEND
    LOOP

    ScreenSave = FALSE
    DrawScreen
END SUB

' TimeDate
' Writes the digital Date and Time for the regular and Screen Saver screens
SUB TimeDate (row)
     COLOR 15
     ' Display the system date
     LOCATE row, 10: PRINT DATE$;

     ' Get and display the system time according to display mode
     a$ = TIME$
     LOCATE row, 60
     IF Military THEN
        ' 24 hour display mode
        PRINT a$
     ELSE
          ' 12 hour display mode
          y = VAL(LEFT$(a$, 2))
          ' Let's determine the hour
          IF y >= 12 THEN
               ' The hour is noon or later
               b$ = " pm"
               IF y > 12 THEN y = y - 12
          ELSE
               ' Hour is morning
               b$ = " am"
               IF y = 0 THEN y = 12     'Burning the midnight oil!
          END IF
          PRINT STR$(y); RIGHT$(a$, 6); b$;
     END IF
END SUB

' Vplot
' Converts the hand length and clock face
' position into a Y coordinate
FUNCTION Vplot (Length, ClockPos)
Vplot = Length * COS(FnRad(ClockPos)) + ClockV
END FUNCTION
