(***********************************************)
(*                                             *)
(* Alarm-Clock                                 *)
(* Copyright (c) 1999 by TMT Development Corp. *)
(* Author: Vadim Bodrov, TMT Development Corp. *)
(*                                             *)
(* Targets:                                    *)
(*   WIN32 GUI application                     *)
(*                                             *)
(***********************************************)

// The working of the alarm clock is pretty much what you would expect. Please
// keep in mind that the time shown on the displayed clock is the time at which
// the alarm is set.  The time of day is not recorded on the clock, because
// your system clock has it.
//
// The alarm time is adjucted by the arrows beneath the time display -- one
// pair for hours and the other for minutes.  When the user clicks the "Hide"
// button, the clock is still visible on the task bar and it will sound if it
// is in the enabled mode.  To keep the alarm form sounding, click the "Mute"
// button.

{$ifndef __WIN32__}
{$define INVALID_TARGET}
{$endif}
{$ifndef __GUI__}
{$define INVALID_TARGET}
{$endif}
{$ifdef INVALID_TARGET}
  This program must be compiled as Win32 GUI application only
{$endif}

program AClock;

uses Windows, Messages, CommDlg, CommCtrl, MMSystem, ShellAPI;

{$r aclock.res}

const
  HOUR_SPIN_CTRL   = 1000;
  MINUTE_SPIN_CTRL = 1001;
  ENABLE_ALARM_BOX = 1002;
  MUTE_BTN         = 1003;
  EXIT_BTN         = 1004;
  DISPLAY_CTRL     = 1005;

  MAIN_DIALOG      = 0101;
  ABOUT_BOX        = 0102;

  POSX: DWORD      = 0074;
  POSY: DWORD      = 0016;

var
  Msg: TMsg;
  maindlg: THandle;
  Digits: hBitMap;
  iDC: hDC;
  Icon: hIcon;
  Menu: hMenu;
  Timer: THandle;
  IconData: TNotifyIconData;
  DoSound, isAlarmEnabled, Flash: Boolean := FALSE;
  SysTime, AlarmTime: TSystemTime;

procedure ShowTime(TimeStruct: TSystemTime);
var
  DC: hDC;
begin
  DC := GetDC(mainDlg);

  if TimeStruct.wHour > 9 then
    BitBlt(DC, POSX + 0, POSY + 0, 24, 33, iDC, (TimeStruct.wHour div 10 * 24) , 0, SRCCOPY)
  else
    BitBlt(DC, POSX + 0, POSY + 0, 24, 33, iDC, 264 , 0, SRCCOPY);

  BitBlt(DC, POSX + 24, POSY + 0, 24, 33, iDC, (TimeStruct.wHour mod 10 * 24) , 0, SRCCOPY);
  BitBlt(DC, POSX + 48, POSY + 0, 24, 33, iDC, 240 , 0, SRCCOPY);

  BitBlt(DC, POSX + 62, POSY + 0, 24, 33, iDC, (TimeStruct.wMinute div 10 * 24) , 0, SRCCOPY);
  BitBlt(DC, POSX + 86, POSY + 0, 24, 33, iDC, (TimeStruct.wMinute mod 10 * 24) , 0, SRCCOPY);

  ReleaseDC(mainDlg, DC);
end;

procedure CrearRect;
var
  DC: hDC;
  Brush: hBrush;
begin
  DC := GetDC(mainDlg);
  Brush := CreateSolidBrush(0);
  SelectObject(DC, Brush);
  Rectangle(DC, POSX, POSY, POSX + 110, POSY + 33);
  DeleteObject(Brush);
  ReleaseDC(mainDlg, DC);
end;

function AboutDlgProc conv arg_stdcall (Dialog: HWND; Mess: UINT; WParam: WParam; LParam: LParam): LongInt;
begin
  case Mess of
    WM_CLOSE:
      begin
        EndDialog(Dialog, 0);
        Result := 1;
      end;
    WM_COMMAND:
      begin
        if LoWord(WParam) = IDOK then
        begin
          SendMessage(Dialog, WM_CLOSE, 0, 0);
          Result := 0;
        end;
      end;
    else
      Result := 0;
  end;
end;

function MainDlgProc conv arg_stdcall (Dialog: HWND; Mess: UINT; WParam: WParam; LParam: LParam): LongInt;
var
  Point: TPoint;
  Rect: TRect;
begin
  Result := 0;
  case Mess of
  WM_INITDIALOG:
    begin
      GetWindowRect(Dialog, Rect);
      POSX := (Rect.Right - Rect.Left - 114) div 2;
      POSY := POSY + 16;
      SetWindowPos(GetDlgItem(Dialog, DISPLAY_CTRL), HWND_TOP, POSX, POSY - 2, 114, 39, 0);
      SetWindowPos(GetDlgItem(Dialog, HOUR_SPIN_CTRL), HWND_TOP, POSX, POSY + 44, 30, 20, 0);
      SetWindowPos(GetDlgItem(Dialog, MINUTE_SPIN_CTRL), HWND_TOP, POSX + 114 - 30, POSY + 44, 30, 20, 0);
      Result := 1;
    end;
  WM_CLOSE:
    ShowWindow(mainDlg, SW_HIDE);
  WM_COMMAND:
    begin
      case LoWord(wParam) of
        1101:
          DialogBoxParam(HInstance, MAKEINTRESOURCE(ABOUT_BOX), mainDlg, @AboutDlgProc, 0);
        1102:
          ShowWindow(mainDlg, SW_SHOW);
        1103, EXIT_BTN:
          if MessageBox(mainDlg, 'Are you really want to exit Alarm-Clock?',
            'Confirm', MB_YESNO or MB_ICONQUESTION) = IDYES then DestroyWindow(Dialog);
        IDHELP:
          DialogBoxParam(hInstance, MAKEINTRESOURCE(ABOUT_BOX), maindlg, @AboutDlgProc, 0);
        IDCLOSE:
          SendMessage(mainDlg, WM_CLOSE, 0 , 0);
        ENABLE_ALARM_BOX:
          begin
            isAlarmEnabled := SendDlgItemMessage(mainDlg, ENABLE_ALARM_BOX,
              BM_GETCHECK, 0, 0) = BST_CHECKED;
            if DoSound then
            begin
              MMSystem.PlaySound(nil, 0, 0);
              EnableWindow(GetDlgItem(mainDlg, MUTE_BTN), FALSE);
              ShowTime(AlarmTime);
            end;
          end;
        MUTE_BTN:
          begin
            MMSystem.PlaySound(nil, 0, 0);
            EnableWindow(GetDlgItem(mainDlg, MUTE_BTN), FALSE);
            ShowTime(AlarmTime);
          end;
      end;
    end;
  WM_TIMER:
    begin
      GetLocalTime(SysTime);
      if (SysTime.wHour = AlarmTime.wHour) and
        (SysTime.wMinute = AlarmTime.wMinute) and
        (isAlarmEnabled) then
      begin
        if not DoSound then
        begin
          DoSound := TRUE;
          MMSystem.PlaySound(MAKEINTRESOURCE(2001), hInstance, SND_LOOP or SND_ASYNC or SND_RESOURCE);
          EnableWindow(GetDlgItem(mainDlg, MUTE_BTN), TRUE);
          ShowWindow(mainDlg, SW_SHOW);
        end else
        begin
          Flash := not Flash;
          if Flash then
            ShowTime(AlarmTime)
          else
            CrearRect;
        end;
      end else
        if DoSound then
        begin
          MMSystem.PlaySound(nil, 0, 0);
          DoSound := FALSE;
          EnableWindow(GetDlgItem(mainDlg, MUTE_BTN), FALSE);
          ShowTime(AlarmTime);
        end;
    end;
  WM_DESTROY:
    PostQuitMessage(0);
  WM_PAINT:
    ShowTime(AlarmTime);
  WM_HSCROLL:
    begin
      if LParam = GetDlgItem(mainDlg, HOUR_SPIN_CTRL) then
        AlarmTime.wHour := HiWord(wParam)
      else
        AlarmTime.wMinute := HiWord(wParam);
      if AlarmTime.wMinute = 60 then
      begin
        AlarmTime.wMinute := 0;
        AlarmTime.wHour +:= 1;
      end;
      if AlarmTime.wMinute = $FFFF then AlarmTime.wMinute := 59;
      if AlarmTime.wHour = 24 then AlarmTime.wHour := 0;
      if AlarmTime.wHour = $FFFF then AlarmTime.wHour := 23;
      SendDlgItemMessage(mainDlg, HOUR_SPIN_CTRL, UDM_SETPOS, 0, MAKELONG(AlarmTime.wHour, 0));
      SendDlgItemMessage(mainDlg, MINUTE_SPIN_CTRL, UDM_SETPOS, 0, MAKELONG(AlarmTime.wMinute, 0));
      ShowTime(AlarmTime);
    end;
  WM_USER + 1:
    begin
      if lParam = WM_LBUTTONDBLCLK then
      begin
        ShowWindow(mainDlg, SW_SHOW);
      end;
      if (lParam = WM_RBUTTONDOWN) or (lParam = WM_LBUTTONDOWN) then
      begin
        GetCursorPos(Point);
        TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_LEFTBUTTON, point.x, point.y, 0, Dialog, nil);
      end;
      Result := 0;
    end;
  end;
end;

begin
  Digits := LoadBitmap(hInstance, MAKEINTRESOURCE(2000));
  iDC := CreateCompatibleDC(0);
  SelectObject(iDC, Digits);

  maindlg := CreateDialog(hInstance, MakeIntResource(MAIN_DIALOG), 0, @MainDlgProc);
  InitCommonControls;
  ShowWindow(maindlg, SW_SHOW);

  Timer := SetTimer(mainDlg, 1, 500, nil);

  Icon := LoadIcon(hInstance, MAKEINTRESOURCE(2002));
  SendMessage(mainDlg, WM_SETICON, ICON_SMALL, Icon);

  with IconData do
  begin
    FillChar(IconData, SizeOf(IconData), 0);
    Wnd := mainDlg;
    uID := 1;
    uFlags := NIF_ICON or NIF_MESSAGE or NIF_TIP;
    uCallbackMessage := WM_USER + 1;
    hIcon := Icon;
    szTip := 'Alarm-Clock'+#0;
    cbSize := SizeOf(IconData);
  end;
  Shell_NotifyIcon(NIM_ADD, @IconData);

  Menu := CreatePopupMenu;
  InsertMenu(Menu, MF_BYPOSITION, 0, 1101, 'About Alarm-Clock');
  InsertMenu(Menu, MF_BYPOSITION or MF_MENUBARBREAK, 0, 0, nil);
  InsertMenu(Menu, MF_BYPOSITION, 0, 1102, 'Open');
  InsertMenu(Menu, MF_BYPOSITION or MF_MENUBARBREAK, 0, 0, nil);
  InsertMenu(Menu, MF_BYPOSITION, 0, 1103, 'Exit');

  GetLocalTime(AlarmTime);
  SendDlgItemMessage(mainDlg, HOUR_SPIN_CTRL, UDM_SETRANGE, 0, MAKELONG(24, -1));
  SendDlgItemMessage(mainDlg, MINUTE_SPIN_CTRL, UDM_SETRANGE, 0, MAKELONG(60, -1));
  SendDlgItemMessage(mainDlg, HOUR_SPIN_CTRL, UDM_SETPOS, 0, MAKELONG(AlarmTime.wHour, 0));
  SendDlgItemMessage(mainDlg, MINUTE_SPIN_CTRL, UDM_SETPOS, 0, MAKELONG(AlarmTime.wMinute, 0));

  while GetMessage(Msg, 0, 0, 0) do
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;

  Shell_NotifyIcon(NIM_DELETE, @IconData);
end.
