#define VERSION "1.6 (10.10.96)"
/***************************************************************************
*                                  *                                       *
* Programm: TolleUhr               * Version: s.o.                         *
*                                  *                                       *
****************************************************************************
*                                                                          *
* Dieses Programm ist Public-Domain, d.h. wer immer Lust dazu hat, darf    *
* dieses Programm kopieren, umschreiben, usw., vorausgesetzt:              *
*                                                                          *
* 1. Die Autorenliste bleibt voll erhalten. (auch in About-Requestern)     *
* 2. Wer etwas am Programm verbricht, muß sich auch dazuschreiben.         *
*                                                                          *
* Es wird keine Haftung für Schäden irgendwelcher Art übernommen.          *
*                                                                          *
* Autoren: Matthias Fleischer  Adlerstraße 30 7302 Ostfildern 2            *
*          (fleischr@izfm.uni-stuttgart.de)                                *
*          .                                                               *
*          .(Auf Paul, schaff was !)                                       *
*                                                                          *
*          Gunther Nikl  Ziegendorfer Chaussee 96 19370 Parchim            *
*          (gnikl@informatik.uni-rostock.de)                               *
*                                                                          *
***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* includes                                                                */
/*                                                                         */
/***************************************************************************/

#include <exec/types.h>

#ifndef REG
#define REG(x) asm(#x)
#endif
#ifndef CONST
#define CONST const
#endif
#ifndef INLINE
#define INLINE inline
#endif

#include <dos/dos.h>
#include <dos/rdargs.h>
#include <dos/datetime.h>
#include <exec/tasks.h>
#include <exec/memory.h>
#include <exec/execbase.h>
#include <devices/audio.h>
#include <devices/timer.h>
#include <hardware/cia.h>
#include <prefs/prefhdr.h>
#include <prefs/wbpattern.h>
#include <graphics/gfxmacros.h>
#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/screennotify.h>
#include <datatypes/pictureclass.h>
#include <libraries/gadtools.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <clib/alib_protos.h>
#include <clib/macros.h>

/*
** structure definitions now!
*/

struct gInfo {
  WORD  left,top,width,height;
  UWORD textnr,kind;
};

struct wInfo {
  UWORD        idcmp,textnr,gadcnt;
  struct gInfo gi[1];
};

struct WinGad {
  struct Window *Win;
  struct Gadget *Gad;
};

struct DateTimeInfo {
  struct DateTime dat;
  UBYTE           buf[34];
};

struct BackFillInfo {
  STRPTR               PictureName;
  Object              *PictureObject;
  struct BitMapHeader *BitMapHeader;
  struct BitMap       *ImageBitMap,
                      *SnapBitMap,
                      *BitMap;
  ULONG                Width,Height;
};

/*
**  sizeof(struct Globals) _MUST_ be a multiple of 4!
*/

struct Globals {

  /*** library stuff ***/

  struct ExecBase      *SysBase;
  struct DosLibrary    *DOSBase;
  struct IntuitionBase *IntuitionBase;
  struct GfxBase       *GfxBase;
  struct Library       *GadtoolsBase,
                       *IconBase,
                       *DataTypesBase;
  struct LocaleBase    *LocaleBase;
  struct Catalog       *Catalog;

  /*** misc stuff ***/

  STRPTR                NameBuf,
                        PubName;
  BPTR                  OldLock;
  LONG                  OldPri;

  /*** device stuff ***/

  struct MsgPort       *WindowPort;
  struct MsgPort       *TimerPort;
  struct timerequest   *TimerIO;
  struct MsgPort       *AudioPort;
  struct IOAudio       *AudioIO[2];
  APTR                  WaveForm;

  /*** graphic stuff ***/

  APTR                  VisualInfo;
  struct Menu          *Menus;
  struct Window        *MainWin;
  struct RastPort      *RPort;
  struct WinGad         ColorWin,
                        AlarmWin;
  struct Window        *AboutReq;

  /*** screen notify ***/

  struct Library       *ScreenNotifyBase;
  struct MsgPort       *NotifyPort;
  APTR                  NotifyHandle;

  /*** common variables ***/

  LONG                  Left,
                        Top,
                        Width,
                        Height,
                        BoLeft,
                        BoTop,
                        Width2,
                        Height2,
                        Depth,
                        ReDrawx1,
                        ReDrawy1,
                        ReDrawx2,
                        ReDrawy2,
                        xMask,
                        yMask;
  PLANEPTR              Buf;
  UWORD                *PlayTune;
  UBYTE                *Color;
  LONG                  Std,
                        Min,
                        Sec,
                        AlStd,
                        AlMin,
                        St,
                        Mi,
                        xOffset,
                        yOffset;
  WORD                  GadSize;
  UWORD                 ImgType;

  /*** flags ***/

  BYTE                  TimerOpen,
                        TimerSent,
                        AudioOpen,
                        AudioSent[2],
                        SoundOn,
                        xDouble,
                        yDouble,
                        ChangedDir,
                        ChangedPri,
                        Active,
                        Alarm,
                        Filter,
                        HiRes,
                        Interlace,
                        SmartHour,
                        GoOn,
                        CloseGad,
                        Backdrop,
                        Activate,
                        Fixed,
                        UseImage,
                        Snap,
                        V39;
  UBYTE                 Seconds,
                        Oval,
                        Show,
                        HandType,
                        HandWidth,
                        Shadow,
                        BorderCount,
                        Chime,
                        NoRemap,
                        RequestNr,
                        ScrNotify,
                        bPad;

  /*** structures ***/

  struct BackFillInfo   BFInfo;
  struct WBArg          ProgArg;
  UBYTE                 Pens[16],
                        Must[4];
  struct NewWindow      NewWindowBuf;
  struct Gadget         GadgetBuf[4];
  struct RastPort       RPort1;
  struct TmpRas         TempRas;
  struct AreaInfo       AreaInfo;
  UWORD                 Table[14],
                        Muster[8][2];
  struct NewGadget      NewGad;
  UWORD                 wPad;
  struct EasyStruct     EasyBuf;
  struct DateTimeInfo   DateTime;
};

#define PUB_SCREEN (gb->NewWindowBuf.Screen)
#define RP1_BITMAP (gb->RPort1.BitMap)

/*
** ptr to the `global' data space (will be on the stack 8-)
*/

REGISTER struct Globals *gb REG(a4);

/*
** redirect library bases to the `global' data space
*/

#define BASE_NAME gb->ScreenNotifyBase
#include <proto/screennotify.h>
#undef BASE_NAME

#define BASE_NAME gb->LocaleBase
#include <proto/locale.h>
#undef BASE_NAME

#define BASE_NAME gb->DataTypesBase
#include <proto/datatypes.h>
#undef BASE_NAME

#define BASE_NAME gb->IntuitionBase
#include <proto/intuition.h>
#undef BASE_NAME

#define BASE_NAME gb->GadtoolsBase
#include <proto/gadtools.h>
#undef BASE_NAME

#define BASE_NAME gb->GfxBase
#include <proto/graphics.h>
#undef BASE_NAME

#define BASE_NAME gb->IconBase
#include <proto/icon.h>
#undef BASE_NAME

#define BASE_NAME gb->SysBase
#include <proto/exec.h>
#undef BASE_NAME

#define BASE_NAME gb->DOSBase
#include <proto/dos.h>
#undef BASE_NAME

#include <proto/iffparse.h>
#include <proto/timer.h>
#include <proto/alib.h>

/*
** strcpy() && strlen()
*/

#include <string.h>

/*
** locale string numbers
*/

#include "tolleuhr_locale.h"

/***************************************************************************/
/*                                                                         */
/* prototypes                                                              */
/*                                                                         */
/***************************************************************************/

VOID AddScreenNotify();
LONG OpenMainWindow();
VOID SnapBackground();
VOID LoadImage();
LONG MakeMenus();
LONG InitClock();
LONG GetArguments(struct WBStartup *);
VOID MergeToolTypes();
VOID ParseArgs(STRPTR,LONG);
VOID ReadStr(UBYTE *,UBYTE *,UBYTE *,LONG);
LONG XtoD(LONG);
LONG OpenDevs();
LONG OpenLibs();
VOID Main();
VOID CloseLibs();
VOID CloseDevs();
VOID DeleteBitMap(struct BitMap *,ULONG);
VOID NewSize1();
VOID CloseAboutReq();
VOID CloseGfx();
VOID FreeWindow(struct WinGad *);
VOID CloseWindowSafely(struct Window *);
VOID RemScreenNotify();
LONG CheckNotify();
VOID CheckAboutReq();
VOID CheckWindows();
VOID HandleMenus(ULONG);
struct BitMap *CreateBitMap(ULONG,ULONG,ULONG);
LONG NewSize2();
VOID NewTitle();
ULONG NextTick();
VOID Rahmen();
VOID ZifferBlatt();
VOID Zeiger(ULONG,LONG,LONG,LONG,LONG);
VOID Zeichnen();
VOID SetPattern();
VOID SaveSettings();
STRPTR SetToolType(STRPTR,STRPTR,LONG,...);
STRPTR GetToolType(LONG);
STRPTR SetStr(STRPTR,STRPTR,LONG);
LONG DtoX(LONG);
VOID TestIfAlarm();
VOID StartTune(CONST UWORD *);
VOID PlayNote();
VOID EndTune();
VOID CreateRequest(struct WinGad *,struct wInfo *,struct TagItem **);
struct Window *OpenWindowShared(struct NewWindow *,CONST struct TagItem *,ULONG);
STRPTR GetCatalogString(LONG);
VOID CopyTiledBitMap(struct BitMap *,LONG,LONG,struct BitMap *,LONG,LONG,LONG,LONG);

/*
** replacements
*/

LONG blt_bitmap(struct BitMap *,LONG,LONG,struct BitMap *,LONG,LONG,LONG,LONG,ULONG,ULONG,PLANEPTR);
Object *new_dt_object(APTR name,ULONG,...);
ULONG get_dt_attrs(Object *,ULONG,...);

/***************************************************************************/
/*                                                                         */
/* `global' defines                                                        */
/*                                                                         */
/***************************************************************************/

#define ciaa (*((struct CIA *)0xbfe001L))

#define GB_GADCOUNT (sizeof(gb->GadgetBuf)/sizeof(struct Gadget))

enum {
  WT_CLOSEGAD=1,WT_BACKDROP=2,WT_FIXED=4,WT_USEIMAGE=8,WT_SNAP=16,WT_LAST=32
};

/***************************************************************************/
/*                                                                         */
/* get bitmap depth (use a V39+ function if available)                     */
/*                                                                         */
/***************************************************************************/

STATIC INLINE ULONG GetBitMapDepth(struct BitMap *bm)
{ ULONG d = bm->Depth;

  if (gb->V39)
    d = GetBitMapAttr(bm,BMA_DEPTH);
  return d;
}

/***************************************************************************/
/*                                                                         */
/* memory clear function (!!! caution: size _MUST_ be a multiple of 4 !!!) */
/*                                                                         */
/***************************************************************************/

STATIC INLINE VOID MEMZEROL(VOID *mem,ULONG size)
{ ULONG *p;
  WORD s;

  for(p=(ULONG *)mem,s=(size/sizeof(ULONG))-1; s>=0; *p++=0,s--);
}

/***************************************************************************/
/*                                                                         */
/* implementation (!!! DO NOT PLACE ANY FUNCTION BEFORE start() !!!)       */
/*                                                                         */
/***************************************************************************/

LONG Start()
{ struct Globals globals;

  MEMZEROL(&globals,sizeof(struct Globals)); gb = &globals;

  gb->SysBase = *(struct ExecBase **)4L;

  Main();

  return 0;
}

/***************************************************************************/
/*                                                                         */
/* version string                                                          */
/*                                                                         */
/***************************************************************************/

CONST UBYTE Version[] = "$VER: TolleUhr "VERSION" by M.Fleischer and G.Nikl in 1993/96";

#define PROGNAME ((UBYTE *)(&Version[6]))

/***************************************************************************/
/*                                                                         */
/* install screen notification (if desired and possible)                   */
/*                                                                         */
/***************************************************************************/

VOID AddScreenNotify()
{ struct PubScreenNode *pubnode;

  if ((pubnode=(struct PubScreenNode *)FindName(LockPubScreenList(),"Workbench")) != NULL)
    if (pubnode->psn_Screen != PUB_SCREEN)
      pubnode=NULL;
  UnlockPubScreenList();

  if (pubnode != NULL)
    if ((gb->ScreenNotifyBase=OpenLibrary(SCREENNOTIFY_NAME,0)) != NULL)
    {
      if ((gb->NotifyPort=CreateMsgPort()) != NULL)
        if ((gb->NotifyHandle=AddWorkbenchClient(gb->NotifyPort,0)) != NULL)
          return;
      RemScreenNotify();
    }
}

/***************************************************************************/
/*                                                                         */
/* open clock window                                                       */
/*                                                                         */
/***************************************************************************/

#define MAINIDCMP (IDCMP_CHANGEWINDOW | IDCMP_REFRESHWINDOW | IDCMP_MENUPICK | \
                   IDCMP_CLOSEWINDOW | IDCMP_ACTIVEWINDOW | IDCMP_INACTIVEWINDOW)

CONST struct TagItem WindowTags[] = {
  { WA_InnerWidth,         148 },
  { WA_InnerHeight,         72 },
  { WA_AutoAdjust,        TRUE },
  { TAG_DONE,             NULL }
};

LONG OpenMainWindow()
{ struct NewWindow *nw;
  struct Window *win;
  LONG ret;

  nw = &gb->NewWindowBuf;

  nw->LeftEdge    = gb->Left;
  nw->TopEdge     = gb->Top;
  nw->Width       = gb->Width;
  nw->Height      = gb->Height;
  nw->BlockPen    = 1;
  nw->Flags       = WFLG_SIMPLE_REFRESH | WFLG_BORDERLESS | WFLG_NEWLOOKMENUS |
                    (gb->Backdrop ? WFLG_BACKDROP : 0) | (gb->Activate ? WFLG_ACTIVATE : 0);
  nw->FirstGadget = NULL;
  nw->Title       = NULL;
  nw->MinWidth    = 10;
  nw->MinHeight   = 10;
  nw->MaxWidth    = 65535;
  nw->MaxHeight   = 65535;
  nw->Type        = PUBLICSCREEN;

  ret = 0;

  if ((gb->MainWin=(win=OpenWindowShared(nw,&WindowTags[2],MAINIDCMP))) != NULL)
  {
    PUB_SCREEN = win->WScreen;
    gb->RPort  = win->RPort;
    SetMenuStrip(win,gb->Menus);
    if (!gb->BFInfo.ImageBitMap)
      OffMenu(win,FULLMENUNUM(1,8,3));
    SetAPen(gb->RPort,gb->Pens[0]);
    if (NewSize2())
    {
      NextTick();
      NewTitle();
      Rahmen();
      ZifferBlatt();
      Zeichnen();
      ret = 1;
    }
  }

  return ret;
}

/***************************************************************************/
/*                                                                         */
/* snap screen background to be used as window image                       */
/*                                                                         */
/***************************************************************************/

VOID SnapBackground()
{ struct Screen *scr = PUB_SCREEN;
  struct BitMap *bm;
  LONG w,h,t,l,sw,sh;

  if ((w=gb->Width) > (sw=scr->Width))
    gb->Width = w = sw;

  if ((l=gb->Left) > sw || l+w > sw)
    gb->Left = l = sw - w;

  if ((h=gb->Height) > (sh=scr->Height))
    gb->Height = h = sh;

  if ((t=gb->Top) > sh || t+h > sh)
    gb->Top = t = sh - h;

  if ((gb->BFInfo.SnapBitMap=(bm=CreateBitMap(w,h,GetBitMapDepth(scr->RastPort.BitMap)))) != NULL)
    { BltBitMap(scr->RastPort.BitMap,l,t,bm,0,0,w,h,0xC0,-1,NULL); gb->UseImage=0; }
}

/***************************************************************************/
/*                                                                         */
/* load background image via datatypes                                     */
/*                                                                         */
/***************************************************************************/

CONST struct TagItem PicTags[] = {
  { DTA_GroupID          ,GID_PICTURE     },
  { PDTA_FreeSourceBitMap,TRUE            },
  { OBP_Precision        ,PRECISION_IMAGE },
  { TAG_DONE             ,NULL            }
};

VOID LoadImage()
{ struct Library *IFFParseBase;
  struct WBPatternPrefs pat;
  struct IFFHandle *handle;
  STRPTR name;
  Object *obj;
  LONG len;

  if ((name=gb->BFInfo.PictureName) == NULL)
  {
    if ((IFFParseBase=OpenLibrary("iffparse.library",37L)) != NULL)
    {
      if ((handle=AllocIFF()) != NULL)
      {
        if ((handle->iff_Stream=(ULONG)Open("env:sys/wbpattern.prefs",MODE_OLDFILE)) != NULL)
        {
          InitIFFasDOS(handle);

          if (!OpenIFF(handle,IFFF_READ))
          {
            if (!StopChunk(handle,ID_PREF,ID_PTRN))
            {
              while(!ParseIFF(handle,IFFPARSE_SCAN))
              {
                if (ReadChunkBytes(handle,&pat,sizeof(struct WBPatternPrefs)) != sizeof(struct WBPatternPrefs))
                  break;

                if (pat.wbp_Which == gb->ImgType && !pat.wbp_Flags)
                {
                  if ((gb->BFInfo.PictureName=AllocVec(1+(len=pat.wbp_DataLength),MEMF_CLEAR|MEMF_ANY)) != NULL)
                  {
                    ReadChunkBytes(handle,gb->BFInfo.PictureName,len);
                  }
                  break;
                }
              }
            }
            CloseIFF(handle);
          }
          Close((BPTR)handle->iff_Stream);
        }
        FreeIFF(handle);
      }
      CloseLibrary(IFFParseBase);
    }
    name=gb->BFInfo.PictureName;
  }

  if (name != NULL)
  {
    gb->BFInfo.PictureObject = obj =
      new_dt_object(name,
                    PDTA_Screen,PUB_SCREEN,
                    PDTA_Remap ,(gb->NoRemap ? FALSE : TRUE),
                    TAG_MORE   ,PicTags);
    if (obj != NULL)
      if (DoMethod(obj,DTM_PROCLAYOUT,NULL,1))
        if (get_dt_attrs(obj,
                     PDTA_BitMapHeader,&gb->BFInfo.BitMapHeader,
                     PDTA_DestBitMap  ,&gb->BFInfo.ImageBitMap,
                     TAG_DONE) != 2)
          { gb->BFInfo.BitMapHeader=NULL; gb->BFInfo.ImageBitMap=NULL; gb->UseImage=0; }
  }
}

/***************************************************************************/
/*                                                                         */
/* create gadgets and menus                                                */
/*                                                                         */
/***************************************************************************/

CONST UBYTE gadgetInfo[] = {
  GADGHNONE                     ,GTYP_CLOSE,
  GADGHNONE|GRELRIGHT           ,GTYP_WDEPTH,
  GADGHNONE|GRELRIGHT|GRELBOTTOM,GTYP_SIZING,
  GADGHNONE|GRELWIDTH|GRELHEIGHT,GTYP_WDRAGGING
};

enum {
  MENU_PROJECT=0,
   MENU_PROJECT_ABOUT,
   MENU_PROJECT_QUIT,
  MENU_SETTINGS,
   MENU_SETTINGS_SECONDS,
   MENU_SETTINGS_OVAL,
   MENU_SETTINGS_SHOW,
    MENU_SETTINGS_SHOW_MINUTES,
    MENU_SETTINGS_SHOW_HOURS,
    MENU_SETTINGS_SHOW_QUARTER,
    MENU_SETTINGS_SHOW_ONE,
    MENU_SETTINGS_SHOW_NONE,
   MENU_SETTINGS_HANDS,
    MENU_SETTINGS_HANDS_LINE,
    MENU_SETTINGS_HANDS_TRIANGLE,
    MENU_SETTINGS_HANDS_RHOMBUS,
    MENU_SETTINGS_HANDS_RECTANGLE,
    MENU_SETTINGS_HANDS_BARLABEL,
    MENU_SETTINGS_HANDS_VERYTHIN,
    MENU_SETTINGS_HANDS_THIN,
    MENU_SETTINGS_HANDS_NORMAL,
    MENU_SETTINGS_HANDS_THICK,
    MENU_SETTINGS_HANDS_VERYTHICK,
   MENU_SETTINGS_SHADOW,
   MENU_SETTINGS_BORDER,
    MENU_SETTINGS_BORDER_NONE,
    MENU_SETTINGS_BORDER_SINGLE,
    MENU_SETTINGS_BORDER_DOUBLE,
    MENU_SETTINGS_BORDER_BARLABEL,
    MENU_SETTINGS_BORDER_HIRES,
    MENU_SETTINGS_BORDER_INTERLACE,
   MENU_SETTINGS_CHIME,
    MENU_SETTINGS_CHIME_NONE,
    MENU_SETTINGS_CHIME_HOURS,
    MENU_SETTINGS_CHIME_QUARTER,
    MENU_SETTINGS_CHIME_BARLABEL,
    MENU_SETTINGS_CHIME_SMART,
   MENU_SETTINGS_ALARM,
    MENU_SETTINGS_ALARM_SET,
    MENU_SETTINGS_ALARM_ON,
   MENU_SETTINGS_WINDOW,
    MENU_SETTINGS_WINDOW_CLOSEGAD,
    MENU_SETTINGS_WINDOW_BACKDROP,
    MENU_SETTINGS_WINDOW_FIXED,
    MENU_SETTINGS_WINDOW_USEIMAGE,
    MENU_SETTINGS_WINDOW_SNAP,
   MENU_SETTINGS_BARLABEL,
   MENU_SETTINGS_SAVE,
  MENU_COLORS,
   MENU_COLORS_SECONDS,
   MENU_COLORS_MINAPEN,
   MENU_COLORS_MINOPEN,
   MENU_COLORS_HOURAPEN,
   MENU_COLORS_HOUROPEN,
   MENU_COLORS_SHADOW,
   MENU_COLORS_STR12,
   MENU_COLORS_QUARTER,
   MENU_COLORS_HOURS,
   MENU_COLORS_MINUTES,
   MENU_COLORS_BORDER0,
   MENU_COLORS_BORDER1,
   MENU_COLORS_BORDER2,
   MENU_COLORS_BORDER3,
  MENU_PATTERN,
   MENU_PATTERN_COLOR0,
   MENU_PATTERN_COLOR1,
   MENU_PATTERN_COLOR2,
   MENU_PATTERN_COLOR3
};

CONST UBYTE MenuInfo[] = {
  NM_TITLE,MSG_PROJECT,0,0,
   NM_ITEM,MSG_ABOUT,0,0,
   NM_ITEM,MSG_QUIT,0,0,
  NM_TITLE,MSG_SETTINGS,0,0,
   NM_ITEM,MSG_SECONDS,CHECKIT|MENUTOGGLE,0,
   NM_ITEM,MSG_OVAL,CHECKIT|MENUTOGGLE,0,
   NM_ITEM,MSG_SHOW,0,0,
    NM_SUB,MSG_MINUTES,CHECKIT,2|4|8|16,
    NM_SUB,MSG_HOURS,CHECKIT,1|4|8|16,
    NM_SUB,MSG_QUARTER,CHECKIT,1|2|8|16,
    NM_SUB,MSG_ONE,CHECKIT,1|2|4|16,
    NM_SUB,MSG_NONE,CHECKIT,1|2|4|8,
   NM_ITEM,MSG_HANDS,0,0,
    NM_SUB,MSG_LINE,CHECKIT,2|4|8,
    NM_SUB,MSG_TRIANGLE,CHECKIT,1|4|8,
    NM_SUB,MSG_RHOMBUS,CHECKIT,1|2|8,
    NM_SUB,MSG_RECTANGLE,CHECKIT,1|2|4,
    NM_SUB,(UBYTE)NM_BARLABEL,0,0,
    NM_SUB,MSG_VERYTHIN,CHECKIT,128+(2|4|8|16),
    NM_SUB,MSG_THIN,CHECKIT,128+(1|4|8|16),
    NM_SUB,MSG_NORMAL,CHECKIT,128+(1|2|8|16),
    NM_SUB,MSG_THICK,CHECKIT,128+(1|2|4|16),
    NM_SUB,MSG_VERYTHICK,CHECKIT,128+(1|2|4|8),
   NM_ITEM,MSG_SHADOW,CHECKIT|MENUTOGGLE,0,
   NM_ITEM,MSG_BORDER,0,0,
    NM_SUB,MSG_NONE,CHECKIT,2|4,
    NM_SUB,MSG_SINGLE,CHECKIT,1|4,
    NM_SUB,MSG_DOUBLE,CHECKIT,1|2,
    NM_SUB,(UBYTE)NM_BARLABEL,0,0,
    NM_SUB,MSG_HIRES,CHECKIT|MENUTOGGLE,0,
    NM_SUB,MSG_INTERLACE,CHECKIT|MENUTOGGLE,0,
   NM_ITEM,MSG_CHIME,0,0,
    NM_SUB,MSG_NONE,CHECKIT,2|4,
    NM_SUB,MSG_HOURS,CHECKIT,1|4,
    NM_SUB,MSG_QUARTER,CHECKIT,1|2,
    NM_SUB,(UBYTE)NM_BARLABEL,0,0,
    NM_SUB,MSG_SMART,CHECKIT|MENUTOGGLE,0,
   NM_ITEM,MSG_ALARM,0,0,
    NM_SUB,MSG_SET,0,0,
    NM_SUB,MSG_ON,CHECKIT|MENUTOGGLE,0,
   NM_ITEM,MSG_WINDOW,0,0,
    NM_SUB,MSG_CLOSEGAD,CHECKIT|MENUTOGGLE,0,
    NM_SUB,MSG_BACKDROP,CHECKIT|MENUTOGGLE,0,
    NM_SUB,MSG_FIXED,CHECKIT|MENUTOGGLE,0,
    NM_SUB,MSG_USEIMAGE,CHECKIT|MENUTOGGLE,WT_SNAP,
    NM_SUB,MSG_SNAP,CHECKIT|MENUTOGGLE,WT_USEIMAGE,
   NM_ITEM,(UBYTE)NM_BARLABEL,0,0,
   NM_ITEM,MSG_SAVESETTINGS,0,0,
  NM_TITLE,MSG_COLORS,0,0,
   NM_ITEM,MSG_SECONDS,0,0,
   NM_ITEM,MSG_MINAPEN,0,0,
   NM_ITEM,MSG_MINOPEN,0,0,
   NM_ITEM,MSG_HOURAPEN,0,0,
   NM_ITEM,MSG_HOUROPEN,0,0,
   NM_ITEM,MSG_SHADOW,0,0,
   NM_ITEM,MSG_STR12,0,0,
   NM_ITEM,MSG_QUARTER,0,0,
   NM_ITEM,MSG_HOURS,0,0,
   NM_ITEM,MSG_MINUTES,0,0,
   NM_ITEM,MSG_BORDER0,0,0,
   NM_ITEM,MSG_BORDER1,0,0,
   NM_ITEM,MSG_BORDER2,0,0,
   NM_ITEM,MSG_BORDER3,0,0,
  NM_TITLE,MSG_PATTERN,0,0,
   NM_ITEM,MSG_COLOR0,0,0,
   NM_ITEM,MSG_COLOR1,0,0,
   NM_ITEM,MSG_COLOR2,0,0,
   NM_ITEM,MSG_COLOR3,0,0
};

#define NM_ITEMCOUNT (sizeof(MenuInfo)/(4*sizeof(UBYTE)))

CONST struct TagItem MenuTags[] = {
  { GTMN_NewLookMenus,TRUE },
  { TAG_DONE         ,NULL }
};

LONG MakeMenus()
{ struct NewMenu menubuf[NM_ITEMCOUNT+1],*nm;
  struct Gadget *gad;
  CONST UBYTE *p;
  LONG tmp,index,ret;
  WORD i,num;

  for(gad=&gb->GadgetBuf[0],p=&gadgetInfo[0],num=1,i=GB_GADCOUNT-1; i>=0; i--)
  { gad->Width      = num;
    gad->Height     = num;
    gad->Flags      = *p++;
    gad->Activation = num; /* GACT_RELVERIFY */
    gad->GadgetType = *p++;
    gad++;
    gad[-1].NextGadget = gad;
  }
  gb->GadSize = 0;

  MEMZEROL(&menubuf[0],sizeof(menubuf));
  for(nm=&menubuf[0],p=&MenuInfo[0],i=NM_ITEMCOUNT-1; i>=0; i--)
  { nm->nm_Type          = *p++;
    if ((tmp=(BYTE)*p++) >= 0) tmp=(LONG)GetCatalogString(tmp);
    nm->nm_Label         = (STRPTR)tmp;
    nm->nm_Flags         = (UWORD)*p++;
    if ((num=(BYTE)*p++) < 0) num=(((num&0x7f))<<5);
    nm->nm_MutualExclude = (ULONG)num;
    nm++;
  }
  menubuf[MENU_PROJECT_ABOUT].nm_CommKey = GetCatalogString(MSG_ABOUTKEY);
  menubuf[MENU_PROJECT_QUIT ].nm_CommKey = GetCatalogString(MSG_QUITKEY);
  menubuf[MENU_SETTINGS_SAVE].nm_CommKey = GetCatalogString(MSG_SAVEKEY);

  ret = 0;

  if ((gb->VisualInfo=GetVisualInfoA(PUB_SCREEN,NULL)) != NULL)
  {
    nm = &menubuf[0]; i = CHECKED;
    nm[MENU_SETTINGS_SECONDS].nm_Flags
     |= i & (gb->Seconds ? -1 : 0);
    nm[MENU_SETTINGS_OVAL].nm_Flags
     |= i & (gb->Oval ? -1 : 0);
    index = MENU_SETTINGS_SHOW+gb->Show+1;
    nm[index].nm_Flags
     |= i;
    index = MENU_SETTINGS_HANDS+gb->HandType+1;
    nm[index].nm_Flags
     |= i;
    index = MENU_SETTINGS_HANDS+gb->HandWidth+6;
    nm[index].nm_Flags
     |= i;
    nm[MENU_SETTINGS_SHADOW].nm_Flags
     |= i & (gb->Shadow ? -1 : 0);
    index = MENU_SETTINGS_BORDER+gb->BorderCount+1;
    nm[index].nm_Flags
     |= i;
    nm[MENU_SETTINGS_BORDER_HIRES].nm_Flags
     |= i & (gb->HiRes ? -1 : 0);
    nm[MENU_SETTINGS_BORDER_INTERLACE].nm_Flags
     |= i & (gb->Interlace ? -1 : 0);
    index = MENU_SETTINGS_CHIME+gb->Chime+1;
    nm[index].nm_Flags
     |= i;
    nm[MENU_SETTINGS_CHIME_SMART].nm_Flags
     |= i & (gb->SmartHour ? -1 : 0);

    nm[MENU_SETTINGS_WINDOW_CLOSEGAD].nm_Flags
     |= i & (gb->CloseGad ? -1 : 0);
    nm[MENU_SETTINGS_WINDOW_BACKDROP].nm_Flags
     |= i & (gb->Backdrop ? -1 : 0);
    nm[MENU_SETTINGS_WINDOW_FIXED].nm_Flags
     |= i & (gb->Fixed ? -1 : 0);
    nm[MENU_SETTINGS_WINDOW_USEIMAGE].nm_Flags
     |= i & (gb->BFInfo.ImageBitMap && gb->UseImage ? -1 : 0);
    nm[MENU_SETTINGS_WINDOW_SNAP].nm_Flags
     |= i & (gb->BFInfo.SnapBitMap && gb->Snap ? -1 : 0);

    if ((gb->Menus=CreateMenusA(&menubuf[0],NULL)) != NULL)
      ret=LayoutMenusA(gb->Menus,gb->VisualInfo,(struct TagItem *)MenuTags);
  }

  return ret;
}

/***************************************************************************/
/*                                                                         */
/* initialize all graphics                                                 */
/*                                                                         */
/***************************************************************************/

LONG InitClock()
{ struct Screen *scr;
  LONG ret;

  if ((scr=LockPubScreen(gb->PubName)) == NULL)
    scr=LockPubScreen(NULL);

  ret = 0;

  if ((PUB_SCREEN=scr) != NULL)
  {
    if (gb->Snap)
      SnapBackground();
    if (gb->DataTypesBase && !gb->Activate)
      LoadImage();
    SetPattern();
    if (MakeMenus())
      ret = OpenMainWindow();
    UnlockPubScreen(NULL,scr);
  }

  return ret;
}

/***************************************************************************/
/*                                                                         */
/* get arguments either from CLI or WB                                     */
/*                                                                         */
/***************************************************************************/

LONG GetArguments(struct WBStartup *wm)
{ struct WBArg *arg;
  STRPTR buf;

  if (wm != NULL)
  {
    arg = wm->sm_ArgList;
    if (wm->sm_NumArgs>1)
      arg++;
    gb->ProgArg.wa_Lock = arg->wa_Lock;
    gb->ProgArg.wa_Name = arg->wa_Name;
    gb->OldLock = CurrentDir(arg->wa_Lock);
    gb->ChangedDir = -1;
    MergeToolTypes();
  }
  else
    if ((gb->NameBuf=(buf=AllocVec(124,MEMF_ANY))) != NULL)
    {
      gb->ProgArg.wa_Lock = GetProgramDir();
      GetProgramName(buf,124);
      gb->ProgArg.wa_Name = FilePart(buf);
      ParseArgs(NULL,0);
    }
    else
      return 0;

  return 1;
}

/*
** read all tooltypes
*/

STATIC INLINE STRPTR stpcpy(STRPTR dst,STRPTR src)
{
  while((*dst++=*src++)); return(dst-1);
}

VOID MergeToolTypes()
{ struct DiskObject *dobj;
  STRPTR argstr,string,p;
  LONG arglen,strln;
  BPTR old_cd;
  char **tt;

  argstr = (STRPTR)&gb->EasyBuf; *argstr = '\n'; arglen = 1; string=NULL;

  old_cd = CurrentDir(gb->ProgArg.wa_Lock);

  if ((dobj=GetDiskObject(gb->ProgArg.wa_Name)) != NULL)
  {
    tt = dobj->do_ToolTypes; strln = 1;
    while (*tt != NULL)
    {
      strln+=strlen(*tt++)+1;
    }

    if ((string=AllocVec((strln+sizeof(ULONG)-1)&~(sizeof(ULONG)-1),MEMF_ANY)) != NULL)
    {
      tt = dobj->do_ToolTypes; p=string;
      while (*tt != NULL)
      {
        p=stpcpy(p,*tt++); *p++ = ' ';
      }
      *p = '\n';

      argstr = string; arglen = strln;
    }

    FreeDiskObject(dobj);
  }

  ParseArgs(argstr,arglen);

  FreeVec(string);

  (VOID)CurrentDir(old_cd);
}

/*
** parse arguments and set default values
*/

enum {
  OPT_NULL=-1,OPT_TOP,OPT_LEFT,OPT_WIDTH,OPT_HEIGHT,OPT_SECONDS,OPT_PATTERN,
  OPT_OVAL,OPT_SHADOW,OPT_SHOWFACE,OPT_HANDTYPE,OPT_HANDWIDTH,OPT_DRAWPENS,
  OPT_BORDERTYPE,OPT_WINDOWTYPE,OPT_CHIME,OPT_SCRNOTIFY,OPT_WBIMAGE,
  OPT_NOREMAP,OPT_IMAGE,OPT_PUBSCREEN,OPT_TASKPRI,OPT_SIZEOF
};

#define TEMPLATE \
  "/M,TOP/N,LEFT/N,WIDTH/N,HEIGHT/N,SECONDS/S,PATTERN/K,OVAL/S,SHADOW/S,"    \
  "SHOWFACE/N,HANDTYPE/N,HANDWIDTH/N,DRAWPENS/K,BORDERTYPE/N,WINDOWTYPE/N,"  \
  "CHIME/N,SCREENNOTIFY/S,WBIMAGE/S,NOREMAP/S,IMAGE/K,PUBSCREEN/K,TASKPRI/N"

CONST UBYTE Pens[] = {
  2,0,2,0,2,1,2,2,2,2,0,1,2,3
};

VOID ParseArgs(STRPTR argstr,LONG length)
{ struct RDArgs *rda,*args;
  LONG result[OPT_SIZEOF+1],*res,def,tmp;
  STRPTR tmpl;

  MEMZEROL(&result[0],sizeof(result)); args = NULL;

  if ((rda=AllocDosObject(DOS_RDARGS,NULL)) != NULL)
  {
    rda->RDA_Source.CS_Buffer = argstr;
    rda->RDA_Source.CS_Length = length;
    tmpl = TEMPLATE; res = &result[0];
    if (rda->RDA_Source.CS_Buffer == NULL)
    {
      res++; tmpl+=3;
    }
    args = ReadArgs(tmpl,res,rda);
  }

  res = &result[1];

  def = 50;
  if (res[OPT_TOP])
  {
    tmp = *(LONG *)res[OPT_TOP]; if (tmp >= 0) def = tmp;
  }
  gb->Top = def;

  def = 50;
  if (res[OPT_LEFT])
  {
    tmp = *(LONG *)res[OPT_LEFT]; if (tmp >= 0) def = tmp;
  }
  gb->Left = def;

  def = 108;
  if (res[OPT_WIDTH])
  {
    tmp = *(LONG *)res[OPT_WIDTH]; if (tmp >= 10) def = tmp;
  }
  gb->Width = def;

  def = 54;
  if (res[OPT_HEIGHT])
  {
    tmp = *(LONG *)res[OPT_HEIGHT]; if (tmp >= 10) def = tmp;
  }
  gb->Height = def;

  gb->Seconds = res[OPT_SECONDS];

  ReadStr(&gb->Must[0],(STRPTR)res[OPT_PATTERN],&gb->Must[0],4);

  gb->Oval = res[OPT_OVAL];

  gb->Shadow = res[OPT_SHADOW];

  def = 1;
  if (res[OPT_SHOWFACE])
  {
    tmp = *(LONG *)res[OPT_SHOWFACE]; if ((ULONG)tmp < 5) def = tmp;
  }
  gb->Show = def;

  def = 2;
  if (res[OPT_HANDTYPE])
  {
    tmp = *(LONG *)res[OPT_HANDTYPE]; if ((ULONG)tmp < 4) def = tmp;
  }
  gb->HandType = def;

  def = 2;
  if (res[OPT_HANDWIDTH])
  {
    tmp = *(LONG *)res[OPT_HANDWIDTH]; if ((ULONG)tmp < 5) def = tmp;
  }
  gb->HandWidth = def;

  ReadStr(&gb->Pens[0],(STRPTR)res[OPT_DRAWPENS],(STRPTR)&Pens[0],14);

  def = 6;
  if (res[OPT_BORDERTYPE])
  {
    tmp = *(LONG *)res[OPT_BORDERTYPE]; if ((ULONG)tmp < 3+4+8) def = tmp;
  }
  gb->Interlace = ((tmp = def & 8) != 0); def -= tmp;
  gb->HiRes = ((tmp = def & 4) != 0); def -= tmp;
  gb->BorderCount = def;


  def = 0;
  if (res[OPT_WINDOWTYPE])
  {
    tmp = *(LONG *)res[OPT_WINDOWTYPE]; if ((ULONG)tmp < WT_LAST) def = tmp;
  }
  gb->Snap = ((tmp = def & WT_SNAP) != 0);
  gb->UseImage = ((tmp = def & WT_USEIMAGE) != 0);
  gb->Fixed = ((tmp = def & WT_FIXED) != 0);
  gb->Backdrop = ((tmp = def & WT_BACKDROP) != 0);
  gb->CloseGad = ((tmp = def & WT_CLOSEGAD) != 0);

  def = 0;
  if (res[OPT_CHIME])
  {
    tmp = *(LONG *)res[OPT_CHIME]; if ((ULONG)tmp < 3+4) def = tmp;
  }
  gb->SmartHour = ((tmp = def & 4) != 0); def -= tmp;
  gb->Chime = def;

  gb->ScrNotify = res[OPT_SCRNOTIFY];

  def = WBP_SCREEN; if (res[OPT_WBIMAGE]) def = WBP_ROOT; gb->ImgType = def;

  gb->NoRemap = res[OPT_NOREMAP];

  if (res[OPT_IMAGE])
    if ((gb->BFInfo.PictureName=AllocVec(1+strlen((STRPTR)res[OPT_IMAGE]),MEMF_ANY)) != NULL)
      strcpy(gb->BFInfo.PictureName,(STRPTR)res[OPT_IMAGE]);

  if (res[OPT_PUBSCREEN])
    if ((gb->PubName=AllocVec(1+strlen((STRPTR)res[OPT_PUBSCREEN]),MEMF_ANY)) != NULL)
      strcpy(gb->PubName,(STRPTR)res[OPT_PUBSCREEN]);

  if (res[OPT_TASKPRI])
  {
    tmp = *(LONG *)res[OPT_TASKPRI]; if (tmp > 5) tmp = 5;
    gb->OldPri = SetTaskPri(gb->WindowPort->mp_SigTask,tmp);
    gb->ChangedPri = -1;
  }

  FreeArgs(args);

  if (rda != NULL)
    FreeDosObject(DOS_RDARGS,rda);
}

/*
** init pen-buffer with `def' and replace with src (if non zero)
*/

VOID ReadStr(UBYTE *dst,UBYTE *src,UBYTE *def,LONG cnt)
{ UBYTE c;

  CopyMem(def,dst,cnt);

  if (src != NULL)
    if (*src=='x' || *src=='X') /* hex */
      do {
        if (!*src++ || (c=*src++)==0)
          return;
        *dst++ = (XtoD(c)<<4) + XtoD(*src);
      } while (--cnt);
    else
      do { /* decimal */
        if ((c=*src++) == 0)
          return;
        *dst++ = XtoD(c);
      } while (--cnt);
}

LONG XtoD(LONG a)
{
  if (a>='0' && a<='9')
    return (a-'0');
  if (a>='a' && a<='f')
    return (a-'a'+10);
  if (a>='A' && a<='F')
    return (a-'A'+10);
  return 0;
}

/***************************************************************************/
/*                                                                         */
/* open all devices                                                        */
/*                                                                         */
/***************************************************************************/

LONG OpenDevs()
{ LONG ret = 0;

  if ((gb->WindowPort=CreateMsgPort()) != NULL)
    if ((gb->TimerPort=CreateMsgPort()) != NULL)
      if ((gb->TimerIO=CreateIORequest(gb->TimerPort,sizeof(struct timerequest))) != NULL)
        if (!OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)gb->TimerIO,0))
        { gb->TimerOpen = -1;
          if ((gb->AudioPort=CreateMsgPort()) != NULL)
            if ((gb->AudioIO[0]=CreateIORequest(gb->AudioPort,sizeof(struct IOAudio))) != NULL)
              if ((gb->AudioIO[1]=CreateIORequest(gb->AudioPort,sizeof(struct IOAudio))) != NULL)
                if (!OpenDevice(AUDIONAME,0,(struct IORequest *)gb->AudioIO[0],0))
                {
                  gb->AudioOpen = -1;
                  if ((gb->WaveForm=AllocMem(8,MEMF_CHIP|MEMF_PUBLIC|MEMF_CLEAR)) != NULL)
                  {
                    ((ULONG *)gb->WaveForm)[0]=0x7f807f80; /* rectangle */
                    ret = 1;
                  }
                }
        }
  return ret;
}

/***************************************************************************/
/*                                                                         */
/* open all libraries                                                      */
/*                                                                         */
/***************************************************************************/

LONG OpenLibs()
{ LONG ret = 0;

  if ((gb->DOSBase=(struct DosLibrary *)OpenLibrary("dos.library",37L)) != NULL)
    if ((gb->IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",37L)) != NULL)
      if ((gb->GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",37L)) != NULL)
        if ((gb->GadtoolsBase=OpenLibrary("gadtools.library",37L)) != NULL)
          if ((gb->IconBase=OpenLibrary("icon.library",37L)) != NULL)
          { gb->DataTypesBase = OpenLibrary("datatypes.library",39L);
            if ((gb->LocaleBase=(struct LocaleBase *)OpenLibrary("locale.library",38L)) != NULL)
              gb->Catalog = OpenCatalogA(NULL,"tolleuhr.catalog",NULL);
            ret = 1;
          }
  return ret;
}

/***************************************************************************/
/*                                                                         */
/* main function                                                           */
/*                                                                         */
/***************************************************************************/

VOID Main()
{ struct WBStartup *wbmsg;
  struct Process *proc;
  ULONG mask;

  proc = (struct Process *)FindTask(NULL); wbmsg = NULL;

  if (!proc->pr_CLI)
  {
    wbmsg = (struct WBStartup *)(WaitPort(&proc->pr_MsgPort),GetMsg(&proc->pr_MsgPort));
  }

  if (gb->SysBase->LibNode.lib_Version >= 37)
  {
    gb->V39 = (gb->SysBase->LibNode.lib_Version >= 39);

    if (OpenLibs())
    {
      if (OpenDevs())
      {
        if (GetArguments(wbmsg))
        {
          if (InitClock())
          {
            if (gb->ScrNotify)
              AddScreenNotify();

            for(gb->AlStd=12,gb->GoOn=-1;;)
            {
              mask = 1L<<SIGBREAKB_CTRL_C | 1L<<gb->WindowPort->mp_SigBit |
                     1L<<gb->TimerPort->mp_SigBit | 1L<<gb->AudioPort->mp_SigBit;

              if (gb->NotifyPort)
                mask |= 1L<<gb->NotifyPort->mp_SigBit;

              if (gb->AboutReq)
                mask |= 1L<<gb->AboutReq->UserPort->mp_SigBit;

              if (Wait(mask) & SIGBREAKF_CTRL_C) /* User-Break */
                break;

              if (GetMsg(gb->TimerPort))
              {
                if (NextTick())
                {
                  NewTitle(); ZifferBlatt(); TestIfAlarm();
                }
                Zeichnen();
              }

              if (GetMsg(gb->AudioPort))
                PlayNote();

              if (gb->NotifyHandle && !CheckNotify())
                break;

              CheckAboutReq();

              gb->Activate = -1; CheckWindows(); gb->Activate = 0;

              if (!gb->GoOn)
                break;
            }
            RemScreenNotify();
          }
          CloseGfx();

          FreeVec(gb->PubName);

          FreeVec(gb->BFInfo.PictureName);

          if (gb->ChangedPri)
            (VOID)SetTaskPri(gb->WindowPort->mp_SigTask,gb->OldPri);

          FreeVec(gb->NameBuf);

          if (gb->ChangedDir)
            (VOID)CurrentDir(gb->OldLock);
        }
      }
      CloseDevs();
    }
    CloseLibs();
  }

  if (wbmsg)
  {
    Forbid();
    ReplyMsg((struct Message *)wbmsg);
  }
}

/***************************************************************************/
/*                                                                         */
/* close all libraries                                                     */
/*                                                                         */
/***************************************************************************/

VOID CloseLibs()
{ struct Catalog *cat;

  if ((cat=gb->Catalog) != NULL)
    CloseCatalog(cat);
  CloseLibrary((struct Library *)gb->LocaleBase);
  CloseLibrary(gb->DataTypesBase);
  CloseLibrary(gb->IconBase);
  CloseLibrary(gb->GadtoolsBase);
  CloseLibrary((struct Library *)gb->GfxBase);
  CloseLibrary((struct Library *)gb->IntuitionBase);
  CloseLibrary((struct Library *)gb->DOSBase);
}

/***************************************************************************/
/*                                                                         */
/* close all devices                                                       */
/*                                                                         */
/***************************************************************************/

VOID StopIO(struct IORequest *ioReq)
{
  AbortIO(ioReq); WaitIO(ioReq);
}

VOID StopAudio()
{
  if (gb->SoundOn) {
    if (gb->AudioSent[1])
      StopIO((struct IORequest *)gb->AudioIO[1]);
    if (gb->AudioSent[0])
      StopIO((struct IORequest *)gb->AudioIO[0]);
    EndTune();
  }
}

VOID CloseDevs()
{ APTR buf;

  if (gb->AudioOpen)
    { StopAudio(); CloseDevice((struct IORequest *)gb->AudioIO[0]); }
  DeleteIORequest((struct IORequest *)gb->AudioIO[1]);
  DeleteIORequest((struct IORequest *)gb->AudioIO[0]);
  DeleteMsgPort(gb->AudioPort);
  if ((buf=gb->WaveForm) != NULL) FreeMem(buf,8);

  if (gb->TimerOpen)
    CloseDevice((struct IORequest *)gb->TimerIO);
  DeleteIORequest((struct IORequest *)gb->TimerIO);
  DeleteMsgPort(gb->TimerPort);

  DeleteMsgPort(gb->WindowPort);
}

/***************************************************************************/
/*                                                                         */
/* close graphic stuff                                                     */
/*                                                                         */
/***************************************************************************/

/*
** free a bitmap
*/

VOID DeleteBitMap(struct BitMap *bm,ULONG depth)
{ PLANEPTR *p;
  ULONG d;

  if (bm != NULL)
  {
    WaitBlit();

    if (gb->V39)
      FreeBitMap(bm);
    else {
      for(p=&bm->Planes[0],d=depth; d!=0; d--)
        FreeVec(*p++);
      FreeVec(bm);
    }
  }
}

/*
** free some temporary stuff
*/

VOID NewSize1()
{ PLANEPTR buf;

  DeleteBitMap(RP1_BITMAP,gb->Depth); RP1_BITMAP=NULL;

  if ((buf=gb->Buf) != NULL)
    { FreeRaster(buf,gb->Width2,gb->Height2); gb->Buf=NULL; }
}

/*
** discard `About'
*/

VOID CloseAboutReq()
{
  FreeSysRequest(gb->AboutReq); gb->AboutReq = NULL;
}

/*
** discard all windows and other gfx stuff
*/

VOID CloseGfx()
{ struct Window *win;
  Object *obj;

  if (gb->TimerSent)
    { StopIO(&gb->TimerIO->tr_node); gb->TimerSent=0; }
  NewSize1();
  CloseAboutReq();
  FreeWindow(&gb->AlarmWin);
  FreeWindow(&gb->ColorWin);
  if ((win=gb->MainWin) != NULL)
    { gb->Top = win->TopEdge; gb->Left = win->LeftEdge;
      gb->Width = win->Width; gb->Height = win->Height; }
  CloseWindowSafely(win);
  gb->MainWin = NULL;
  FreeMenus(gb->Menus);
  gb->Menus = NULL;
  FreeVisualInfo(gb->VisualInfo);
  gb->VisualInfo = NULL;
  if (!gb->Activate)
    { if ((obj=gb->BFInfo.PictureObject) != NULL)
        DisposeDTObject(obj);
      gb->BFInfo.PictureObject = NULL;
      gb->BFInfo.BitMapHeader = NULL;
      gb->BFInfo.ImageBitMap = NULL; }
  DeleteBitMap(gb->BFInfo.SnapBitMap,gb->Depth);
  gb->BFInfo.SnapBitMap = NULL;
}

/*
** close a tool window
*/

VOID FreeWindow(struct WinGad *wg)
{
  CloseWindowSafely(wg->Win); wg->Win = NULL;
  FreeGadgets(wg->Gad); wg->Gad = NULL;
}

/*
** close a window with shared IDCMP
*/

VOID CloseWindowSafely(struct Window *win)
{ struct Node *msg,*succ;

  if (win != NULL)
  {
    Forbid();
    msg=win->UserPort->mp_MsgList.lh_Head; /* assumes valid port */
    while ((succ=msg->ln_Succ)!=NULL)
    { if (((struct IntuiMessage *)msg)->IDCMPWindow==win)
      {
        Remove(msg); ReplyMsg((struct Message *)msg);
      }
      msg=succ;
    }
    win->UserPort=NULL;
    ModifyIDCMP(win,0L);
    Permit();
    ClearMenuStrip(win);
    CloseWindow(win);
  }
}

/***************************************************************************/
/*                                                                         */
/* remove screen notification                                              */
/*                                                                         */
/***************************************************************************/

VOID RemScreenNotify()
{ APTR handle;

  if ((handle=gb->NotifyHandle) != NULL)
    while (!RemWorkbenchClient(handle)) Delay(10);
  gb->NotifyHandle = NULL;
  DeleteMsgPort(gb->NotifyPort);
  gb->NotifyPort = NULL;
  CloseLibrary(gb->ScreenNotifyBase);
  gb->ScreenNotifyBase = NULL;
}

/***************************************************************************/
/*                                                                         */
/* check screen notification                                               */
/*                                                                         */
/***************************************************************************/

LONG CheckNotify()
{ struct ScreenNotifyMessage *snm;
  LONG ret=1;

  while ((snm=(struct ScreenNotifyMessage *)GetMsg(gb->NotifyPort)) != NULL)
  {
    if (snm->snm_Type == SCREENNOTIFY_TYPE_WORKBENCH)
      switch((LONG)snm->snm_Value)
      { case TRUE:
          if (gb->MainWin || !ret || (ret=InitClock()))
            break;
        case FALSE:
          CloseGfx();
        default:
          break;
      }
    ReplyMsg(&snm->snm_Message);
  }
  return ret;
}

/***************************************************************************/
/*                                                                         */
/* process all input events                                                */
/*                                                                         */
/***************************************************************************/

VOID CheckAboutReq()
{ struct Window *req;

  if ((req=gb->AboutReq) != NULL)
    if (SysReqHandler(req,NULL,FALSE) != -2)
      CloseAboutReq();
}

/*
** process all windows with shared IDCMP
*/

VOID CheckWindows()
{ struct IntuiMessage *imsg;
  struct Window *iwin;
  ULONG class,code;
  APTR iadr;
  LONG tmp;

  while ((imsg=GT_GetIMsg(gb->WindowPort)) != NULL)
  { class = imsg->Class;
    code  = imsg->Code;
    iadr  = imsg->IAddress;
    iwin  = imsg->IDCMPWindow;
    GT_ReplyIMsg(imsg);
    if (gb->MainWin == iwin)
    { switch (class)
      { case CHANGEWINDOW:
          if (gb->Snap)
          { CloseGfx();
            if (!(gb->GoOn=InitClock()))
              return; }
          else
          { NewSize1();
            if (!(gb->GoOn=NewSize2()))
              return;
            Rahmen();
            ZifferBlatt();
            Zeichnen(); }
          break;
        case REFRESHWINDOW:
          gb->ReDrawx1=(tmp=gb->BoLeft);
          gb->ReDrawx2=tmp+gb->Width2-1;
          gb->ReDrawy1=(tmp=gb->BoTop);
          gb->ReDrawy2=tmp+gb->Height2-1;
          BeginRefresh(gb->MainWin);
          Rahmen();
          Zeichnen();
          EndRefresh(gb->MainWin,TRUE);
          break;
        case MENUPICK:
          HandleMenus(code);
          if (!gb->GoOn)
            return;
          break;
        case CLOSEWINDOW:
          gb->GoOn = 0; return;
          break;
        case ACTIVEWINDOW:
          StopAudio();
          gb->Active = -1;
          Rahmen();
          break;
        case INACTIVEWINDOW:
          gb->Active = 0;
          Rahmen();
          break;
      }
    }
    else
      if (gb->ColorWin.Win == iwin)
      { if (class == CLOSEWINDOW || class == GADGETUP)
        { FreeWindow(&gb->ColorWin);
          if (class!=CLOSEWINDOW) /* kann nur das Palette-Gadget sein */
          { *gb->Color = code;
            SetPattern();
            NewSize1();
            if (!(gb->GoOn=NewSize2()))
              return;
            Rahmen();
            ZifferBlatt();
            Zeichnen();
          }
        }
      }
      else
        if (gb->AlarmWin.Win == iwin)
        { if (class==CLOSEWINDOW || class==MOUSEMOVE || class==GADGETUP)
          { if (class!=CLOSEWINDOW)
            { switch(((struct Gadget *)iadr)->GadgetID)
              { case 1:
                  gb->St = code;
                  goto endit;
                case 2:
                  gb->Mi = code;
                  goto endit;
                case 3:
                  gb->AlStd = gb->St; gb->AlMin = gb->Mi; gb->Alarm = -1;
                case 4:
                  break;
              }
            }
            FreeWindow(&gb->AlarmWin);
            if (gb->Alarm)
            { struct MenuItem *item=ItemAddress(gb->Menus,FULLMENUNUM(1,7,1));
              if (!(item->Flags&CHECKED))
              { ClearMenuStrip(gb->MainWin);
                item->Flags |= CHECKED;
                ResetMenuStrip(gb->MainWin,gb->Menus);
              }
            }
endit:      ;
          }
        }
  }
}

/*
** function for Menu-Handling
*/

CONST UWORD ColorWinInfo[] = {
  CLOSEWINDOW | PALETTEIDCMP, MSG_CHOOSE,
  1,
  4,2,144,68,0,PALETTE_KIND,
};

CONST UWORD AlarmWinInfo[] = {
  CLOSEWINDOW | BUTTONIDCMP | SLIDERIDCMP, MSG_ALARM,
  4,
  42,15,20,39,0         ,SLIDER_KIND,
  82,15,20,39,0         ,SLIDER_KIND,
   2,58,70,12,MSG_USE   ,BUTTON_KIND,
  76,58,70,12,MSG_CANCEL,BUTTON_KIND,
};

CONST struct TagItem AlarmGadTags1[] = {
  { GTSL_Min        ,1               },
  { GTSL_Max        ,12              },
  { GTSL_MaxLevelLen,10              },
  { GTSL_LevelPlace ,PLACETEXT_ABOVE },
  { PGA_FREEDOM     ,LORIENT_VERT    },
  { GA_RelVerify    ,TRUE            },
  { TAG_DONE        ,NULL            }
};

CONST struct TagItem AlarmGadTags2[] = {
  { GTSL_Min        ,0               },
  { GTSL_Max        ,59              },
  { GTSL_MaxLevelLen,5               },
  { GTSL_LevelPlace ,PLACETEXT_ABOVE },
  { PGA_FREEDOM     ,LORIENT_VERT    },
  { GA_RelVerify    ,TRUE            },
  { TAG_DONE        ,NULL            }
};

VOID HandleMenus(ULONG code)
{ struct TagItem tags[6],*tt[4];
  struct MenuItem *item;
  struct Window *req;
  ULONG mnum,inum,snum,*p,ok=1;
  BYTE check;

  while ((UWORD)code != MENUNULL)
  { item = ItemAddress(gb->Menus,code);
    mnum = MENUNUM(code); inum = ITEMNUM(code); snum = SUBNUM(code);
    (UWORD)code = item->NextSelect;

    switch (mnum)
    { case 0: /* Project */
        switch (inum)
        { case 0: /* About */
            if (gb->AboutReq == NULL)
            {
              gb->EasyBuf.es_StructSize   = 5*sizeof(ULONG);
              gb->EasyBuf.es_Flags        = 0;
              gb->EasyBuf.es_Title        = GetCatalogString(MSG_ABOUT);
              gb->EasyBuf.es_TextFormat   = PROGNAME;
              gb->EasyBuf.es_GadgetFormat = GetCatalogString(MSG_DESCRIPTION);
              req = BuildEasyRequestArgs(gb->MainWin,&gb->EasyBuf,NULL,NULL);
              if ((ULONG)req>1)
                gb->AboutReq = req;
            }
            break;
          case 1: /* Quit */
            gb->GoOn = 0; return;
            break;
        }
        break;
      case 1: /* Settings */
        check = (item->Flags&CHECKED ? 1 : 0);
        switch (inum)
        { case  0: /* Seconds */
            gb->Seconds = check;
            StopIO(&gb->TimerIO->tr_node);
            NextTick();
            break;
          case  1: /* Oval */
            gb->Oval = check;
            break;
          case  2: /* Show */
            if (check)
              gb->Show = snum;
            break;
          case  3: /* Hands */
            if (check)
              switch (snum)
              { case 0: case 1: case 2: case 3:
                  gb->HandType  = snum;
                  break;
                case 5: case 6: case 7: case 8: case 9:
                  gb->HandWidth = snum-5;
                  break;
              }
            break;
          case  4: /* Shadow */
            gb->Shadow = check;
            break;
          case  5: /* Border */
            switch (snum)
            { case 0: case 1: case 2:
                if (check)
                  gb->BorderCount = snum;
                break;
              case 4:
                gb->HiRes = check;
                break;
              case 5:
                gb->Interlace = check;
                break;
            }
            break;
          case  6: /* Chime */
            switch (snum)
            { case 0: case 1: case 2:
                if (check)
                  gb->Chime = snum;
                break;
              case 4:
                gb->SmartHour = check;
                break;
            }
          case  7: /* Alarm */
            switch (snum)
            { case 0:
                if (gb->AlarmWin.Win == NULL)
                {
                  gb->St = gb->AlStd; tt[0] = &tags[0];
                  p = (ULONG *)tt[0];
                  *p++ = GTSL_Level; *p++ = gb->AlStd;
                  *p++ = GTSL_LevelFormat; *p++ = (ULONG)"%2ld";
                  *p++ = TAG_MORE; *p = (ULONG)&AlarmGadTags1[0];
                  gb->Mi = gb->AlMin; tt[1] = &tags[3];
                  p = (ULONG *)tt[1];
                  *p++ = GTSL_Level; *p++ = gb->AlMin;
                  *p++ = GTSL_LevelFormat; *p++ = (ULONG)"%02ld";
                  *p++ = TAG_MORE; *p = (ULONG)&AlarmGadTags2[0];
                  tt[2] = NULL; tt[3] = NULL;
                  CreateRequest(&gb->AlarmWin,(struct wInfo *)&AlarmWinInfo[0],&tt[0]);
                }
                break;
              case 1:
                gb->Alarm = check;
                break;
            }
            break;
          case  8: /* Window Properties */
            switch (snum)
            { case 0:
                gb->CloseGad = check;
                break;
              case 1:
                if (gb->Backdrop != check)
                  { gb->Backdrop = check; ok=0; }
                break;
              case 2:
                gb->Fixed = check;
                break;
              case 3:
                gb->Snap = 0; gb->UseImage = check;
                DeleteBitMap(gb->BFInfo.SnapBitMap,gb->Depth);
                gb->BFInfo.SnapBitMap = NULL;
                break;
              case 4:
                gb->UseImage = 0; if ((gb->Snap=check)) { ok=0; }
                DeleteBitMap(gb->BFInfo.SnapBitMap,gb->Depth);
                gb->BFInfo.SnapBitMap = NULL;
                break;
            }
            break;
          case 10: /* Save Settings */
            SaveSettings();
            break;
        }
        break;
      case 2: /* Colours */
      case 3: /* Pattern */
        if (gb->ColorWin.Win == NULL)
        {
          gb->Color = (mnum == 2 ? &gb->Pens[inum] : &gb->Must[inum]);
          tt[0] = &tags[0];
          p = (ULONG *)tt[0];
          *p++ = GTPA_Depth; *p++ = gb->Depth; *p = TAG_DONE;
          CreateRequest(&gb->ColorWin,(struct wInfo *)&ColorWinInfo[0],&tt[0]);
        }
        break;
    }
  }
  if (!ok)
  {
    CloseGfx(); gb->GoOn=InitClock();
  }
  else
  {
    NewSize1();
    if (!(gb->GoOn=NewSize2()))
      return;
    Rahmen();
    ZifferBlatt();
    Zeichnen();
  }
}

/***************************************************************************/
/*                                                                         */
/* create a bitmap (use a V39+ function if available)                      */
/*                                                                         */
/***************************************************************************/

struct BitMap *CreateBitMap(ULONG width,ULONG height,ULONG depth)
{ struct BitMap *bm;
  PLANEPTR *p;
  LONG size,plus,i;

  if (gb->V39)
    return AllocBitMap(width,height,depth,BMF_MINPLANES,PUB_SCREEN->RastPort.BitMap);

  plus=0; if (depth>8) plus=(depth-8)*sizeof(PLANEPTR);

  if ((bm=AllocVec(sizeof(struct BitMap)+plus,MEMF_ANY)) != NULL)
  {
    InitBitMap(bm,depth,width,height);

    p=&bm->Planes[0]; size=bm->BytesPerRow*bm->Rows; i=depth;
    do {
      if ((*p++=AllocVec(size,MEMF_CHIP)) == NULL)
        break;
    } while(--i);

    if (i != 0)
      { DeleteBitMap(bm,depth-i); bm=NULL; }
  }
  return bm;
}

/***************************************************************************/
/*                                                                         */
/* resize invisible gadgets, init hidden drawing area                      */
/*                                                                         */
/***************************************************************************/

CONST WORD Sizes[][4] = { /* Für Gadgets */
  /* close */ {  0, 0, 3, 2 },
  /* depth */ { -3, 0, 3, 2 },
  /* size  */ { -3,-2, 3, 2 },
  /* drag  */ {  0, 2, 0,-4 }
};

LONG NewSize2()
{ struct Window *win = gb->MainWin;
  struct Gadget *gad;
  struct BitMap *bm;
  LONG tmp,w,w2,h,h2,bc,ret;
  WORD i,j,k,newsize,*s;

  bc = gb->BorderCount;
  tmp = bc; if (gb->HiRes) tmp += (bc==2)<<1;
  gb->BoLeft = tmp;
  tmp = bc; if (gb->Interlace) tmp += (bc==2)<<1;
  gb->BoTop  = tmp;

  w = win->Width;  gb->Width  = w;
  h = win->Height; gb->Height = h;

  w2 = w-(gb->BoLeft<<1); gb->Width2  = w2;
  h2 = h-(gb->BoTop<<1);  gb->Height2 = h2;

  gb->xDouble = (gb->xMask == -2 && w2 > 12);
  gb->yDouble = (gb->yMask == -2 && h2 > 12);

  tmp = 0; if (w2 > 25) tmp = (w2 > 50 ? 2 : 1);
  gb->xOffset = tmp;
  tmp = 0; if (h2 > 25) tmp = (h2 > 50 ? 2 : 1);
  gb->yOffset = tmp;

  tmp = w; if (w > h) tmp = h; newsize = tmp/8;

  RemoveGList(win,win->FirstGadget,-1);

  if (gb->GadSize != newsize)
  { gb->GadSize = newsize;
    for (gad=&gb->GadgetBuf[0],s=(WORD *)&Sizes[0],i=GB_GADCOUNT-1; i>=0; gad++,i--)
      for (j=4-1,k=2; j>=0; k++,j--)
        ((UWORD *)gad)[k] = *s++*gb->GadSize;
  }

  if (!gb->Fixed)
    { tmp=3; gad=&gb->GadgetBuf[1]; if (gb->Backdrop) { gad+=1; tmp-=1; }
      AddGList(win,gad,0,tmp,NULL); }
  if (gb->CloseGad)
    AddGadget(win,&gb->GadgetBuf[0],0);

  bm = NULL;
  if (gb->UseImage)
    { bm=gb->BFInfo.ImageBitMap; w2=gb->BFInfo.BitMapHeader->bmh_Width; h2=gb->BFInfo.BitMapHeader->bmh_Height; }
  if (gb->Snap)
    { bm=gb->BFInfo.SnapBitMap; w2=gb->Width; h2=gb->Height; }
  gb->BFInfo.BitMap=bm; gb->BFInfo.Width=w2; gb->BFInfo.Height=h2;

  InitRastPort(&gb->RPort1); ret = 0;

  if ((RP1_BITMAP=CreateBitMap(gb->Width2,gb->Height2,gb->Depth=GetBitMapDepth(PUB_SCREEN->RastPort.BitMap))) != NULL)
    if ((gb->Buf=AllocRaster(gb->Width2,gb->Height2)) != NULL)
    {
      gb->RPort1.TmpRas  = InitTmpRas(&gb->TempRas,gb->Buf,RASSIZE(gb->Width2,gb->Height2));
      InitArea(&gb->AreaInfo,&gb->Table[0],5);
      gb->RPort1.AreaInfo= &gb->AreaInfo;
      ret = 1;
    }

  return ret;
}

/***************************************************************************/
/*                                                                         */
/* screen title will show the current date                                 */
/*                                                                         */
/***************************************************************************/

VOID NewTitle()
{
  struct DateTimeInfo *dti = &gb->DateTime;
  STRPTR p;

  DateStamp(&dti->dat.dat_Stamp);
  dti->dat.dat_StrDay  = &dti->buf[ 0];
  dti->dat.dat_StrDate = &dti->buf[18];
  DateToStr(&dti->dat);
  p=dti->dat.dat_StrDay;
  while (*p++); p[-1] = ','; *p++ = ' ';
  strcpy(p,dti->dat.dat_StrDate);
  SetWindowTitles(gb->MainWin,(UBYTE *)-1L,dti->dat.dat_StrDay);
}

/***************************************************************************/
/*                                                                         */
/* issue a timer request                                                   */
/*                                                                         */
/***************************************************************************/

ULONG NextTick()
{ struct timerequest *treq = gb->TimerIO;
  struct Device *TimerBase;
  ULONG tmp,old,std,min,sec;

  TimerBase=treq->tr_node.io_Device;
  GetSysTime(&treq->tr_time);
  treq->tr_time.tv_micro = 1100000-treq->tr_time.tv_micro;
                               /* ^ Ein ganz besonders netter Effekt: Das Timer */
                               /* Device rundet aktuelle Zeit und Wartezeit ab  */
  sec = treq->tr_time.tv_secs;
  tmp = 0; if (!gb->Seconds) tmp = 59-sec%60;
  treq->tr_time.tv_secs = tmp;
  treq->tr_node.io_Command = TR_ADDREQUEST;
  SendIO(&treq->tr_node);
  gb->TimerSent = -1;

  old=gb->Std+gb->Min;
  min=sec/60;
  gb->Sec=sec%60;
  std=min/12;
  gb->Min=(min%=60);
  gb->Std=(std%=60);

  return (std+min-old); /* sec==0 ist unzuverlässig bei hoher CPU-Auslastung */
}

/***************************************************************************/
/*                                                                         */
/* draw border                                                             */
/*                                                                         */
/***************************************************************************/

VOID Rahmen()
{ struct RastPort *rp = gb->RPort;
  ULONG pen,pen11,pen12;
  LONG w,h,bl,bt;

  if (gb->BorderCount)
  {
    w = gb->Width; h = gb->Height; pen11 = gb->Pens[11]; pen12 = gb->Pens[12];

    if (gb->Active && gb->BorderCount == 1)
      { pen=pen11; pen11=pen12; pen12=pen; }

    SetAPen(rp,pen11);
    Move(rp,1  ,h-1);
    Draw(rp,w-1,h-1);
    Draw(rp,w-1,1  );
    SetAPen(rp,pen12);
    Move(rp,0  ,h-1);
    Draw(rp,0  ,0  );
    Draw(rp,w-1,0  );

    if (gb->BorderCount == 2)
    {
      bl = gb->BoLeft; bt = gb->BoTop;
      SetAPen(rp,gb->Pens[12]);
      Move(rp,bl  ,h-bt);
      Draw(rp,w-bl,h-bt);
      Draw(rp,w-bl,bt  );
      SetAPen(rp,gb->Pens[11]);
      Move(rp,bl-1,h-bt);
      Draw(rp,bl-1,bt-1);
      Draw(rp,w-bl,bt-1);
      pen = gb->Pens[10]; if (gb->Active) pen = gb->Pens[13];
      SetAPen(rp,pen);

      if (gb->HiRes)
      {
        RectFill(rp,1,1,2,h-2);
        RectFill(rp,w-3,1,w-2,h-2);
      }

      if (gb->Interlace)
      {
        RectFill(rp,1,1,w-2,2);
        RectFill(rp,1,h-3,w-2,h-2);
      }
    }
  }
  SetAPen(rp,gb->Pens[0]);
}

/***************************************************************************/
/*                                                                         */
/* create the clock                                                        */
/*                                                                         */
/***************************************************************************/

CONST BYTE sinus[] = { /* sinus-Tabelle */
  0,13,26,39,52,64,75,85,94,103,110,116,121,124,126,
  127,126,124,121,116,110,103,94,85,75,64,52,39,26,13,
  0,-13,-26,-39,-52,-64,-75,-85,-94,-103,-110,-116,-121,-124,-126,
  -127,-126,-124,-121,-116,-110,-103,-94,-85,-75,-64,-52,-39,-26,-13
},
           cosinus[] = { /* cosinus-Tabelle */
  127,126,124,121,116,110,103,94,85,75,64,52,39,26,13,
  0,-13,-26,-39,-52,-64,-75,-85,-94,-103,-110,-116,-121,-124,-126,
  -127,-126,-124,-121,-116,-110,-103,-94,-85,-75,-64,-52,-39,-26,-13,
  0,13,26,39,52,64,75,85,94,103,110,116,121,124,126
};

CONST BYTE srect[] = { /* Für rechteckiges Zifferblatt */
  0,13,27,41,57,73,92,111,124,127,127,127,127,127,127,
  127,127,127,127,127,127,127,124,111,92,73,57,41,27,13,
  0,-13,-27,-41,-57,-73,-92,-111,-124,-127,-127,-127,-127,-127,-127,
  -127,-127,-127,-127,-127,-127,-127,-124,-111,-92,-73,-57,-41,-27,-13
},
           crect[] = {
  127,127,127,127,127,127,127,124,111,92,73,57,41,27,13,
  0,-13,-27,-41,-57,-73,-92,-111,-124,-127,-127,-127,-127,-127,-127,
  -127,-127,-127,-127,-127,-127,-127,-124,-111,-92,-73,-57,-41,-27,-13,
  0,13,27,41,57,73,92,111,124,127,127,127,127,127,127
};

CONST UBYTE pent[] = { /* Pen-Tabelle f. Zifferblatt */
  6,8,8,7,8,8,7,8,8,7,8,8
};

CONST BYTE dx1[] = { /* Daten für Zifferblatt */
  -2, 2, 1,-2, 1, 2,-2,-2,-1,-2,-1,-2
},
           dy1[] = {
  -4,-1,-2,-2, 2, 1,-2, 1, 2,-2,-2,-1
},
           dx2[] = {
   4,-2, 1, 4, 1,-2, 4, 2,-1, 4,-1, 2
},
           dy2[] = {
   0, 3, 2, 0,-2,-3, 0,-3,-2, 0, 2, 3
},
           dx3[] = {
   0,-2,-3, 0,-3,-2, 0, 2, 3, 0, 3, 2
},
           dy3[] = {
   8,-1, 2, 4,-2, 1, 4, 1,-2, 4, 2,-1
},
           dx4[] = {
  -4, 2,-1,-4,-1, 2,-4,-2, 1,-4, 1,-2
},
           dy4[] = {
   0,-3,-2, 0, 2, 3, 0, 3, 2, 0,-2,-3
};

VOID ZifferBlatt()
{ struct RastPort *rp = &gb->RPort1;
  LONG i,a,b,c,x,y,bt,bl;

  if (gb->BFInfo.BitMap)
  {
    x=0; y=0; if (gb->Snap) { x=gb->BoLeft; y=gb->BoTop; }

    CopyTiledBitMap(gb->BFInfo.BitMap,gb->BFInfo.Width,gb->BFInfo.Height,
                    RP1_BITMAP,gb->Width2,gb->Height2,x,y);
  }
  else
  {
    BNDRYOFF (rp);
    SetAPen  (rp,-1);
    SetAfPt  (rp,&gb->Muster[0][0],-1);
    RectFill (rp,0,0,gb->Width2-1,gb->Height2-1);
    SetAfPt  (rp,0,0);
  }

  i = gb->Show; a = (i > 1 ? (i == 3 ? 60 : 15) : (i ? 5 : 1));
  if (i < 4)
    for (i=0; i<60; i+=a)
    { x = gb->Width2 /2 + (((gb->Oval ?   sinus[i] : srect[i])*gb->Width2 /300) & gb->xMask);
      y = gb->Height2/2 - (((gb->Oval ? cosinus[i] : crect[i])*gb->Height2/300) & gb->yMask);
      if (i%5)
      { if (i%5==1)
        { c=gb->Pens[9];
          SetAPen(rp,c);
          SetOPen(rp,c);
        }
        AreaMove (rp,x+gb->Width2/100,y);
        AreaDraw (rp,x,y+gb->Height2/100);
        AreaDraw (rp,x-gb->Width2/100,y);
        AreaDraw (rp,x,y-gb->Height2/100);
        AreaEnd  (rp);
      }
      else
      {
        b=i/5;
        c=gb->Pens[pent[b]];
        SetAPen(rp,c);
        SetOPen(rp,c);
        AreaMove (rp,x+=gb->Width2*dx1[b]/100,y+=gb->Height2*dy1[b]/100);
        AreaDraw (rp,x+=gb->Width2*dx2[b]/100,y+=gb->Height2*dy2[b]/100);
        AreaDraw (rp,x+=gb->Width2*dx3[b]/100,y+=gb->Height2*dy3[b]/100);
        AreaDraw (rp,x+=gb->Width2*dx4[b]/100,y+=gb->Height2*dy4[b]/100);
        AreaEnd  (rp);
      }
    }
  if (gb->Shadow)
  { Zeiger(gb->Std,440,1,gb->Pens[5],gb->Pens[5]);
    Zeiger(gb->Min,300,1,gb->Pens[5],gb->Pens[5]); }
  Zeiger(gb->Std,440,0,gb->Pens[3],gb->Pens[4]);
  Zeiger(gb->Min,300,0,gb->Pens[1],gb->Pens[2]);

  bl = gb->BoLeft;
  gb->ReDrawx1 = bl; gb->ReDrawx2 = bl + gb->Width2  - 1;
  bt = gb->BoTop;
  gb->ReDrawy1 = bt; gb->ReDrawy2 = bt + gb->Height2 - 1;
}

/*
** create hands
*/

CONST LONG HandWidth[] = {
  4000,3250,2500,1750,1000
};

VOID Zeiger(ULONG winkel,LONG lfactor,LONG offset,LONG apen,LONG open)
{ struct RastPort *rp;
  LONG x0,y0,x1,y1,x2,y2,tmp;

  tmp = gb->Width2;
  x2 = sinus  [winkel]*tmp/lfactor;
  x1 = cosinus[winkel]*tmp/HandWidth[gb->HandWidth];
  x0 = tmp/2 + (offset ? gb->xOffset : 0);

  tmp = gb->Height2;
  y2 = cosinus[winkel]*tmp/lfactor;
  y1 = sinus  [winkel]*tmp/HandWidth[gb->HandWidth];
  y0 = tmp/2 + (offset ? gb->yOffset : 0);

  rp = &gb->RPort1;

  SetAPen(rp,apen);
  SetOPen(rp,open);

  switch (gb->HandType)
  { case 0:
      SetAPen(rp,open);
      Move (rp,x0,y0);
      Draw (rp,x0+x2,y0-y2);
      if (gb->xDouble)
      { Move (rp,1+x0,y0);
        Draw (rp,1+x0+x2,y0-y2); }
      if (gb->yDouble)
      { Move (rp,x0,1+y0);
        Draw (rp,x0+x2,1+y0-y2); }
      break;
    case 1:
      AreaMove (rp,x0+x2     ,y0-y2     );
      AreaDraw (rp,x0+x1-x2/4,y0+y1+y2/4);
      AreaDraw (rp,x0-x1-x2/4,y0-y1+y2/4);
      AreaEnd  (rp);
      break;
    case 2:
      AreaMove (rp,x0+x2  ,y0-y2  );
      AreaDraw (rp,x0+x1  ,y0+y1  );
      AreaDraw (rp,x0-x2/4,y0+y2/4);
      AreaDraw (rp,x0-x1  ,y0-y1  );
      AreaEnd  (rp);
      break;
    case 3:
      AreaMove (rp,x0+=x2-x1/2,y0-=y2+y1/2);
      AreaDraw (rp,x0+=x1     ,y0+=y1     );
      AreaDraw (rp,x0-=x2*5/4 ,y0+=y2*5/4 );
      AreaDraw (rp,x0-=x1     ,y0-=y1     );
      AreaEnd  (rp);
      break;
  }
}

/***************************************************************************/
/*                                                                         */
/* display the clock (blit from the `hidden' window to the real one)       */
/*                                                                         */
/***************************************************************************/

VOID Zeichnen()
{ struct RastPort *rp = gb->RPort;
  LONG tmp,x1,y1,x2,y2;

  BltBitMapRastPort(RP1_BITMAP,gb->ReDrawx1-gb->BoLeft,gb->ReDrawy1-gb->BoTop,
                    rp,gb->ReDrawx1,gb->ReDrawy1,gb->ReDrawx2-gb->ReDrawx1+1,
                    gb->ReDrawy2-gb->ReDrawy1+1,0xc0);
  if (gb->Seconds)
  {
    x1 = gb->Width/2;
    x2 = x1+sinus[gb->Sec]*gb->Width2/300;
    y1 = gb->Height/2;
    y2 = y1-cosinus[gb->Sec]*gb->Height2/300;

    Move(rp,x1,y1);
    Draw(rp,x2,y2);

    if (gb->xDouble)
    { Move(rp,x1+1,y1);
      Draw(rp,x2+1,y2); }

    if (gb->yDouble)
    { Move(rp,x1,y1+1);
      Draw(rp,x2,y2+1); }

    if (x1 > x2)
    { tmp=x1; x1=x2; x2=tmp; }
    if (gb->xDouble)
      x2++;
    gb->ReDrawx1 = x1; gb->ReDrawx2 = x2;

    if (y1 > y2)
    { tmp=y1; y1=y2; y2=tmp; }
    if (gb->yDouble)
      y2++;
    gb->ReDrawy1 = y1; gb->ReDrawy2 = y2;
  }
}

/***************************************************************************/
/*                                                                         */
/* initialize background pattern mask                                      */
/*                                                                         */
/***************************************************************************/

VOID SetPattern()
{ LONG i,a,b,c,d;
  UBYTE *p1;
  UWORD *p2;

  p1=&gb->Must[0]; a=*p1++; b=*p1++; c=*p1++; d=*p1;

  if (a==b && c==d) /* nur horizontale Streifen */
    gb->xMask = -1;
  else
    gb->xMask = -2;

  if (a==c && b==d) /* nur vertikale Streifen */
    gb->yMask = -1;
  else
    gb->yMask = -2;

  for (p2=&gb->Muster[0][0],i=0; i<8; i++)
  { *p2=(a&1<<i?0x5555:0)|(b&1<<i?0xaaaa:0); p2++;
    *p2=(c&1<<i?0x5555:0)|(d&1<<i?0xaaaa:0); p2++; }
}

/***************************************************************************/
/*                                                                         */
/* save current settings to the programs icon                              */
/*                                                                         */
/***************************************************************************/

enum {
  TT_TOP=0,TT_LEFT,TT_WIDTH,TT_HEIGHT,TT_SHOWFACE,TT_HANDTYPE,TT_HANDWIDTH,
  TT_BORDERTYPE,TT_WINDOWTYPE,TT_CHIME,TT_SECONDS,TT_OVAL,TT_SHADOW,
  TT_DRAWPENS,TT_PATTERN
};

CONST STRPTR ToolTypes[] = {
  "TOP","LEFT","WIDTH","HEIGHT","SHOWFACE","HANDTYPE","HANDWIDTH","BORDERTYPE",
  "WINDOWTYPE","CHIME","SECONDS","OVAL","SHADOW","DRAWPENS","PATTERN"
};

#define TTCOUNT (sizeof(ToolTypes)/sizeof(ToolTypes[0]))

VOID SaveSettings()
{ struct DiskObject *dobj;
  BPTR old_cd;

  old_cd = CurrentDir(gb->ProgArg.wa_Lock);

  if ((dobj=GetDiskObjectNew(gb->ProgArg.wa_Name)) != NULL) /* Tooltypes einlesen */
  {
    UBYTE **tt,*t,*tbuf,*tbuf1;
    char **tmp,*p,**p1,**p2;
    LONG arg,i;

    p1=dobj->do_ToolTypes; p2=p1; do {} while(*p2++);

    if ((tbuf=AllocVec(((STRPTR)p2-(STRPTR)p1)+sizeof(ToolTypes)+40*TTCOUNT,MEMF_ANY)))
    {
      tt=(UBYTE **)(tbuf+40*TTCOUNT);

      p1=dobj->do_ToolTypes; p2=(char **)tt; do {} while((*p2++=*p1++));

      for (i=0;i<TTCOUNT;i++) /* eigene Tooltypes entfernen */
      { if (FindToolType(tt,t=GetToolType(i)))
        { p2=(char **)tt; do {} while(*++p2 && FindToolType((UBYTE **)p2,t));
          do { p=*p2++; } while((p2[-2]=p));
        }
      }

      /* Tooltypes zählen */
      p1=(char **)tt; do {} while(*p1++); --p1; tbuf1=tbuf;

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_TOP,gb->MainWin->TopEdge);

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_LEFT,gb->MainWin->LeftEdge);

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_WIDTH,gb->Width);

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_HEIGHT,gb->Height);

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_SHOWFACE,gb->Show);

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_HANDTYPE,gb->HandType);

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_HANDWIDTH,gb->HandWidth);

      *p1++=tbuf1;
      arg = gb->BorderCount + (gb->HiRes ? 4 : 0) + (gb->Interlace ? 8 : 0);
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_BORDERTYPE,arg);

      *p1++=tbuf1;
      arg = (gb->CloseGad ? WT_CLOSEGAD : 0) + (gb->Backdrop ? WT_BACKDROP : 0) +
            (gb->Fixed ? WT_FIXED : 0) + (gb->UseImage ? WT_USEIMAGE : 0) +
            (gb->Snap ? WT_SNAP : 0);
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_WINDOWTYPE,arg);

      *p1++=tbuf1;
      arg = gb->Chime + (gb->SmartHour ? 4 : 0);
      tbuf1=SetToolType(tbuf1,"%s=%ld",TT_CHIME,arg);

      if (gb->Seconds)
        { *p1++=tbuf1; tbuf1=SetToolType(tbuf1,"%s",TT_SECONDS); }

      if (gb->Oval)
        { *p1++=tbuf1; tbuf1=SetToolType(tbuf1,"%s",TT_OVAL); }

      if (gb->Shadow)
        { *p1++=tbuf1; tbuf1=SetToolType(tbuf1,"%s",TT_SHADOW); }

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=x",TT_DRAWPENS); tbuf1=SetStr(tbuf1,&gb->Pens[0],14);

      *p1++=tbuf1;
      tbuf1=SetToolType(tbuf1,"%s=x",TT_PATTERN); tbuf1=SetStr(tbuf1,&gb->Must[0],4);

      *p1=NULL;

      tmp=dobj->do_ToolTypes;
      dobj->do_ToolTypes=(char **)tt;
      PutDiskObject(gb->ProgArg.wa_Name,dobj);
      dobj->do_ToolTypes=tmp;

      FreeVec(tbuf);
    }
    FreeDiskObject(dobj);
  }
  CurrentDir(old_cd);
}

/*
** make a tooltype string
*/

CONST ULONG tricky=0x16c04e75; /* move.b d0,(a3)+ ; rts */

STRPTR SetToolType(STRPTR buf,STRPTR fmt,LONG num,...)
{
  ((STRPTR *)&num)[0] = GetToolType(num);
  RawDoFmt(fmt,(APTR)&num,(void (*)())&tricky,buf);
  for(;*buf++;); return buf;
}

/*
** get a tooltype name
*/

STRPTR GetToolType(LONG num)
{
  return (STRPTR)ToolTypes[num];
}

/*
** make an ascii pen-string
*/

STRPTR SetStr(STRPTR dst,STRPTR src,LONG cnt)
{
  --dst;
  do
  { *dst++=DtoX(*src>>4);
    *dst++=DtoX(*src++&0xf);
  } while (--cnt);
  *dst++='\0'; return dst;
}

LONG DtoX(LONG a)
{ if ((a+='0')>('9'))
    a+=7;
  return(a);
}

/***************************************************************************/
/*                                                                         */
/* audio stuff                                                             */
/*                                                                         */
/***************************************************************************/

CONST UWORD AlarmTune[] = {
  800,800,800,800,800,800,800,1600,
  800,800,800,800,800,800,800,1600,
  800,800,800,800,800,800,800,1600,
  800,800,800,800,800,800,800,1600,
  800,800,800,800,800,800,800,4,0
};

CONST UWORD HourTune[] = {
  800,1600,800,1600,800,1600,800,1600,
  800,1600,800,1600,800,1600,800,1600,
  800,1600,800,1600,800,1600,800,4,0
};

CONST UWORD QuarterTune[] = {
  400,400,400,400,400,4,0
};

VOID TestIfAlarm()
{ CONST UWORD *tune;
  ULONG st2;
  LONG mi2,i;

  st2=gb->Std; mi2 = gb->Min;
  if (!(st2/=5))
    st2=12;
  tune = AlarmTune;
  if (!gb->Alarm || mi2!=gb->AlMin || st2!=gb->AlStd)
  { if (!gb->Chime)
      return;
    if (!mi2)
    {
      tune = HourTune; i=22; if (gb->SmartHour) i=24-(st2<<=1);
    }
    else
    { if (gb->Chime != 2)
        return;
      tune = QuarterTune;
      for(i=6;;)
       { i-=2;
         if ((mi2-=15)<0)
           return;
         if (mi2==0)
           break;
       }
    }
    tune = &tune[i];
  }
  StartTune(tune);
}

/*
** allocate audio channels and initiate the sound
*/

CONST UBYTE ChannelMap[] = { 1,2,4,8 }; /* Sound Channel Allocation Map */

VOID StartTune(CONST UWORD *tune)
{ struct IOAudio *io;

  if (!gb->SoundOn)
  {
    io = gb->AudioIO[0];
    io->ioa_Request.io_Command = ADCMD_ALLOCATE;
    io->ioa_Request.io_Message.mn_Node.ln_Pri = 90; /* ALARM-Level */
    io->ioa_Request.io_Flags   = ADIOF_NOWAIT;
    io->ioa_Data               = (UBYTE *)&ChannelMap[0];
    io->ioa_Length             = sizeof(ChannelMap);
    BeginIO(&io->ioa_Request);
    if (!WaitIO(&io->ioa_Request))
    {
      gb->SoundOn = -1;
      if (!(ciaa.ciapra & CIAF_LED))
        { ciaa.ciapra |= CIAF_LED; gb->Filter = -1; }

      CopyMemQuick(io,gb->AudioIO[1],sizeof(struct IOAudio));
      gb->PlayTune = (UWORD *)tune;
      PlayNote();
      PlayNote();
    }
  }
}

/*
** beep, beep, ...
*/

VOID PlayNote()
{ struct IOAudio *io;
  LONG old,new;
  BYTE *sent = &gb->AudioSent[0];

  old = gb->RequestNr;
  new = !old;
  gb->RequestNr = new;

  sent[old] =  0;
  if (!*gb->PlayTune)
  { if (!sent[new]) /* Anderer Request auch zurück ? */
      EndTune(); return; }
  sent[old] = -1;   /* Merken */
  io = gb->AudioIO[old];
  io->ioa_Request.io_Command = CMD_WRITE;
  io->ioa_Request.io_Flags   = ADIOF_PERVOL;
  io->ioa_Data               = (UBYTE *)&((ULONG *)gb->WaveForm)[old];
  io->ioa_Length             = 4;
  io->ioa_Period             = 500; /* ekliger Piepton */
  io->ioa_Volume             = 64;
  io->ioa_Cycles             = *(gb->PlayTune)++;
  BeginIO(&io->ioa_Request);
}

/*
** end audio output
*/

VOID EndTune()
{ struct IOAudio *io = gb->AudioIO[0];

  io->ioa_Request.io_Command=ADCMD_FREE;
  DoIO(&io->ioa_Request);
  if (gb->Filter)
    { ciaa.ciapra &= ~CIAF_LED; gb->Filter = 0; }
  gb->SoundOn = 0;
}

/***************************************************************************/
/*                                                                         */
/* create a new window and its gadgets                                     */
/*                                                                         */
/***************************************************************************/

CONST struct TextAttr Topaz = {
  "topaz.font",8,FS_NORMAL,FPF_ROMFONT
};

VOID CreateRequest(struct WinGad *wg,struct wInfo *wi,struct TagItem **tagptr)
{ struct Gadget *context;

  if ((context=CreateContext(&wg->Gad)) != NULL)
  {
    struct NewWindow *nw = &gb->NewWindowBuf;
    struct Window *win;
    struct gInfo *gi;
    UWORD cnt;

    nw->LeftEdge    = 100;
    nw->TopEdge     = 100;
    nw->Width       = 0;
    nw->Height      = 0;
    nw->Flags       = WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|WFLG_ACTIVATE|WFLG_RMBTRAP;
    nw->FirstGadget = wg->Gad;
    nw->Title       = GetCatalogString(wi->textnr);

    gb->NewGad.ng_GadgetID = 0; cnt = wi->gadcnt; gi = &wi->gi[0];
    do
    { struct NewGadget *ng = &gb->NewGad;
      struct Screen *scr = nw->Screen;
      ULONG num;

      ng->ng_LeftEdge   = gi->left + scr->WBorLeft;
      ng->ng_TopEdge    = gi->top  + scr->WBorLeft + scr->RastPort.TxHeight;
      ng->ng_Width      = gi->width;
      ng->ng_Height     = gi->height;
      if ((num=gi->textnr))
        num = (ULONG)GetCatalogString(num);
      ng->ng_GadgetText = (STRPTR)num;
      ng->ng_TextAttr   = (struct TextAttr *)&Topaz;
      ng->ng_GadgetID++;
      ng->ng_VisualInfo = gb->VisualInfo;

      if ((context=CreateGadgetA(gi->kind,context,ng,tagptr[0])) == NULL)
      {
        FreeGadgets(wg->Gad); wg->Gad = NULL; return;
      }

      gi++; tagptr++;
    } while(--cnt);

    if ((wg->Win=(win=OpenWindowShared(&gb->NewWindowBuf,&WindowTags[0],wi->idcmp))) != NULL)
    {
      SetWindowTitles(win,(UBYTE *)-1L,PROGNAME); GT_RefreshWindow(win,NULL);
    }
    else
    {
      FreeGadgets(wg->Gad); wg->Gad = NULL;
    }
  }
}

/***************************************************************************/
/*                                                                         */
/* open a window for shared IDCMP                                          */
/*                                                                         */
/***************************************************************************/

struct Window *OpenWindowShared(struct NewWindow *nw,CONST struct TagItem *tl,ULONG idcmp)
{ struct Window *win;

  if ((win=OpenWindowTagList(nw,(struct TagItem *)tl)) != NULL)
  {
    win->UserPort = gb->WindowPort;
    if (!ModifyIDCMP(win,idcmp))
    {
      CloseWindowSafely(win); win=NULL;
    }
  }
  return win;
}

/***************************************************************************/
/*                                                                         */
/* get localized string                                                    */
/*                                                                         */
/***************************************************************************/

CONST STRPTR LocStrings[] = {
  /* MSG_PROJECT     */ "Project",
  /* MSG_ABOUT       */ "About",
  /* MSG_ABOUTKEY    */ "?",
  /* MSG_QUIT        */ "Quit",
  /* MSG_QUITKEY     */ "Q",
  /* MSG_SETTINGS    */ "Settings",
  /* MSG_SECONDS     */ "Seconds",
  /* MSG_OVAL        */ "Oval",
  /* MSG_SHOW        */ "Show",
  /* MSG_MINUTES     */ "Minutes",
  /* MSG_HOURS       */ "Hours",
  /* MSG_QUARTER     */ "Quarter",
  /* MSG_ONE         */ "Twelve",
  /* MSG_NONE        */ "None",
  /* MSG_HANDS       */ "Hands",
  /* MSG_LINE        */ "Line",
  /* MSG_TRIANGLE    */ "Triangle",
  /* MSG_RHOMBUS     */ "Rhombus",
  /* MSG_RECTANGLE   */ "Rectangle",
  /* MSG_VERYTHIN    */ "Very Thin",
  /* MSG_THIN        */ "Thin",
  /* MSG_NORMAL      */ "Normal",
  /* MSG_THICK       */ "Thick",
  /* MSG_VERYTHICK   */ "Very Thick",
  /* MSG_SHADOW      */ "Shadow",
  /* MSG_BORDER      */ "Border",
  /* MSG_SINGLE      */ "Single",
  /* MSG_DOUBLE      */ "Double",
  /* MSG_HIRES       */ "HiRes",
  /* MSG_INTERLACE   */ "Interlace",
  /* MSG_CHIME       */ "Chime",
  /* MSG_SMART       */ "Smart",
  /* MSG_ALARM       */ "Alarm",
  /* MSG_SET         */ "Set",
  /* MSG_ON          */ "On",
  /* MSG_USEIMAGE    */ "Use Image",
  /* MSG_CLOSEGAD    */ "Close Gadget",
  /* MSG_SAVESETTING */ "Save Settings",
  /* MSG_SAVEKEY     */ "S",
  /* MSG_COLORS      */ "Colors",
  /* MSG_MINAPEN     */ "Min. Area",
  /* MSG_MINOPEN     */ "Min. Outline",
  /* MSG_HOURAPEN    */ "Hour Area",
  /* MSG_HOUROPEN    */ "Hour Outline",
  /* MSG_STR12       */ "Twelve",
  /* MSG_BORDER0     */ "Border 0",
  /* MSG_BORDER1     */ "Border 1",
  /* MSG_BORDER2     */ "Border 2",
  /* MSG_BORDER3     */ "Border 3",
  /* MSG_PATTERN     */ "Pattern",
  /* MSG_COLOR0      */ "Color 0",
  /* MSG_COLOR1      */ "Color 1",
  /* MSG_COLOR2      */ "Color 2",
  /* MSG_COLOR3      */ "Color 3",
  /* MSG_DESCRIPTION */ "a really nice clock !",
  /* MSG_CHOOSE      */ "Choose one:",
  /* MSG_USE         */ "Use",
  /* MSG_CANCEL      */ "Cancel",
  /*
  ** new with 1.5
  */
  /* MSG_WINDOW      */ "Window",
  /* MSG_BACKDROP    */ "Backdrop",
  /* MSG_FIXED       */ "Immovable",
  /*
  ** new with 1.6
  */
  /* MSG_SNAP        */ "Transparent"
};

STRPTR GetCatalogString(LONG strnum)
{ struct Catalog *cat;
  STRPTR loc;

  loc = LocStrings[strnum];
  if ((cat=gb->Catalog) != NULL)
    loc = GetCatalogStr(cat,strnum,loc);
  return loc;
}

/***************************************************************************/
/*                                                                         */
/* duplicate a bitmap (based on the work of P.Carette + W.Dörwald)         */
/*                                                                         */
/***************************************************************************/

VOID CopyTiledBitMap(struct BitMap *Src,LONG SrcSizeX,LONG SrcSizeY,
                     struct BitMap *Dst,LONG DstSizeX,LONG DstSizeY,
                     LONG SrcOffsetX,LONG SrcOffsetY)
{ LONG Pos;  /* used as starting position in the "exponential" blit */
  LONG Size; /* used as bitmap size in the "exponential" blit       */

  /* adjust source sizes */
  SrcSizeX -= SrcOffsetX; SrcSizeY -= SrcOffsetY;

  /* blit the first piece of the tile */
  blt_bitmap(Src,SrcOffsetX,SrcOffsetY,Dst,0,0,MIN(SrcSizeX,DstSizeX),MIN(SrcSizeY,DstSizeY),0xC0,-1,NULL);

  /* this loop generates the first row of the tiles */
  for (Pos = SrcSizeX,Size = MIN(SrcSizeX,DstSizeX-Pos);Pos<DstSizeX;)
  {
    blt_bitmap(Dst,0,0,Dst,Pos,0,Size,MIN(SrcSizeY,DstSizeY),0xC0,-1,NULL);
    Pos += Size;
    Size = MIN(Size<<1,DstSizeX-Pos);
  }

  /* this loop blit the first row down several times to fill the whole dest rect */
  for (Pos = SrcSizeY,Size = MIN(SrcSizeY,DstSizeY-Pos);Pos<DstSizeY;)
  {
    blt_bitmap(Dst,0,0,Dst,0,Pos,DstSizeX,Size,0xC0,-1,NULL);
    Pos += Size;
    Size = MIN(Size<<1,DstSizeY-Pos);
  }
}

/***************************************************************************/
/*                                                                         */
/* replacement functions                                                   */
/*                                                                         */
/***************************************************************************/

LONG blt_bitmap(struct BitMap *src,LONG xsrc,LONG ysrc,struct BitMap *dst,
                LONG xdst,LONG ydst,LONG xsize,LONG ysize,ULONG minterm,
                ULONG mask,PLANEPTR tempA)
{
  return BltBitMap(src,xsrc,ysrc,dst,xdst,ydst,xsize,ysize,minterm,mask,tempA);
}

Object *new_dt_object(APTR name,ULONG data,...)
{
  return NewDTObjectA(name,(struct TagItem *)&data);
}

ULONG get_dt_attrs(Object *obj,ULONG data,...)
{
  return GetDTAttrsA(obj,(struct TagItem *)&data);
}

/***************************************************************************/
/*                                                                         */
/* The End                                                                 */
/*                                                                         */
/***************************************************************************/
