IMPLEMENTATION MODULE FramePainter; (* * REPERTOIRE * Release 1.6 * By Charles Bradford and Cole Brecheen * (c) Copyright 1985-1992 PMI * Green Bay, Wisconsin * All rights reserved * (414) 468-6040 * * $Header: D:/logfiles/mods/framepai.mov 1.6 17 Mar 1991 17:55:28 coleb $ * *) IMPORT FrameManager; IMPORT GenLists; IMPORT Key; IMPORT LowLevel; IMPORT M2Strings; IMPORT MsColors; IMPORT Numbers; IMPORT FAPI; IMPORT PosUtils; IMPORT Rectangles; IMPORT ScrnTypes; IMPORT ScrnUtl1; IMPORT StrEdit; IMPORT SYSTEM; IMPORT VWindows; VAR Initialized : BOOLEAN; PROCEDURE Init(); BEGIN IF Initialized THEN RETURN; ELSE Initialized := TRUE; END; FrameManager.Init(); GenLists.Init(); Key.Init(); LowLevel.Init(); M2Strings.Init(); MsColors.Init(); Numbers.Init(); PosUtils.Init(); Rectangles.Init(); ScrnTypes.Init(); ScrnUtl1.Init(); StrEdit.Init(); VWindows.Init(); END Init; PROCEDURE InColorMode(): BOOLEAN; VAR ModeData: FAPI.VIOMODEINFO; BEGIN ModeData.cb := SYSTEM.TSIZE(FAPI.VIOMODEINFO); IF FAPI.VIOGETMODE(SYSTEM.ADR(ModeData), 0) # 0 THEN RETURN FALSE; END; IF ModeData.color = FAPI.UCHAR(0) THEN RETURN FALSE; ELSE RETURN TRUE; END; END InColorMode; PROCEDURE DrawLine( col1, row1, col2, row2: CARDINAL; TheChar: CHAR ); VAR row : CARDINAL; linestr : ARRAY [0..80] OF CHAR; BEGIN IF col1 < col2 THEN (*We're drawing a horizontal line.*) LowLevel.Fill( SYSTEM.ADR(linestr), 1+HIGH(linestr), TheChar); StrEdit.SetLength( linestr, col2 - col1 +1); VWindows.DrawStr( VWindows.CurrentWindow, col1, row1, SYSTEM.ADR(linestr), col2 - col1 + 1 ); ELSE (*We're drawing a vertical line.*) FOR row := row1 TO row2 DO VWindows.DrawStr( VWindows.CurrentWindow, col1, row, SYSTEM.ADR(TheChar), 1 ); END; END; END DrawLine; PROCEDURE GetFrameCoords( TheFrame: ScrnTypes.DisplayFrame; VAR rect: Rectangles.ARectangle ); BEGIN Rectangles.DefineRectangle( rect, TheFrame^.startcol + 1, TheFrame^.startrow + 1, TheFrame^.endcol + 1, TheFrame^.endrow + 1 ) END GetFrameCoords; PROCEDURE RedrawEdField( TheFrame: ScrnTypes.DisplayFrame; FieldCount: CARDINAL; VAR area: Rectangles.ARectangle; Selected: BOOLEAN ); VAR cnt, TypeCode, AbsCol, AbsRow, TmpLength, EdFieldHeight, EdFieldWidth : CARDINAL; TmpList: GenLists.GenList; TmpAdr: SYSTEM.ADDRESS; EdArea, intersection : Rectangles.ARectangle; PROCEDURE DrawPartOfStr( StrAdr: SYSTEM.ADDRESS; StrSize, col, row: CARDINAL; VAR area: Rectangles.ARectangle ); VAR StrArea, intersection : Rectangles.ARectangle; LeadChop: CARDINAL; BEGIN StrSize := LowLevel.ScanEQ( StrSize, 0C, StrAdr ); Rectangles.DefineRectangle( StrArea, col, row, col + StrSize - 1, row ); IF Rectangles.RectsIntersect( StrArea, area, intersection ) THEN IF StrSize # 0 THEN (* Added this test 8 Nov 88 at suggestion of John McMonagle because DrawStr does a NullScan if last parameter is 0. *) IF intersection.col1 > INTEGER(col) THEN LeadChop := CARDINAL(intersection.col1) - col; ELSE LeadChop := 0; END; VWindows.DrawStr( TheFrame^.WindowHandle, intersection.col1, intersection.row1, LowLevel.AddAddr(StrAdr, LeadChop), (intersection.col2 - intersection.col1) + 1 ); END; END; END DrawPartOfStr; BEGIN AbsRow := ScrnUtl1.AbsFieldRow( TheFrame, FieldCount ); AbsCol := ScrnUtl1.AbsFieldCol( TheFrame, FieldCount ); ScrnUtl1.GetEdField( TheFrame, FieldCount, TmpList ); IF Selected THEN ScrnUtl1.SetPointerBarColors( TheFrame ); ELSE ScrnUtl1.SetFieldColors( TheFrame, FieldCount ); END; EdFieldHeight := ScrnUtl1.FieldHeight( TheFrame, FieldCount ); EdFieldWidth := ScrnUtl1.FieldWidth( TheFrame, FieldCount ); Rectangles.DefineRectangle( EdArea, AbsCol, AbsRow, AbsCol + EdFieldWidth - 1, AbsRow + EdFieldHeight - 1 ); IF Rectangles.RectsIntersect( EdArea, area, intersection ) THEN VWindows.ClearPart( TheFrame^.WindowHandle, intersection.col1, intersection.row1, intersection.col2, intersection.row2 ); END; EdFieldHeight := Numbers.Min( EdFieldHeight, GenLists.ListLength( TmpList ) ); (*EdFieldHeight gets the lesser of the full height of the editor window or the number of lines presently in it.*) FOR cnt := 1 TO EdFieldHeight DO (* write each row in editor window *) GenLists.GetElmtAdr( TmpList, cnt, TmpAdr, TmpLength, TypeCode ); DrawPartOfStr( TmpAdr, TmpLength, AbsCol, CARDINAL(AbsRow) + cnt - 1, intersection ); END; END RedrawEdField; PROCEDURE RedrawArea( TheFrame: ScrnTypes.DisplayFrame; col1, row1, col2, row2: CARDINAL ); VAR inside: BOOLEAN; FrameRect, TheArea, intersection, EdRect, EdToDraw: Rectangles.ARectangle; TmpFieldPtr: ScrnTypes.InputFieldPtr; ImagePtr: ScrnTypes.ImageElmtPtr; tmprow, tmpcol, tmpfield, LeadChop, EdFieldCount, ImageCount, FieldListEnd, ImageListEnd, TmpLength: CARDINAL; EdFieldHeight, EdFieldWidth, AbsoluteRow, AbsoluteCol: INTEGER; TheBox : VWindows.BoxStr; tmpforeg, tmpbackg: MsColors.AColor; tmpatrb: MsColors.AMonoAttribute; BEGIN (* First, we find each field that's in the area we have to redraw, taking TheFrame^.RowsScrolled and TheFrame^.ColsScrolled into account.*) GetFrameCoords( TheFrame, FrameRect ); Rectangles.DefineRectangle( TheArea, col1, row1, col2, row2 ); (* We could check here to make sure TheArea is inside FrameRect, but we'll let the caller be responsible for that. *) IF TheFrame^.ClearFirst THEN (* clear underlying window if this screen is "ClearFirst" *) ScrnUtl1.SetNormalColors( TheFrame ); VWindows.ClearPart( TheFrame^.WindowHandle, col1, row1, col2, row2 ); END; IF TheFrame^.EntryBox[0] # 0C THEN (* Oh no. We may have to restore some or all of the box. *) Rectangles.ShrinkRect( FrameRect ); IF Rectangles.RectIsInside( TheArea, FrameRect ) THEN (* Breathe sigh of relief. TheArea does not extend into the box. *) Rectangles.ExpandRect( FrameRect ); ELSE Rectangles.ExpandRect( FrameRect ); IF Rectangles.RectsIntersect( FrameRect, TheArea, intersection ) THEN (* Actually, we already know they intersect; this just computes the intersection for us. *) END; (* The code below is temporarily determining whether the frame is active from capitalization of the .action char. If it's uppercase, we know the frame is inactive, so we redraw the ExitBox. *) IF TheFrame^.action <= 'Z' THEN TheBox := TheFrame^.ExitBox; ELSE TheBox := TheFrame^.EntryBox; END; ScrnUtl1.SetBorderColors( TheFrame ); IF TheArea.col2 >= FrameRect.col2 THEN DEC( col2 ); (* We're doing something tricky here. We know there's a box, and we know the area to be redrawn extends into it on the right side, so that means we also know that col2 is wrong--the area to be redrawn is actually one column less wide than we had supposed. *) DrawLine( intersection.col2, intersection.row1, intersection.col2, intersection.row2, TheBox[4] ); IF intersection.row1 = FrameRect.row1 THEN VWindows.DrawStr( VWindows.CurrentWindow, intersection.col2, intersection.row1, SYSTEM.ADR(TheBox[2]), 1 ); END; IF intersection.row2 = FrameRect.row2 THEN VWindows.DrawStr( VWindows.CurrentWindow, intersection.col2, intersection.row2, SYSTEM.ADR(TheBox[7]), 1 ); END; END; IF TheArea.col1 <= FrameRect.col1 THEN INC( col1 ); DrawLine( intersection.col1, intersection.row1, intersection.col1, intersection.row2, TheBox[3] ); IF intersection.row1 = FrameRect.row1 THEN VWindows.DrawStr( VWindows.CurrentWindow, intersection.col1, intersection.row1, SYSTEM.ADR(TheBox[0]), 1 ); END; IF intersection.row2 = FrameRect.row2 THEN VWindows.DrawStr( VWindows.CurrentWindow, intersection.col1, intersection.row2, SYSTEM.ADR(TheBox[5]), 1 ); END; END; IF TheArea.row2 >= FrameRect.row2 THEN DEC( row2 ); DrawLine( intersection.col1, intersection.row2, intersection.col2, intersection.row2, TheBox[6] ); IF intersection.col1 = FrameRect.col1 THEN VWindows.DrawStr( VWindows.CurrentWindow, intersection.col1, intersection.row2, SYSTEM.ADR(TheBox[5]), 1 ); END; IF intersection.col2 = FrameRect.col2 THEN VWindows.DrawStr( VWindows.CurrentWindow, intersection.col2, intersection.row2, SYSTEM.ADR(TheBox[7]), 1 ); END; END; IF TheArea.row1 <= FrameRect.row1 THEN INC( row1 ); DrawLine( intersection.col1, intersection.row1, intersection.col2, intersection.row1, TheBox[1] ); IF intersection.col1 = FrameRect.col1 THEN VWindows.DrawStr( VWindows.CurrentWindow, intersection.col1, intersection.row1, SYSTEM.ADR(TheBox[0]), 1 ); END; IF intersection.col2 = FrameRect.col2 THEN VWindows.DrawStr( VWindows.CurrentWindow, intersection.col2, intersection.row1, SYSTEM.ADR(TheBox[2]), 1 ); END; END; ScrnUtl1.SetNormalColors( TheFrame ); END; END; IF NOT GenLists.Initialized( TheFrame^.ImageList ) THEN RETURN; END; EdFieldCount := 0; ImageCount := 1; ImageListEnd := ScrnUtl1.NumberOfImages( TheFrame ); WHILE ImageCount <= ImageListEnd DO ScrnUtl1.GetImagePtr( TheFrame, ImageCount, ImagePtr ); TmpLength := M2Strings.Length(ImagePtr^.text); tmprow := ImagePtr^.row; tmpcol := ImagePtr^.col; tmpfield := ImagePtr^.field; tmpforeg := ImagePtr^.foreg; tmpbackg := ImagePtr^.backg; tmpatrb := ImagePtr^.atrb; ScrnUtl1.DecodeWord( tmprow ); ScrnUtl1.DecodeWord( tmpcol ); ScrnUtl1.DecodeWord( tmpfield ); ScrnUtl1.DecodeByte( tmpforeg ); ScrnUtl1.DecodeByte( tmpbackg ); ScrnUtl1.DecodeByte( tmpatrb ); IF tmprow <= TheFrame^.headline THEN (* Is this image element protected from scrolling? *) AbsoluteRow := INTEGER(TheFrame^.startrow + tmprow); (* If so, it's always going to be in the same place; we don't have to subtract RowsScrolled to find out where to write it. *) ELSE (* This image element is not protected from scrolling; we have to add the absolute starting row of the window and then subtract RowsScrolled to find out where to write it. *) AbsoluteRow := INTEGER(TheFrame^.startrow + tmprow) - INTEGER( TheFrame^.RowsScrolled ); IF AbsoluteRow <= INTEGER(TheFrame^.startrow + TheFrame^.headline) THEN (* Image element has been scrolled up out of the visible part of the window; it's either underneath the area protected by the headline, or it's above the window altogether; could be negative. *) AbsoluteRow := AbsoluteRow - INTEGER( TheFrame^.headline ); END; END; (*A negative value for AbsoluteRow here indicates that the field would have been scrolled off the top of the screen.*) AbsoluteCol := INTEGER(TheFrame^.startcol + tmpcol) - INTEGER( TheFrame^.ColsScrolled ); LeadChop := 0; IF (tmpfield # 0) THEN ScrnUtl1.GetFieldPtr( TheFrame, tmpfield, TmpFieldPtr ); END; IF (tmpfield # 0) AND (TmpFieldPtr^.typ = ScrnTypes.EditorCode) THEN AbsoluteRow := ScrnUtl1.AbsFieldRow( TheFrame, tmpfield ); AbsoluteCol := ScrnUtl1.AbsFieldCol( TheFrame, tmpfield ); EdFieldHeight := ScrnUtl1.FieldHeight( TheFrame, tmpfield ); EdFieldWidth := ScrnUtl1.FieldWidth( TheFrame, tmpfield ); Rectangles.DefineRectangle(EdRect , AbsoluteCol, AbsoluteRow, (AbsoluteCol + EdFieldWidth) - 1, (AbsoluteRow + EdFieldHeight) - 1 ); inside := Rectangles.RectsIntersect(TheArea,EdRect,EdToDraw); ELSE IF NOT Numbers.IntIsBetween(row1, AbsoluteRow, row2) THEN inside := FALSE; ELSIF (AbsoluteCol > INTEGER(col2)) THEN inside := FALSE; ELSIF (AbsoluteCol < INTEGER(col1)) THEN IF (AbsoluteCol + INTEGER(TmpLength)) < INTEGER(col1) THEN inside := FALSE; ELSE LeadChop := CARDINAL(INTEGER(col1) - AbsoluteCol); inside := TRUE; END; ELSE inside := TRUE; END; END; IF inside THEN IF (tmpfield # 0) THEN (* Uh oh. It's not a normal piece of text. *) IF (TmpFieldPtr^.typ = ScrnTypes.GroupMember) AND TmpFieldPtr^.selected THEN (* It's a choice field (member of a group) and it's selected, so we use selection colors *) ScrnUtl1.SetSelectionColors( TheFrame ); ELSIF (TmpFieldPtr^.typ = ScrnTypes.EditorCode) AND (EdFieldCount = 0) THEN (* Note the first editor field, but don't write it. Editor fields have to be written last because lines below onto which the bottom part of the editor field overlaps may not be in the background color. Spaces will be written over them in whatever that color is, and we don't want to wipe out the bottom of the editor field. *) EdFieldCount := tmpfield; ELSE (* use normal colors for this field*) ScrnUtl1.SetFieldColors( TheFrame, tmpfield ); END; ELSE (* use normal colors for this field*) VWindows.SetForeColor( TheFrame^.WindowHandle, tmpforeg ); VWindows.SetBackColor( TheFrame^.WindowHandle, tmpbackg ); VWindows.SetMonoAttr( TheFrame^.WindowHandle, tmpatrb ); END; IF (tmpfield = 0) OR (TmpFieldPtr^.typ # ScrnTypes.EditorCode) THEN IF (AbsoluteCol + INTEGER(TmpLength)) >= (INTEGER(col2) + 1) THEN (*If the string would extend past the right boundary.*) TmpLength := CARDINAL((( INTEGER(col2) - AbsoluteCol) + 1) - INTEGER(LeadChop)); VWindows.DrawStr( TheFrame^.WindowHandle, CARDINAL( (AbsoluteCol + INTEGER(LeadChop))), CARDINAL(AbsoluteRow), LowLevel.AddAddr(SYSTEM.ADR(ImagePtr^.text), LeadChop), TmpLength ); ELSE VWindows.DrawStr( TheFrame^.WindowHandle, CARDINAL( (AbsoluteCol + INTEGER(LeadChop))), CARDINAL(AbsoluteRow), LowLevel.AddAddr(SYSTEM.ADR(ImagePtr^.text), LeadChop), TmpLength - LeadChop ); END; END; END; INC( ImageCount ); END; IF EdFieldCount = 0 THEN RETURN; END; FieldListEnd := ScrnUtl1.FieldListTotal( TheFrame ); WHILE (EdFieldCount <= FieldListEnd) DO ScrnUtl1.GetFieldPtr( TheFrame, EdFieldCount, TmpFieldPtr ); IF (TmpFieldPtr^.typ = ScrnTypes.EditorCode) THEN (* It's an editor field. *) AbsoluteRow := ScrnUtl1.AbsFieldRow( TheFrame, EdFieldCount ); AbsoluteCol := ScrnUtl1.AbsFieldCol( TheFrame, EdFieldCount ); EdFieldHeight := ScrnUtl1.FieldHeight( TheFrame, EdFieldCount ); EdFieldWidth := ScrnUtl1.FieldWidth( TheFrame, EdFieldCount ); Rectangles.DefineRectangle(EdRect , AbsoluteCol, AbsoluteRow, (AbsoluteCol + EdFieldWidth) - 1, AbsoluteRow + EdFieldHeight - 1 ); IF Rectangles.RectsIntersect(TheArea,EdRect,EdToDraw) THEN (* We now know we have to display at least part of the editor field *) RedrawEdField( TheFrame, EdFieldCount, EdToDraw, FALSE ); END; END; INC( EdFieldCount ); END; END RedrawArea; PROCEDURE RedrawField( TheFrame: ScrnTypes.DisplayFrame; TheField: CARDINAL; Selected: BOOLEAN ); (* Selected tells us whether the pointer bar is on this field, not whether it's a selected member of a choice group; we'll figure out the latter from the field itself.*) VAR FieldRec: ScrnTypes.InputFieldRecord; ImageRec: ScrnTypes.ImageElement; spot : CARDINAL; CurrentChoice, save : BOOLEAN; SelChar: ARRAY [0..0] OF CHAR; FrameArea : Rectangles.ARectangle; BEGIN CurrentChoice := FALSE; ScrnUtl1.GetFieldRec( TheFrame, TheField, FieldRec ); ScrnUtl1.GetFieldImageRec( TheFrame, TheField, ImageRec ); IF Selected THEN ScrnUtl1.SetPointerBarColors( TheFrame ); ELSIF (FieldRec.typ = ScrnTypes.GroupMember) AND (FieldRec.selected) THEN CurrentChoice := TRUE; (* We do this to prevent selection character from being highlighted if the choice is already selected. *) ScrnUtl1.SetSelectionColors( TheFrame ); ELSE ScrnUtl1.SetFieldColors( TheFrame, TheField ); END; IF (FieldRec.typ = ScrnTypes.EditorCode) THEN Rectangles.DefineRectangle( FrameArea, TheFrame^.startcol + 1, TheFrame^.startrow + 1, TheFrame^.endcol + 1, TheFrame^.endrow + 1 ); RedrawEdField( TheFrame, TheField, FrameArea, Selected ); RETURN; END; ScrnUtl1.GetFieldImageRec( TheFrame, TheField, ImageRec ); VWindows.DrawStr( TheFrame^.WindowHandle, ImageRec.col + TheFrame^.startcol - TheFrame^.ColsScrolled, ImageRec.row + TheFrame^.startrow - TheFrame^.RowsScrolled, SYSTEM.ADR(ImageRec.text), M2Strings.Length(ImageRec.text) ); (*Now hilite the selection character, if there is one.*) IF CurrentChoice OR (Selected AND NOT InColorMode()) THEN (*Don't bother if it's a choice field in selection colors, and don't bother if the field is already selected and the screen isn't in a color mode.*) RETURN; END; IF (FieldRec.typ = ScrnTypes.GroupMember) AND (FieldRec.ChoiceKey <= Key.Extended) THEN SelChar[0] := CHR(FieldRec.ChoiceKey); ELSIF (FieldRec.typ = ScrnTypes.GotoCode) AND (FieldRec.MenuKey <= Key.Extended) THEN SelChar[0] := CHR(FieldRec.MenuKey); ELSE RETURN; END; save := PosUtils.CaseSensitive; PosUtils.CaseSensitive := TRUE; IF NOT PosUtils.PresentPos( SelChar, ImageRec.text, spot ) THEN PosUtils.CaseSensitive := save; RETURN; END; PosUtils.CaseSensitive := save; ScrnUtl1.SetSelCharColors( TheFrame, TheField ); IF Selected THEN VWindows.SetBackColor( TheFrame^.WindowHandle, TheFrame^.pbbak ); END; VWindows.DrawStr( TheFrame^.WindowHandle, ImageRec.col + TheFrame^.startcol - TheFrame^.ColsScrolled + spot, ImageRec.row + TheFrame^.startrow - TheFrame^.RowsScrolled, SYSTEM.ADR(ImageRec.text[spot]), 1 ); END RedrawField; PROCEDURE ShowDisplayFrame(VAR FrameRec : ScrnTypes.DisplayFrame; Col1, Row1, ColX, RowX : CARDINAL); (* Displays the screen in FrameRec on the screen in the window defined by Col1, Row1, ColX, RowX. Col1, Row1 = upper left hand corner of window; ColX, RowX = lower right corner coordinates. Note: if Col1=0, then this proc uses the default values for the window coordinates.*) BEGIN (* ShowDisplayFrame *) IF Col1 > 0 THEN (*Correct the origin: starting col and row are decremented by one to represent "zero".*) FrameRec^.startcol := Col1-1; FrameRec^.endcol := ColX-1; FrameRec^.startrow := Row1-1; FrameRec^.endrow := RowX - 1; ELSE Col1 := FrameRec^.startcol + 1; Row1 := FrameRec^.startrow + 1; ColX := FrameRec^.endcol + 1; RowX := FrameRec^.endrow + 1; END; FrameManager.FrameActivated( FrameRec ); FrameManager.FrameIsInForeGround( FrameRec ); (* We have to call both because FrameActivated won't bring the frame to the foreground if it is already active. Note that we don't do this in RedrawArea, because RedrawArea is what FrameManager uses to restore areas of the screen -- if each call to RedrawArea changed the ordering of frames in FrameManager's FrameList, it would get stuck. *) IF NOT ScrnUtl1.BoxInFrame(FrameRec) THEN RedrawArea( FrameRec, Col1, Row1, ColX, RowX ); ELSE (* draw the proper box for this screen *) ScrnUtl1.SetBorderColors( FrameRec ); VWindows.DrawBox( FrameRec^.WindowHandle, FrameRec^.ExitBox, Col1, Row1, ColX, RowX ); (*We draw the ExitBox because the frame is not active at this point.*) ScrnUtl1.WriteBetween( FrameRec^.WindowHandle, FrameRec^.Caption, Col1 + 1, Row1, ColX - 1, RowX ); RedrawArea( FrameRec, Col1 + 1, Row1 + 1, ColX - 1, RowX - 1 ); END; END ShowDisplayFrame; BEGIN Initialized := FALSE; Init(); END FramePainter.