home *** CD-ROM | disk | FTP | other *** search
- (* ------------------------------------------------------ *)
- (* BUFFER5.PAS *)
- (* *)
- (* Diese Unit implementiert das Objekt BufferObj, das *)
- (* das dazu dient, Daten in einem Puffer zu verarbeiten. *)
- (* Falls der Hauptspeicher nicht ausreicht, werden die *)
- (* Daten auf Diskette/Festplatte ausgelagert. *)
- (* *)
- (* (c) 1990 R.Reichert & TOOLBOX *)
- (* ------------------------------------------------------ *)
- UNIT Buffer5;
-
- INTERFACE
-
- CONST
- { "MaxLines" und "Columns" sollten vor dem Aufruf von }
- { "BufferObj.Init" auf den gewünschten Wert gesetzt }
- { werden. Der maximale Wrt für "MaxLines" ist 18382, der }
- { für "MaxColumns" 32766, der Speicherbedarf errechnet }
- { sich nach "Columns". }
- { Da die Belegung und Freigabe des Speichers über }
- { "GetMem" und "FreeMem" erfolgt, darf die Variable }
- { "Columns" nicht während des Programmlaufs geändert }
- { werden. }
-
- MaxMaxLines = 16000;
- MaxLines : WORD = 2000;
- MaxColumns = 32766;
- MinColumns = 10;
- FreeHeap : LongInt = 1024;
- BufInitErr = -1;
- BufNoErr = 0;
- BufInsLineErr = 1;
- BufDelLineErr = 2;
- BufWriteStrErr = 3;
- BufCheckXYErr = 4;
- BufFileErr = 5;
- BufDiskFull = 6;
-
- BufErrTxt : ARRAY [BufInitErr..BufDiskFull] OF
- STRING [80] =
- ('Fehler in Init (nicht genügend Speicher für ' +
- 'Datenstruktur).',
- 'Kein Fehler aufgetreten',
- 'Fehler beim Einfügen einer Zeile',
- 'Fehler beim Löschen einer Zeile',
- 'Fehler in WriteStrXY',
- 'X/Y-Koordinate außerhalb Puffer (XYInBuf)!',
- 'Fehler beim Auslagern',
- 'Festplatte/Diskette voll, Auslagern nicht mehr' +
- ' möglich');
-
- TYPE
- LineEndType = (WriteOver, CutEnd, CutPrevWord);
- FormatTypes = (Left, Center, Right);
- OneLinePtr = ^OneLine;
- OneLine = ARRAY [0..MaxColumns] OF WORD;
- BufferPtr = ^Buffer;
- Buffer = ARRAY [0..MaxMaxLines] OF OneLinePtr;
-
- BufferObjPtr = ^BufferObj;
-
- BufferObj =
- OBJECT
- InitError, { Fehler in Init }
- MoveBufCur, { "Cursor" bewegen ? }
- KillLineRest, { Zeilenrest löschen }
- KillWrite, { vor Schreiben löschen }
- LineFeed, { Zeilenvorschub ? }
- AllSaved, { Alles gespeichert ? }
- SaveData : BOOLEAN; { Datei löschen ? }
- BufErrorL1, { Fehler-Nr Level 1 }
- BufErrorL2, { Level 2, }
- BufDosErr, { Dos-Fehlercode }
- BufCurX, { Cursor-X-Position }
- BufCurY, { Cursor-Y-Position }
- Columns, { Anzahl Spalten }
- Lines : INTEGER; { Anz. Zeilen }
- BufCol, { "Schreib"-Farbe }
- BufBackCol, { Hintergrundfarbe }
- Attr : BYTE; { res. Attrib. }
- TextBuf : BufferPtr; { Pufferzeiger }
- LineForm : FormatTypes; { Zeilenende }
- LineEnd : LineEndType; { Ausrichtung }
- WordEndChars,
- SEFileName, { Dateiname zum Auslagern }
- EndName,
- SEPath : STRING;
- InfoLine : OneLinePtr;
- f : FILE;
-
- CONSTRUCTOR Init(BufData : BufferObj);
-
- PROCEDURE ErrorHandling(Nr : INTEGER); VIRTUAL;
- PROCEDURE SetMaxLines(NewML : INTEGER); VIRTUAL;
- PROCEDURE GetNewLine(Attribut : BYTE;
- VAR NewLine : OneLine); VIRTUAL;
- PROCEDURE SaveAll; VIRTUAL;
- PROCEDURE SaveFrom(y : INTEGER); VIRTUAL;
- PROCEDURE SaveLine(y : INTEGER); VIRTUAL;
- PROCEDURE SaveNewLine(y : INTEGER;
- VAR Line : OneLine;
- OnlyDisk : BOOLEAN); VIRTUAL;
- PROCEDURE SaveNewDrive(NewDrive : STRING); VIRTUAL;
- PROCEDURE Flush; VIRTUAL;
- PROCEDURE LoadLine(y : INTEGER;
- VAR Line : OneLine); VIRTUAL;
- PROCEDURE LoadPart(y1, y2 : INTEGER); VIRTUAL;
- PROCEDURE CloseFile; VIRTUAL;
- PROCEDURE InsLines(y, No : INTEGER); VIRTUAL;
- PROCEDURE CopyLine(Source, Dest : INTEGER); VIRTUAL;
- PROCEDURE DelLines(y, No : INTEGER); VIRTUAL;
- PROCEDURE WriteStrXY(x, y : INTEGER;
- Str : STRING); VIRTUAL;
- PROCEDURE WriteStr(Str : STRING); VIRTUAL;
- PROCEDURE FormatLine(VAR Line : OneLine); VIRTUAL;
- FUNCTION GetCutPos(Str : STRING;
- x : INTEGER) : INTEGER; VIRTUAL;
- FUNCTION GetLastWord(Str : STRING;
- x : INTEGER) : INTEGER; VIRTUAL;
- FUNCTION Convert2Str(y : INTEGER) : STRING; VIRTUAL;
- FUNCTION GetLineLength(y : INTEGER): INTEGER;VIRTUAL;
- PROCEDURE SetWriteColor(Col, BackCol : BYTE); VIRTUAL;
- PROCEDURE ChangeColor(x1, y1, x2, y2 : INTEGER;
- NewCol, NewBackCol:BYTE);VIRTUAL;
- PROCEDURE SetBufCursor(x, y : INTEGER); VIRTUAL;
- PROCEDURE GetBufXYColors(x, y : INTEGER;
- VAR Col, BackCol : BYTE);
- VIRTUAL;
- FUNCTION GetBufXYAttr(x, y : INTEGER) : BYTE; VIRTUAL;
- FUNCTION XYInBuf(x, y : INTEGER) : BOOLEAN; VIRTUAL;
-
- DESTRUCTOR Done; VIRTUAL;
- END;
-
- CONST
- BufData : BufferObj = (InitError : FALSE;
- MoveBufCur : FALSE;
- KillLineRest : FALSE;
- KillWrite : FALSE;
- LineFeed : FALSE;
- AllSaved : FALSE;
- SaveData : FALSE;
- BufErrorL1 : 0;
- BufErrorL2 : 0;
- BufDosErr : 0;
- BufCurX : 1;
- BufCurY : 1;
- Columns : 0;
- Lines : 0;
- BufCol : 1;
- BufBackCol : 0;
- Attr : 0;
- TextBuf : NIL;
- LineForm : Left;
- LineEnd : CutPrevWord;
- WordEndChars : ')+!?,./;:-─ ';
- SEFileName : '';
- EndName : '';
- SEPath : '');
-
- IMPLEMENTATION
-
- CONST
- CopyTL = 1; InsTL = 2; GetLLTL = 3; SetMLTL = 4;
- LPTL = 5; WrtStrTL = 6; ConvTL = 7; TL = 8;
-
- VAR
- TempLines : ARRAY [CopyTL..TL] OF OneLinePtr;
- Time : LongInt ABSOLUTE $40:$6C;
- i : INTEGER;
-
- (* ------------------------------------------------------ *)
- (* Initialisiert mit den in "BufData" übergebenen Daten. *)
- (* Wenn nicht genügend Speicher für die Datenstruktur *)
- (* vorhanden ist, werden "BufErrorL2" und "InitError" ge- *)
- (* setzt und die Standardprozedur "Fail" aufgerufen. *)
- (* Ein Programm sollte immer den Init-Error abfragen und *)
- (* im Fehlerfall abbrechen. Die Methoden fragen *)
- (* "InitError" nicht ab! *)
- (* Am Ende werden ein paar Variablen gesetzt, die zu Be- *)
- (* ginn immer den gleichen Wert haben (müssen). Falls für *)
- (* "Columns" oder "Lines" ungültige Werte angegeben wur- *)
- (* wurden, ist "InitError" TRUE. Dasselbe geschieht, wenn *)
- (* nicht genügend Speicher für die Aufnahme der Zeilen *)
- (* vorhanden ist. *)
- (* ------------------------------------------------------ *)
- CONSTRUCTOR BufferObj.Init(BufData : BufferObj);
- VAR
- MemLimit, i : LongInt;
- BEGIN
- Self := BufData;
- GetMem(TextBuf, 4 * Succ (MaxLines));
- IF (TextBuf = NIL) OR { Genügend Speicher? }
- (Columns > MaxColumns) OR { Columns zulässig ? }
- (Columns < MinColumns) OR
- (Lines > MaxLines) THEN BEGIN { Lines in Ordnung ? }
- InitError := TRUE;
- ErrorHandling(BufInitErr); Done; Fail;
- END;
- InitError := FALSE;
- MemLimit := (MemAvail - (FreeHeap + 2 * Succ(Columns)));
-
- { Genug Speicher für temp. Zeilen und InfoLine ? }
- IF 2 * Succ(Columns) * Succ(TL) > MemLimit THEN BEGIN
- InitError := TRUE;
- ErrorHandling(BufInitErr); Done; Fail;
- END;
- FOR i := CopyTL TO TL DO BEGIN { Speicher für die }
- GetMem(TempLines[i], 2 * Succ(Columns));
- IF TempLines[i] = NIL THEN { temp. Zeilen }
- InitError := TRUE
- END;
- GetMem(InfoLine, 2 * Succ(Columns));
- IF (InitError) OR (InfoLine = NIL) THEN BEGIN
- ErrorHandling(BufInitErr); Done; Fail;
- END;
- GetNewLine(0, InfoLine^);
- BufErrorL1 := BufNoErr; BufErrorL2 := BufNoErr;
- BufDosErr := 0; AllSaved := FALSE;
- BufCurX := 1; BufCurY := 1;
- FOR i := 1 TO MaxLines DO
- TextBuf^[i] := NIL;
- IF Lines > 0 THEN BEGIN
- i := Lines; Lines := 0;
- { Ausreichend Speicher für die angeforderten Zeilen ? }
- IF 2 * Succ (Columns) * i < MemLimit THEN
- SetMaxLines (i)
- ELSE BEGIN
- InitError := TRUE;
- ErrorHandling(BufInitErr); Done; Fail;
- END;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Setzt "BufErrorL1" bzw. "L2" von "BufferObj" mit "Nr". *)
- (* Falls die Schalter "Test" oder "Test2" für bedingte *)
- (* Compilierung gesetzt sind, wird die Fehlermeldung aus- *)
- (* gegeben (Testphase eines Programms). *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.ErrorHandling(Nr : INTEGER);
- BEGIN
- {$IFDEF TEST}
- WriteLn (^G, '*** Fehler: ', BufErrTxt[Nr]);
- {$ENDIF}
- {$IFDEF TEST2}
- WriteLn (Lst, ^G'*** Fehler: ', BufErrTxt[Nr]);
- {$ENDIF}
- IF (Nr = BufDiskFull) OR (Nr = BufFileErr) THEN
- BufErrorL1 := Nr
- ELSE
- BufErrorL2 := Nr;
- END;
-
- (* ------------------------------------------------------ *)
- (* Setzt Lines neu, wobei sich die Angabe "NewML" relativ *)
- (* auf den momentanen Wert von Lines bezieht. Es ist also *)
- (* möglich, die letzten "NewML" Zeilen zu löschen. Beim *)
- (* hinzufügen wird geprüft, ob noch genügend Speicher *)
- (* vorhanden ist. Reicht der Speicher nicht aus, wird *)
- (* alles in eine temporäre Datei geschrieben und die neue *)
- (* Zeile daran angehängt. Lines erhält den neuen Wert. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.SetMaxLines(NewML : INTEGER);
- VAR
- OldLines : INTEGER;
- Kill : BOOLEAN;
-
- (* -------------------------------------------------- *)
- (* Löscht Zeilen zwischen "OldLines" und "Lines". *)
- PROCEDURE KillLines;
- VAR
- i : INTEGER;
- BEGIN
- FOR i := Lines DOWNTO OldLines DO
- IF (TextBuf^[i] <> NIL) THEN BEGIN
- FreeMem(TextBuf^[i], 2 * Succ(Columns));
- TextBuf^[i] := NIL
- END ELSE IF (BufErrorL1 <> BufFileErr) THEN BEGIN
- Seek(f, Pred(i));
- Truncate (f);
- IF BufErrorL1 = BufDiskFull THEN
- BufErrorL1 := BufNoErr
- END;
- Lines := Pred(OldLines);
- END;
-
- (* -------------------------------------------------- *)
- (* Gegenstück zu KillLines, hängt neue Zeilen an. *)
- (* Wenn im Speicher kein Platz mehr vorhanden ist, *)
- (* wird ausgelagert. *)
- PROCEDURE NewLines;
- VAR
- i, DummyLine, MemLimit : LongInt;
- BEGIN
- DummyLine := 0;
- MemLimit := 2 * Succ(Columns) + FreeHeap;
- Attr := BufCol + BufBackCol * 16;
- FOR i := OldLines TO Lines DO
- IF (MemAvail > MemLimit) THEN BEGIN
- GetMem(TextBuf^[i], 2 * Succ (Columns));
- GetNewLine(Attr, TextBuf^[i]^)
- END ELSE BEGIN
- IF (NOT AllSaved) AND
- (BufErrorL1 <> BufFileErr) THEN
- SaveAll;
- IF (BufErrorL1 <> BufDiskFull) AND
- (BufErrorL1 <> BufFileErr) THEN BEGIN
- GetNewLine(Attr, TempLines [SetMLTL]^);
- SaveNewLine(i, TempLines [SetMLTL]^, TRUE)
- END;
- IF (BufErrorL1 = BufDiskFull) OR
- (BufErrorL1 = BufFileErr) THEN
- Inc(DummyLine);
- END;
- Dec(Lines, DummyLine);
- END;
-
- BEGIN { SetMaxLines }
- IF NOT (NewML = 0) THEN BEGIN { +/- Lines ? }
- Kill := FALSE;
- IF NewMl > 0 THEN BEGIN { hinzufügen ? }
- OldLines := Succ(Lines);
- Lines := Lines + NewMl;
- IF Lines > MaxLines THEN Lines := MaxLines
- END ELSE BEGIN
- IF Lines + NewMl < 0 THEN NewMl := -Lines;
- OldLines := Succ(Lines + NewMl);
- Kill := TRUE;
- IF OldLines < 0 THEN OldLines := 0
- END;
- IF Kill THEN KillLines
- ELSE NewLines;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Gibt eine neue Zeile mit Attribut in "NewLine" zurück. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.GetNewLine(Attribut : BYTE;
- VAR NewLine : OneLine);
- VAR
- i : INTEGER;
- BEGIN
- IF Attribut = 0 THEN
- FillChar(NewLine, SizeOf(OneLine), 0)
- ELSE BEGIN
- FOR i := 1 TO Columns DO
- NewLine[i] := Attribut SHL 8;
- NewLine [0] := 0
- { Hier muß "Längenwort" explizit gesetzt werden! }
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Bildet einen Dateinamen aus der aktuellen Uhrzeit. In *)
- (* In diese temporäre Datei wird ausgelagert. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.SaveAll;
- BEGIN
- Str(Time, SEFileName);
- IF Length(SEFileName) > 8 THEN
- Delete(SEFileName, 1, Length(SEFileName) - 8);
- IF (SEPath <> '') AND
- (SEPath[Length(SEPath)] <> '\') AND
- (SEPath[Length(SEPath)] <> ':') THEN
- SEPath := SEPath + '\';
- SEFileName := SEPath + SEFileName + '.$$$';
- {$I-}
- Assign(f, SEFileName);
- Rewrite(f, Succ(Columns) * 2);
- {$I+}
- BufDosErr := IOResult;
- IF BufDosErr <> 0 THEN
- ErrorHandling(BufFileErr)
- ELSE BEGIN
- AllSaved := TRUE; SaveFrom(0);
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Speichert von y bis Lines. Bedingung: AllSaved = TRUE! *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.SaveFrom(y : INTEGER);
- VAR
- i : INTEGER;
- BEGIN
- IF AllSaved THEN BEGIN { schon einmal gespeichert? }
- i := y;
- IF (BufErrorL1 <> BufFileErr) THEN
- WHILE (NOT(i > Lines)) AND
- (BufErrorL1 <> BufDiskFull) DO BEGIN
- SaveLine(i); Inc(i); { zeilenweise speichern }
- END;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Speichert eine Zeile aus dem Speicher ab. Die Routine *)
- (* prüft nur, ob die Zeile nicht ausgelagert ist, nimmt *)
- (* jedoch keine Bereichsüberprüfung vor. Als "Nullte" *)
- (* Zeile wird "InfoLine" gespeichert. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.SaveLine(y : INTEGER);
- BEGIN
- IF y = 0 THEN
- SaveNewLine(y, InfoLine^, TRUE)
- ELSE IF TextBuf^[y] <> NIL THEN
- SaveNewLine(y, TextBuf^[y]^, TRUE)
- END;
-
- (* ------------------------------------------------------ *)
- (* Speichert die übergebene Zeile "Line" ab. Hier werden *)
- (* Bereichs- und Dateifehler erkannt. Ist die Zeile nicht *)
- (* ausgelagert, wird "Line" nicht extern gespeichert, *)
- (* sondern im Speicher abgelegt. OnlyDisk = TRUE erzwingt *)
- (* ein Schreiben auf Diskette. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.SaveNewLine(y : INTEGER;
- VAR Line : OneLine;
- OnlyDisk : BOOLEAN);
- BEGIN
- IF (y >= 0) AND (y <= Lines) AND (AllSaved) AND
- ((OnlyDisk) OR (TextBuf^[y] = NIL)) AND
- (BufErrorL1 <> BufFileErr) THEN BEGIN
- {$I-}
- Seek(f, y); BlockWrite(f, Line, 1);
- {$I+}
- BufDosErr := IOResult;
- IF (BufDosErr <> 0) THEN Errorhandling(BufDiskFull);
- END;
- IF (XYInBuf(1, y)) AND (NOT OnlyDisk) AND
- (TextBuf^[y] <> NIL) THEN
- Move(Line, TextBuf^[y]^, 2 * Succ(Columns));
- END;
-
- (* ------------------------------------------------------ *)
- (* Falls eine Diskette voll ist, kann mit "SaveNewDrive" *)
- (* auf einen anderen Datenträger gespeichert werden. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.SaveNewDrive(NewDrive : STRING);
- BEGIN
- IF AllSaved THEN BEGIN
- IF (BufErrorL1 = BufDiskFull) OR
- (BufErrorL1 = BufFileErr) THEN
- BufErrorL1 := BufNoErr;
- {$I-}
- Close (f);
- {$I+}
- SEPath := NewDrive; AllSaved := FALSE; SaveAll;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Bildet die Turbo-Prozedur Flush() nach, die nur für *)
- (* Textdateien gültig ist. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.Flush;
- BEGIN
- IF (AllSaved) AND (BufErrorL1 <> BufFileErr) THEN BEGIN
- Close(f); Reset(f, 2 * Succ(Columns));
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Lädt eine Zeile aus der Datei oder dem Speicher *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.LoadLine(y : INTEGER;
- VAR Line : OneLine);
- BEGIN
- IF (y >= 0) AND (y <= Lines) AND (AllSaved) AND
- (BufErrorL1 <> BufFileErr) AND
- (TextBuf^[y] = NIL) THEN BEGIN
- {$I-}
- Seek(f, y); BlockRead(f, Line, 1);
- {$I+}
- BufDosErr := IOResult;
- IF BufDosErr <> 0 THEN
- ErrorHandling(BufFileErr);
- END ELSE IF (TextBuf^[y] <> NIL) AND
- (XYInBuf(1, y)) THEN
- Move(TextBuf^[y]^, Line, 2 * Succ(Columns));
- END;
-
- (* ------------------------------------------------------ *)
- (* Lädt aus einer ausgelagerten Datei den Teil von "y1" *)
- (* bis "y2", sofern möglich. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.LoadPart(y1, y2 : INTEGER);
- VAR
- i, j, No : INTEGER;
-
- PROCEDURE SearchSave(BegCol, EndCol, Max : INTEGER;
- VAR Counter : INTEGER);
- VAR
- i : INTEGER;
- BEGIN
- i := BegCol;
- WHILE NOT (i > EndCol) AND
- NOT (Counter >= Max) DO BEGIN
- IF TextBuf^[i] <> NIL THEN BEGIN
- SaveLine(i); Inc(Counter);
- FreeMem(TextBuf^[i], 2 * Succ(Columns));
- TextBuf^[i] := NIL
- END;
- Inc(i);
- END;
- END;
-
- BEGIN { LoadPart }
- IF XYInBuf(1, y1) AND XYInBuf(1, y2) AND AllSaved AND
- (BufErrorL1 <> BufFileErr) THEN BEGIN
- No := 0; j := 0;
- FOR i := y1 TO y2 DO
- IF TextBuf^[i] = NIL THEN Inc(No);
- SearchSave(1, y1, No, j);
- SearchSave(Succ(y2), Lines, No, j);
- i := y1; No := 0;
- WHILE NOT (i > y2) AND NOT (No >= j) DO BEGIN
- IF TextBuf^[i] = NIL THEN BEGIN
- LoadLine (i, TempLines[LPTL]^);
- Inc(No);
- GetMem(TextBuf^[i], 2 * Succ(Columns));
- Move(TempLines[LPTL]^, TextBuf^[i]^,
- 2 * Succ(Columns));
- END;
- Inc(i);
- END;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Wenn "SaveData" FALSE ist, wird die temporäre Datei ge-*)
- (* löscht, ansonsten alles nochmal gespeichert, und, wenn *)
- (* "EndName" <> '' ist, die Datei in "EndName" umbenannt. *)
- (* ACHTUNG: Nur am Ende von Done aus aufrufen! *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.CloseFile;
- BEGIN
- IF AllSaved THEN BEGIN
- IF SaveData THEN SaveFrom(0);
- {$I-}
- Close(f);
- {$I+}
- BufDosErr := IOResult;
- IF (BufDosErr <> 0) THEN
- ErrorHandling(BufFileErr)
- ELSE BEGIN
- IF (NOT SaveData) AND (EndName = '') THEN
- Erase(f) { wenn nicht retten, dann löschen }
- ELSE IF EndName <> '' THEN
- Rename(f, EndName) { oder umbenennen }
- { ACHTUNG: Es darf keine Datei mit dem Namen }
- { "EndName" existieren, sonst bleibt die Umbe- }
- { nennung erfolglos. }
- END;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Fügt an der Position "y" eine Anzahl von Zeilen ein. *)
- (* "y" kann einen Wert von 0 bis "Lines" annehmen. Die *)
- (* neuen Zeilen werden von y+1 an abwärts eingefügt. *)
- (* Beim Versucht, mehr Zeilen einzufügen, als "MaxLines" *)
- (* verkraftet, wird "No" eingeschränkt. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.InsLines(y, No : INTEGER);
- VAR
- OldLines, i : INTEGER;
- BEGIN
- IF (y >= 0) AND (y <= Lines) THEN BEGIN
- IF y + No > MaxLines THEN No := MaxLines - y;
- OldLines := Lines;
- SetMaxLines (No);
- IF BufErrorL1 <> 0 THEN BEGIN
- SetMaxLines(-(Lines-OldLines));
- Exit;
- END;
- LoadLine(Lines, TempLines[InsTL]^);
- FOR i := Lines DOWNTO y+No DO CopyLine(i-No, i);
- FOR i := y TO Pred(y+No) DO
- SaveNewLine(i, TempLines[InsTL]^, FALSE);
- END ELSE
- ErrorHandling(BufInsLineErr);
- END;
-
- (* ------------------------------------------------------ *)
- (* Kopiert eine Zeile von "Source" nach "Dest" um. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.CopyLine(Source, Dest : INTEGER);
- BEGIN
- IF XYInBuf(1, Source) AND XYInBuf(1, Dest) THEN BEGIN
- LoadLine(Source, TempLines[CopyTL]^);
- SaveNewLine(Dest, TempLines [CopyTL]^, FALSE);
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Löscht, angefangen mit der Zeile "y", "No" Zeilen *)
- (* aus dem Puffer bzw der Datei. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.DelLines(y, No : INTEGER);
- VAR
- i : INTEGER;
- BEGIN
- IF XYInBuf(1, y) AND (No > 0) THEN BEGIN
- IF y + No > Succ(Lines) THEN No := Succ(Lines - y);
- FOR i := Succ(y+No) TO Lines DO CopyLine(i, i-No);
- SetMaxLines(-No);
- END ELSE
- ErrorHandling(BufDelLineErr);
- END;
-
- (* ------------------------------------------------------ *)
- (* Schreibt an "x", "y" den String "str" mit dem Attribut *)
- (* "Attr" in den Puffer. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.WriteStrXY(x, y : INTEGER;
- Str : STRING);
- VAR
- s1, s2 : STRING;
- cp, y2 : INTEGER;
- LNo : BYTE;
- OnePlus : BOOLEAN;
-
- (* -------------------------------------------------- *)
- (* Erledigt folgende Vorgänge: *)
- (* Länge retten; kopieren von "Str" nach "Line"; neue *)
- (* Länge berechnen; formatieren von "Line". *)
- PROCEDURE WriteOneLine(Str : STRING;
- x, y : INTEGER;
- VAR Line : OneLine);
- VAR
- i, OldLength, NewLength : INTEGER;
- BEGIN
- IF KillWrite THEN GetNewLine(Attr, Line);
- CASE LineForm OF
- Center : x := Columns DIV 2 - Length(Str) DIV 2;
- Right : x := Columns - Length(Str);
- END;
- OldLength := Line[0]; { alte Länge retten }
- Attr := BufCol + BufBackCol * 16;
- FOR i := 1 TO Length(Str) DO { Kopieren }
- Line[Pred(x+i)] := Ord(Str[i]) + Attr SHL 8;
- NewLength := Pred(x + Length(s1));
- IF (OldLength > NewLength) AND
- NOT KillLineRest THEN NewLength := OldLength;
- Line[0] := NewLength;
- FormatLine(Line);
- SaveNewLine(y, Line, FALSE) { und speichern }
- END;
-
- BEGIN { WriteStringXY }
- IF y = Succ(Lines) THEN BEGIN
- OnePlus := TRUE;
- IF NOT AllSaved THEN SetMaxLines(1)
- ELSE Inc (Lines)
- END ELSE
- OnePlus := FALSE;
- IF NOT XYInBuf(1, y) THEN BEGIN
- ErrorHandling(BufWriteStrErr); Exit;
- END;
- IF x > Columns THEN BEGIN
- x := 1; Inc(y);
- IF NOT XYInBuf(1, y) THEN { neue Zeile benötigt? }
- SetMaxLines (1);
- IF (BufErrorL1 = BufDiskFull) OR
- (BufErrorL1 = BufFileErr) THEN Exit;
- END;
- IF TextBuf^[y] = NIL THEN BEGIN
- IF KillWrite OR OnePlus THEN
- GetNewLine (Attr, TempLines[WrtStrTL]^)
- ELSE
- LoadLine(y, TempLines[WrtStrTL]^);
- IF BufErrorL1 = BufFileErr THEN Exit;
- END ELSE
- LoadLine(y, TempLines[WrtStrTL]^);
- s2 := ''; Lno := 1;
- IF x + Length(Str) > Columns THEN BEGIN
- cp := GetCutPos(Str, x);
- IF cp = 0 THEN { Falls keine Umbruchstelle gefunden }
- cp := Succ(Columns-x); { wird, "abschneiden" }
- s1 := Copy(Str, 1, cp);
- s2 := Copy(Str, cp+1, Length(Str)-cp);
- IF NOT (LineEnd = CutEnd) THEN Lno := 2;
- IF Length(s2) > Columns THEN S2[0] := Chr(Columns);
- END ELSE
- s1 := Str;
- WriteOneLine(s1, x, y, TempLines[WrtStrTL]^);
- IF LNo > 1 THEN BEGIN
- y2 := Succ(y);
- IF NOT XYInBuf(1, y2) THEN BEGIN
- SetMaxLines(1);
- IF (BufErrorL1 = BufDiskFull) OR
- (BufErrorL1 = BufFileErr) THEN Exit;
- END;
- LoadLine(y2, TempLines[WrtStrTL]^);
- WriteOneLine(s2, 1, y2, TempLines[WrtStrTL]^);
- END;
- IF MoveBufCur THEN BEGIN { Cursor analog bewegen ? }
- BufCurY := Pred(BufCurY + LNo);
- IF LNo > 1 THEN
- BufCurX := Length(s2)
- ELSE
- BufCurX := x + Length(s1);
- IF LineFeed THEN BEGIN
- IF y = Lines THEN SetMaxLines(1);
- IF NOT (BufErrorL1 = BufDiskFull) AND
- NOT (BufErrorL1 = BufFileErr) THEN BEGIN
- BufCurX := 1; Inc(BufCurY);
- END;
- END;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Schreibt "Str" an die aktuelle Cursorposition. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.WriteStr(Str : STRING);
- BEGIN
- WriteStrXY(BufCurX, BufCurY, Str);
- END;
-
- (* ------------------------------------------------------ *)
- (* Hier nur als Dummy, da eine Zeile nicht speziell for- *)
- (* matiert werden muß. Die Prozedur könnte zum Beispiel *)
- (* zum Formatieren von Blöcken eingesetzt werden. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.FormatLine(VAR Line : OneLine);
- BEGIN
- END;
-
- (* ------------------------------------------------------ *)
- (* Errechnet die Position des letzten Wortes in einem *)
- (* String. Ein Wortende wird von "x" an abwärts gesucht. *)
- (* ACHTUNG: Keine Bereichsüberprüfung von "x"! *)
- (* ------------------------------------------------------ *)
- FUNCTION BufferObj.GetLastWord(Str : STRING;
- x : INTEGER) : INTEGER;
- BEGIN
- Inc(x);
- REPEAT
- Dec(x);
- UNTIL (Pos(Str[x], WordEndChars) > 0) OR (x < 1);
- IF Pos(Str[x], WordEndChars) > 0 THEN
- GetLastWord := x
- ELSE
- GetLastWord := 0;
- END;
-
- (* ------------------------------------------------------ *)
- (* Errechnet die "Schnittstelle" in einem String, der *)
- (* über zwei Zeilen gehen soll. Berücksichtigt wird hier *)
- (* "LineEnd", das angibt, wie eine Zeile beendet wird. *)
- (* ------------------------------------------------------ *)
- FUNCTION BufferObj.GetCutPos(Str : STRING;
- x : INTEGER) : INTEGER;
- VAR
- cp : INTEGER;
- BEGIN
- CASE LineEnd OF
- CutPrevWord : cp := GetLastWord(Str, Columns - x + 2);
- WriteOver,
- CutEnd : cp := Succ(Columns - x);
- END;
- GetCutPos := cp;
- END;
-
- (* ------------------------------------------------------ *)
- (* Gibt die Länge der durch "y" spezifierten Zeile zurück *)
- (* Ist "y" nicht im Pufferbereich, ist der Wert -1. *)
- (* ------------------------------------------------------ *)
- FUNCTION BufferObj.GetLineLength(y : INTEGER) : INTEGER;
- BEGIN
- IF XYInBuf(1, y) THEN BEGIN
- LoadLine(y, TempLines[GetLLTL]^);
- GetLineLength := TempLines[GetLLTL]^[0]
- END ELSE
- GetLineLength := -1;
- END;
-
- (* ------------------------------------------------------ *)
- (* Wandelt Line in einen String um. Ist "y" länger als *)
- (* 255 Zeichen, so wird der Rest ignoriert. *)
- (* ------------------------------------------------------ *)
- FUNCTION BufferObj.Convert2Str(y : INTEGER) : STRING;
- VAR
- i : INTEGER;
- Str : STRING;
- BEGIN
- i := 1; Str := '';
- LoadLine(y, TempLines[ConvTL]^);
- IF TempLines[ConvTL]^[0] <> 0 THEN
- REPEAT
- Str := Str + Chr(Lo(TempLines[ConvTL]^[i]));
- Inc(i); { das i.te Zeichen kopieren }
- UNTIL (i > TempLines[ConvTL]^[0]) OR (i > 255);
- Convert2Str := Str;
- END;
-
- (* ------------------------------------------------------ *)
- (* Setzt die Schreibfarben für den Buffer neu. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.SetWriteColor(Col, BackCol : BYTE);
- BEGIN
- BufCol := Col; BufBackCol := BackCol;
- Attr := BufCol + BufBackCol * 16;
- END;
-
- (* ------------------------------------------------------ *)
- (* Ändert im Pufferbereich x1-x2, y1-y2 die Farben in die *)
- (* durch "NewCol" und "NewBackCol" angegebenen. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.ChangeColor(x1, y1, x2, y2 : INTEGER;
- NewCol,
- NewBackCol : BYTE);
- VAR
- x, y, NewAttr : INTEGER;
- BEGIN
- IF XYInBuf(x1, y1) AND XYInBuf(x2, y2) AND
- (x2 >= x1) AND (y2 >= y1) THEN BEGIN
- NewAttr := NewCol + NewBackCol * 16;
- FOR y := y1 TO y2 DO BEGIN
- LoadLine(y, TempLines [TL]^);
- FOR x := x1 TO x2 DO
- TempLines[TL]^[x] :=
- Lo(TempLines[TL]^[x]) + WORD(NewAttr SHL 8);
- SaveNewLine(y, TempLines[TL]^, FALSE);
- END;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Setzt den Puffer-Cursor neu. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.SetBufCursor(x, y : INTEGER);
- BEGIN
- IF XYInBuf(x, y) THEN BEGIN
- BufCurX := x; BufCurY := y;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Gibt die Farbe des Zeichens mit der Position x/y im *)
- (* Puffer zurück. *)
- (* ------------------------------------------------------ *)
- PROCEDURE BufferObj.GetBufXYColors(x, y : INTEGER;
- VAR Col,
- BackCol : BYTE);
- VAR
- a : BYTE;
- BEGIN
- IF XYInBuf(x, y) THEN BEGIN
- LoadLine(y, TempLines[TL]^);
- a := Hi(TempLines[TL]^[x]);
- Col := a AND 15; BackCol := a AND 112 DIV 16
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Gibt das Attribut der Position x/y zurück. *)
- (* ------------------------------------------------------ *)
- FUNCTION BufferObj.GetBufXYAttr(x, y: INTEGER) : BYTE;
- BEGIN
- IF XYInBuf(x, y) THEN BEGIN
- LoadLine(y, TempLines[TL]^);
- GetBufXYAttr := Hi(TempLines[TL]^[x]);
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Prüft, ob die übergebenen Koordinaten im Pufferberich *)
- (* sind. *)
- (* ------------------------------------------------------ *)
- FUNCTION BufferObj.XYInBuf(x, y : INTEGER) : BOOLEAN;
- BEGIN
- IF (x >= 1) AND (y >= 1) AND (x <= Columns) AND
- (y <= Lines) THEN
- XYInBuf := TRUE
- ELSE BEGIN
- XYInBuf := FALSE;
- ErrorHandling(BufCheckXYErr);
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Gibt den benutzten Speicher frei. *)
- (* ------------------------------------------------------ *)
- DESTRUCTOR BufferObj.Done;
- VAR
- i : INTEGER;
- BEGIN
- CloseFile;
- IF TextBuf <> NIL THEN BEGIN
- FOR i := 1 TO Lines DO
- IF TextBuf^[i] <> NIL THEN BEGIN
- FreeMem(TextBuf^[i], 2 * Succ(Columns));
- TextBuf^[i] := NIL;
- END;
- END;
- IF InfoLine <> NIL THEN BEGIN
- FreeMem(InfoLine, 2 * Succ(Columns));
- InfoLine := NIL;
- END;
- FOR i := CopyTL TO TL DO
- IF TempLines[i] <> NIL THEN
- FreeMem(TempLines[i], 2 * Succ(Columns));
- IF TextBuf <> NIL THEN BEGIN
- FreeMem(TextBuf, 2 * Succ(MaxLines));
- TextBuf := NIL;
- END;
- END;
-
- (* ------------------------------------------------------ *)
- (* Eigene Fehlerroutine (S.113 im Handbuch zu 5.5). *)
- (* Wenn eine eigene Fehlerroutine im Fehlerfall 1 zurück- *)
- (* gibt, erscheint nach dem Aufruf von "New" oder *)
- (* "GetMem" ein NIL-Pointer, wenn ein Fehler aufgetreten *)
- (* ist, anstatt das Programm abzubrechen. *)
- (* ------------------------------------------------------ *)
- {$F+}
- FUNCTION HeapFunc(Size : WORD) : INTEGER;
- {$F-}
- BEGIN
- HeapFunc := 1;
- END;
-
- (* ------------------------------------------------------ *)
- (* Initialisierungsteil der Unit. "HeapError" wird auf *)
- (* "HeapFunc" gesetzt. *)
- (* ------------------------------------------------------ *)
- BEGIN
- HeapError := @HeapFunc;
- FOR i := CopyTL TO TL DO TempLines[i] := NIL
- END.
- (* ------------------------------------------------------ *)
- (* Ende von BUFFER5.PAS *)
-