home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Pascal / Applications / CornerClock 1.5 / CornerClock.p < prev    next >
Encoding:
Text File  |  1996-02-29  |  13.8 KB  |  631 lines  |  [TEXT/CWIE]

  1. CornerClock is a pascal program based upon Masafumi Ueda's MBarClock program.
  2.  
  3. MBarClock was a program which displayed the time or date in the menu bar to the left
  4. of the Apple menu.  CornerClock uses that same feature and adds sound.  
  5.  
  6. I converted Masafumi's MBarClock from C to Pascal, and then began forging it into 
  7. a chime replacement program.  Currently, if you have long chimes activated with your
  8. clock which comes with system 7.5, you may experience a very annoying distorted
  9. sound which interrupts your chime.  This is a known bug with the system, as it does not
  10. appear to lock the sound handle when it plays it (it should lock it because it plays it
  11. asynchronously).  CornerClock plays the hourly chime asynchronously, but locks the
  12. handle so you get a smooth play.
  13.  
  14. CornerClock will play a grandfather clock chime on the hour (3 chimes as 3:00, 4 at 4:00, etc.).
  15.  
  16. When you place the cursor over the time, it will change to the date for 2 seconds, then change back
  17. to the time (same feature in MBarClock except now there is sound).  I've also added some menus
  18. to CornerClock so that you can toggle the date and time, and force a chime.
  19.  
  20. There are many enhancements which can be made, like changing the chime sound, changing the
  21. volume, etc., but I'm releasing it as is to provide the Pascal community with some more sample 
  22. Pascal code.  Feel free to send me any questions or notes of thanks.
  23.  
  24. 2/4/96:  v1.1 - Fixed bug so that 12:00 noon displays as 12:00 instead of 0:00.
  25. 2/10/96: v1.2 - Added a toggle under the File menu for disabling the chime.
  26. 2/29/96: v1.5 - Added a File menu item for choosing the default display (date or time).
  27.               - Chime and default display selections are now saved in a Preference file.
  28.               - Application now handles an AEQuit so that it properly exits when asked 
  29.                 to quit from another app.
  30.  
  31. CornerClock ©Bill Catambay, 1996,  catambay@aol.com
  32. All rights reserved worldwide.
  33. }
  34. Program CornerClock;
  35.  
  36. Uses 
  37.     Toolbox, Sound, Resources, Icons;
  38.     
  39. Const
  40.     kAbout = 0;
  41.     kDisplayDate = 1;
  42.     kDisplayTime = 2;
  43.     kChime = 3;
  44.     iDate = 1;
  45.     iTime = 2;
  46.     iForce = 3;
  47.     iChime = 5;
  48.     iDefault = 6;
  49.     iQuit = 8;
  50.     mApple = 128;
  51.     mFile = 129;
  52.     mEdit = 130;
  53.     mDefault = 131;
  54.  
  55. Type
  56.     PrefsRec    =    record
  57.         chimeOn:    boolean;
  58.         default:    integer;
  59.         end;
  60.     PrefsPtr    =    ^PrefsRec;
  61.     PrefsHandle =    ^PrefsPtr;
  62.     
  63. Var
  64.     AppleMenu, FileMenu, EditMenu, DefaultMenu: MenuHandle;
  65.     ClockPort: CGrafPtr;
  66.     NUMs: array[0..10] of CIconHandle;
  67.     running: Boolean;
  68.     ClockRect: array[0..3] of Rect;
  69.     Corner: Rect;
  70.     default: integer;
  71.     bkGnd: RgnHandle;
  72.     dispStat: integer;
  73.     chimes: integer;
  74.     chimeStart: longint;
  75.     chimeOn: boolean;
  76.     timerStart: longint;
  77.     gBackground: boolean;
  78.     sounds: array[0..3] of SndListHandle;
  79.     sndChans: array[0..3] of SndChannelPtr;
  80.     backupChan: SndChannelPtr;
  81.     forceChime: boolean;
  82.     fRefnum,vRefnum: integer;
  83.     gOSErr: OSErr;
  84.     
  85. Procedure Die;
  86.  
  87. Var
  88.     startCount:    longint;
  89.     
  90.     begin
  91.     SysBeep(30);
  92.     SysBeep(30);
  93.     SysBeep(30);
  94.     startCount := TickCount;
  95.     repeat
  96.     until TickCount - startCount > 30;
  97.     ExitToShell;
  98.     end;
  99.  
  100. Procedure CheckMachine;
  101.  
  102. Var
  103.     sysEnv: SysEnvRec;
  104.     i: integer;
  105.     
  106.     begin
  107.     i := SysEnvirons(curSysEnvVers, sysEnv);
  108.     if i <> noErr then
  109.         Die;
  110.     if not sysEnv.hasColorQD then
  111.         Die;
  112.     if sysEnv.systemVersion < $700 then
  113.         Die;
  114.     end;
  115.  
  116. Procedure OpenClockPort;
  117.  
  118.     begin
  119.     ClockPort := CGrafPtr(NewPtrClear(sizeof(CGrafPort)));
  120.     if ClockPort = NIL then
  121.         Die;
  122.     OpenCPort(ClockPort);
  123.     end;
  124.  
  125. Procedure SetupMenu;
  126.  
  127.     begin
  128.     ClearMenuBar;
  129.     AppleMenu := GetMenu(mApple);
  130.     InsertMenu(AppleMenu,0);
  131.     AppendResMenu(AppleMenu,'DRVR');
  132.     FileMenu := GetMenu(mFile);
  133.     InsertMenu(FileMenu,0);
  134.     EditMenu := GetMenu(mEdit);
  135.     InsertMenu(EditMenu,0);
  136.     DefaultMenu := GetMenu(mDefault);
  137.     InsertMenu(DefaultMenu,-1);
  138.     DrawMenuBar;
  139.     end;
  140.  
  141. Procedure LoadIcons;
  142.  
  143. Var
  144.     i: integer;
  145.     
  146.     begin
  147.     for i := 0 to 10 do
  148.         NUMs[i] := GetCIcon(2000 + i);
  149.     end;
  150.  
  151. Procedure SetupRects;
  152.  
  153. Var
  154.     rgn: RgnHandle;
  155.     r: Rect;
  156.     
  157.     begin
  158.     SetRect(ClockRect[0], 2,2,10,10);
  159.     SetRect(ClockRect[1], 8,2,16,10);
  160.     SetRect(ClockRect[2], 2,10,10,18);
  161.     SetRect(ClockRect[3], 8,10,16,18);
  162.     bkGnd := NewRgn;
  163.     SetRect(corner,0,0,16,19);
  164.     RectRgn(bkGnd,corner);
  165.     UnionRect(ClockRect[0], ClockRect[3], r);
  166.     r.bottom := r.bottom - 1;
  167.     r.right := r.right - 2;
  168.     rgn := NewRgn;
  169.     RectRgn(rgn, r);
  170.     DiffRgn(bkGnd, rgn, bkGnd);
  171.     DisposeRgn(rgn);
  172.     end;
  173.  
  174. Function LoadPrefs: OSerr;
  175.  
  176. Var
  177.     myPrefs:    PrefsHandle;
  178.     
  179.     begin
  180.     myPrefs := PrefsHandle(GetResource('pref',128));
  181.     if myPrefs = NIL then
  182.         begin
  183.         chimeOn := true;
  184.         default := kDisplayTime;
  185.         LoadPrefs := ResError;
  186.         end
  187.     else
  188.         begin
  189.         chimeOn := myPrefs^^.chimeOn;
  190.         default := myPrefs^^.default;
  191.         ReleaseResource(handle(myPrefs));
  192.         LoadPrefs := noErr;
  193.         end;
  194.     end; { of LoadPrefs }
  195.  
  196. Function SavePrefs: OSErr;
  197.  
  198. Var
  199.     myPrefs:    PrefsHandle;
  200.     
  201.     Procedure Check(result: OSErr; isResource: boolean);
  202.  
  203.         begin
  204.         if result <> noErr then 
  205.             begin
  206.             if myPrefs <> nil then
  207.                 if isResource then
  208.                     ReleaseResource(Handle(myPrefs))
  209.                 else
  210.                     DisposeHandle(Handle(myPrefs));
  211.             SavePrefs := result;
  212.             exit(SavePrefs);
  213.             end;
  214.         end;
  215.         
  216.     begin
  217.     myPrefs := PrefsHandle(GetResource('pref', 128));
  218.     if myPrefs = NIL then 
  219.         begin
  220.         myPrefs := PrefsHandle(NewHandle(sizeof(PrefsRec)));
  221.         Check(MemError, false);
  222.         AddResource(handle(myPrefs), 'pref', 128, 'Defaults');
  223.         Check(ResError, false);
  224.         end;
  225.     myPrefs^^.chimeOn := chimeOn;
  226.     myPrefs^^.default := default;
  227.     ChangedResource(Handle(myPrefs));
  228.     Check(ResError, true);
  229.     UpdateResFile(CurResFile);
  230.     Check(ResError, true);
  231.     ReleaseResource(Handle(myPrefs));
  232.     SavePrefs := noErr;
  233.     end; { of SavePrefs }
  234.     
  235. Function OpenPrefs(var fRefnum: integer; var vRefnum: integer; prefName: Str255): OSerr;
  236.  
  237. Var
  238.     err:        OSerr;
  239.     DirID:        longint;
  240.     fileSpec:    FSSpec;
  241.  
  242.     begin
  243.     Err := FindFolder(kOnSystemDisk, kPreferencesFolderType,
  244.         kCreateFolder, vRefNum, DirID);
  245.     if Err <> noErr then
  246.         begin
  247.         openPrefs := err;
  248.         exit(openPrefs);
  249.         end;
  250.     Err := FSMakeFSSpec(vRefNum, DirID, prefName, fileSpec);
  251.     if (err <> Noerr) and (err <> fnfErr) then
  252.         begin
  253.         openPrefs := err;
  254.         exit(openPrefs);
  255.         end;
  256.     if Err = fnfErr then { not there so create it }
  257.         begin
  258.         Err := FSpCreate (fileSpec, 'CCLK', 'pref', 0);
  259.         if err <> Noerr then
  260.             begin
  261.             openPrefs := err;
  262.             exit(openPrefs);
  263.             end;
  264.         end;
  265.     fRefnum := FSpOpenResFile(fileSpec, fsCurPerm);
  266.     if fRefnum < 0 then
  267.         begin
  268.         FSpCreateResFile(fileSpec, 'CCLK', 'pref', smSystemScript);
  269.         err := ResError;
  270.         if err <> Noerr then
  271.             begin
  272.             openPrefs := err;
  273.             exit(openPrefs);
  274.             end;
  275.         fRefnum := FSpOpenResFile(fileSpec, fsCurPerm);
  276.         end;
  277.     err := ResError;
  278.     if err <> Noerr then
  279.         begin
  280.         openPrefs := err;
  281.         exit(openPrefs);
  282.         end;
  283.     openPrefs := LoadPrefs;
  284.     end;
  285.  
  286. Function GotRequiredParams (var theAppleEvent: AppleEvent): OSErr;
  287.  
  288. Var
  289.     myErr: OSErr;
  290.     returnedType: DescType;
  291.     actualSize: Size;
  292.  
  293.     Begin
  294.     myErr := AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, returnedType, 
  295.         Nil, 0, actualSize);
  296.     If myErr = errAEDescNotFound Then
  297.         GotRequiredParams := noErr
  298.     Else If myErr = noErr Then
  299.         GotRequiredParams := errAEParamMissed;
  300.     End;    { of GotRequiredParams }
  301.  
  302. Function HandleQuitEvent(var theAppleEvent: AppleEvent; var reply: AppleEvent;
  303.                            handlerRefcon: LongInt): OSErr;
  304.     
  305.     Begin
  306.     gOSErr := GotRequiredParams(theAppleEvent);
  307.     If gOSErr = noErr Then
  308.         running := false;
  309.     if (reply.dataHandle = NIL) | (handlerRefcon = 0) then  ; { read variable to eliminate compiler warning }
  310.     HandleQuitEvent := gOSErr;
  311.     End;    { of MyHandleQuiteEvent }
  312.  
  313. Procedure AdjustMenu;
  314.  
  315. Var
  316.     err:    OSErr;
  317.     
  318.     begin
  319.     if chimeOn then
  320.         begin
  321.         chimes := 0;
  322.         chimeStart := 0;
  323.         forceChime := False;
  324.         EnableItem(FileMenu, iForce);
  325.         end
  326.     else
  327.         begin
  328.         err := SndDisposeChannel(backupChan, True);
  329.         backupChan := NIL;
  330.         DisableItem(FileMenu, iForce);
  331.         end;
  332.     CheckItem(FileMenu, iChime, chimeOn);
  333.     if default = kDisplayTime then
  334.         CheckItem(DefaultMenu, kDisplayDate, False)
  335.     else
  336.         CheckItem(DefaultMenu, kDisplayTime, False);
  337.     CheckItem(DefaultMenu, default, True);
  338.     end; { AdjustMenu }
  339.     
  340. Procedure InitAppl;
  341.  
  342. Var
  343.     i: integer;
  344.     err: OSErr;
  345.     
  346.     begin
  347.     CheckMachine;
  348.     OpenClockPort;
  349.     SetupMenu;
  350.     LoadIcons;
  351.     SetupRects;
  352.     running := true;
  353.     chimes := 0;
  354.     chimeStart := 0;
  355.     forceChime := False;
  356.     gBackground := false;
  357.     for i := kAbout to kChime do
  358.         begin
  359.         sounds[i] := SndListHandle(GetResource(soundListRsrc,128 + i));
  360.         HLockHi(Handle(sounds[i]));
  361.         sndChans[i] := NIL;
  362.         end;
  363.     backupChan := NIL;
  364.     err := OpenPrefs(fRefnum,vRefnum,'CornerClock Preferences');
  365.     if err <> noErr then
  366.         Die;
  367.     dispStat := default;
  368.     AdjustMenu;
  369.     gOSErr := AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, 
  370.         @HandleQuitEvent, 0, false);
  371.     end;
  372.  
  373. Procedure DoSound(sndID: integer);
  374.  
  375. Var
  376.     err: OSErr;
  377.     sndCmd: SndCommand;
  378.     sndHead: SoundHeaderPtr;
  379.     SndChanStat: SCStatus;
  380.     
  381.     begin
  382.     if sounds[sndID] = NIL then
  383.         begin
  384.         sysBeep(1);
  385.         exit(DoSound);
  386.         end;
  387.     if (sndChans[sndID] <> NIL) and (backupChan <> NIL) then
  388.         begin
  389.         err := SndDisposeChannel(backupChan, True);
  390.         backupChan := NIL;
  391.         end;
  392.     if sndChans[sndID] <> NIL then
  393.         begin
  394.         backupChan := sndChans[sndID];
  395.         sndChans[sndID] := NIL;
  396.         end;
  397.     err := SndNewChannel(sndChans[sndID], sampledSynth, 0, NIL);
  398.     if (err <> noErr) or (sndChans[sndID] = NIL) then
  399.         begin
  400.         sysBeep(1);
  401.         exit(DoSound);
  402.         end;
  403.     sndHead := SoundHeaderPtr(longint(sounds[sndID]^) + 20);
  404.     sndCmd.cmd := bufferCmd;
  405.     sndCmd.param1 := 0;
  406.     sndCmd.param2 := ORD4(sndHead);
  407.     err := SndDoCommand(sndChans[sndID], sndCmd, false);
  408.     err := SndChannelStatus(sndChans[sndID], sizeof(SndChanStat), @SndChanStat);
  409.     if err <> noErr then
  410.         begin
  411.         sysBeep(1);
  412.         err := SndDisposeChannel(sndChans[sndID], True);
  413.         sndChans[sndID] := NIL;
  414.         exit(DoSound);
  415.         end;
  416. {    
  417.     chimeStart := TickCount;
  418.     While SndChanStat.scChannelBusy and (not Button) and (Err = NoErr) and (SndArray[1] <> NIL) do
  419.         Err := SndChannelStatus(SndArray[1], sizeof(SndChanStat), @SndChanStat);}
  420.     end;
  421.     
  422. Procedure DoAbout;
  423.  
  424. Var
  425.     p: GrafPtr;
  426.     d: DialogPtr;
  427.     
  428.     begin
  429.     GetPort(p);
  430.     d := GetNewDialog(128, NIL, WindowPtr(-1));
  431.     DrawDialog(d);
  432.     DoSound(kAbout);
  433.     repeat until Button;
  434.     DisposeDialog(d);
  435.     SetPort(p);
  436.     FlushEvents(mDownMask, 0);
  437.     end;
  438.  
  439. Procedure SwitchView;
  440.  
  441.     begin
  442.     if dispStat = kDisplayDate then
  443.         dispStat := kDisplayTime 
  444.     else
  445.         dispStat := kDisplayDate;
  446.     doSound(dispStat);
  447.     if dispStat <> default then
  448.         timerStart := TickCount;
  449.     end;
  450.  
  451. Procedure ForceView(newView: integer);
  452.  
  453.     begin
  454.     dispStat := newView;
  455.     doSound(dispStat);
  456.     if dispStat <> default then
  457.         timerStart := TickCount;
  458.     end;
  459.         
  460. Procedure DoMenu(SelMenu: longint);
  461.  
  462. Var
  463.     item,i: integer;
  464.     s:         Str255;
  465.     
  466.     begin
  467.     item := LoWord(SelMenu);
  468.     case HiWord(SelMenu) of
  469.         mApple:    if item = 1 then
  470.                     DoAbout
  471.                 else
  472.                     begin
  473.                     GetMenuItemText(AppleMenu, item, s);
  474.                     i := OpenDeskAcc(s);
  475.                     end;
  476.         mFile:    case item of
  477.                 iDate:    ForceView(kDisplayDate);
  478.                 iTime:    ForceView(kDisplayTime);
  479.                 iForce:    if chimeOn then
  480.                             forceChime := True;
  481.                 iChime:    begin
  482.                         chimeOn := not chimeOn;
  483.                         AdjustMenu;
  484.                         end;
  485.                 iQuit:    running := false;
  486.                 {CASE}    end;
  487.         mDefault:
  488.                 if default <> item then
  489.                     begin
  490.                     default := item;
  491.                     AdjustMenu;
  492.                     end;
  493.     {CASE}        end;
  494.     HiliteMenu(0);
  495.     end;
  496.  
  497. Procedure UpdateWindow(wp: WindowPtr);
  498.  
  499.     begin
  500.     SetPort(wp);
  501.     BeginUpdate(wp);
  502.     EndUpdate(wp);
  503.     end;
  504.  
  505. Procedure DrawDigit(upper,lower: integer; upShow10, loShow10: boolean);
  506.  
  507. Var
  508.     p: GrafPtr;
  509.     
  510.     begin
  511.     GetPort(p);
  512.     SetPort(GrafPtr(ClockPort));
  513.     ForeColor(blackColor);
  514.     PaintRgn(bkGnd);
  515.     if (upper >= 10) | upShow10 then
  516.         PlotCIcon(ClockRect[0], NUMs[ (upper div 10) mod 10])
  517.     else
  518.         PlotCIcon(ClockRect[0], NUMs[10]);
  519.     PlotCIcon(ClockRect[1], NUMs[upper mod 10]);
  520.     if (lower >= 10) | loShow10 then
  521.         PlotCIcon(ClockRect[2], NUMs[(lower div 10) mod 10])
  522.     else
  523.         PlotCIcon(ClockRect[2], NUMs[10]);
  524.     PlotCIcon(ClockRect[3], NUMs[lower mod 10]);
  525.     SetPort(p);
  526.     end;
  527.  
  528. Procedure DrawClock;
  529.  
  530. Const    
  531.     chimeMinute = 0;
  532.     
  533. Var
  534.     dt: DateTimeRec;
  535.     p: GrafPtr;
  536.     pt: point;
  537.     
  538.     begin
  539.     GetPort(p);
  540.     SetPort(GrafPtr(ClockPort));
  541.     GetTime(dt);
  542.     GetMouse(pt);
  543.     if PtInRect(pt, corner) and (dispStat = default) then
  544.         SwitchView;
  545.     if (dispStat <> default) & (timerStart + 300 < TickCount) then
  546.         SwitchView;
  547.     ForeColor(blackColor);
  548.     PaintRgn(bkGnd);
  549.     case dispStat of 
  550.         kDisplayDate:    DrawDigit(dt.month, dt.day, false, false);
  551.         kDisplayTime:    if dt.hour = 12 then
  552.                             DrawDigit(dt.hour, dt.minute, false, true)
  553.                         else
  554.                             DrawDigit(dt.hour mod 12, dt.minute, false, true);
  555.     {CASE}                end;
  556.     if chimeOn then
  557.         begin
  558.         if ((dt.minute = chimeMinute) or forceChime) and (chimes = 0) then
  559.             chimes := dt.hour mod 12;
  560.         if (chimes > 0) & ((chimeStart = 0) | (chimeStart + 120 < TickCount)) then
  561.             begin
  562.             doSound(kChime);
  563.             chimeStart := TickCount;
  564.             dec(chimes);
  565.             if chimes = 0 then
  566.                 dec(chimes);
  567.             end
  568.         else if (chimes = -1) & (dt.minute <> chimeMinute) then
  569.             begin
  570.             chimes := 0;
  571.             forceChime := False;
  572.             chimeStart := 0;
  573.             end;
  574.         end;
  575.     SetPort(p);
  576.     end;
  577.  
  578. Procedure MainLoop;
  579.  
  580. Var
  581.     event: EventRecord;
  582.     dw: WindowPtr;
  583.     ascii: char;
  584.     sleep: longint;
  585.     i: integer;
  586.     err: OSErr;
  587.     
  588.     begin
  589.     repeat
  590.         DrawClock;
  591.         if chimes > 0 then
  592.             sleep := 1
  593.         else
  594.             sleep := 20;
  595.         if WaitNextEvent(everyEvent, event, sleep, NIL) then
  596.             case event.what of
  597.                 mouseDown:    case FindWindow(event.where, dw) of
  598.                                 inMenuBar:    DoMenu(MenuSelect(event.where));
  599.                             {CASE}            end;
  600.                 keyDown:    begin
  601.                             ascii := chr(BAnd(event.message,charCodeMask));
  602.                             if BAnd(event.modifiers,cmdKey) > 0 then
  603.                                 DoMenu(MenuKey(ascii));
  604.                             end;
  605.                 updateEvt:    UpdateWindow(WindowPtr(event.message));
  606.                 osEvt:        if BAnd(brotl(event.message,8),$FF) = suspendResumeMessage then
  607.                                 gBackground := BAnd(event.message,resumeFlag) = 0;
  608.                 kHighLevelEvent:
  609.                             gOSErr := AEProcessAppleEvent(Event);
  610.             {CASE}            end;
  611.     until not running;
  612.     if SavePrefs <> noErr then
  613.         sysBeep(30);
  614.     DisposeRgn(bkGnd);    
  615.     for i := 0 to 2 do
  616.         begin
  617.         HUnLock(Handle(sounds[i]));
  618.         DisposeHandle(Handle(sounds[i]));
  619.         err := SndDisposeChannel(sndChans[i], True);
  620.         end;
  621.     if backupChan <> NIL then
  622.         err := SndDisposeChannel(backupChan, True);
  623.     end;
  624.  
  625. begin
  626. InitToolbox;
  627. FlushEvents(everyEvent, 0);
  628. InitAPPL;
  629. MainLoop;
  630. end.