#!perl -w # Analog Clock v2.0 # 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 # # 2.0 # * Added live resizing. # * Added Close command. # * Removed 'Title Bar' option. # * Moved text time. # * Added 'Run Script...' option for executing other scripts while the clock is running. # 1.1 # Cleaned up several areas. # Fixed hour hand. # Added circle WDEF. # 1.0 # First release. package KMacAnalogClock; 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; $resize = 0; $tstr = scalar localtime; 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, 300); # Initial window position & size $lgray = new RGBColor ((55000) x 3); $dgray = new RGBColor ((12000) x 3); $tgray = new RGBColor ((32000) x 3); $black = new RGBColor (( 0) x 3); $white = new RGBColor ((65535) x 3); # Colors are pre-calculated for speed. sub SetMouse { use Mac::LowMem; my ($pt) = @_; LMSetMouseTemp($pt); LMSetRawMouseLocation($pt); LMSetCursorNew(1); } sub InitWin { # Create clock window and set hooks my ($bbox) = @_; $win->dispose if $win; # Destroy previous window if present $win = new MacColorWindow ( $bbox, 'Clock', 1, \&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 * .5; # $midh, $midv is center-point of clock face $midv = $sPt->v * .5; my $mdv = $midv - ($midv < 20 ? 2 : 5); my $mdh = $midh - ($midh < 20 ? 2 : 5); $md = ($mdh > $mdv ? $mdv : $mdh); $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}; } TextSize(20); while (StringWidth($tstr) > $sPt->h - 20) { TextSize($win->window->txSize - 1); last if $win->window->txSize <= 6; } my ($ascent, $descend) = GetFontInfo(); $tpos_v = $midv + $ascent + 2; # V pos of text display $tpos_vb = $midv + $ascent + $descend + 2; # bottom of text rectangle calc_hands(); # Recalculate tip values InvalRect new Rect (0, 0, $sPt->h, $sPt->v); # Force update }); $win->layout; # initial setup of clock face RGBBackColor($lgray); # set background color $win->sethook(idle => \&idler); $win->sethook(redraw => sub { # redraw procedure # draw text time if ($showtext and $win->window->txSize > 6) { RGBForeColor($white); MoveTo($tpos_hl+1, $tpos_v+1); DrawString $tstr; RGBForeColor($tgray); MoveTo($tpos_hl, $tpos_v); DrawString $tstr; } # draw embossed circle RGBForeColor($white); FrameOval($circrect); RGBForeColor($dgray); FrameOval($circrect_e); # draw hands RGBForeColor($black); foreach (@hands) { MoveTo($midh, $midv); LineTo(@{$_->{tip}}); } }); $win->sethook(click => sub { # click handler if ($resize) {$resize = 0; ShowCursor()} 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 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 == 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 { InvalRect new Rect ($tpos_hl, $midv, $tpos_hr, $tpos_vb) if $showtext; $showtext = !$showtext; }], ['Resize' => sub { SetMouse LocalToGlobal new Point $md, $md; $resize = GetMouse; HideCursor(); $wsiz = 0; }], [], ['Close' => sub {exit}], ['Run Script...' => sub { local $^W; require 'StandardFile.pl'; my $file = StandardFile::GetFile('TEXT') or return; {package main; do $file} exit; }], ); $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. sub idler { 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, $midv, $tpos_hr, $tpos_vb) if $showtext; # mark text time area for updating } if ($resize) { SetPort $win->window; my $pt = GetMouse; $osiz = $wsiz; $wsiz = ($pt->h + $pt->v); $wsiz = 30 if $wsiz < 30; $wsiz = 600 if $wsiz > 600; if ($osiz != $wsiz) { SizeWindow $win->window, $wsiz, $wsiz; $win->layout; $win->update; } SetMouse LocalToGlobal new Point (($wsiz/2+.5)x2); } } WaitNextEvent while 1; END {$win->dispose; ShowCursor if $resize} __END__ ===== Want to unsubscribe from this list? ===== Send mail with body "unsubscribe" to macperl-request@macperl.org