UNIT bildschirm; {$project vt }
{ Bildschirmausgaben zum Programm VideoText }

INTERFACE; FROM vt USES pagelist,decode,cct,sys;

VAR incomplete: Boolean;  { Seitenaufbau unterbrochen? }
    concealed: Boolean;   { verdeckte Zeichen wirklich verdeckt? }
    queue_input: String[10];
    colht: Integer;   { muß zum spaltenweisen Blättern extern bekannt sein }
    pgoffx: Integer;  { provisorische Hilfe für die IFF-Routine }

PROCEDURE cursoroff;
PROCEDURE cursoron;
PROCEDURE short_msg(s: str80; style: Byte);
PROCEDURE add_msg(s: str80; style: Byte);
PROCEDURE mark_queue(really: Boolean);
PROCEDURE queue_me;
PROCEDURE redraw_queue(job: Integer);
FUNCTION pos_from_queue(x,y: Integer): Integer;
PROCEDURE mark_list(really: Boolean);
PROCEDURE redraw_list;
PROCEDURE update_list(start: p_onepage; delta: Integer);
FUNCTION page_from_list(x,y: Integer): p_onepage;
PROCEDURE fileinfo;
PROCEDURE test(active: Word);
PROCEDURE writepage(seite: p_onepage, verdeckt: Boolean);
FUNCTION number_from_page(x,y: Integer): Integer;
PROCEDURE redraw_all;
FUNCTION click_action(x,y: Integer): Char;

{ ---------------------------------------------------------------------- }

IMPLEMENTATION;

{$opt q,s+,i+ } { keine Laufzeitprüfungen außer Stack und Feldindizes }

{ Bildschirmlayout: }
VAR screenw, screenht: Integer;
    xp, yp: Integer;              { Position der Seite }
    xt, yt: Integer;              { Position des Statusfeldes }
    xs, ys, ws, sp_maxdisp: Integer;  { Position und Breite der Seitensuche }
    xq, yq, head, tail: Integer;  { Pos. u. Aufteilung der Warteschlange }
    xl, yl, cols, colw: Integer;  { Pos. und Gliederung der Seitenliste }
    xm, ym, wm: Integer;          { Position und Breite der Meldezeile }
    xf, xf2, xf3, xf4, yf, wf: Integer; { Position und Breite der Dateiinfo }

VAR listoffset: Integer;
    lsizex: Integer;   { x-Position der Listengröße }
    msglen: Integer;
    umlaut,umltab: String[8];

PROCEDURE cursoroff;
BEGIN
  Write(#155'0 p');  { Cursor unsichtbar }
END;

PROCEDURE cursoron;
BEGIN
  Write(#155' p');  { Cursor wieder sichtbar }
END;

PROCEDURE short_msg{(s: str80; style: Byte)};   { normal: style=0 }
BEGIN
  msglen := 0;
  add_msg(s, style);
END;
  
PROCEDURE add_msg{(s: str80; style: Byte)};
VAR i,j,l: Integer;
BEGIN
  IF ym<1 THEN Exit; { zu kleiner Bildschirm }
  l := Length(s);
  FOR i := 1 TO l DO BEGIN   { Umlaute ersetzen }
    j := Pos(s[i],umlaut);
    IF j>0 THEN s[i] := umltab[j];
  END;
  FOR i := l+1 TO wm-msglen DO s[i] := ' ';
  IF msglen<wm THEN BEGIN
    GotoXY(xm+msglen,ym);
    s[wm-msglen+1] := #0;
    IF style<>0 THEN Write(#155'0m');  { Mischen von Attributen verhindern }
    Write(#155,style,'m',s);
    msglen := msglen + l;
  END;
END;

PROCEDURE queue_entry(pg,sp: Integer);
{ besorgt v. a. die richtige Dekodierung der Unterseitennummer }
BEGIN
  Write(hexstr(pg,0));
  CASE sp OF
    -3: Write('/!');
    -2: Write('/.');
    -1: Write('/*');
     0: ;
    OTHERWISE IF sp>0 THEN Write('/',hexstr(sp,0))
      ELSE Write('*',-10-sp); { auch '*0' möglich! }
  END;
END;

PROCEDURE mark_queue{(really: Boolean)};
{ Gibt die Position des Job-Markers <thisjob> als '>' am Bildschirm aus, }
{ <thisjob> kann Werte von <maxactive>-1 bis -<queued> annehmen. }
{ Für <really>=FALSE wird stattdessen ' ' ausgegeben. }
VAR y,j: Integer;
    special: Boolean;
BEGIN
  IF yq<1 THEN Exit; { zu kleiner Bildschirm }
  special := False;
  y := yq - thisjob - 2;
  IF thisjob<0 THEN BEGIN
    y := y + 2;
    IF NOT fifo THEN y := y + 1;
    IF (queued>=colht) AND (-thisjob>head) THEN BEGIN
      { unvollständige Anzeige der Schlange }
      IF -thisjob>queued-tail THEN
        y := y + colht-1 - queued { zeigt auf hinteres Ende }
      ELSE BEGIN
        special := True;
        y := y + thisjob + head + 1; { zeigt auf die '...' }
        j := (firstinq - thisjob - 2) MOD qlen + 1;
      END;
    END;
  END;
  GotoXY(xq,y);
  IF really THEN
    Write('>')
  ELSE 
    Write(' '); 
  IF special THEN BEGIN
    Write('('); queue_entry(queue[j].pg,queue[j].sp); Write(')');
  END;
END;

PROCEDURE queue_me;
{ Die aktuelle Benutzereingabe am Ende (bzw. Anfang) der Warteschlange }
{ darstellen. }
VAR s: String[10];
BEGIN
  IF yq<1 THEN Exit; { zu kleiner Bildschirm }
  { den für Eingaben reservierten Platz ansteuern: }
  IF NOT fifo THEN
    GotoXY(xq+1,yq+1)
  ELSE IF queued<colht THEN
    GotoXY(xq+1,yq+1+queued)
  ELSE
    GotoXY(xq+1,yq+colht);
  Write(#155'0m');
  IF queued<qlen THEN BEGIN 
    Write(queue_input,#155'7m');
    IF Length(queue_input)<8 THEN BEGIN
      s := '         '; s[8-Length(queue_input)] := #0;
      Write(' '#155'0m',s);
    END ELSE
      Write(#8,queue_input[8]);
  END;
END;

PROCEDURE redraw_queue{(job: Integer)};
{ Gibt für job<0 die aktuelle Belegung der Warteschlange und der aktiven Jobs }
{ am Bildschirm aus, sonst wird nur der Job mit der angegebenen Nummer neu }
{ ausgegeben: interessant ist dabei vor allem der Status der eingelesenen }
{ Unterseiten. }
VAR i,j,y,max: Integer;
BEGIN
  IF yq<1 THEN Exit; { zu kleiner Bildschirm }
  GotoXY(xs,ys); Write(#155'0m Seitensuche:');
  FOR j := 0 TO maxactive-1 DO
    IF (job<0) OR (j=job) THEN WITH activejobs[j] DO BEGIN
      GotoXY(xs,ys+maxactive-j);
      Write(Copy(blank40,1,ws)); { nicht das Testfeld überschreiben! }
      GotoXY(xs+1,ys+maxactive-j);
      IF pg=0 THEN Write('---')
      ELSE BEGIN
        queue_entry(pg,sp);
        Write('  ');
        IF sp_check[0] THEN Write('+');
        IF sp_max>0 THEN     { echte Unterseiten eingetroffen }
          IF (sp>0) OR (sp IN [-3..-1]) THEN  { zu einer Einzelanforderung? }
            Write('+')
          ELSE BEGIN
            max := get_bcd(sp_max); IF max>sp_maxdisp THEN max := sp_maxdisp-2;
            Write('(');
            FOR i := 1 TO max DO
              IF sp_check[i] THEN Write('+') ELSE Write('-');
            IF get_bcd(sp_max)>max THEN Write('...') ELSE Write(')');
          END;
      END;
    END;
  GotoXY(xq,yq);
  IF fifo THEN Write(' Jobs:') ELSE Write(' Jobs!');
  IF job<0 THEN BEGIN
    y := yq + 1;
    IF fifo THEN y := y + colht - 1;
    GotoXY(xq,y); Write('         ');
    FOR j := 1 TO colht-1 DO BEGIN
      y := yq+j; IF NOT fifo THEN y := y + 1;
      GotoXY(xq,y); Write('         ');
      IF j<=queued THEN BEGIN
        GotoXY(xq+1,y);
        i := (firstinq + j - 2) MOD qlen + 1;
        IF (queued>=colht) AND (j>head) THEN
          i := (firstinq + queued + j - colht - 1) MOD qlen + 1;
        IF (queued>=colht) AND (j=head+1) THEN
          Write('...')
        ELSE
          queue_entry(queue[i].pg,queue[i].sp);
      END;
    END; { Ende FOR-Schleife }
    queue_me;
  END;
  mark_queue(True);
END;

FUNCTION pos_from_queue{(x,y: Integer): Integer};
{ wurde auf eine Nummer in der Warteschlange geklickt? }
BEGIN
  pos_from_queue := maxactive;  { unmöglicher Wert }
  IF yq<1 THEN Exit; { zu kleiner Bildschirm }
  IF (x<xq) OR (x>xq+8) THEN Exit;
  IF (y>ys) AND (y<=ys+maxactive) THEN
    pos_from_queue := maxactive + ys - y { Position in der Seitensuche }
  ELSE BEGIN
    y := y - yq; IF NOT fifo THEN Dec(y);
    IF (y>0) AND (y<colht) AND(y<=queued) THEN
      IF (queued<colht) OR (y<=head) THEN
        pos_from_queue := -y
      ELSE
        pos_from_queue := -(y-colht+queued+1);
  END;
END;

{ Der Ärger mit der Seitenliste ... }

FUNCTION adjust_offset: Integer;
{ Da die Seitenliste meist nicht ganz auf den Bildschirm paßt, gibt ein }
{ Offset an, ab wo sie sichtbar ist, dieser <listoffset> wird hier neu }
{ berechnet. Der Rückgabewert sagt, um wieviele Spalten die Liste (falls }
{ bereits angezeigt) verschoben werden muß. (>0: rechts kommen Spalten dazu) }
{ Besteht die Liste aus nur einer Spalte, gibt der Rückgabewert an, wie }
{ weit sie vertikal gescrollt werden muß. (>0: unten kommen Zeilen dazu) }
VAR nr,delta: Integer;
BEGIN
  nr := posn_in_list(thispage);   { wo steht der Zeiger? }
  delta := 0;
  IF cols>1 THEN BEGIN
    WHILE nr<listoffset DO BEGIN
      listoffset := listoffset-colht; Dec(delta);  END;
    WHILE nr-listoffset>=cols*colht DO BEGIN
      listoffset := listoffset+colht; Inc(delta);  END;
  END ELSE BEGIN
    IF nr<listoffset THEN delta := nr-listoffset;
    IF nr>=listoffset+colht THEN delta := nr-(listoffset+colht-1);
    listoffset := listoffset + delta;
  END;
  adjust_offset := delta;
END;

PROCEDURE listentry(seite: p_onepage; nr: Integer);
{ Einen Listeneintrag erzeugen, von dem seine Nummer im sichtbaren Bereich }
{ der Liste bekannt ist. }
VAR i,x,y: Integer;
    s: String[20];
BEGIN
  x := xl+colw*(nr DIV colht); y := yl+1+nr MOD colht;
  GotoXY(x,y); Write(#155'0m');
  s := '';
  IF seite<>Nil THEN BEGIN
    s := ' '+hexstr(seite^.pg,0)+'/'+hexstr(seite^.sp,0);
    IF (seite^.cbits AND PF_LOCAL<>0) THEN 
      Write(#155'7m') { Invers }
    ELSE IF NOT seite^.dejavu THEN 
      Write(#155'1m'); { Fett }
    IF (seite^.cbits AND PF_PSEUDO<>0) THEN 
      Write(#155'2m'); { Hilite }
  END;
  i := Length(s);
  WHILE i<colw DO BEGIN Inc(i); s[i] := ' '; END;
  s[i+1] := #0;
  Write(s);
END;

PROCEDURE redraw_column(col: Integer);
{ gibt eine Spalte der Seitenliste aus }
VAR i: Integer;
    hilf: p_onepage;
BEGIN
  hilf := page_on_posn(listoffset+(col-1)*colht);
  FOR i := 1 TO colht DO BEGIN
    listentry(hilf,(i-1)+(col-1)*colht);
    IF hilf<>Nil THEN hilf := hilf^.next;
  END;
END;

PROCEDURE redraw_rows(rows: Integer);
{ gibt die ersten <rows> bzw. die letzten <-rows> Zeilen der Seitenliste }
{ aus, nicht ganz trivial }
VAR i,j: Integer;
    hilf: p_onepage;
BEGIN
  hilf := page_on_posn(listoffset);
  FOR i := 1 TO cols DO
    FOR j := 1 TO colht DO BEGIN
    IF (j<=rows) OR (j>colht+rows) THEN
      listentry(hilf,(j-1)+(i-1)*colht);
    IF hilf<>Nil THEN hilf := hilf^.next;
  END;
END;

PROCEDURE mark_list{(really: Boolean)};
{ Gibt die Position des Seiten-Markers <thispage> am Bildschirm aus, für }
{ <really>=FALSE wird die Markierung dagegen aufgehoben. }
{ Scrollt die Liste auch weiter, falls nötig. }
VAR nr,i,x,y: Integer;
    dx: Integer;
BEGIN
  IF yl<1 THEN Exit; { zu kleiner Bildschirm }
  { scrollen? }
  dx := adjust_offset;
  IF cols>1 THEN BEGIN
    FOR i := cols DOWNTO 1 DO BEGIN
      IF dx>=i THEN  scroll_text(yl+1,yl+colht,xl,xl+cols*colw-1,0,colw);
      IF dx<=-i THEN  scroll_text(yl+1,yl+colht,xl,xl+cols*colw-1,0,-colw);
    END;
    FOR i := 1 TO cols DO
      IF (i+dx>cols) OR (i+dx<1) THEN
        redraw_column(i);
  END ELSE BEGIN  { 1 Spalte vertikal scrollen }
    IF Abs(dx)<colht THEN FOR i := colht DOWNTO 1 DO BEGIN
      IF dx>=i THEN  scroll_text(yl+1,yl+colht,xl,xl+colw-1,1,0);
      IF dx<=-i THEN  scroll_text(yl+1,yl+colht,xl,xl+colw-1,-1,0);
    END;
    IF dx<>0 THEN redraw_rows(-dx);
  END;
  IF thispage<>Nil THEN thispage^.dejavu := True;
  nr := posn_in_list(thispage) - listoffset;   { wo steht der Zeiger? }
  listentry(thispage,nr);
  x := xl+colw*(nr DIV colht); y := yl+1+nr MOD colht;
  GotoXY(x,y); 
  IF really THEN
    Write('>')
  ELSE 
    Write(' '); 
END;

PROCEDURE redraw_list;
{ aktualisiert die gesamte Seitenliste }
VAR i: Integer;
    hilf: p_onepage;
BEGIN
  IF yl<1 THEN Exit; { zu kleiner Bildschirm }
  IF adjust_offset<>0 THEN; { Wert ist egal }
  GotoXY(xp-6,yl); Write(#155'0m',listsize:5);
  lsizex := xp-1-Length(IntStr(listsize)); 
  GotoXY(xl,yl); Write(' Seiten:');
  hilf := page_on_posn(listoffset);
  FOR i := 0 TO cols*colht-1 DO BEGIN
    listentry(hilf,i);
    IF hilf<>Nil THEN hilf := hilf^.next;
  END;
  mark_list(True);
END;

PROCEDURE update_list{(start: p_onepage; delta: Integer)};
{ Es wurden Seiten in die Liste eingefügt (delta>0) bzw. entfernt (delta<0). }
{ <start> ist die erste Seite, die sich dadurch geändert hat. }
{ delta=0 ist auch zulässig, z. B. um für eine aktualisierte Nummer wieder }
{ Fettdruck zu erzwingen. }
VAR i,nr: Integer;
    hilf: p_onepage;
BEGIN
  IF yl<1 THEN Exit; { zu kleiner Bildschirm }
  lsizex := xp-1-Length(IntStr(listsize));
  GotoXY(lsizex-1,yl); Write(#155'0m ',listsize);
  { wo auf dem Bildschirm ist die bezeichnete Seite? }
  nr := posn_in_list(start) - listoffset;
  IF delta=0 THEN BEGIN
    IF nr IN [0..cols*colht-1] THEN listentry(start,nr);
    Exit;
  END;
  { ein paar Spalten komplett scrollen, eine nur zum Teil }
  FOR i := 1 TO cols DO BEGIN
    IF (i-1)*colht>=nr THEN
      scroll_text(yl+1,yl+colht,xl+(i-1)*colw,xl+i*colw-1,-delta,0)
    ELSE IF i*colht>=nr+Abs(delta) THEN
      scroll_text(yl+1+nr MOD colht,yl+colht,xl+(i-1)*colw,xl+i*colw-1,-delta,0)
  END;
  hilf := start;
  FOR i := 0 TO delta DO BEGIN { einer zuviel, ist aber Absicht ... }
    IF nr+i IN [0..cols*colht-1] THEN listentry(hilf,nr+i);
    IF hilf<>Nil THEN hilf := hilf^.next;
  END;
  redraw_rows(delta);
  { '>' neu setzten, hat evtl. sogar den Bildschirm verlassen: }
  mark_list(True);
END;

FUNCTION page_from_list{(x,y: Integer): p_onepage};
{ zu einer angeklickten Zeichenposition die Seite herausfinden }
VAR nr: Integer;
    hilf: p_onepage;
BEGIN
  page_from_list := Nil;
  IF yl<1 THEN Exit; { zu kleiner Bildschirm }
  IF (y IN [yl+1..yl+colht]) AND (x IN [xl-2..xl+cols*colw+2]) THEN BEGIN
    nr := listoffset + y - (yl+1) + colht*(Round((x-xl)/colw+0.5)-1);
    page_from_list := page_on_posn(nr);
  END;
END;

PROCEDURE fileinfo;
VAR l,space: Integer;
BEGIN
  IF yf<1 THEN Exit; { zu kleiner Bildschirm }
  GotoXY(xf,yf);
  Write(#155'0mDatei ('); xf2 := xf + 6;
  xf3 := xf2 + 6;
  IF asciifile THEN BEGIN  Write('ASCII): '); xf3 := xf3 + 3; END
    ELSE Write('VT): ');
  space := screenw - xf3; IF withicon THEN space := space - 10;
  l := Length(outputname);
  IF l>space THEN Write(#155'2m...',Copy(outputname,l-space+4,space-3))
    ELSE Write(#155'2m',outputname);
  IF l>space THEN xf4 := xf3 + space ELSE xf4 := xf3 + l;
  IF withicon THEN Write(#155'0m, mit Icon');
  ClrEoL;
END;

PROCEDURE test{(active: Word)};
{ Decodertest, sollte aus einer Schleife heraus aufgerufen werden. }
{ Steuerbits: %00001 = Uhrzeit und Status abfragen }
{             %00010 = Sendernamen anzeigen }
{             %10000 = regulärer Aufruf, leere Felder ignorieren }
VAR stat: Byte;
    i: Integer;
    zeit: str80;
    ch: char;
    tag,min,tic: Long;
PROCEDURE zweistellig(x: Integer);  BEGIN  Write(x DIV 10, x MOD 10);  END;
BEGIN
  IF yt<1 THEN Exit; { zu kleiner Bildschirm }
  GotoXY(xt,yt);
  Write(#155'0mStatus:');
  IF (active AND $11)=0 THEN BEGIN
    Write(Copy(blank40,1,17)); Write(#155,'2m');
    FOR i := 1 TO 2 DO BEGIN
      GotoXY(xt,yt+i); Write(Copy(blank40,1,24));
    END;
  END;
  IF (active AND $12)=0 THEN BEGIN
    GotoXY(xt,yt+3); Write(Copy(blank40,1,24)); 
  END;
  IF (active AND $11=0) OR ((active AND 1)<>0) THEN BEGIN
    GotoXY(xt+10,yt); Write('Bus:');
    GotoXY(xt+15,yt+1); Write('AV:');
    GotoXY(xt+15,yt+2); Write('VT:');
    GotoXY(xt,yt+1); Write('OS:');
    GotoXY(xt,yt+2); Write('VT:');
  END;
  IF (active AND 1)<>0 THEN BEGIN
    Write(#155,'2m');
    stat := VTstat;
    GotoXY(xt+15,yt);
    IF i2c_status=0 THEN
      Write('OK  ') ELSE Write('Err',i2c_status);
    FOR i := 1 TO 2 DO BEGIN
      GotoXY(xt+19,yt+i);
      IF i2c_status=0 THEN
        IF (stat AND i)<>0 THEN Write('ja  ')  ELSE  Write('nein')
      ELSE
        Write('    ')
    END;
    { Zeit aus dem VT-Seitenspeicher abfragen: }
    gettime(zeit);
    GotoXY(xt+4,yt+2); Write(zeit);
    { zum Vergleich: Amiga-Zeit }
    telltime(tag,min,tic);
    GotoXY(xt+4,yt+1);
    zweistellig(min DIV 60); Write(':');
    zweistellig(min MOD 60); Write(':');
    zweistellig(tic DIV 50);
  END;
  IF (active AND 2)<>0 THEN BEGIN
    { Sendername: }
    gethead(zeit);
    GotoXY(xt,yt+3); Write(#155,'2m',zeit);
  END;
  Write(#155'0m');
END;

PROCEDURE writepage{(seite: p_onepage, verdeckt: Boolean)};
{ Seite am Bildschirm ausgeben }
VAR zeile,i,i0,i1,j,j0: Integer;
    farbe,farbe0: Word;
    out: bigstring;
    x: Byte;
    s,attrib: str80;
    dblheight,rastergfx,special: Boolean;
    normal: String[10];
BEGIN
  incomplete := True;
  i0 := 0; i1 := 24;
  visblpage := seite;
  concealed := verdeckt;
  normal := #155'0;3'+colperms[7]+';4'+colperms[0]+'m'; { weiß auf schwarz }
  dblheight := False; rastergfx := False;
  IF seite<>Nil THEN
    IF seite^.pg>0 THEN 
      seite^.chars[0] := 2  { Seitennummer zunächst grün }
    ELSE BEGIN
      { Mieser Trick, um eine Seite bestimmen zu lassen, daß nur ein Teil }
      { ihrer Zeilen ausgegeben wird (z. B. steht 0/104 für Zeile 1 bis 4): }
      i0 := seite^.sp DIV 100; i1 := seite^.sp MOD 100;
      IF i1>24 THEN i1 := 24;
    END;
  FOR i := i0 TO i1 DO BEGIN
    zeile := i MOD 24;
    IF i=24 THEN BEGIN
      { 1. Zeile nochmal, mit weißer Seitennummer: }
      IF seite<>Nil THEN IF seite^.pg>0 THEN
        seite^.chars[0] := 7;
      dblheight := False;
    END;
    IF dblheight THEN
      { auf eine doppelthohe Zeile folgt nur eine leere Zeile }
      dblheight := False
    ELSE BEGIN
      { normale Zeile ausgeben }
      IF seite<>Nil THEN
        decode_line(seite, zeile, verdeckt, out, attrib, dblheight, rastergfx)
      ELSE
        out := blank40;
      GotoXY(xp,yp+zeile); 
      Write(normal,out); IF screenw>40 THEN Write(normal,' ');
      IF rastergfx THEN BEGIN  { Zeile, die gerasterte Grafikzeichen enthält }
        special := False; farbe := 0;
        FOR j := 0 TO 39 DO BEGIN  { zu rasternde Abschnitte suchen }
          farbe0 := farbe; farbe := Ord(attrib[j+1]);
          IF (farbe<>farbe0) AND special THEN BEGIN
            raster_line(yp+zeile,xp+j0,xp+j-1,farbe0 AND 7);
            j0 := j; special := (farbe AND 16<>0);
          END;
          IF (farbe AND 16<>0) AND NOT special THEN BEGIN
            j0 := j; special := True;
          END;
        END;
        IF special THEN
          raster_line(yp+zeile,xp+j0,xp+39,farbe0 AND 7);
      END;
      IF zeile=23 THEN dblheight := False; { unterste Zeile nie doppelthoch! }
      IF dblheight THEN BEGIN   { Handhabung doppelthoher Zeilen }
        special := False;
        FOR j := 1 TO Length(out) DO BEGIN   { alles außer den ANSI-Codes }
          { entfernen -> erzeugt Kopie der Hintergrundfarben der Zeile }
          IF out[j] = #155 THEN special := True;
          IF NOT special THEN out[j] := ' ';
          IF out[j] = 'm' THEN special := False;
        END;
        GotoXY(xp,yp+zeile+1); 
        Write(normal,out); IF screenw>40 THEN Write(normal,' ');
        special := False;
        FOR j := 0 TO 39 DO   { doppelthohe Abschnitte suchen }
          CASE seite^.chars[40*zeile+j] OF
            13: BEGIN j0 := j; special := True; END;
            12: IF special THEN BEGIN
                stretch_line(yp+zeile,xp+j0,xp+j); special := False;
              END;
            OTHERWISE;
          END;
        IF special THEN
          stretch_line(yp+zeile,xp+j0,xp+39);
      END;
    END;
    event_scan(False); IF newevent THEN  Exit;
  END;
  incomplete := False;   { Seite komplett }
END;

FUNCTION number_from_page{(x,y: Integer): Integer};
{ versucht zu einer angeklickten Bildschirmposition herauszufinden, auf }
{ was für eine Nummer geklickt wurde }
{ Ergebnis ist eine BCD-Zahl, -1: keine Nummer gefunden }
VAR i,j,j0,n,m: Integer;
    ok,special,hidden: Boolean;
BEGIN
  number_from_page := -1;
  IF NOT ((x IN [xp..xp+39]) AND (y IN [yp..yp+23])) OR (visblpage=Nil) THEN 
    Exit;
  n := -1;
  IF (visblpage^.cbits AND PF_ISMAP)<>0 THEN BEGIN
    { Sonderseite: TOP-Belegungsschema }
    i := 40*(y-yp); { 1. Zeichen der Zeile }
    j0 := 0;  { vorangehenden Doppelpunkt finden }
    FOR j := 3 TO x-xp-2 DO
      IF visblpage^.chars[i+j]=Ord(':') THEN j0 := j;
    IF j0>0 THEN BEGIN { Zahl vor dem Doppelpunkt auswerten }
      n := 0;
      FOR j := j0-3 TO j0-1 DO
        n := (n SHL 4) + visblpage^.chars[i+j]-48;
      IF x-xp-2 < j0+10 THEN
        n := n + (x-xp-2-j0)
      ELSE
        n := -1;
    END;
  END;
  IF n<0 THEN BEGIN  { noch nichts gefunden? }
    i := 40*(y-yp); { 1. Zeichen der Zeile }
    { Sonderfall: untere Hälfte einer doppelthohen Zeile? }
    special := False;
    IF y>yp THEN
      FOR j := -40 TO -1 DO
        IF visblpage^.chars[i+j]=13 THEN special := True;
    IF special THEN i := i-40;  { eine Zeile höher gehen }
    { versuchen eine Zahl zu lesen, die (x,y) enthält }
    ok := True; j0 := x-xp;
    FOR j := x-xp DOWNTO 0 DO BEGIN
      IF NOT (visblpage^.chars[i+j] IN [48..57]) THEN ok := False;
      IF ok THEN j0 := j;
    END;
    ok := True; n := 0; m := 0;
    FOR j := j0 TO 39 DO BEGIN
      IF NOT (visblpage^.chars[i+j] IN [48..57]) THEN ok := False;
      IF ok THEN BEGIN
        n := (n SHL 4) + visblpage^.chars[i+j]-48; Inc(m);
      END;                    
    END;
    IF m=0 THEN n := -1;  { keine Ziffern gefunden }
    { die gefundene Zahl ist aber möglicherweise gar nicht sichtbar! }
    special := False; hidden := False;
    FOR j := 0 TO j0 DO
      IF visblpage^.chars[i+j]<32 THEN BEGIN
        hidden := False;
        CASE visblpage^.chars[i+j] OF
          0..7: special := False;
          16..23: special := True;
          24: hidden := True;
          OTHERWISE;
        END;
      END;
    IF special OR (hidden AND concealed) THEN n := -1;
  END;
  number_from_page := n;
END;


PROCEDURE do_layout;
{ ausgehend von der Bildschirmgröße Position der diversen Sachen festlegen }
BEGIN
  xp := screenw-40; yp := 1;   { Seite }
  IF xp<1 THEN xp := 1;
  xt := xp-24; yt := 2;  { Statusfeld }
  IF screenht>30 THEN BEGIN
    xt := xp; yt := yp+25;
  END;
  IF xt<1 THEN yt := 0;  { -> nicht darstellen! }
  xs := 1; ys := 1; ws := xt-1; IF yt<1 THEN ws := xp-1;  { Seitensuche }
  sp_maxdisp := ws-9;
  IF ws<9 THEN ys := 0;  { -> nicht darstellen! }
  xq := 1; yq := ys+6; colht := screenht-yq-4;  { Warteschlange }
  head := (colht-1) DIV 2; tail := colht-head-2; 
  IF ws<9 THEN yq := 0;  { -> nicht darstellen! }
  colw := 9;
  xl := xq+colw; yl := yq; cols := (xp-xl) DIV colw; { Seitenliste }
  IF cols<1 THEN yl := 0; { -> nicht darstellen! }
  xm := 1; ym := screenht-2; wm := xp-1;    { Meldezeile }
  IF ym>yp+28 THEN wm := screenw;
  IF wm>79 THEN wm := 79; 
  IF wm<1 THEN ym := 0;  { -> nicht darstellen! }
  xf := 1; yf := screenht; wf := screenw; { Dateiinfo }
  pgoffx := xp-1;
END;

PROCEDURE redraw_all;
{ kompletter Neuaufbau des Bildschirms, inklusive clrscr }
VAR s: str80;
    i: Integer;
    c: Char;
BEGIN
  Write(#27'c');  { Console-Reset, wichtig um die Fontgröße anzupassen }
  cursoroff;
  { Fensterausmaße abfragen: }
  Write(#155' q'); s := '';
  REPEAT
    c := ReadCon(Con); s := s + c;
  UNTIL c=#0;
  i := 1; REPEAT Inc(i) UNTIL s[i]=';'; REPEAT Inc(i) UNTIL s[i]=';';
  Inc(i); screenht := 0;
  WHILE s[i] IN ['0'..'9'] DO BEGIN
    screenht := 10*screenht+Ord(s[i])-48; Inc(i); END;
  Inc(i); screenw := 0;
  WHILE s[i] IN ['0'..'9'] DO BEGIN
    screenw := 10*screenw+Ord(s[i])-48; Inc(i); END;
  do_layout; { und Abmessungen auswerten }
  redraw_queue(-1); test(0);
  redraw_list; fileinfo;
  writepage(thispage,True);
END;

FUNCTION click_action{(x,y: Integer): Char};
{ Ein paar bestimmte Sachen, die man anklicken kann }
BEGIN
  click_action := ' ';
  IF (y=yt) AND (x IN [xt..xt+6]) THEN click_action := 'T'; { "Status" }
  IF (y=ys) AND (x IN [xs+1..xs+12]) THEN click_action := 'S'; { "Seitensuche" }
  IF (y=yq) AND (x IN [xq+1..xq+5]) THEN click_action := 'J'; { "Jobs" }
  IF (y=yl) AND (x IN [xl+1..xl+6]) THEN click_action := 'L'; { "Seiten" }
  IF (y=yl) AND (x IN [lsizex..xp-2]) THEN click_action := 'K'; { Löschen }
  IF (y=yf) THEN BEGIN
    IF x IN [xf2..xf3-3] THEN click_action := 'F'; { Format }
    IF x IN [xf3..xf4-1] THEN click_action := 'N'; { Dateiname }
    IF x>=xf4 THEN click_action := 'I'; { Icon? }
  END;
  IF (x>=xp) THEN CASE y-yp OF
      0..7: click_action := '-'; { oberes Seitendrittel }
      8..15: click_action := '0'; { Seitenmitte }
      16..23: click_action := '+'; { unteres Seitendrittel }
      OTHERWISE;
    END;
END;

BEGIN { Initialisierungsteil }
  listoffset := 0;
  incomplete := False;
  concealed := True;
  queue_input := '';
  screenw := 80; screenht := 26; do_layout; { dumme Default-Werte }
  umlaut := 'äöüßÄÖÜ'; umltab := '{|}~[\]';
END.

