[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

[MacPerl] Clock v1.1



#!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