//==================================================================== // HTML Text box, written by Marco // Simply call SetContents to change window contents. // Only callback available is for LaunchKFURL. // ==================================================================== class KFGUI_HTMLTextBox extends KFGUI_MultiComponent; struct FTextLine { var string Text,URL; var color Color,ALColor; var Font Font; var float FontScale; var byte Align,FontSize; var int X,Y,XS,YS,Tab,TOffset; var byte LineSkips; var array ImgList; var bool bHasURL,bSplit; }; var array Lines; struct FImageEntry { var Surface Img; var int X,Y,XS,YS,YOffset,XOffset; var byte Align,Style; }; var array Images; var FImageEntry BgImage; var float OldXSize,OldYSize; var int YSize,HoverOverLinkLine,OldHoverLine; var() Color BGColor,WhiteColor,BlueColor,RedColor; var KFGUI_ScrollBarV MyScrollBar; var string TitleString; var int CurTab; var byte DefaultFontSize; var bool bNeedsInit,bHasSplitLines,bNeedScrollbar; function InitMenu() { Super.InitMenu(); MyScrollBar = KFGUI_ScrollBarV(FindComponentID('Scrollbar')); } final function int AddText( string Input, color TextColor, byte TextAlign, byte FontSize, out byte NumSkips ) { local int i; i = Lines.Length; Lines.Length = i+1; Lines[i].Text = Input; Lines[i].Color = TextColor; Lines[i].Align = TextAlign; Lines[i].FontSize = FontSize; Lines[i].LineSkips = NumSkips; Lines[i].Tab = CurTab; NumSkips = 0; return i; } final function string ParseLinkType( string URL ) { if( InStr(URL,"//")>0 ) return URL; if( Left(URL,4)~="ftp." ) return "ftp://"$URL; return "http://"$URL; } final function AddImage( string Input ) { local string Temp; local byte Align,Sty; local Material M; local int X,Y,XS,YS,i,j,z; Align = 3; Temp = GetOption(Input, "ALIGN="); if (Temp != "") { switch( Caps(Temp) ) { case "LEFT": case "0": Align = 0; break; case "CENTER": case "1": Align = 1; break; case "RIGHT": case "2": Align = 2; break; } } Temp = GetOption(Input, "STYLE="); if (Temp != "") { switch( Caps(Temp) ) { case "NORMAL": case "0": Sty = 0; break; case "STRETCH": case "1": Sty = 1; break; case "TILEDX": case "2": Sty = 2; break; case "TILEDY": case "3": Sty = 3; break; case "TILED": case "4": Sty = 4; break; } } Temp = GetOption(Input, "SRC="); if (Temp != "") M = Surface(DynamicLoadObject(Temp,Class'Surface')); if( M==None ) M = class'Canvas'.Default.DefaultTexture; X = int(GetOption(Input, "VSPACE=")); Y = int(GetOption(Input, "HSPACE=")); XS = int(GetOption(Input, "WIDTH=")); YS = int(GetOption(Input, "HEIGHT=")); if( XS==0 ) XS = M.GetSurfaceWidth(); if( YS==0 ) YS = M.GetSurfaceHeight(); i = Images.Length; Images.Length = i+1; Images[i].Img = M; Images[i].XOffset = X; Images[i].YOffset = Y; Images[i].XS = XS; Images[i].YS = YS; Images[i].Style = Sty; Images[i].Align = Align; j = Lines.Length-1; z = Lines[j].ImgList.Length; Lines[j].ImgList.Length = z+1; Lines[j].ImgList[z] = i; } final function SetContents( string Input ) { local string LeftText,HTML,RightText,Output,Temp,Link; local int Index; local color TextColor,LinkColor,ALinkColor,OrgTextColor; local byte Alignment,FontScaler,NextLineSkips; CurTab = 0; BGColor.A = 0; BgImage.Img = None; Lines.Length = 0; Images.Length = 0; TitleString = ""; bHasSplitLines = false; bNeedsInit = true; // First remove new liners Input = Repl(Input, Chr(13)$Chr(10), ""); Input = Repl(Input, Chr(13), ""); Input = Repl(Input, Chr(10), ""); Input = Repl(Input, Chr(9), " "); Input = Repl(Input, "\\n", "
"); TextColor = WhiteColor; OrgTextColor = WhiteColor; LinkColor = BlueColor; ALinkColor = RedColor; FontScaler = 3; DefaultFontSize = 3; Index = -1; while (Input != "") { ParseHTML(Input, LeftText, HTML, RightText); switch (GetTag(HTML)) { // multiline HTML tags case "P": Output $= LeftText; if( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); NextLineSkips = 2; Output = ""; } else ++NextLineSkips; break; case "BR": Output $= LeftText; if( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); NextLineSkips = 1; Output = ""; } else ++NextLineSkips; break; case "BODY": Temp = GetOption(HTML, "BGCOLOR="); if (Temp != "") BGColor = ParseColor(Temp); Temp = GetOption(HTML, "LINK="); if (Temp != "") LinkColor = ParseColor(Temp); Temp = GetOption(HTML, "ALINK="); if (Temp != "") ALinkColor = ParseColor(Temp); Temp = GetOption(HTML, "TEXT="); if (Temp != "") { TextColor = ParseColor(Temp); OrgTextColor = TextColor; } Temp = GetOption(HTML, "SIZE="); if (Temp != "") { FontScaler = int(Temp); DefaultFontSize = FontScaler; } Temp = GetOption(Input, "IMG="); if (Temp != "") { if( BGColor.A==0 ) BGColor = Class'Hud'.Default.WhiteColor; BgImage.Img = Surface(DynamicLoadObject(Temp,Class'Surface')); if( BgImage.Img==None ) BgImage.Img = Class'Canvas'.Default.DefaultTexture; BgImage.X = BgImage.Img.GetSurfaceWidth(); BgImage.Y = BgImage.Img.GetSurfaceHeight(); switch( Caps(GetOption(Input, "IMGSTYLE=")) ) { case "TILED": BgImage.XS = BgImage.X; BgImage.YS = BgImage.Y; BgImage.Style = 1; Temp = GetOption(Input, "TILEX="); if (Temp != "") BgImage.XS = int(Temp); Temp = GetOption(Input, "TILEY="); if (Temp != "") BgImage.YS = int(Temp); break; case "FITX": BgImage.Style = 2; break; case "FITY": BgImage.Style = 3; break; default: // FIT BgImage.Style = 0; } BgImage.Align = 0; if( GetOption(Input, "IMGLOCK=")=="0" ) BgImage.Align = 1; } Output $= LeftText; break; case "CENTER": Output $= LeftText; if ( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; } NextLineSkips = Max(NextLineSkips,1); Alignment = 1; break; case "RIGHT": Output $= LeftText; if ( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; } NextLineSkips = Max(NextLineSkips,1); Alignment = 2; break; case "/CENTER": case "/RIGHT": Index = AddText(Output $ LeftText,TextColor,Alignment,FontScaler,NextLineSkips); ++NextLineSkips; Alignment = 0; Output = ""; break; // Inline HTML tags case "H1": Output $= LeftText; if ( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; } NextLineSkips = Max(NextLineSkips,1); FontScaler = 5; Alignment = 1; break; case "/H1": Index = AddText(Output $ LeftText,TextColor,Alignment,FontScaler,NextLineSkips); ++NextLineSkips; Output = ""; FontScaler = DefaultFontSize; Alignment = 0; break; case "FONT": Output $= LeftText; if( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; } Temp = GetOption(HTML, "COLOR="); if (Temp != "") TextColor = ParseColor(Temp); Temp = GetOption(HTML, "SIZE="); if (Temp != "") FontScaler = int(Temp); break; case "/FONT": Output $= LeftText; if( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; } TextColor = OrgTextColor; FontScaler = DefaultFontSize; break; case "TAB": Output $= LeftText; if( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; } CurTab = int(GetOption(HTML, "X=")); break; case "/TAB": Output $= LeftText; if( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; } CurTab = 0; break; case "TITLE": Output $= LeftText; break; case "/TITLE": TitleString = LeftText; break; case "A": Output $= LeftText; if( Output!="" ) { Index = AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; } Link = GetOption(HTML, "HREF="); break; case "/A": Output $= LeftText; Index = AddText(Output,LinkColor,Alignment,FontScaler,NextLineSkips); Lines[Index].ALColor = ALinkColor; Lines[Index].bHasURL = true; if( Link=="" ) Lines[Index].URL = ParseLinkType(Output); else Lines[Index].URL = ParseLinkType(Link); Output = ""; FontScaler = DefaultFontSize; Alignment = 0; break; case "IMG": Output $= LeftText; if( Output!="" || NextLineSkips>0 ) AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); Output = ""; AddImage(HTML); break; default: Output = Output $ LeftText; break; } Input = RightText; } AddText(Output,TextColor,Alignment,FontScaler,NextLineSkips); } // Get the next HTML tag, the text before it and everthing after it. final function ParseHTML(string Input, out string LeftText, out string HTML, out string RightText) { local int i; i = InStr(Input, "<"); if (i == -1) { LeftText = Input; HTML = ""; RightText = ""; return; } LeftText = Left(Input, i); HTML = Mid(Input, i); i = InStr(HTML, ">"); if (i == -1) { RightText = ""; return; } RightText = Mid(HTML, i+1); HTML = Left(HTML, i+1); } final function string GetTag(string HTML) { local int i; if (HTML == "") return ""; HTML = Mid(HTML, 1); // lose < i = FirstMatching(InStr(HTML, ">"), InStr(HTML, " ")); if (i == -1) return Caps(HTML); else return Caps(Left(HTML, i)); } final function string GetOption(string HTML, string Option) { local int i, j; local string s; i = InStr(Caps(HTML), Caps(Option)); if (i == 1 || Mid(HTML, i-1, 1) == " ") { s = Mid(HTML, i+Len(Option)); j = FirstMatching(InStr(s, ">"), InStr(s, " ")); s = Left(s, j); if (Left(s, 1) == "\"") s = Mid(s, 1); if (Right(s, 1) == "\"") s = Left(s, Len(s) - 1); return s; } return ""; } final function int FirstMatching(int i, int j) { if (i == -1) return j; if (j == -1) return i; return Min(i, j); } final function Color ParseColor(string S) { local Color C; local int i; S = Caps(S); if (Left(S, 1) == "#") { C.R = (GetHexDigit(Mid(S, 1, 1)) << 4) + GetHexDigit(Mid(S, 2, 1)); C.G = (GetHexDigit(Mid(S, 3, 1)) << 4) + GetHexDigit(Mid(S, 4, 1)); C.B = (GetHexDigit(Mid(S, 5, 1)) << 4) + GetHexDigit(Mid(S, 6, 1)); } else if (Left(S, 4) == "RGB(") { S = Mid(S, 4); i = InStr(S,","); C.R = int(Left(S,i)); S = Mid(S,i+1); i = InStr(S,","); C.G = int(Left(S,i)); C.B = int(Mid(S,i+1)); } else { switch( S ) { case "RED": C.R = 255; C.G = 0; C.B = 0; break; case "BLUE": C.R = 0; C.G = 0; C.B = 255; break; case "GREEN": C.R = 0; C.G = 255; C.B = 0; break; case "YELLOW": C.R = 255; C.G = 255; C.B = 0; break; case "BLACK": C.R = 0; C.G = 0; C.B = 0; break; default: // WHITE C.R = 255; C.G = 255; C.B = 255; } } C.A = 255; return C; } final function byte GetHexDigit(string D) { local byte i; i = Asc(D); if( i>=48 && i<=57 ) // i>='0' && i<='9' return (i-48); // i-'0' return Min(i-55,15); // i-('A'-10) } final function SplitLine( int iLine, int iOffset ) { local int i; local string S; ++iLine; Lines.Insert(iLine,1); S = Lines[iLine-1].Text; for( i=iOffset; i0 ) { while( true ) { if( i>=Lines.Length || (i>0 && Lines[i].LineSkips>0) ) { for( j=iStart; j=Lines.Length ) break; X = 0; iStart = i; PrevY = BestHeight; BestHeight = 0; } if( Lines[i].FontSize>=247 ) Lines[i].Font = Owner.CurrentStyle.PickFont(Max(Lines[i].FontSize-247,0),Lines[i].FontScale); else Lines[i].Font = Owner.CurrentStyle.PickFont(Max(FontSize+Lines[i].FontSize,0),Lines[i].FontScale); Canvas.Font = Lines[i].Font; if( Lines[i].Text=="" ) { Canvas.TextSize("ABC",XS,YS,Lines[i].FontScale,Lines[i].FontScale); XS = 0; } else Canvas.TextSize(Lines[i].Text,XS,YS,Lines[i].FontScale,Lines[i].FontScale); if( Lines[i].LineSkips>0 ) { if( PrevY==0 ) PrevY = YS; Y+=(PrevY*Lines[i].LineSkips); } X = Max(X,Lines[i].Tab); Lines[i].TOffset = X; Lines[i].Y = Y; Lines[i].YS = YS; BestHeight = Max(BestHeight,YS); if( (X+XS)>Canvas.ClipX ) { // Split to next row. Remain = Canvas.ClipX-X; iLastWord = 0; iLen = Len(Lines[i].Text); for( j=1; jCanvas.ClipY); if( bNeedScrollbar ) { MyScrollBar.SetDisabled(false); MyScrollBar.UpdateScrollSize(0,YSize,Canvas.ClipY,512); } else MyScrollBar.SetDisabled(true); } simulated final function DrawTileStretchedClipped( Surface M, float XS, float YS ) { Canvas.CurX += Canvas.OrgX; Canvas.CurY += Canvas.OrgY; if( Canvas.CurXCanvas.ClipX ) XS = (Canvas.ClipX-Canvas.CurX); if( (Canvas.CurY+YS)>Canvas.ClipY ) YS = (Canvas.ClipY-Canvas.CurY); if( Texture(M)!=None ) Canvas.DrawTileStretched(Texture(M),XS,YS); } function DrawMenu() { local float YS; local int i,YOffset,MX,MY; local bool bMouseOnClient; if( bNeedsInit || OldXSize!=CompPos[2] || OldYSize!=CompPos[3] ) { bNeedsInit = false; InitHTMLArea(); } if( bNeedScrollbar ) YOffset = MyScrollBar.CurrentScroll; if( BGColor.A>0 ) { C.SetPos(0,0); C.DrawColor = BGColor; if( BgImage.Img!=None ) { if( BgImage.Align==1 ) // not locked on screen. MX = YOffset; switch( BgImage.Style ) { case 0: // Stretched to fit C.DrawTileClipped(BgImage.Img,C.ClipX,C.ClipY,0,MX,BgImage.X,BgImage.Y); break; case 1: // Tiled C.DrawTileClipped(BgImage.Img,C.ClipX,C.ClipY,0,MX,BgImage.XOffset,BgImage.YOffset); break; case 2: // Fit X C.DrawTileClipped(BgImage.Img,C.ClipX,C.ClipY,0,MX,BgImage.X,BgImage.YS); break; case 3: // Fit Y C.DrawTileClipped(BgImage.Img,C.ClipX,C.ClipY,0,MX,BgImage.XS,BgImage.Y); break; } } else C.DrawTile(Texture'WhiteTexture',C.ClipX,C.ClipY,0,0,1,1); } MX = Controller.MouseX-C.OrgX; MY = Controller.MouseY-C.OrgY; bMouseOnClient = (MX>=0 && MX<=C.ClipX && MY>=0 && MY<=C.ClipY); HoverOverLinkLine = -1; MY+=YOffset; C.DrawColor = Class'HUD'.Default.WhiteColor; for( i=0; iC.ClipY ) continue; switch( Images[i].Align ) { case 0: // Left case 3: // Unaligned, postition after text. C.CurX = 0; break; case 1: // Center C.CurX = (C.ClipX-Images[i].XS)/2; break; case 1: // Right C.CurX = C.ClipX-Images[i].XS; break; } C.CurX += Images[i].X; switch( Images[i].Style ) { case 1: // Stretched DrawTileStretchedClipped(C,Images[i].Img,Images[i].XS,Images[i].YS); break; case 2: // Tiled on X axis C.DrawTileClipped(Images[i].Img,Images[i].XS,Images[i].YS,0,0,Images[i].XS,Images[i].Img.MaterialVSize()); break; case 3: // Tiled on Y axis C.DrawTileClipped(Images[i].Img,Images[i].XS,Images[i].YS,0,0,Images[i].Img.MaterialUSize(),Images[i].YS); break; case 4: // Fully tiled C.DrawTileClipped(Images[i].Img,Images[i].XS,Images[i].YS,0,0,Images[i].XS,Images[i].YS); break; default: // Normal C.DrawTileClipped(Images[i].Img,Images[i].XS,Images[i].YS,0,0,Images[i].Img.MaterialUSize(),Images[i].Img.MaterialVSize()); } } for( i=0; iC.ClipY ) break; // Check if mouse hovers over URL if( bMouseOnClient && Lines[i].bHasURL && MX>=Lines[i].X && MX<=(Lines[i].X+Lines[i].XS) && MY>=Lines[i].Y && MY<=(Lines[i].Y+Lines[i].YS) ) { HoverOverLinkLine = i; bMouseOnClient = false; // No need to check on rest anymore. C.DrawColor = Lines[i].ALColor; } else C.DrawColor = Lines[i].Color; C.Font = Lines[i].Font; C.DrawTextClipped(Lines[i].Text); if( Lines[i].bHasURL ) { YS = Max(Lines[i].YS/15,1); C.SetPos(Lines[i].X,Lines[i].Y+Lines[i].YS-(YS*2)-YOffset); if( C.CurY=0 ) { Controller.PlayInterfaceSound(CS_Hover); SetToolTipText(Lines[HoverOverLinkLine].URL); } else SetToolTipText(""); } } function bool LaunchURL(GUIComponent Sender) { if( HoverOverLinkLine>=0 ) { if( Left(Lines[HoverOverLinkLine].URL,8)~="kfurl://" ) LaunchKFURL(Mid(Lines[HoverOverLinkLine].URL,8)); else if( Left(Lines[HoverOverLinkLine].URL,5)~="kf://" ) ChangeGameURL(Mid(Lines[HoverOverLinkLine].URL,5)); else LaunchURLPage(Lines[HoverOverLinkLine].URL); } } delegate LaunchKFURL( string URL ); delegate ChangeGameURL( string URL ) { Class'SRLevelCleanup'.Static.AddSafeCleanup(PlayerOwner(),URL); } delegate LaunchURLPage( string URL ) { PlayerOwner().Player.Console.DelayedConsoleCommand("START "$URL); } defaultproperties { bNeedsInit=true PropagateVisibility=true OnDraw=RenderHTMLText OnClick=LaunchURL Begin Object Class=GUIVertScrollBar Name=TheScrollbar bBoundToParent=true bScaleToParent=true WinWidth=0.03 WinLeft=0.97 WinTop=0.0 WinHeight=1.0 bVisible=true PropagateVisibility=true OnPreDraw=TheScrollbar.GripPreDraw End Object MyScrollBar=TheScrollbar StyleName="NoBackground" bAcceptsInput=True Begin Object Class=GUIToolTip Name=GUIListBoxBaseToolTip ExpirationSeconds=0 End Object ToolTip=GUIListBoxBaseToolTip WhiteColor=(R=255,G=255,B=255,A=255) BlueColor=(R=0,G=0,B=255,A=255) RedColor=(R=255,G=0,B=0,A=255) }