#!perl -w # Analog Clock v1.1 # by Kevin Reid <kpreid@ibm.net> # # Displays an analog clock in a window. # Control-click for options. # # This program may be freely distributed, and you may # use code from it, as long as my name stays on it. # Programming information: # # The array @hands stores information about the clock hands. # 0: hour 1: minute 2: second # Values: # len (x, y): length of hand in pixels # ang: current angle of hand in radians # tip (x, y): current position of end of hand # begin & end: temp values for storing rectangle enclosing hand # Revision history # # 1.1 # Cleaned up several areas. # Fixed hour hand. # Added circle WDEF. # 1.0 # First release. use Mac::Events; use Mac::Windows; use Mac::QuickDraw; use Mac::Menus; use Time::Local; @divs = (.4, .75, .90); # length of hour, minute, second hands $showtext = 1; $showtitle = 1; $tstr = ''; for (0..2) { $hands[$_]{div} = $divs[$_]; $hands[$_]{ang} = 0; $hands[$_]{bbox} = new Rect (0,0,0,0); } $pi = 3.14159; # Originally, I used the pi from Math::Trig, but that apparently $pi2 = $pi * 2; # returned a complex number, which caused some problems. $wrect = new Rect (100, 100, 300, 250); # Initial window position & size $lgray = new RGBColor ((50000) x 3); $dgray = new RGBColor ((10000) x 3); $black = new RGBColor (( 0) x 3); $white = new RGBColor ((65535) x 3); # Colors are pre-calculated for speed. sub InitWin { # Create clock window and set hooks my ($bbox) = @_; if (!$showtitle and !$showtext) { my $size = $useht ? $bbox->bottom - $bbox->top : $bbox->right - $bbox->left; $bbox = new Rect ($bbox->left, $bbox->top, $bbox->left + $size, $bbox->top + $size); } $win->dispose if $win; # Destroy previous window if present $win = new MacColorWindow ( $bbox, 'Clock', 1, $showtitle ? documentProc : ($showtext ? plainDBox : \&CircDef_1), 1, ); $win->sethook(layout => sub { # Handle resizing of window my $wRect = $win->window->contRgn->rgnBBox; # get bounds of window (in global coordinates) SetPort($win->window); my $sPt = GlobalToLocal(new Point ($wRect->right, $wRect->bottom)); # $sPt now holds position of bottom-right corner $midh = $sPt->h * .50; # $midh, $midv is center-point of clock face $midv = ($sPt->v - ($showtext ? 20 : 0)) * .50; my $mdv = $midv - 5; my $mdh = $sPt->h / 2 - 5; $md = (($useht = $mdh > $mdv) ? $mdv : $mdh); $tpos_v = $sPt->v - 10; # V pos of text display $circrect = new Rect ($midh - $md + 1, $midv - $md + 1, $midh + $md + 1, $midv + $md + 1); $circrect_e = new Rect ($midh - $md, $midv - $md, $midh + $md, $midv + $md); # Set up hands foreach (@hands) { $_->{len}[0] = $md * $_->{div}; $_->{len}[1] = $md * $_->{div}; } calc_hands(); # Recalculate tip values InvalRect new Rect (0, 0, $sPt->h, $sPt->v); # Force update }); $win->layout; # initial setup of clock face $win->sethook(drawgrowicon => sub {}); # hide grow box RGBBackColor($lgray); # set background color $win->sethook(redraw => sub { # redraw procedure # draw embossed circle RGBForeColor($white); FrameOval($circrect); RGBForeColor($dgray); FrameOval($circrect_e); # draw hands RGBForeColor($black); foreach (@hands) { MoveTo($midh, $midv); LineTo(@{$_->{tip}}); } # draw text time if ($showtext) { MoveTo($tpos_hl, $tpos_v); DrawString $tstr; } }); $win->sethook(click => sub { # click handler if ($Mac::Events::CurrentEvent->modifiers & controlKey) { # if control key is down, then display menu my ($mw, $pt) = @_; $m->insert; # set up menu my $mp = LocalToGlobal(GetMouse); # find mouse position SetItemMark $m->{menu}, 1, $showtext ? "\cR" : ''; # set up check marks SetItemMark $m->{menu}, 2, $showtitle ? "\cR" : ''; PopUpMenuSelect $m->{menu}, $mp->v, $mp->h, 1; # display menu $m->delete; # remove menu } else { # if control key is up, drag window DragWindow($win->window, $Mac::Events::CurrentEvent->where); } }); } # create clock window InitWin($wrect); sub calc_hands { # calculate hand & text positions foreach (@hands) { $_->{tip}[0] = sin($_->{ang})*$_->{len}[0]+$midh; # calculate end-point of hand from angle $_->{tip}[1] = -cos($_->{ang})*$_->{len}[1]+$midv; my @box = map {($$_[0] < $$_[1]) ? $_ : [$$_[1], $$_[0]]} ([$_->{tip}[0], $midh], [$_->{tip}[1], $midv]); $_->{bbox} = new Rect ($box[0][0], $box[1][0], $box[0][1] + 1, $box[1][1] + 1); # These messy lines calculate a rectangle that # encloses the hand, so that window updates # can redraw only that which needs to be redrawn. } $tpos_hl = $midh - StringWidth($tstr) / 2; $tpos_hr = $midh + StringWidth($tstr) / 2; } sub CircDef_1 { my($variant, $win, $msg, $param) = @_; if ($msg == wNew) {;} elsif ($msg == wCalcRgns) { my($r) = OffsetRect($win->portRect, -$win->portBits->bounds->left, -$win->portBits->bounds->top ); my $circ = NewRgn(); OpenRgn; FrameOval $r; CloseRgn $circ; CopyRgn $circ, $win->contRgn; CopyRgn $circ, $win->strucRgn; } elsif ($msg == wHit) { if (PtInRgn($param, $win->contRgn)) { return wInContent; } } return 0; } # create options menu $m = new MacHierMenu 2000, '', ( ['Text Time' => sub { $showtext = !$showtext; # toggle flag if (!$showtitle) { # if no title bar, InitWin($win->window->contRgn->rgnBBox); # switch between square and round windows } else { $win->layout; # re-calculate clock face position } }], ['Title Bar' => sub { $showtitle = !$showtitle; # toggle flag InitWin($win->window->contRgn->rgnBBox); # recreate window with different WDEF }], ); $t = $tt = 0; # In order to avoid flickering, the clock is updated # only once per second. The $t variable stores the time # when the clock was last updated, and when the current # time differs from the stored time, the clock is updated. # enter event loop while ($win->window) { if ($t != ($tt = time) and $t = $tt) { # see above SetPort($win->window); foreach (@hands) { InvalRect $_->{bbox}; # mark hand areas for updating; since # the hands will no longer be there, # they must be erased. } $tstr = scalar localtime; # get text time $secs_today = time % 86400; # calc seconds since beginning of day $hands[0]{ang} = $secs_today % 43200 / 43200 * $pi2; # calc hour hand position $hands[1]{ang} = $secs_today % 3600 / 3600 * $pi2; # calc minute hand position $hands[2]{ang} = $secs_today % 60 / 60 * $pi2; # calc second hand position calc_hands(); # calculate hand positions foreach (@hands) { InvalRect $_->{bbox}; # mark NEW hand areas for updating, too. } InvalRect new Rect ($tpos_hl, $tpos_v - 10, $tpos_hr, $tpos_v + 10) if $showtext; # mark text time area for updating } WaitNextEvent; } END {$win->dispose} # force window close if script is aborted __END__ -- Kevin Reid. | Macintosh. "I'm me." | Think different. ***** Want to unsubscribe from this list? ***** Send mail with body "unsubscribe" to mac-perl-request@iis.ee.ethz.ch