mysticbbs/mystic/bbs_edit_ansi.pas

1522 lines
40 KiB
ObjectPascal
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Unit bbs_Edit_Ansi;
// ====================================================================
// Mystic BBS Software Copyright 1997-2013 By James Coyle
// ====================================================================
//
// This file is part of Mystic BBS.
//
// Mystic BBS is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mystic BBS is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mystic BBS. If not, see <http://www.gnu.org/licenses/>.
//
// ====================================================================
{$I M_OPS.PAS}
Interface
Uses
m_FileIO,
BBS_MsgBase_Ansi;
Const
fseMaxCutText = 60;
GlyphTypeMax = 10;
GlyphTypeStr : Array[1..10] of String[10] = (
('纶坷倌趁戳'),
('松蝗纪禾故'),
('颜冈就称迪'),
('抑酚侥呵缎'),
('锱呜阻闆湙'),
('<27>辈圻茌撖'),
('<06> '),
(''),
('<27><><EFBFBD>霁'),
('<27>耵蹶濅<E8B9B6>')
);
Type
TEditorANSI = Class
Owner : Pointer;
ANSI : TMsgBaseANSI;
WinY1 : Byte;
WinY2 : Byte;
WinX1 : Byte;
WinX2 : Byte;
WinSize : Byte;
RowSize : Byte;
CurX : Byte;
CurY : SmallInt;
CurAttr : Byte;
QuoteAttr : Byte;
CurLength : Byte;
TopLine : LongInt;
CurLine : LongInt;
InsertMode : Boolean;
DrawMode : Boolean;
GlyphMode : Boolean;
GlyphPtr : Byte;
WrapMode : Boolean;
ClearEOL : Boolean;
LastLine : LongInt;
QuoteTopPage : SmallInt;
QuoteCurLine : SmallInt;
CutText : Array[1..fseMaxCutText] of RecAnsiBufferLine;
CutTextPos : Word;
CutPasted : Boolean;
Save : Boolean;
Forced : Boolean;
Done : Boolean;
Subject : String;
Template : String;
DrawTemplate : String;
SavedInsert : Boolean;
MaxMsgLines : Word;
MaxMsgCols : Byte;
Constructor Create (Var O: Pointer; TemplateFile: String);
Destructor Destroy; Override;
Function IsAnsiLine (Line: LongInt) : Boolean;
Function IsBlankLine (Var Line; LineSize: Byte) : Boolean;
Function GetLineLength (Var Line; LineSize: Byte) : Byte;
Function GetWrapPos (Var Line; LineSize, WrapPos: Byte) : Byte;
Procedure TrimLeft (Var Line; LineSize: Byte);
Procedure TrimRight (Var Line; LineSize: Byte);
Procedure DeleteLine (Line: LongInt);
Procedure InsertLine (Line: LongInt);
Function GetLineText (Line: Word) : String;
Procedure SetLineText (Line: LongInt; Str: String);
Procedure FindLastLine;
Procedure WordWrap;
Procedure ReformParagraph;
Procedure LocateCursor;
Procedure ToggleInsert (Toggle: Boolean);
Procedure ReDrawTemplate (Reset: Boolean);
Procedure DrawPage (StartY, EndY: Byte; ExitEOF: Boolean);
Procedure ScrollUp;
Procedure ScrollDown (Draw: Boolean);
Function LineUp (Reset: Boolean) : Boolean;
Function LineDown (Reset: Boolean) : Boolean;
Procedure PageUp;
Procedure PageDown;
Procedure DrawLine (Line: LongInt; XP, YP: Byte);
Procedure DoEnter;
Procedure DoBackSpace;
Procedure DoDelete;
Procedure DoChar (Ch: Char);
Function Edit : Boolean;
Procedure Quote;
Procedure QuoteWindow;
Procedure EditorCommands;
Procedure DrawCommands;
Procedure MessageUpload;
End;
Implementation
Uses
m_Strings,
BBS_Records,
BBS_Core,
BBS_Ansi_MenuBox;
Constructor TEditorANSI.Create (Var O: Pointer; TemplateFile: String);
Begin
Inherited Create;
Owner := O;
ANSI := TMsgBaseANSI.Create(NIL, False);
WinX1 := 1;
WinX2 := 79;
WinY1 := 2;
WinY2 := 23;
WinSize := WinY2 - WinY1 + 1;
RowSize := WinX2 - WinX1 + 1;
CurX := 1;
CurY := 1;
CurLine := 1;
TopLine := 1;
CurAttr := 7;
QuoteAttr := 9;
InsertMode := True;
DrawMode := False;
GlyphMode := False;
GlyphPtr := 6;
WrapMode := True;
ClearEOL := RowSize >= 79;
LastLine := 1;
CutPasted := False;
CutTextPos := 0;
Template := TemplateFile;
MaxMsgLines := mysMaxMsgLines;
MaxMsgCols := 79;
FillChar (CutText, SizeOf(CutText), 0);
End;
Destructor TEditorANSI.Destroy;
Begin
Inherited Destroy;
ANSI.Free;
End;
Function TEditorANSI.GetLineText (Line: Word) : String;
Var
Count : Word;
Begin
Result := '';
For Count := 1 to GetLineLength(ANSI.Data[Line], RowSize) Do
If ANSI.Data[Line][Count].Ch = #0 Then
Result := Result + ' '
Else
Result := Result + ANSI.Data[Line][Count].Ch;
End;
Procedure TEditorANSI.SetLineText (Line: LongInt; Str: String);
Var
Count : Byte;
Begin
FillChar (ANSI.Data[Line], SizeOf(ANSI.Data[Line]), 0);
For Count := 1 to Length(Str) Do Begin
ANSI.Data[Line][Count].Ch := Str[Count];
ANSI.Data[Line][Count].Attr := CurAttr;
End;
End;
Procedure TEditorANSI.FindLastLine;
Begin
LastLine := MaxMsgLines;
While (LastLine > 1) And IsBlankLine(ANSI.Data[LastLine], 80) Do
Dec(LastLine);
End;
Function TEditorANSI.IsAnsiLine (Line: LongInt) : Boolean;
Var
Count : Byte;
Begin
Result := False;
If GetLineLength(ANSI.Data[Line], 80) >= RowSize Then Begin
Result := True;
Exit;
End;
For Count := 1 to 80 Do
If (Ord(ANSI.Data[Line][Count].Ch) < 32) or (Ord(ANSI.Data[Line][Count].Ch) > 128) Then Begin
Result := True;
Exit;
End;
End;
Function TEditorANSI.IsBlankLine (Var Line; LineSize: Byte) : Boolean;
Var
EndPos : Byte;
Data : Array[1..255] of RecAnsiBufferChar absolute Line;
Begin
EndPos := LineSize;
While (EndPos > 0) and (Data[EndPos].Ch = #0) Do
Dec (EndPos);
Result := EndPos = 0;
End;
Procedure TEditorANSI.TrimLeft (Var Line; LineSize: Byte);
Var
Data : Array[1..255] of RecAnsiBufferChar absolute Line;
EndPos : Byte;
Begin
EndPos := 1;
While (EndPos <= LineSize) and (Data[1].Ch = ' ') Do Begin
Move (Data[2], Data[1], SizeOf(RecAnsiBufferChar) * (LineSize - 1));
Data[LineSize].Ch := #0;
Inc (EndPos);
End;
End;
Procedure TEditorANSI.TrimRight (Var Line; LineSize: Byte);
Var
Data : Array[1..255] of RecAnsiBufferChar absolute Line;
Begin
While ((Data[LineSize].Ch = ' ') or (Data[LineSize].Ch = #0)) Do Begin
Data[LineSize].Ch := #0;
Dec (LineSize);
End;
End;
Procedure TEditorANSI.DeleteLine (Line: LongInt);
Var
Count : LongInt;
Begin
For Count := Line to MaxMsgLines - 1 Do
ANSI.Data[Count] := ANSI.Data[Count + 1];
FillChar (ANSI.Data[MaxMsgLines], SizeOf(RecAnsiBufferLine), #0);
If LastLine > 1 Then Dec(LastLine);
End;
Procedure TEditorANSI.InsertLine (Line: LongInt);
Var
Count : LongInt;
Begin
For Count := MaxMsgLines DownTo Line + 1 Do
ANSI.Data[Count] := ANSI.Data[Count - 1];
FillChar(ANSI.Data[Line], SizeOf(RecAnsiBufferLine), #0);
If LastLine < MaxMsgLines Then Inc(LastLine);
End;
Function TEditorANSI.GetWrapPos (Var Line; LineSize: Byte; WrapPos: Byte) : Byte;
Var
Data : Array[1..255] of RecAnsiBufferChar absolute Line;
Begin
If GetLineLength(Line, LineSize) < WrapPos Then Begin
Result := 0;
Exit;
End;
Result := LineSize;
While (Result > 0) and ((Data[Result].Ch <> ' ') or (Result > WrapPos)) Do
Dec (Result);
End;
Function TEditorANSI.GetLineLength (Var Line; LineSize: Byte) : Byte;
Var
Data : Array[1..255] of RecAnsiBufferChar absolute Line;
Begin
Result := LineSize;
While (Result > 0) and (Data[Result].Ch = #0) Do
Dec (Result);
End;
Procedure TEditorANSI.WordWrap;
Var
WrapData : Array[1..255] of RecAnsiBufferChar;
TempStr : Array[1..255] of RecAnsiBufferChar;
NewLine : Array[1..255] of RecAnsiBufferChar;
Count : LongInt;
LineSize : Byte;
StartY : Byte;
StartLine : LongInt;
EndLine : LongInt;
First : Boolean = True;
Procedure Update;
Var
NewY : LongInt;
Begin
NewY := StartY + EndLine - StartLine + 1;
If NewY > WinSize Then NewY := WinSize;
If CurY > WinSize Then
ScrollDown(True)
Else
DrawPage (StartY, NewY, True);
End;
Begin
FillChar (WrapData, SizeOf(WrapData), #0);
Count := CurLine;
StartY := CurY;
StartLine := Count;
While Count <= MaxMsgLines Do Begin
If Count > LastLine Then LastLine := Count;
FillChar (TempStr, SizeOf(TempStr), #0);
Move (Ansi.Data[Count], TempStr, SizeOf(Ansi.Data[Count]));
If Not IsBlankLine(WrapData, 255) Then Begin
If IsBlankLine(TempStr, 255) Then Begin
If Count < LastLine Then Begin
InsertLine(Count);
EndLine := MaxMsgLines;
End Else
EndLine := Count;
Move (WrapData, ANSI.Data[Count], SizeOf(Ansi.Data[Count]));
Update;
Exit;
End;
FillChar (NewLine, SizeOf(NewLine), #0);
LineSize := GetLineLength(WrapData, 255);
Move (WrapData, NewLine, LineSize * SizeOf(RecAnsiBufferChar));
NewLine[LineSize + 1].Ch := ' ';
NewLine[LineSize + 1].Attr := WrapData[LineSize].Attr;
Move (TempStr, NewLine[LineSize + 2], GetLineLength(TempStr, 255) * SizeOf(RecAnsiBufferChar));
Move (NewLine, TempStr, SizeOf(NewLine));
End;
FillChar (WrapData, SizeOf(WrapData), #0);
LineSize := GetWrapPos(TempStr, 255, RowSize);
If LineSize > 0 Then Begin
Move (TempStr[LineSize], WrapData, (GetLineLength(TempStr, 255) - LineSize + 1) * SizeOf(RecAnsiBufferChar));
FillChar (TempStr[LineSize], (255 - LineSize) * SizeOf(RecAnsiBufferChar), #0);
TrimLeft (WrapData, 255);
If First Then Begin
If CurX > LineSize Then Begin
CurX := CurX - LineSize;
Inc (CurY);
Inc (CurLine);
End;
First := False;
End;
End;
FillChar (ANSI.Data[Count], SizeOf(ANSI.Data[Count]), #0);
Move (TempStr, ANSI.Data[Count], RowSize * SizeOf(RecAnsiBufferChar));
If LineSize = 0 Then Begin
EndLine := Count;
Update;
Exit;
End;
Inc (Count);
End;
End;
Procedure TEditorANSI.ToggleInsert (Toggle: Boolean);
Begin
If Toggle Then InsertMode := Not InsertMode;
Session.io.AnsiColor (Session.io.ScreenInfo[3].A);
Session.io.AnsiGotoXY (Session.io.ScreenInfo[3].X, Session.io.ScreenInfo[3].Y);
If InsertMode Then Session.io.BufAddStr('INS') Else Session.io.BufAddStr('OVR'); { ++lang++ }
End;
Procedure TEditorANSI.ReDrawTemplate (Reset: Boolean);
Var
Count : LongInt;
Begin
FillChar (Session.io.ScreenInfo, SizeOf(Session.io.ScreenInfo), 0);
TBBSCore(Owner).io.AllowArrow := True;
If DrawMode Then Begin
// temp until we show file
Session.io.ScreenInfo[1].Y := 3;
Session.io.ScreenInfo[2].Y := 24;
Session.io.AnsiColor(7);
Session.io.AnsiClear;
WriteXY (1, 1, 7, 'Draw Mode ESC/Menu - Insert: ' + Session.io.OutON(InsertMode) + ' GlyphMode: ' + Session.io.OutON(GlyphMode) + ' Set: ');
WriteXY (53, 1, CurAttr, GlyphTypeStr[GlyphPtr]);
WriteXY (1, 2, 8, strRep('-', 79));
End Else Begin
Session.io.PromptInfo[2] := Subject;
Session.io.OutFile (Template, True, 0);
ToggleInsert (False);
End;
WinX1 := 1;
WinX2 := MaxMsgCols; //79
// WinX1 := Session.io.ScreenInfo[1].X;
// WinX2 := Session.io.ScreenInfo[2].X;
WinY1 := Session.io.ScreenInfo[1].Y;
WinY2 := Session.io.ScreenInfo[2].Y;
WinSize := WinY2 - WinY1 + 1;
RowSize := WinX2 - WinX1 + 1;
// if rowsize > msgmaxcols then rowsize := maxmsgcols;
ClearEOL := RowSize >= 79;
If Reset Then Begin
CurX := 1;
CurY := 1;
CurAttr := Session.io.ScreenInfo[1].A;
QuoteAttr := Session.io.ScreenInfo[2].A;
FindLastLine;
If LastLine > 1 Then
For Count := 1 to LastLine Do
If Session.Msgs.IsQuotedText(GetLineText(Count)) Then
ANSI.SetLineColor(QuoteAttr, Count)
Else
ANSI.SetLineColor(CurAttr, Count);
End;
DrawPage (1, WinSize, False);
End;
Procedure TEditorANSI.LocateCursor;
Begin
CurLength := GetLineLength(ANSI.Data[CurLine], RowSize);
If CurX < 1 Then CurX := 1;
If CurX > CurLength Then CurX := CurLength + 1;
If CurY < 1 Then CurY := 1;
While TopLine + CurY - 1 > LastLine Do
Dec (CurY);
// With TBBSCore(Owner).io Do Begin
// AnsiGotoXY (1, 1);
// BufAddStr ('X:' + strI2S(CurX) + ' Y:' + strI2S(CurY) + ' CL:' + strI2S(CurLine) + ' TopL:' + strI2S(TopLine) + ' Last:' + strI2S(LastLine) + ' Len:' + strI2S(GetLineLength(ANSI.Data[CurLine], 80)) + ' Row:' + strI2S(RowSize) + ' ');
// End;
With TBBSCore(Owner).io Do Begin
AnsiGotoXY (WinX1 + CurX - 1, WinY1 + CurY - 1);
AnsiColor (CurAttr);
BufFlush;
End;
End;
Procedure TEditorANSI.DrawPage (StartY, EndY: Byte; ExitEOF: Boolean);
Var
CountY : LongInt;
Begin
For CountY := StartY to EndY Do Begin
If TopLine + CountY - 1 > LastLine + 1 Then Begin
TBBSCore(Owner).io.AnsiGotoXY (WinX1, WinY1 + CountY - 1);
TBBSCore(Owner).io.AnsiColor (7);
If ClearEOL Then
TBBSCore(Owner).io.AnsiClrEOL
Else
TBBSCore(Owner).io.BufAddStr (strRep(' ', RowSize));
End Else
If TopLine + CountY - 1 = LastLine + 1 Then Begin
TBBSCore(Owner).io.AnsiGotoXY (WinX1, WinY1 + CountY - 1);
TBBSCore(Owner).io.AnsiColor (8);
TBBSCore(Owner).io.BufAddStr (strPadC('(END)', RowSize, ' '));
If ExitEOF Then Exit;
End Else
DrawLine (TopLine + CountY - 1, 1, CountY);
End;
End;
Procedure TEditorANSI.ScrollUp;
Var
NewTop : LongInt;
Begin
NewTop := TopLine - (WinSize DIV 2) + 1;
If NewTop < 1 Then NewTop := 1;
CurY := CurLine - NewTop + 1;
TopLine := NewTop;
DrawPage(1, WinSize, False);
End;
Procedure TEditorANSI.ScrollDown (Draw: Boolean);
Var
NewTop : LongInt;
Begin
NewTop := TopLine + (WinSize DIV 2) + 1;
While NewTop >= MaxMsgLines Do
Dec (NewTop, 2);
CurY := CurLine - NewTop + 1;
TopLine := NewTop;
If Draw Then
DrawPage(1, WinSize, False);
End;
Function TEditorANSI.LineUp (Reset: Boolean) : Boolean;
Begin
Result := False;
If CurLine = 1 Then Exit;
Dec (CurLine);
Dec (CurY);
// might be able to use curlength
If Reset or (CurX > GetLineLength(ANSI.Data[CurLine], 80)) Then
CurX := GetLineLength(ANSI.Data[CurLine], 80) + 1;
If CurY < 1 Then Begin
ScrollUp;
Result := True;
End;
End;
Function TEditorANSI.LineDown (Reset: Boolean) : Boolean;
Begin
Result := False;
If CurLine >= LastLine Then Exit;
// If CurLine >= MaxMsgLines Then Exit;
Inc (CurLine);
Inc (CurY);
If Reset Then CurX := 1;
If CurX > GetLineLength(ANSI.Data[CurLine], 80) Then
CurX := GetLineLength(ANSI.Data[CurLine], 80) + 1;
If CurY > WinSize Then Begin
Result := True;
ScrollDown(True);
End;
End;
Procedure TEditorANSI.DrawLine (Line: LongInt; XP, YP: Byte);
Var
Count : Byte;
LineLen : Byte;
Begin
TBBSCore(Owner).io.AnsiGotoXY (WinX1 + XP - 1, WinY1 + YP - 1);
LineLen := GetLineLength(ANSI.Data[Line], RowSize);
For Count := XP to LineLen Do Begin
If ANSI.Data[Line][Count].Ch = #0 Then Begin
TBBSCore(Owner).io.AnsiColor (7);
TBBSCore(Owner).io.BufAddChar (' ');
End Else Begin
TBBSCore(Owner).io.AnsiColor (ANSI.Data[Line][Count].Attr);
TBBSCore(Owner).io.BufAddChar (ANSI.Data[Line][Count].Ch);
End;
End;
If LineLen < RowSize Then
If ClearEOL Then Begin
TBBSCore(Owner).io.AnsiColor (7);
TBBSCore(Owner).io.AnsiClrEOL;
End Else Begin
TBBSCore(Owner).io.AnsiColor (7);
TBBSCore(Owner).io.BufAddStr (strRep(' ', RowSize - LineLen));
End;
End;
Procedure TEditorANSI.DoDelete;
Var
JoinLen : Byte;
JoinPos : Byte;
JoinBuf : Array[1..255] of RecAnsiBufferChar;
Begin
If CurX <= CurLength Then Begin
Move (ANSI.Data[CurLine][CurX + 1], ANSI.Data[CurLine][CurX], (CurLength - CurX + 1) * SizeOf(RecAnsiBufferChar));
ANSI.Data[CurLine][CurLength].Ch := #0;
DrawLine (CurLine, CurX, CurY);
End Else
If CurLine < LastLine Then
If (CurLength = 0) and (LastLine > 1) Then Begin
DeleteLine (CurLine);
DrawPage (CurY, WinSize, False);
End Else Begin
JoinLen := GetLineLength(ANSI.Data[CurLine + 1], RowSize);
If CurLength + JoinLen <= RowSize Then Begin
Move (ANSI.Data[CurLine + 1], ANSI.Data[CurLine][CurX], SizeOf(RecAnsiBufferChar) * JoinLen);
DeleteLine (CurLine + 1);
DrawPage (CurY, WinSize, False); //optimize
End Else Begin
JoinPos := GetWrapPos(ANSI.Data[CurLine + 1], RowSize, RowSize - CurLength);
If JoinPos > 0 Then Begin
Move (ANSI.Data[CurLine + 1], ANSI.Data[CurLine][CurX], SizeOf(RecAnsiBufferChar) * (JoinPos - 1));
FillChar (JoinBuf, SizeOf(JoinBuf), #0);
Move (ANSI.Data[CurLine + 1][JoinPos + 1], JoinBuf, (JoinLen - JoinPos + 1) * SizeOf(RecAnsiBufferChar));
Move (JoinBuf, ANSI.Data[CurLine + 1], RowSize * SizeOf(RecAnsiBufferChar));
DrawPage (CurY, CurY + 1, True);
End;
End;
End;
End;
Procedure TEditorANSI.DoBackSpace;
Var
JoinPos : Byte;
JoinBuf : Array[1..255] of RecAnsiBufferChar;
Begin
If CurX > 1 Then Begin
Dec (CurX);
Move (ANSI.Data[CurLine][CurX + 1], ANSI.Data[CurLine][CurX], SizeOf(RecAnsiBufferChar) * (80 - CurX + 1));
ANSI.Data[CurLine][80].Ch := #0;
If CurX > GetLineLength(ANSI.Data[CurLine], 80) Then
TBBSCore(Owner).io.OutBS(1, True)
Else
DrawLine (CurLine, CurX, CurY);
End Else
If CurLine > 1 Then Begin
If GetLineLength(ANSI.Data[CurLine - 1], 80) + CurLength <= RowSize Then Begin
CurX := GetLineLength(ANSI.Data[CurLine - 1], 80) + 1;
Move (ANSI.Data[CurLine], ANSI.Data[CurLine - 1][CurX], SizeOf(RecAnsiBufferChar) * CurLength);
DeleteLine (CurLine);
If Not LineUp(False) Then DrawPage (CurY, WinSize, False); //optimize
End Else Begin
JoinPos := GetWrapPos(ANSI.Data[CurLine], RowSize, RowSize - GetLineLength(ANSI.Data[CurLine - 1], RowSize));
If JoinPos > 0 Then Begin
CurX := GetLineLength(ANSI.Data[CurLine - 1], 80) + 1;
Move (ANSI.Data[CurLine], ANSI.Data[CurLine - 1][CurX], SizeOf(RecAnsiBufferChar) * (JoinPos - 1));
FillChar (JoinBuf, SizeOf(JoinBuf), #0);
Move (ANSI.Data[CurLine][JoinPos + 1], JoinBuf, (CurLength - JoinPos + 1) * SizeOf(RecAnsiBufferChar));
Move (JoinBuf, ANSI.Data[CurLine], RowSize * SizeOf(RecAnsiBufferChar));
If Not LineUp(False) Then DrawPage (CurY, WinSize, False);
End Else Begin
LineUp(False);
CurX := CurLength + 1;
End;
End;
End;
End;
Procedure TEditorANSI.DoChar (Ch: Char);
Var
CharAttr : Byte;
Begin
CharAttr := CurAttr;
If DrawMode Then Begin
If (Ch in ['0'..'9']) And GlyphMode Then
Ch := GlyphTypeStr[GlyphPtr][strS2I(Ch) + 1]
End Else
If (Session.io.ScreenInfo[6].A <> 0) and (Pos(Ch, '0123456789') > 0) Then
CharAttr := Session.io.ScreenInfo[6].A
Else
If (Session.io.ScreenInfo[5].A <> 0) and (Pos(Ch, '.,!@#$%^&*()_+-=~`''"?;:<>\/[]{}|') > 0) Then
CharAttr := Session.io.ScreenInfo[5].A
Else
If (Session.io.ScreenInfo[4].A <> 0) and (Ch = UpCase(Ch)) Then
CharAttr := Session.io.ScreenInfo[4].A;
If InsertMode Then Begin
Move (ANSI.Data[CurLine][CurX], ANSI.Data[CurLine][CurX + 1], SizeOf(RecAnsiBufferChar) * (CurLength - CurX + 1));
ANSI.Data[CurLine][CurX].Ch := Ch;
ANSI.Data[CurLine][CurX].Attr := CharAttr;
If CurLength < RowSize {-1} Then Begin
If CurX <= CurLength Then
DrawLine (CurLine, CurX, CurY)
Else Begin
TBBSCore(Owner).io.AnsiColor (CharAttr);
TBBSCore(Owner).io.BufAddChar (Ch);
End;
Inc (CurX);
End Else Begin
Inc (CurX);
WordWrap;
End;
End Else
If CurX <= RowSize Then Begin
ANSI.Data[CurLine][CurX].Ch := Ch;
ANSI.Data[CurLine][CurX].Attr := CharAttr;
TBBSCore(Owner).io.AnsiColor (CharAttr);
TBBSCore(Owner).io.BufAddChar (Ch);
Inc (CurX);
End;
End;
Procedure TEditorANSI.PageUp;
Var
NewTop : LongInt;
Begin
If CurLine = 1 Then Exit;
If TopLine = 1 Then Begin
CurLine := 1;
CurY := 1;
CurX := 1;
Exit;
End;
Dec (CurLine, WinSize);
If CurLine < 1 Then Begin
CurLine := 1;
NewTop := 1;
End Else Begin
NewTop := TopLine - WinSize;
If NewTop < 1 Then NewTop := 1;
End;
CurY := CurLine - NewTop + 1;
TopLine := NewTop;
DrawPage (1, WinSize, False);
End;
Procedure TEditorANSI.PageDown;
Var
NewTop : LongInt;
Begin
If CurLine = LastLine Then Exit;
If (LastLine > TopLine) And (LastLine <= TopLine + WinSize - 1) Then Begin
CurLine := LastLine;
CurY := CurLine - TopLine + 1;
CurX := 1;
Exit;
End;
Inc (CurLine, WinSize);
If CurLine > LastLine Then CurLine := LastLine;
NewTop := TopLine + WinSize;
While NewTop >= LastLine - (WinSize DIV 2) Do
Dec (NewTop);
If NewTop < 1 Then NewTop := 1;
CurY := CurLine - NewTop + 1;
TopLine := NewTop;
DrawPage (1, WinSize, False);
End;
Procedure TEditorANSI.DoEnter;
Var
TempLine : RecAnsiBufferLine;
Begin
If InsertMode and IsBlankLine(ANSI.Data[MaxMsgLines], 80) Then Begin
If CurX > CurLength Then Begin
InsertLine (CurLine + 1);
If Not LineDown(True) Then DrawPage(CurY, WinSize, True);
End Else Begin
TempLine := ANSI.Data[CurLine];
InsertLine (CurLine + 1);
FillChar (ANSI.Data[CurLine][CurX], SizeOf(RecAnsiBufferChar) * (80 - CurX + 1), #0);
Move (TempLine[CurX], ANSI.Data[CurLine + 1][1], SizeOf(RecAnsiBufferChar) * (80 - CurX + 1));
If Not LineDown(True) Then
DrawPage (CurY - 1, WinSize, True);
End;
End Else Begin
If CurLine = LastLine Then
InsertLine (CurLine + 1);
If Not LineDown(True) Then
DrawPage (CurY - 1, WinSize, True);
End;
End;
Procedure TEditorANSI.Quote;
Var
InFile : Text;
Start : Integer;
Finish : Integer;
NumLines : Integer;
Text : Array[1..mysMaxMsgLines] of String[80];
PI1 : String;
PI2 : String;
Begin
Assign (InFile, Session.TempPath + 'msgtmp');
{$I-} Reset (InFile); {$I+}
If IoResult <> 0 Then Begin
Session.io.OutFullLn (Session.GetPrompt(158));
Exit;
End;
NumLines := 0;
Session.io.AllowPause := True;
While Not Eof(InFile) Do Begin
Inc (NumLines);
ReadLn (InFile, Text[NumLines]);
End;
Close (InFile);
PI1 := Session.io.PromptInfo[1];
PI2 := Session.io.PromptInfo[2];
Session.io.OutFullLn('|CL' + Session.GetPrompt(452));
For Start := 1 to NumLines Do Begin
Session.io.PromptInfo[1] := strI2S(Start);
Session.io.PromptInfo[2] := Text[Start];
Session.io.OutFullLn (Session.GetPrompt(341));
If (Session.io.PausePtr >= Session.User.ThisUser.ScreenSize) and (Session.io.AllowPause) Then
Case Session.io.MorePrompt of
'N' : Break;
'C' : Session.io.AllowPause := False;
End;
End;
Session.io.AllowPause := True;
Session.io.OutFull (Session.GetPrompt(159));
Start := strS2I(Session.io.GetInput(3, 3, 11, ''));
Session.io.OutFull (Session.GetPrompt(160));
Finish := strS2I(Session.io.GetInput(3, 3, 11, ''));
If (Start > 0) and (Start <= NumLines) and (Finish <= NumLines) Then Begin
If Finish = 0 Then Finish := Start;
For NumLines := Start to Finish Do Begin
If LastLine = MaxMsgLines Then Break;
If Not IsBlankLine(Ansi.Data[CurLine], 80) Then Begin
Inc (CurLine);
Inc (CurY);
InsertLine (CurLine);
End;
SetLineText (CurLine, Text[NumLines]);
ANSI.SetLineColor (QuoteAttr, CurLine);
If CurY > WinSize Then
ScrollDown(False);
End;
End;
If CurLine < MaxMsgLines Then Begin
Inc (CurLine);
Inc (CurY);
InsertLine(CurLine);
If CurY > WinSize Then
ScrollDown(False);
End;
Session.io.PromptInfo[1] := PI1;
Session.io.PromptInfo[2] := PI2;
End;
Procedure TEditorANSI.QuoteWindow;
Var
QText : Array[1..mysMaxMsgLines] of String[79];
QTextSize : Byte;
InFile : Text;
QuoteLines : Integer;
NoMore : Boolean;
Procedure UpdateBar (On: Boolean);
Begin
Session.io.AnsiGotoXY (1, QuoteCurLine + Session.io.ScreenInfo[2].Y);
If On Then
Session.io.AnsiColor (Session.io.ScreenInfo[3].A)
Else
Session.io.AnsiColor (Session.io.ScreenInfo[2].A);
Session.io.BufAddStr (strPadR(QText[QuoteTopPage + QuoteCurLine], 79, ' '));
End;
Procedure UpdateWindow;
Var
Count : Integer;
Begin
Session.io.AnsiGotoXY (1, Session.io.ScreenInfo[2].Y);
Session.io.AnsiColor (Session.io.ScreenInfo[2].A);
For Count := QuoteTopPage to QuoteTopPage + QTextSize - 1 Do Begin
If Count <= QuoteLines Then Session.io.BufAddStr (QText[Count]);
Session.io.AnsiClrEOL;
If Count <= QuoteLines Then Session.io.BufAddStr(#13#10);
End;
UpdateBar(True);
End;
Var
Ch : Char;
QWinSize : Byte;
QWinDataPos : Byte;
QWinData : Array[1..15] of String[79];
Procedure AddQuoteWin (S: String);
Var
Count : Byte;
Begin
If QWinDataPos < QWinSize Then Begin
Inc (QWinDataPos);
End Else Begin
For Count := 2 to QWinSize Do
QWinData[Count - 1] := QWinData[Count]
End;
QWinData[QWinDataPos] := S;
End;
Procedure DrawQWin;
Var
Count : Byte;
Begin
Session.io.AnsiColor (Session.io.ScreenInfo[1].A);
For Count := 1 to QWinSize + 1 Do Begin
Session.io.AnsiGotoXY (WinX1, WinY1 + Count - 1);
If Count <= QWinSize Then
Session.io.BufAddStr(QWinData[Count]);
Session.io.AnsiClrEOL;
End;
End;
Var
Temp : Integer;
Begin
Assign (InFile, Session.TempPath + 'msgtmp');
{$I-} Reset(InFile); {$I+}
If IoResult <> 0 Then Exit;
NoMore := False;
QWinDataPos := 0;
QuoteLines := 0;
While Not Eof(InFile) Do Begin
Inc (QuoteLines);
ReadLn (InFile, QText[QuoteLines]);
End;
Close (InFile);
Session.io.OutFile ('ansiquot', True, 0);
FillChar (QWinData, SizeOf(QWinData), 0);
QTextSize := Session.io.ScreenInfo[3].Y - Session.io.ScreenInfo[2].Y + 1;
QWinSize := Session.io.ScreenInfo[1].Y - WinY1 + 1;
For Temp := CurLine - ((QWinSize DIV 2) + 1) To CurLine - 1 Do
If Temp >= 1 Then AddQuoteWin(GetLineText(Temp));
DrawQWin;
UpdateWindow;
Repeat
Ch := Session.io.GetKey;
If Session.io.IsArrow Then Begin
Case Ch of
#71 : If QuoteCurLine > 0 Then Begin
QuoteTopPage := 1;
QuoteCurLine := 0;
NoMore := False;
UpdateWindow;
End;
#72 : Begin
If QuoteCurLine > 0 Then Begin
UpdateBar(False);
Dec(QuoteCurLine);
UpdateBar(True);
End Else
If QuoteTopPage > 1 Then Begin
Dec (QuoteTopPage);
UpdateWindow;
End;
NoMore := False;
End;
#73,
#75 : Begin
If QuoteTopPage > QTextSize Then
Dec (QuoteTopPage, QTextSize)
Else Begin
QuoteTopPage := 1;
QuoteCurLine := 0;
End;
NoMore := False;
UpdateWindow;
End;
#79 : Begin
If QuoteLines <= QTextSize Then
QuoteCurLine := QuoteLines - QuoteTopPage
Else Begin
QuoteTopPage := QuoteLines - QTextSize + 1;
QuoteCurLine := QTextSize - 1;
End;
UpdateWindow;
End;
#80 : If QuoteTopPage + QuoteCurLine < QuoteLines Then Begin
If QuoteCurLine = QTextSize - 1 Then Begin
Inc (QuoteTopPage);
UpdateWindow;
End Else Begin
UpdateBar(False);
Inc (QuoteCurLine);
UpdateBar(True);
End;
End;
#77,
#81 : Begin
If QuoteLines <= QTextSize Then
QuoteCurLine := QuoteLines - QuoteTopPage
Else
If QuoteTopPage + QTextSize - 1 < QuoteLines - QTextSize + 1 Then
Inc (QuoteTopPage, QTextSize)
Else Begin
QuoteTopPage := QuoteLines - QTextSize + 1;
QuoteCurLine := QTextSize - 1;
End;
UpdateWindow;
End;
End;
End Else
Case Ch of
#27 : Break;
#13 : If (LastLine < MaxMsgLines) and (Not NoMore) Then Begin
If QuoteTopPage + QuoteCurLine = QuoteLines Then NoMore := True;
InsertLine (CurLine);
SetLineText (CurLine, QText[QuoteTopPage + QuoteCurLine]);
ANSI.SetLineColor (QuoteAttr, CurLine);
Inc (CurLine);
Inc (CurY);
If CurY > WinSize Then
ScrollDown(False);
AddQuoteWin(QText[QuoteTopPage + QuoteCurLine]);
DrawQWin;
If QuoteTopPage + QuoteCurLine < QuoteLines Then
If QuoteCurLine = QTextSize - 1 Then Begin
Inc (QuoteTopPage);
UpdateWindow;
End Else Begin
UpdateBar(False);
Inc (QuoteCurLine);
UpdateBar(True);
End;
End;
End;
Until False;
Session.io.OutFull('|16');
If CurLine < MaxMsgLines Then Begin
Inc (CurLine);
Inc (CurY);
InsertLine(CurLine);
If CurY > WinSize Then
ScrollDown(False);
End;
End;
Procedure TEditorANSI.EditorCommands;
Var
Ch : Char;
Str : String;
Begin
Done := False;
Save := False;
Repeat
Session.io.OutFull (Session.GetPrompt(354));
{$IFDEF TESTEDITOR}
Ch := Session.io.OneKey ('?ACDHQRSTU', True);
{$ELSE}
Ch := Session.io.OneKey ('?ACHQRSTU', True);
{$ENDIF}
Case Ch of
'?' : Session.io.OutFullLn (Session.GetPrompt(355));
'A' : If Forced Then Begin
Session.io.OutFull (Session.GetPrompt(307));
Exit;
End Else Begin
Done := Session.io.GetYN(Session.GetPrompt(356), False);
Exit;
End;
'C' : Exit;
'D' : Begin
DrawMode := True;
SavedInsert := InsertMode;
InsertMode := False;
Exit;
End;
'H' : Begin
Session.io.OutFile ('fshelp', True, 0);
Exit;
End;
'Q' : Begin
If Session.User.ThisUser.UseLBQuote Then
QuoteWindow
Else
Quote;
Exit;
End;
'R' : Exit;
'S' : Begin
Save := True;
Done := True;
End;
'T' : Begin
Session.io.OutFull(Session.GetPrompt(463));
Str := Session.io.GetInput(60, 60, 11, Subject);
If Str <> '' Then Subject := Str;
Session.io.PromptInfo[2] := Subject;
Exit;
End;
'U' : Begin
MessageUpload;
Exit;
End;
End;
Until Done;
End;
Procedure TEditorANSI.DrawCommands;
Var
Ch : Char;
Begin
Repeat
Session.io.OutFull ('|CR|09Draw Commands (?/Help): ');
Ch := Session.io.OneKey ('?GQ', True);
Case Ch of
'?' : Session.io.OutFullLn ('|CR(Q)uit Draw Mode (G)lyph Mode');
'G' : Begin
GlyphMode := Not GlyphMode;
Exit;
End;
'Q' : Begin
DrawMode := False;
InsertMode := SavedInsert;
Exit;
End;
End;
Until False;
End;
Procedure TEditorANSI.MessageUpload;
Var
FN : String[100];
T1 : String[30];
T2 : String[60];
OK : Boolean;
F : File;
B : Array[1..2048] of Char;
BR : LongInt;
Begin
OK := False;
T1 := Session.io.PromptInfo[1];
T2 := Session.io.PromptInfo[2];
Session.io.OutFull (Session.GetPrompt(352));
If Session.LocalMode Then Begin
FN := Session.io.GetInput(70, 70, 11, '');
If FN = '' Then Exit;
OK := FileExist(FN);
End Else Begin
FN := Session.TempPath + Session.io.GetInput(70, 70, 11, '');
If Session.FileBase.SelectProtocol(True, False) = 'Q' Then Exit;
Session.FileBase.ExecuteProtocol(1, FN);
OK := Session.FileBase.dszSearch(JustFile(FN));
End;
If OK Then Begin
Assign (F, FN);
Reset (F, 1);
ANSI.Lines := CurLine;
Ansi.CurX := CurX;
Ansi.CurY := CurLine;
While Not Eof(F) Do Begin
BlockRead (F, B, SizeOf(B), BR);
If BR = 0 Then Break;
ANSI.ProcessBuf(B, BR);
End;
Close(F);
End;
If Not Session.LocalMode Then FileErase(FN);
DirClean (Session.TempPath, 'msgtmp');
Session.io.PromptInfo[1] := T1;
Session.io.PromptInfo[2] := T2;
FindLastLine;
End;
Procedure TEditorANSI.ReformParagraph;
Var
Line : LongInt;
LineLen : Byte;
JoinPos : Byte;
JoinLen : Byte;
JoinBuf : Array[1..255] of RecAnsiBufferChar;
Begin
Line := CurLine;
Repeat
If (Line = LastLine) or IsBlankLine(ANSI.Data[Line], RowSize) Then Break;
TrimRight (ANSI.Data[Line], RowSize);
TrimLeft (ANSI.Data[Line + 1], RowSize);
LineLen := GetLineLength(ANSI.Data[Line], RowSize);
JoinLen := GetLineLength(ANSI.Data[Line + 1], RowSize);
JoinPos := GetWrapPos(ANSI.Data[Line + 1], JoinLen, RowSize - LineLen);
If JoinLen = 0 Then Break;
If LineLen + JoinLen < RowSize Then Begin
Move (ANSI.Data[Line + 1], ANSI.Data[Line][LineLen + 2], SizeOf(RecAnsiBufferChar) * JoinLen);
ANSI.Data[Line][LineLen + 1].Ch := ' ';
DeleteLine (Line + 1);
End Else
If JoinPos > 0 Then Begin
Move (ANSI.Data[Line + 1], ANSI.Data[Line][LineLen + 2], SizeOf(RecAnsiBufferChar) * (JoinPos - 1));
ANSI.Data[Line][LineLen + 1].Ch := ' ';
FillChar (JoinBuf, SizeOf(JoinBuf), #0);
Move (ANSI.Data[Line + 1][JoinPos + 1], JoinBuf, (JoinLen - JoinPos + 1) * SizeOf(RecAnsiBufferChar));
Move (JoinBuf, ANSI.Data[Line + 1], RowSize * SizeOf(RecAnsiBufferChar));
End Else
Inc (Line);
Until False;
DrawPage (CurY, WinSize, False);
// need to optimize this output.
End;
Function TEditorANSI.Edit : Boolean;
Var
Ch : Char;
Count : LongInt;
Begin
Result := False;
QuoteCurLine := 0;
QuoteTopPage := 1;
ReDrawTemplate(True);
Repeat
LocateCursor;
Ch := TBBSCore(Owner).io.GetKey;
If Session.io.IsArrow Then Begin
Case Ch of
#71 : CurX := 1;
#72 : LineUp(False);
#73 : PageUp;
#75 : If CurX > 1 Then Dec(CurX) Else LineUp(True);
#77 : If CurX <= CurLength Then Inc(CurX) Else LineDown(True);
#79 : CurX := CurLength + 1;
#80 : If CurLine < LastLine Then LineDown(False);
#81 : PageDown;
#83 : DoDelete;
End;
End Else
Case Ch of
^B : ReformParagraph;
^F : CurX := 1;
^G : CurX := CurLength + 1;
^H : DoBackSpace;
^I : If CurLength < RowSize Then Begin
If (CurX < RowSize) and (CurX MOD 5 = 0) Then
DoChar(' ');
While (CurX < RowSize) and (CurX MOD 5 <> 0) Do Begin
CurLength := GetLineLength(ANSI.Data[CurLine], RowSize);
DoChar(' ');
End;
End;
^K : Begin
If CutPasted Then Begin
CutTextPos := 0;
CutPasted := False;
End;
If CutTextPos < fseMaxCutText Then Begin
Inc (CutTextPos);
CutText[CutTextPos] := ANSI.Data[CurLine];
DeleteLine(CurLine);
DrawPage (CurY, WinSize, False); //optimize + 1
End;
End;
^M : DoEnter;
^O : Begin
Session.io.OutFile('fshelp', True, 0);
ReDrawTemplate(False);
End;
^Q : If Not DrawMode Then Begin
If Session.User.ThisUser.UseLBQuote Then
QuoteWindow
Else
Quote;
ReDrawTemplate(False);
End;
^U : If CutTextPos > 0 Then Begin
CutPasted := True;
For Count := CutTextPos DownTo 1 Do
If LastLine < MaxMsgLines Then Begin
InsertLine(CurLine);
ANSI.Data[CurLine] := CutText[Count];
End;
DrawPage (CurY, WinSize, False);
End;
^V : ToggleInsert(True);
^Y : If (CurLine < LastLine) or ((CurLine = LastLine) And Not IsBlankLine(ANSI.Data[CurLine], 80)) Then Begin
DeleteLine (CurLine);
If CurLine > LastLine Then
InsertLine (CurLine);
DrawPage (CurY, WinSize, False);
End;
^Z,
^[ : Begin
If DrawMode Then
DrawCommands
Else
EditorCommands;
If (Not Save) and (Not Done) Then ReDrawTemplate(False);
End;
#32..
#254 : If (CurLength >= RowSize) and (GetWrapPos(ANSI.Data[CurLine], RowSize, RowSize) = 0) And InsertMode Then Begin
DoEnter;
DoChar(Ch);
End Else
If (CurX = 1) and (Ch = '/') and (Not DrawMode) Then Begin
EditorCommands;
If (Not Save) and (Not Done) Then ReDrawTemplate(False);
End Else
DoChar(Ch);
End;
Until Done;
Session.io.AllowArrow := False;
If Save Then FindLastLine;
Result := Save;
Session.io.AnsiGotoXY (1, Session.User.ThisUser.ScreenSize);
End;
End.