#define INTUI_V36_NAMES_ONLY
#include <exec/types.h>
#include <exec/lists.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/text.h>
#include <intuition/intuition.h>
#include <devices/inputevent.h>
#include <libraries/tracker.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

/* Gadget-IDs */
#define GID_Up	    1
#define GID_PUp     2
#define GID_Top     3
#define GID_Down    4
#define GID_PDown   5
#define GID_Bottom  6

/* helpful pseudo-qualifiers: */
#define IEQUALIFIER_SHIFT (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
#define IEQUALIFIER_ALT (IEQUALIFIER_LALT | IEQUALIFIER_RALT)

#define INVALID -1  /* speziell für Stunde */
struct Termin { struct Termin *naechster, *voriger;
		short minute, stunde, monat,tag, /* short, damit keine ext's */
		      jahr;
		char  *text;
	      };

extern struct Library *IntuitionBase;

UBYTE *daynames[]={ "Montag", "Dienstag", "Mittwoch", "Donnerstag",
		    "Freitag", "Samstag", "Sonntag", "Woche" },
      *monthnames[]={ "Januar", "Februar", "März", "April", "Mai", "Juni",
	"Juli", "August", "September", "Oktober", "November", "Dezember" };

struct Window	*MyWin=NULL;
struct RastPort *MyRP;
struct TextFont *Font=NULL;
struct Library	*TrackerBase=NULL;
struct Termin	*Termine=NULL, *NeueTermine=NULL, *geloeschteTermine=NULL,
		Jetzt;
UWORD *pattern;
short TextHeight,	/* Höhe des Fonts */
      TextBaseline,	/* rp->TxBaseline */
      MaxDayNameWidth,	/* max. Breite eines Wochentags-Namens in Pixeln */
      MaxDigitWidth,	/* max. Breite einer Ziffer in Pixeln */
      MaxNumWidth,	/* max. Breite einer Zahl zwischen 0 und 59 in Pixeln */
      CalBoxWidth,	/* Breite der Monatsübersicht (mit Wochentagen) in
			   Pixeln */
      LinesShown,	/* Anzahl der Zeilen, die in Window passen */
      LinesAbove,	/* Anzahl der Zeilen, die (wegen Scrolling) oben
			   versteckt werden */
      LinesToHide,	/* Anzahl der Zeilen, die (wegen Scrolling) oben
			   noch versteckt werden sollen [während des Aufbaus
			   der Liste] */
      LinesUnder;	/* Anzahl der Zeilen, die (wegen Windowrahmen) unten
			   versteckt werden */
char *MainFileName="Termine", *OldFileName=NULL;
BOOL TerminBackup=TRUE;
#define MyIDCMP ( IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_RAWKEY | \
		  IDCMP_REFRESHWINDOW | IDCMP_CHANGEWINDOW | \
		  IDCMP_GADGETUP | IDCMP_GADGETDOWN )
extern char wtitle[];		/* von TKal-main.c */
extern struct Gadget ggbottom;	/* von TKal-GGs.c */
Tag OWTags[]={ WA_Top,11, WA_Left,0, WA_Height,200, WA_Width,320,
	       WA_Activate,TRUE, WA_Title,(ULONG)wtitle, WA_IDCMP,MyIDCMP,
	       WA_RMBTrap,TRUE, WA_DragBar,TRUE, WA_SizeGadget,TRUE,
	       WA_SizeBBottom,TRUE, WA_CloseGadget,TRUE,
	       WA_DepthGadget,TRUE, WA_GimmeZeroZero,TRUE,
	       WA_SimpleRefresh,TRUE, WA_Gadgets,(ULONG)&ggbottom, TAG_END };
#define WTop	OWTags[1]
#define WLeft	OWTags[3]
#define WHeight OWTags[5]
#define WWidth	OWTags[7]

#ifdef PRIVATE
extern BOOL before(struct Termin *t1, struct Termin *t2);
extern void EnQueue(struct Termin **anchor, struct Termin *new),
	    DeQueue(struct Termin **anchor, struct Termin *obsolete),
	    BadMsg(char *txt), error(char *txt);
extern short MyTextWidth(UBYTE *txt);
extern struct GfxBase *GfxBase;
#else
BOOL before(struct Termin *t1, struct Termin *t2)
{ /* ist Termin *t1 VOR Termin *t2 ? [unbekannt / identisch -> FALSE] */

    if(t1->jahr<t2->jahr)       goto true;
    if(t1->jahr>t2->jahr)       goto false;
    if(t1->monat<t2->monat)     goto true;
    if(t1->monat>t2->monat)     goto false;
    if(t1->tag<t2->tag)         goto true;
    if(t1->tag>t2->tag)         goto false;
    if(t1->stunde==INVALID || t2->stunde==INVALID)  goto false;
    if(t1->stunde<t2->stunde)   goto true;
    if(t1->stunde>t2->stunde)   goto false;
    if(t1->minute<t2->minute)   goto true;
false:
    return(FALSE);
true:
    return(TRUE);
}

void EnQueue(struct Termin **anchor, struct Termin *new)
{   struct Termin *this;

    if(*anchor==NULL)
	{ /* bisher leere Liste */
	new->voriger=new->naechster=NULL;
	*anchor=new;
	return;
	}
    for(this=*anchor; this->naechster; this=this->naechster)
	if(before(new, this))
	    { /* vor *this einfügen */
	    new->voriger=this->voriger;
	    new->naechster=this;
	    if(this==*anchor)   *anchor = new;
	    else		this->voriger->naechster = new;
	    this->voriger = new;
	    return;
	    }
    /* nach *this am Ende der Liste anhängen */
    this->naechster=new;
    new->voriger=this;
    new->naechster=NULL;
}

void DeQueue(struct Termin **anchor, struct Termin *obsolete)
{
    if(*anchor==obsolete)   *anchor=obsolete->naechster;
    if(obsolete->naechster) obsolete->naechster->voriger=obsolete->voriger;
    if(obsolete->voriger)   obsolete->voriger->naechster=obsolete->naechster;
    obsolete->naechster=obsolete->voriger=NULL;
}

void CloseAll(void) { if(TrackerBase) CloseLibrary(TrackerBase); }

void BadMsg(char *txt)
{
    struct EasyStruct erq;

    if(IntuitionBase->lib_Version<36 || TrackerBase==NULL)
	{
	Write(Output(), txt, strlen(txt));
	Write(Output(), "\n", 1);
	}
    else{
	erq.es_StructSize=sizeof(struct EasyRequest);
	erq.es_Flags=0;
	erq.es_Title="TK";
	erq.es_GadgetFormat="Schade !";
	erq.es_TextFormat=(UBYTE *)txt;
	EasyRequest(MyWin, &erq, NULL, NULL);
	}
}

void error(char *txt)
{
    static char EHead[]="TK meldet einen Fehler:\n";
    char *all;

    if(TrackerBase==NULL)   goto error_non_trk;
    if(all=TRK_AllocMem(strlen(EHead)+strlen(txt)+1, MEMF_ANY))
	{
	strcpy(all, EHead);
	strcat(all, txt);
	BadMsg(all);
	}
    else{
error_non_trk:
	BadMsg(EHead);
	BadMsg(txt);
	}
    CloseAll();
    exit(RETURN_ERROR);
}

short MyTextWidth(UBYTE *txt)
{ return(TextLength(MyRP, txt, strlen(txt))); }

void OpenLibs(void)
{
    extern struct Library *GfxBase,*DOSBase,*SysBase;

    if(IntuitionBase->lib_Version<36 || GfxBase->lib_Version<36 ||
       DOSBase->lib_Version<36 || SysBase->lib_Version<36)
	error("OS2 nötig");
    if((TrackerBase=OpenLibrary("tracker.library", 4))==NULL)
	error("tracker.library V4+ nötig");
}
#define GfxBase ((struct GfxBase *)GfxBase)
#endif

void OpenGfx(void)
{
    register short i,j;
    char buf[4];

    if((pattern=TRK_AllocMem(8,MEMF_CLEAR|MEMF_CHIP))==NULL)
	error("8 Bytes ChipMem nicht verfügbar !?!");
    *pattern=0xaaaa;	*(pattern+1)=0x5555;
    /* Abschätzung der Mindestgröße des Windows */
    TextHeight=(Font ? Font->tf_YSize : GfxBase->DefaultFont->tf_YSize);
    i=35+13*TextHeight+22;
    if(WHeight<i)   WHeight=i;
    j=(Font ? Font->tf_XSize : GfxBase->DefaultFont->tf_XSize);
    i=22*j+42;
    if(WWidth<i)    WWidth=i;
    if((MyWin=TRK_OpenWindowTagList(NULL, (struct TagItem *)OWTags))==NULL)
	error("Window läßt sich nicht öffnen");
    MyRP=MyWin->RPort;
    SetAfPt(MyRP, pattern, -1);
    if(Font)    SetFont(MyRP, Font);
    TextHeight=MyRP->TxHeight;
    TextBaseline=MyRP->TxBaseline;
    for(MaxDayNameWidth=i=0;i<8;++i)
	{
	j=MyTextWidth(daynames[i]);
	if(j>MaxDayNameWidth) MaxDayNameWidth=j;
	}
    for(MaxNumWidth=MaxDigitWidth=i=0; i<10; ++i)
	{
	sprintf(buf, "%ld", i);
	j=MyTextWidth(buf);
	if(j>MaxDigitWidth) MaxDigitWidth=j;
	if(0<i && i<6 && j>MaxNumWidth) MaxNumWidth=j;
	}
    MaxNumWidth+=MaxDigitWidth;
    /* wir vermuten das schlimmstmögliche: 6 Wochenspalten */
    CalBoxWidth=42+6*MaxNumWidth+MaxDayNameWidth;
    WindowLimits(MyWin, CalBoxWidth+MyWin->BorderLeft+MyWin->BorderRight,
		 35+13*TextHeight+MyWin->BorderTop+MyWin->BorderBottom,
		 -1,-1);
}

#ifdef PRIVATE
extern short MyText(short x, short y, UBYTE *txt),
	     days_p_m(short mon, short year);
extern void TMyText(short x, short y, char *txt), TText0(char *txt),
	    TText(struct RastPort *rp, char *txt, short len),
	    CenterText(short x0, short y, UBYTE *txt, short cellwidth),
	    HLine(short x0, short x1, short y),
	    VLine(short x, short y0, short y1);
extern long jd(struct Termin *t);
#else
void Text0(char *txt) { Text(MyRP,txt,strlen(txt)); }

short MyText(short x, short y, UBYTE *txt)
{
    Move(MyRP, x,y+TextBaseline);
    Text0(txt);
    return(MyRP->cp_x-x);
}

void CenterText(short x0, short y, UBYTE *txt, short cellwidth)
{ MyText(x0+(cellwidth-MyTextWidth(txt))/2, y, txt); }

void HLine(short x0, short x1, short y)
{ Move(MyRP, x0,y); Draw(MyRP, x1,y); }

void _VLine(short x, short y0, short y1)
{ Move(MyRP, x,y0); Draw(MyRP, x,y1); }

void VLine(short x, short y0, short y1)
{ _VLine(x,y0,y1); _VLine(x+1,y0,y1); }

short days_p_m(short mon, short year)
{
    static short dpm[12]={31,28,31,30,31,30,31,31,30,31,30,31};

    if((year%4==0)&&(mon==1))   return(29);
    return(dpm[mon]);
}

long jd(struct Termin *t)
{ /* ermittelt so etwas wie eine Tagesnummer in Anlehnung an die
     julianischen Daten. Nullpunkt: 1.1.1990 */
    register short cnt;
    register long zwerg;

    zwerg=0;
    for(cnt=1990; cnt<t->jahr; ++cnt)
	{
	zwerg+=365;
	if((cnt & 4)==0 && cnt!=2000) ++zwerg;
	}
    for(cnt=1; cnt<t->monat; ++cnt) zwerg+=days_p_m(cnt,t->jahr);
    return(zwerg+t->tag);
}

void TMyText(short x, short y, char *txt)
{
    if(LinesToHide|LinesUnder)
	 Move(MyRP, x+MyTextWidth(txt), y);
    else MyText(x,y,txt);
}

void TText0(char *txt)
{
    if(LinesToHide|LinesUnder)
	 Move(MyRP, MyRP->cp_x+MyTextWidth(txt), MyRP->cp_y);
    else Text0(txt);
}

void TText(struct RastPort *rp, char *txt, short len)
{
    if(LinesToHide|LinesUnder)
	 Move(rp, rp->cp_x+TextLength(rp, txt, len), rp->cp_y);
    else Text(rp,txt,len);
}
#endif

void ListDates(void)
{ /* Termine einblenden ohne Kontrolle, ob veraltete dabei sind
    [die müssen vorher herausgefischt werden, da sie sonst zumindest in
     unvorhersagbaren Farben erscheinen] */
    register int j;
    register struct Termin *termin;
    register short y;
    short days,offs_x, linecnt;
    long jd_heute, jd_termin;
    char *rest, buf[25];
    static char f1[]="%ld.";

    days=days_p_m(Jetzt.monat-1,Jetzt.jahr);

    SetAfPt(MyRP, NULL, 0);
    SetAPen(MyRP,0);
    RectFill(MyRP, 0, 32+10*TextHeight, MyWin->GZZWidth, MyWin->GZZHeight);
    SetAfPt(MyRP, pattern, -1);
    SetAPen(MyRP,1);
    SetDrMd(MyRP,JAM1);

    y=32+10*TextHeight;
    jd_heute=jd(&Jetzt);
    LinesToHide=LinesAbove;
    LinesUnder=LinesShown=0;
    for(termin=Termine; termin; termin=termin->naechster)
	{
	if((y+TextHeight)<MyWin->GZZHeight) LinesShown+=LinesToHide==0;
	else				    ++LinesUnder;
	jd_termin=jd(termin);
	SetAPen(MyRP, (jd_termin == jd_heute) ? 2 :
		      ((jd_termin < jd_heute+4) ? 3 : 1));
	sprintf(buf,f1,termin->tag);
	TMyText(0,y,buf);
	if(termin->monat>Jetzt.monat || termin->jahr>Jetzt.jahr)
	    {
	    sprintf(buf,f1,termin->monat);
	    TText0(buf);
	    }
	if(termin->jahr>Jetzt.jahr)
	    {
	    sprintf(buf,"%ld",termin->jahr);
	    TText0(buf);
	    }
	TText(MyRP," ",1);
	if(termin->stunde!=INVALID)
	    { /* Zeit einblenden */
	    sprintf(buf, "%ld:%02ld:", termin->stunde, termin->minute);
	    TText0(buf);
	    }
	offs_x=MyRP->cp_x;
	rest=termin->text;
	while(MyTextWidth(rest)>MyWin->GZZWidth-offs_x)
	    {
	    j=strlen(rest);
	    do	{
		do  --j; while(j>0 && *(rest+j)!=' ');
		if(j<=0)    break;  /* da is eben ein Wort, das nicht
				       in eine Zeile paßt ! */
		} while(TextLength(MyRP, rest, j)>MyWin->GZZWidth-offs_x);
	    if(j==0)    do ++j; while(*(rest+j)!=0 && *(rest+j)!=' ');
	    TText(MyRP, rest, j);
/*	       TText(MyRP, rest, (j>0) ? j : strlen(rest)); */
	    if(LinesToHide) --LinesToHide;
	    else	    y+=TextHeight;
	    Move(MyRP, offs_x, TextBaseline+y);
	    if((y+TextHeight)<MyWin->GZZHeight) LinesShown+=LinesToHide==0;
	    else				++LinesUnder;
	    rest+=j;
	    if(*rest==' ') ++rest;
	    }
	if(*rest)
	    {
	    TText0(rest);
	    if(LinesToHide) --LinesToHide;
	    else	    y+=TextHeight+1;
	    }
	}
}

struct tm *GetTime(void)
{ /* aktuelle Systemzeit in den Termin 'Jetzt' umrechnen */
    ULONG secs,dmmy;
    struct tm *mytm;

    Jetzt.naechster=Jetzt.voriger=Jetzt.text=NULL;

    CurrentTime(&secs,&dmmy);   /* die Mikros interessieren keinen */
    mytm=localtime(&secs);

    Jetzt.minute=mytm->tm_min;
    Jetzt.stunde=mytm->tm_hour;
    Jetzt.monat=mytm->tm_mon+1;
    Jetzt.tag=mytm->tm_mday;
    Jetzt.jahr=mytm->tm_year+1900;
    return(mytm);
}

#ifdef PRIVATE
extern char *GetTempString(long len);
#else
char *GetTempString(long len)
{   char *erg;
    erg=TRK_AllocMem(len, MEMF_ANY);
    if(erg==NULL)   error("temporärer Speicher nicht verfügbar");
    return(erg);
}
#endif

char *TerminString(register struct Termin *termin)
{   static char *text=NULL;

    if(text)    TRK_FreeMem(text);
    text=GetTempString(25+strlen(termin->text));
    sprintf(text, "%ld.%ld.%ld", termin->tag,termin->monat, termin->jahr);
    if(termin->stunde!=INVALID)
	{ char buff[10];
	sprintf(buff, ",%ld:%02ld", termin->stunde, termin->minute);
	strcat(text, buff);
	}
    strcat(text, ":");
    strcat(text, termin->text);
    strcat(text, "\n");
    return(text);
}

#ifdef PRIVATE
extern short first_d_m(short wday, short mday);
#else
short first_d_m(short wday, short mday)
{
    short i,f_wday=wday;

    for(i=mday;i>1;i--)
	{
	--f_wday;
	if(f_wday<0)    f_wday=6;
	}
    return(f_wday);
}
#endif

void RedrawWindow(void)
{
    register int i,j;
    register short x,y;
    ULONG secs;
    short days,first_wd,offs_x,width,week;
    char *rest, buf[25];
    static char thead[]="Termine:";
    struct tm *mytm;

    SetDrMd(MyRP,JAM1);
    SetRast(MyRP,0);
    SetAPen(MyRP,1);
    offs_x=(MyWin->GZZWidth-CalBoxWidth)/2;
    for(i=0;i<8;++i)
	MyText(offs_x+6,6+TextHeight+i*(TextHeight+3),daynames[i]);
    j=MyTextWidth(thead);
    MyText((MyWin->GZZWidth-j)/2, 31+9*TextHeight, thead);

    mytm=GetTime();
    first_wd=(first_d_m(mytm->tm_wday,Jetzt.tag)+6)%7;
    week=(mytm->tm_yday-Jetzt.tag+3)/7;
    days=days_p_m(Jetzt.monat-1,Jetzt.jahr);
    for(i=1;i<=days;i++)
	{
	x=offs_x+((first_wd+i-1)/7)*(MaxNumWidth+6)+MaxDayNameWidth+12;
	y=((first_wd+i-1)%7+1)*(TextHeight+3)+3;
	if(i==Jetzt.tag)    SetAPen(MyRP,2);
	else		    SetAPen(MyRP,1);
	sprintf(buf,"%ld",i);
	CenterText(x, y, buf, MaxNumWidth);
	if(((first_wd+i)%7==0)||(i==days))
	    {
	    SetAPen(MyRP,1);
	    sprintf(buf,"%ld", week++);
	    CenterText(x, 27+8*TextHeight, buf, MaxNumWidth);
	    }
	}
    width=x+MaxNumWidth+5-offs_x;

    RectFill(MyRP, 0,0, offs_x, 30+10*TextHeight);
    RectFill(MyRP, offs_x+width,0, MyWin->GZZWidth, 30+10*TextHeight);
    RectFill(MyRP, offs_x, 30+9*TextHeight,
		   (MyWin->GZZWidth-j-4)>>1, 30+10*TextHeight);
    RectFill(MyRP, (MyWin->GZZWidth+j+4)>>1, 30+9*TextHeight,
		   offs_x+width, 30+10*TextHeight);

    VLine(offs_x+2,1,28+9*TextHeight);
    VLine(offs_x+width-3,2,3+TextHeight);
    sprintf(buf, "%s %ld", monthnames[Jetzt.monat-1], Jetzt.jahr);
    CenterText(offs_x,3, buf, width);
    for(y=1;y<10*(TextHeight+3);y+=(TextHeight+3))
	HLine(offs_x+3,offs_x+width-2,y);
    for(x=MaxDayNameWidth+8;x<width;x+=MaxNumWidth+6)
	VLine(offs_x+x,4+TextHeight,27+9*TextHeight);
    if(CalBoxWidth!=width+1)
	{
	CalBoxWidth=width+1;
	WindowLimits(MyWin, width+1+MyWin->BorderLeft+MyWin->BorderRight,
		     MyWin->MinHeight, -1,-1);
	}
    ListDates();
}

short GetShort(char **ptr)
{ /* eine SHORT-Zahl lesen - wenn möglich */
    register short zwerg;
    register char *c;

    c=*ptr;
    if(!isdigit(*c)) return(INVALID);
    for(zwerg=0;isdigit(*c);++c)
	{
	zwerg*=10;
	zwerg+=*c-'0';
	}
    *ptr=c+1;
    return(zwerg);
}

struct Termin *parse(char **current_ptr)
{
    register struct Termin *neutermin;
    register char *current, *parsed;
    short help;

    if(!(neutermin=TRK_AllocMem(sizeof(struct Termin),MEMF_CLEAR)))
	error("Speichermangel");
    if((neutermin->tag=GetShort(current_ptr))==INVALID)     goto parse_err;
    if((neutermin->monat=GetShort(current_ptr))==INVALID)   goto parse_err;
    if((neutermin->jahr=GetShort(current_ptr))==INVALID)    goto parse_err;
    if(neutermin->jahr<100 && neutermin->jahr>50)   neutermin->jahr+=1900;
    if(neutermin->jahr<50)  neutermin->jahr+=2000;
    neutermin->text=parsed=*current_ptr;
    /* evtl. Terminator '\n' -> 0 wandeln */
    while(*parsed!='\n' && *parsed!=0)  ++parsed;
    if(*parsed=='\n')   *parsed=0;
    /* gibt es eine Zeitangabe ? */
    *current_ptr=neutermin->text;   /* wenn ja, dann da ! */
    if((neutermin->stunde=GetShort(current_ptr))!=INVALID)
	{
	if((neutermin->minute=GetShort(current_ptr))!=INVALID)
	    {
	    current=*current_ptr;
	    if(*current==':')   ++current;
	    while(*current==' ' || *current=='\t')  ++current;
	    help=current-neutermin->text;
	    neutermin->text+=help;
	    }
	else neutermin->stunde=INVALID;
	}
    *current_ptr=parsed;
    return(neutermin);
parse_err:
    TRK_FreeMem(neutermin);
    return(NULL);
}

void GetTermine(void)
{
    char *current;
    ULONG TerminSize,got;
    BPTR FH;
    struct Termin *neu;

    if(!(FH=TRK_Open(MainFileName,MODE_OLDFILE)))
	error("Kann Termindatei nicht öffnen");
    Seek(FH,0L,OFFSET_END);
    TerminSize=Seek(FH,0L,OFFSET_BEGINNING);
    if(!(current=TRK_AllocMem(TerminSize+1,MEMF_CLEAR)))
	error("Kann Speicher für Termindatei nicht allozieren");
    got=Read(FH,current,TerminSize+1);
    TRK_Close(FH);
    if(got!=TerminSize) error("Lesefehler in Termindatei");
    *(current+TerminSize)=0;

    /* Termine parsen */
    do	{
	if(!(neu=parse(&current)))  return;
	EnQueue(&Termine, neu);
	} while(*(++current));
}

#ifdef PRIVATE
extern void Up(void), Top(void), Down(void), Bottom(void),
	    PUp(void), PDown(void);
#else
void Up(void)       { if(LinesAbove) { --LinesAbove; ListDates(); } }
void Top(void)      { if(LinesAbove) { LinesAbove=0; ListDates(); } }
void Down(void)     { if(LinesUnder) { ++LinesAbove; ListDates(); } }
void Bottom(void)   { if(LinesUnder) { LinesAbove+=LinesUnder; ListDates(); } }

#define MAX(a,b) (a>b ? a:b)

void PUp(void)
{   if(LinesAbove)
	{
	LinesAbove-=MAX(LinesShown-1, 1);
	if(LinesAbove<0) LinesAbove=0;
	ListDates();
	}
}

void PDown(void)
{   if(LinesUnder)
	{
	LinesAbove += (LinesUnder<LinesShown) ? LinesUnder : MAX(LinesShown-1, 1);
	ListDates();
	}
}
#endif

void RewriteTerminFiles(void)
{   register struct Termin *termin;
    struct Termin *next;
    register BPTR File;
    char *tmpname;

    tmpname=GetTempString(5+strlen(MainFileName));
    strcpy(tmpname, MainFileName);
    strcat(tmpname, ".tmp");
    if((File=TRK_Open(tmpname, MODE_NEWFILE))==NULL)
	error("neues Terminfile nicht generierbar");
    for(termin=Termine; termin; termin=termin->naechster)
	if(FPuts(File, TerminString(termin)))
	    error("DOS-Schreibfehler im neuen Terminfile");
    TRK_Close(File);
    SetProtection(tmpname, FIBF_EXECUTE);
    if(!DeleteFile(MainFileName))
	{
	BadMsg("TKal kann das Terminfile nicht löschen");
	goto FreeTmp;
	}
    if(!Rename(tmpname, MainFileName))
	{
	BadMsg("TKal kann das neue Terminfile nicht korrekt umbenennen");
	goto FreeTmp;
	}

    if(!TerminBackup)
	{
	for(next=geloeschteTermine; termin=next; )
	    {
	    next=termin->naechster;
	    TRK_FreeMem(termin);
	    }
	geloeschteTermine=NULL;
	}
    if(geloeschteTermine==NULL) goto FreeTmp;

    if(OldFileName==NULL)
	{
	OldFileName=GetTempString(5+strlen(MainFileName));
	strcpy(OldFileName, MainFileName);
	strcat(OldFileName, ".alt");
	}
    if((File=TRK_Open(OldFileName, MODE_READWRITE))==NULL)
	{
	BadMsg("neues Alt-Terminfile weder verfügbar noch generierbar");
	goto FreeTmp;
	}
    Seek(File, 0, OFFSET_END);
    while(termin=geloeschteTermine)
	{
	if(FPuts(File, TerminString(termin)))
	    {
	    BadMsg("DOS-Schreibfehler im Alt-Terminfile");
	    break;
	    }
	DeQueue(&geloeschteTermine, termin);
	TRK_FreeMem(termin);
	}
    TRK_Close(File);
    SetProtection(OldFileName, FIBF_EXECUTE);

FreeTmp:
    TRK_FreeMem(tmpname);
}

void UpDate(void)
{ /* ggf. neue Termine in die Haupt-Liste aufnehmen und alte aussondern
     und das ganze in Window und Datei schreiben */
    register struct Termin *termin;
    BOOL changed;

    /* evtl. neue Termine einfügen */
    changed=(NeueTermine!=NULL);
    while(termin=NeueTermine)
	{
	DeQueue(&NeueTermine, termin);
	EnQueue(&Termine, termin);
	}

    /* evtl. alte Termine streichen: */
    GetTime();  /* Jetzt setzen */
    for(termin=Termine; termin; )
	{
	if(before(termin, &Jetzt))
	    {
	    DeQueue(&Termine, termin);
	    EnQueue(&geloeschteTermine, termin);
	    termin=Termine;
	    changed=TRUE;
	    }
	else termin=termin->naechster;
	}

    if(changed)
	{
	LinesAbove=0;
	ListDates();
	RewriteTerminFiles();
	}
}

void kernel(void)
{
    BOOL CONTINUE=TRUE;
    struct IntuiMessage *imsg;
    WORD im_ggid,im_code;
    UWORD im_qual;
    ULONG im_class;

    OpenGfx();
    GetTermine();
    LinesAbove=0;
    RedrawWindow();
    UpDate();
    while(CONTINUE)
	{
	WaitPort(MyWin->UserPort);
	while(imsg=(struct IntuiMessage *)GetMsg(MyWin->UserPort))
	    {
	    im_class=imsg->Class;
	    im_code=imsg->Code;
	    im_qual=imsg->Qualifier;
	    if(im_class==IDCMP_GADGETUP || im_class==IDCMP_GADGETDOWN)
		im_ggid=((struct Gadget *)imsg->IAddress)->GadgetID;
	    ReplyMsg((struct Message *)imsg);
	    switch(im_class)
		{
		case IDCMP_CLOSEWINDOW: CONTINUE=FALSE; break;
		case IDCMP_RAWKEY:
		    switch(im_code)
			{
			case 0x4c:  Up();       break;
			case 0x4d:  Down();     break;
			case 0x4e:  PDown();    break;
			case 0x4f:  PUp();      break;
			}
		    break;
		case IDCMP_VANILLAKEY:
		    CONTINUE=(im_code!=27);
		    if(!CONTINUE || (im_qual && IEQUALIFIER_NUMERICPAD)==0)
			break;
		    switch(im_code)
			{
			case '6':
			case '2':   Down();     break;
			case '8':
			case '4':   Up();       break;
			case '9':   PUp();      break;
			case '3':   PDown();    break;
			case '7':   Top();      break;
			case '1':   Bottom();   break;
			}
		    break;
		case IDCMP_CHANGEWINDOW: LinesAbove=0; RedrawWindow(); break;
		case IDCMP_REFRESHWINDOW:
		    BeginRefresh(MyWin);
		    RedrawWindow();
		    EndRefresh(MyWin,TRUE);
		    break;
		case IDCMP_GADGETUP:
		    switch(im_ggid)
			{
			case GID_Up:	    Up();       break;
			case GID_PUp:	    PUp();      break;
			case GID_Top:	    Top();      break;
			case GID_Down:	    Down();     break;
			case GID_PDown:     PDown();    break;
			case GID_Bottom:    Bottom();   break;
			}
		    break;
		}
	    }
	}
}
