PROGRAM VTview;
FROM vt USES pagelist,decode,sys2; {$opt q,s+,i+ }
{ Stellt roh abgespeicherte Videotextseiten auf einem eigenen Screen dar. }
CONST version = '$VER: VTview 2.5  (18.04.96)';

{$incl "dos.lib", "workbench/startup.h", "icon.lib" }

VAR j, timing, countdown, anzseiten: Integer;
    auto, cycle, conceal, stop: Boolean;
    incomplete,concealed: Boolean; { Rückmeldungen von writepage() }
    titel,inputname: Str80; STATIC;
    l: Long;

{ Ein paar Routinen aus dem VT-Hauptprogramm deklarieren und includen: }
CONST pgoffx=1;
PROCEDURE writepage(seite: p_onepage, verdeckt: Boolean); FORWARD;
FUNCTION number_from_page(x,y: Integer): Integer; FORWARD;
FUNCTION filetype(name: Str80): Integer; FORWARD;
FUNCTION getpages(filename: Str80; sorted: Boolean): Integer; FORWARD;
CONST unit_bildschirm=1; unit_datei=1;
{$path "PAS:prg/vt/"; incl "dbluse.p" }

{ ###################################################################### }
{ -------------------------- Initialisierungen ------------------------- }
{ ###################################################################### }

PROCEDURE get_args;
{ Wertet CLI- oder WorkBench-Argumente aus: Die spezifizierten Dateien }
{ werden mit getpages() eingelesen. }
{ ToolTypes:                  CLI-Parameter:  }
{ MODE=MAN|AUTO|CYCLE         -a -c }
{ FLAGS=REVEAL|CONCEAL        -r }
{ TIMING=<secs>               -t<secs> }
VAR c: char;
    s: ^String[1000];
    len,i,j,ok: integer;
    hail: p_WBStartup;
    arg: p_WBArg;
    olddir: BPTR;
    icon: p_DiskObject;
    entry: Str;
    name: Str80;
FUNCTION is_space(ch: Char): Boolean;
BEGIN  is_space := (ch=' ') OR (ch=#9) OR (ch=#10) OR (ch=#13);  END;
BEGIN
  conceal := True;
  auto := False;
  cycle := False;
  timing := 2;
  anzseiten := 0;
  IF fromWB then BEGIN
    OpenLib(IconBase,'icon.library',0);
    hail := StartupMessage;
    arg := hail^.sm_ArgList;
    FOR i := 1 TO hail^.sm_NumArgs do BEGIN
      olddir := CurrentDir(arg^.wa_Lock);
      name := arg^.wa_Name;
      IF filetype(name)=1 THEN    { nur VTPG-Dateien lesen }
        anzseiten := anzseiten + getpages(name,False);
      icon := GetDiskObject(arg^.wa_Name);
      IF icon<>Nil THEN BEGIN
        entry := FindToolType(icon^.do_ToolTypes, 'MODE');
        IF ptr(entry)<>Nil THEN BEGIN
          IF MatchToolValue(entry,'MAN') THEN auto := False;
          IF MatchToolValue(entry,'AUTO') THEN BEGIN
            auto := True; cycle := False; END;
          IF MatchToolValue(entry,'CYCLE') THEN BEGIN
            auto := True; cycle := True; END;
        END;
        entry := FindToolType(icon^.do_ToolTypes, 'FLAGS');
        IF ptr(entry)<>Nil THEN BEGIN
          IF MatchToolValue(entry,'REVEAL') THEN conceal := False;
          IF MatchToolValue(entry,'CONCEAL') THEN conceal := True;
        END;
        entry := FindToolType(icon^.do_ToolTypes, 'TIMING');
        IF Ptr(entry)<>Nil THEN
          Val(entry,timing,ok);
        FreeDiskObject(icon);
      END;
      IF i>1 THEN
        olddir := CurrentDir(olddir);
      { auf nächsten WBArg-Zeiger zugreifen: }
      arg := Ptr(Long(arg)+SizeOf(WBArg));
    END;
    CloseLib(IconBase);
  END ELSE IF ParameterLen>0 THEN BEGIN
    s := ParameterStr; len := ParameterLen;
    { Parameterzeile in Worte zerlegen, wie der argv[] in C es schon ist :-( }
    i := 1; while i<=len do BEGIN
      WHILE is_space(s^[i]) DO Inc(i);
      j := i + 1;
      IF s^[i]='"' THEN BEGIN
        Inc(i); while (s^[j]<>'"') AND (j<=len) do Inc(j);
      END ELSE BEGIN
        WHILE NOT is_space(s^[j]) AND (j<=len) do Inc(j);
      END;
      { Zeiger i: 1. Zeichen des Wortes, j: 1. Trennzeichen dahinter }
      IF s^[i]='-' THEN BEGIN
        i := i+2;
        CASE s^[i-1] OF
          't': Val(copy(s^,i,j-i),timing,ok);
          'r': conceal := False;
          'a': auto := True;
          'c': BEGIN auto := True; cycle := True; END;
          otherwise BEGIN
            writeln('usage:');
            writeln('VTview <file> <file> ... -r[eveal] -a[uto] -c[ycle] -t<secs> ');
            writeln('with <file> containing raw VideoText pages ("VTPG" format)');
          END;
        END;
      END ELSE
        IF filetype(Copy(s^,i,j-i))=1 THEN
          anzseiten := anzseiten + getpages(Copy(s^,i,j-i),False)
        ELSE
          Writeln('Keine VTPG-Datei: ',Copy(s^,i,j-i));
      i := j + 1;
    END;
  END;
END;

{ ###################################################################### }
{ ------------------ Hauptprogramm/Ereignisverwaltung ------------------ }
{ ###################################################################### }

CONST stacksize=10;

VAR pgstack: ARRAY[1..stacksize] OF RECORD pg,sp: Integer; END;
    stackptr,stackbot: Integer;

PROCEDURE push(seite: p_onepage);
{ "vergeßlicher" Stack: Überlauf führt nicht zum Abweisen der neuen }
{ Seitennummer, sondern zum Vergessen der ältesten. }
BEGIN
  IF seite<>Nil THEN BEGIN
    pgstack[stackptr].pg := seite^.pg;
    pgstack[stackptr].sp := seite^.sp;
    Inc(stackptr); IF stackptr>stacksize THEN stackptr := 1;
    IF stackptr=stackbot THEN BEGIN
      Inc(stackbot); IF stackbot>stacksize THEN stackbot := 1;
    END;
  END;
END;

FUNCTION pop: p_onepage;
VAR seite: p_onepage;
    nr: Integer;
BEGIN
  seite := Nil;
  WHILE (stackptr<>stackbot) AND (seite=Nil) DO BEGIN
    Dec(stackptr); IF stackptr=0 THEN stackptr := stacksize;
    seite := hunt_in_list(pgstack[stackptr].pg,pgstack[stackptr].sp,True);
  END;
  pop := seite;
END;

PROCEDURE handle_menu(code: Long);
VAR item,subitem: Word;
BEGIN
  menupicked := False;
  item := code SHR 8;
  subitem := code AND $FF;
  CASE item OF
    { *** VTview *** }
    $0101: stop := True; { Ende }
    OTHERWISE;
  END;
END;

PROCEDURE handle_escseq(s: str80);
{ wie handle_key, aber für die ESC-Sequenzen der Sondertasten }
VAR nr: Integer;
BEGIN
  { Cursor: Seitenliste durchblättern }
  IF s=' A' THEN s := 'R'; { doofe Codes, ändern! }
  IF s=' @' THEN s := 'Q';
  IF (s[1] IN ['A'..'D','Q'..'T']) AND (thispage<>Nil) THEN BEGIN
    nr := thispage^.pg;
    CASE s[1] OF
      'A': IF (thispage^.prev<>Nil) THEN
        thispage := thispage^.prev;
      'B': IF (thispage^.next<>Nil) THEN
        thispage := thispage^.next;
      'C': thispage := next_magazine(thispage);
      'D': thispage := prev_magazine(thispage);
      'Q': WHILE (thispage^.next<>Nil) DO
        thispage := thispage^.next;
      'R': thispage := root;
      'S': WHILE (thispage^.next<>Nil) AND (thispage^.pg=nr) DO
        thispage := thispage^.next;
      'T': WHILE (thispage^.prev<>Nil) AND (thispage^.pg=nr) DO
        thispage := thispage^.prev;
      OTHERWISE;
    END;
    update_slider(posn_in_list(thispage),anzseiten);
    writepage(thispage,conceal);
  END;
END;

PROCEDURE handle_key(key: char);
VAR pg: p_onepage;
BEGIN
  CASE key OF
    #27: BEGIN pg := pop; IF pg<>Nil THEN BEGIN
          thispage := pg; update_slider(posn_in_list(pg),anzseiten);
          writepage(thispage,conceal);
        END;
      END;
    ' ': writepage(thispage,True);
    '?': writepage(thispage,false);
    #155: handle_escseq(escseq);
    OTHERWISE;
  END;
END;

PROCEDURE handle_click;
VAR pg: p_onepage;
    nr: Integer;
BEGIN
  mouseclicked := False;
  nr := number_from_page(clickedx,clickedy);
  IF nr>=0 THEN BEGIN   { Nummer in einer Seite angeklickt }
    pg := hunt_in_list(nr,0,True);
    IF pg=Nil THEN pg := hunt_in_list(nr,1,True);
    IF pg=Nil THEN pg := hunt_in_list(nr,0,False);
    IF pg<>Nil THEN IF pg^.pg<>nr THEN pg := Nil;
    IF (pg<>Nil) AND (pg<>thispage) THEN BEGIN  { Seite anspringen }
      push(thispage);
      thispage := pg; update_slider(posn_in_list(pg),anzseiten);
      writepage(thispage,conceal);
    END;
  END ELSE CASE clickedy OF
      1..8: handle_escseq('A'); { oberes Seitendrittel }
      9..16: handle_key(#27); { Seitenmitte }
      17..24: handle_escseq('B'); { unteres Seitendrittel }
      OTHERWISE;
    END;
END;

PROCEDURE handle_slider;
VAR pg: p_onepage;
    nr: Integer;
BEGIN
  slidermoved := False;
  nr := Round(sliderposn*(anzseiten-1));
  pg := page_on_posn(nr);
  IF (pg<>Nil) AND (pg<>thispage) THEN BEGIN  { Seite anspringen }
    thispage := pg; writepage(thispage,conceal);
    update_slider(posn_in_list(pg),anzseiten);
  END;
END;

BEGIN   { Hauptprogramm }
  get_args;   { Parameter holen, Seiten einlesen }
  IF anzseiten=0 THEN BEGIN
    IF NOT fileselect('VT-Seiten laden',False,inputname) THEN Exit;
    IF filetype(inputname)=1 THEN    { nur VTPG-Dateien lesen! }
      anzseiten := getpages(inputname,False);
  END;
  colperm := $03746521; colperms := hexstr(colperm,8);
            { 01234567  07613542 }
  AddExitServer(sysclean); sysinit(version);
  update_slider(0,anzseiten);
  Write(#155'0 p'); { Cursor aus }
  countdown := timing;
  stackbot := 1; stackptr := stackbot;
  writepage(thispage,conceal);
  REPEAT
    IF NOT newevent THEN event_scan;
    IF newevent THEN BEGIN
      auto := False; newevent := False;
      IF mouseclicked THEN handle_click;
      IF menupicked THEN handle_menu(menucode);
      IF slidermoved THEN handle_slider;
      IF taste<>#0 THEN handle_key(taste);
    END ELSE IF auto THEN BEGIN
      Delay(50); Dec(countdown);
      IF countdown<=0 THEN
        IF thispage<>Nil THEN BEGIN
          IF thispage^.next=Nil THEN
            IF cycle THEN thispage := root ELSE stop := True
          ELSE
            thispage := thispage^.next;
          IF NOT stop THEN BEGIN
            update_slider(posn_in_list(thispage),anzseiten);
            writepage(thispage,conceal);
            countdown := timing;
          END;
        END ELSE
          stop := True;
    END ELSE
      l := Wait(-1);
  UNTIL stop;
  SetStdIO(Nil); CloseConsole(Con);
  kill_list; sysclean;
END.

