package com.jmxp;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import com.jmxp.libmxp.alignType;
import com.jmxp.structures.SendStruct;
import com.jmxp.structures.flagStruct;
import com.jmxp.structures.formatStruct;
import com.jmxp.structures.linkStruct;
public class MXPState
{
enum mxpMode { openMode, secureMode, lockedMode};
//initial LOCKED state?
boolean initiallyLocked;
// currently implemented MXP version
String mxpVersion;
String clientName, clientVersion;
int Hattribs[] = new int[6];
Color Hfg[] = new Color[6], Hbg[] = new Color[6];
String Hfont [] = new String[6];
int Hsize[] = new int[6];
int defaultsize;
String ttFont;
/** list of existing frames */
HashMap<String, Boolean> frames = new HashMap<String, Boolean>();
//screen and font parameters
int sX, sY; // width/height of the screen
int wX, wY; // width/height of the output window
int fX, fY; // width/height of character X
//user-defined values
Color defaultfg, defaultbg;
String defaultfont;
int defaultattribs;
Color gaugeColor;
/**
* current mode
*/
private mxpMode mode;
private ResultHandler results;
private ElementManager elements;
private EntityManager entities;
/** temporary secure mode? */
private boolean tempMode;
/** did we just leave secure mode?*/
private boolean wasSecureMode;
//variables
private boolean inVar;
/** current default mode */
private mxpMode defaultmode;
private String varName, varValue;
//links
private boolean inLink, isALink;
private String linkText;
/** list of closing tags */
private ArrayList<closingTag> closingTags = new ArrayList<closingTag>();
//text attributes
private boolean bold, italic, underline, strikeout;
private Color fgcolor, bgcolor;
private String curfont;
private int cursize;
//paragraphs
private boolean inParagraph; //in P tag; no method returns this
private boolean ignoreNextNewLine; //after NOBR; no method returns this
private String lastcmd;
private boolean gotmap;
//current window
private String curWindow, prevWindow;
//SUPPORTS stuff...
private boolean suplink, supgauge, supstatus, supsound, supframe, supimage, suprelocate;
private class closingTag
{
//tag name (lowercase)
String name;
//closing result, if there's exactly one
MXPResult closingresult;
//usually only zero or one element, but sometimes more :-)
List<MXPResult> closingresults;
};
public MXPState(ResultHandler resh, ElementManager elm,
EntityManager enm, String package_name, String version)
{
results = resh;
elements = elm;
entities = enm;
//currently implemented MXP version
mxpVersion = "1.0";
//starting MXP mode is LOCKED, to prevent problems with non-MXP MUDs).
//This goes against the MXP protocol, therefore there's a setting that will keep the OPEN
//mode, if desired - see public API.
mode = mxpMode.lockedMode;
defaultmode = mxpMode.lockedMode;
initiallyLocked = true;
tempMode = false;
wasSecureMode = false;
//some default values...
MXPColors colors = MXPColors.self();
defaultfg = colors.getColor("gray");
defaultbg = colors.getColor("black");
defaultfont = "Courier";
defaultsize = 12;
defaultattribs = 0;
//by default, all headers are written in the same font (Courier), they are bold and they
//differ in sizes...
for (int i = 0; i < 6; i++)
{
Hfont[i] = "Courier";
Hfg[i] = defaultfg;
Hbg[i] = defaultbg;
Hattribs[i] = libmxp.Bold;
}
Hsize[0] = 32;
Hsize[1] = 24;
Hsize[2] = 20;
Hsize[3] = 16;
Hsize[4] = 14;
Hsize[5] = 12;
ttFont = "Courier";
setDefaultGaugeColor (colors.getColor("white"));
//PACKAGE and VERSION are defined in config.h
clientName = package_name;
clientVersion = version;
//some default screen and font attributes...
fX = 16;
fY = 8;
sX = 800;
sY = 600;
suplink = supgauge = supstatus = supframe = supimage = suprelocate = false;
//params
reset();
}
private void reset()
{
bold = (defaultattribs & libmxp.Bold) != 0;
italic = (defaultattribs & libmxp.Italic) != 0;
underline = (defaultattribs & libmxp.Underline) != 0;
strikeout = (defaultattribs & libmxp.Strikeout) != 0;
fgcolor = defaultfg;
bgcolor = defaultbg;
curfont = defaultfont;
cursize = defaultsize;
inVar = false;
varValue = "";
inParagraph = false;
ignoreNextNewLine = false;
inLink = false;
isALink = false;
linkText = "";
gotmap = false;
curWindow = "";
prevWindow = "";
}
public void setDefaultGaugeColor(Color color)
{
gaugeColor = color;
}
/**
* return current mode
*/
public mxpMode getMXPMode()
{
return mode;
}
public void gotSUPPORT(List<String> params) throws Exception
{
commonTagHandler();
if (!params.isEmpty()) //some parameters - this is not supported at the moment
results.addToList(results.createWarning (
"Received <support> with parameters, but this isn't supported yet..."));
String res;
res = "\u001b[1z<SUPPORTS +!element +!attlist +!entity +var +b +i +u +s +c +h +font";
res += " +nobr +p +br +sbr +version +support +h1 +h2 +h3 +h4 +h5 +h6 +hr +small +tt";
if (suplink)
res += " +a +send +expire";
if (supgauge)
res += " +gauge";
if (supstatus)
res += " +status";
if (supsound)
res += " +sound +music";
if (supframe)
res += " +frame +dest";
if (supimage)
res += " +image";
if (suprelocate)
res += " +relocate +user +password";
res += ">\r\n";
results.addToList (results.createSendThis (res));
commonAfterTagHandler();
}
/** stuff common for all tags */
private void commonTagHandler()
{
//got a new tag - close outstanding entities, if any (unless we're in LOCKED mode)
if (mode != mxpMode.lockedMode)
{
String t = entities.expandEntities ("", true);
if (!(t.isEmpty()))
gotText(t, false);
}
//outstanding tags are closed, if we're going out of secure mode, unless a change back to secure
//mode occurs
if (wasSecureMode)
{
closeAllTags ();
wasSecureMode = false;
}
//error is reported, if we're inside VAR...
if (inVar)
results.addToList (results.createError ("Got a tag inside a variable!"));
}
public void gotText (String text, boolean expandentities)
{
if (text.isEmpty())
return;
//temp-secure mode . ERROR!
if (tempMode)
{
tempMode = false;
mode = defaultmode;
results.addToList (results.createError ("Temp-secure line tag not followed by a tag!"));
}
//outstanding tags are closed, if we're going out of secure mode, unless a change back to secure
//mode occurs
if (wasSecureMode)
{
closeAllTags ();
wasSecureMode = false;
}
//expand entities, if needed
String t;
if (expandentities && (mode != mxpMode.lockedMode))
t = entities.expandEntities (text, false);
else
t = text;
//special handling if we're in a variable or a link
if (inVar)
varValue += t;
if (inLink)
linkText += t;
//text can be sent is it's not a part of a link or of a variable
if (!(inVar || inLink))
//add text to the list of things to send
results.addToList (results.createText(t));
}
private void closeAllTags()
{
if (closingTags.isEmpty())
return;
//process open tags one by one...
while (!closingTags.isEmpty())
{
//closingTags is a FIFO queue, tho technically it's a list
int last = closingTags.size()-1;
closingTag tag = closingTags.get(last);
closingTags.remove(last);
results.addToList (results.createWarning ("Had to auto-close tag " + tag.name + "."));
closeTag(tag);
}
}
private void closeTag(closingTag tag)
{
//some tags need special handling...
if (tag.name.equals("p"))
{
inParagraph = false;
ignoreNextNewLine = false;
//also send a newline after end of paragraph... MXP docs say nothing about this :(
results.addToList (results.createText ("\r\n"));
}
if (tag.name.equals("var"))
{
tag.closingresult = null;
tag.closingresults = null;
results.addToList (results.createVariable (varName, varValue, false));
results.addToList (results.createText (varName + ": " + varValue));
entities.addEntity (varName, varValue);
inVar = false;
varName = "";
varValue = "";
}
if (tag.name.equals("a"))
{
if (inLink && isALink)
{
linkStruct ls = (linkStruct)tag.closingresult.data;
//assign text, using URL if no text given
String lt = linkText.isEmpty() ? (ls.url != null ? ls.url : "") : linkText;
lt = stripANSI (lt);
ls.text = lt;
}
else
//this should never happen
results.addToList (results.createError ("Received </A> tag, but I'm not in a link!"));
linkText = "";
inLink = false;
isALink = false;
}
if (tag.name.equals("send"))
{
if (gotmap)
{
//don't send this closing result
results.deleteResult (tag.closingresult);
tag.closingresult = null;
if (!linkText.isEmpty())
results.addToList (results.createError
("Received image map and a command in one SEND tag!"));
}
else if (inLink && (!isALink))
{
SendStruct ss = (SendStruct) tag.closingresult.data;
//assign text, also assign to command if none given
//assign linkText to ss.text
linkText = stripANSI (linkText);
ss.text = linkText;
if (ss.hint != null )
{
//expand &text; in hint
String hint = ss.hint;
boolean found = true, havematch = false;
while (found)
{
hint = hint.replaceFirst("&text;", linkText);
if ( hint.indexOf("&text;") < 0 ) found = false; //no more matches
}
if (havematch) //apply changes if needed
{
//assign hint to ss.hint
ss.hint = hint;
}
}
if (!ss.command.isEmpty())
{
String cmd = ss.command;
//also expand &text; in href
boolean found = true, havematch = false;
while (found)
{
if ( cmd.indexOf("&text;") > 0 )
{
cmd = cmd.replace("&text;", linkText);
havematch = true;
}
else
found = false; //no more matches
}
if (havematch) //apply changes if needed
{
//assign cmd to ss.command
ss.command = cmd;
}
}
else if (!linkText.isEmpty())
{
//assign linkText to ss.command
ss.command = linkText;
}
}
else
//this should never happen
results.addToList (results.createError ("Received </SEND> tag, but I'm not in a link!"));
linkText = "";
inLink = false;
isALink = false;
gotmap = false;
}
//handle applying/sending of closing results, is any
if (tag.closingresult!=null)
{
//apply result, reverting changes made by opening tag
applyResult(tag.closingresult);
//and send the changes to the client app
results.addToList (tag.closingresult);
}
if (tag.closingresults!=null)
{
//the same for remaining closing tags...
for (MXPResult item : tag.closingresults)
{
applyResult(item);
results.addToList(item);
}
}
//finally, the closing tag gets deleted
//note that this won't delete the results themselves - they will be deleted after
//they are processed by the client app
tag.closingresults = null;
tag = null;
}
private String stripANSI (String s)
{
// first of all, find out whether there are any ANSI sequences
boolean ansi = false;
for (int i = 0; i < s.length(); ++i)
if (s.charAt(i) == 27) ansi = true;
if (!ansi) return s;
// there are ANSI sequences - have to get rid of them
String res = "";
ansi = false;
for (int i = 0; i < s.length(); ++i) {
if (!ansi) {
if (s.charAt(i) == 27)
ansi = true;
else
res += s.charAt(i);
} else {
// ANSI seq is ended by a-z,A-Z
if ( Character.isLetter(s.charAt(i)) )
ansi = false;
}
}
return res;
}
private void applyResult (MXPResult what)
{
switch (what.type) {
case 5: {
formatStruct fs = (formatStruct) what.data;
int usemask = fs.usemask;
if ((usemask & formatStruct.USE_BOLD) != 0 )
bold = (fs.attributes & libmxp.Bold) != 0;
if ((usemask & formatStruct.USE_ITALICS) != 0 )
italic = (fs.attributes & libmxp.Italic) != 0;
if ((usemask & formatStruct.USE_UNDERLINE) != 0 )
underline = (fs.attributes & libmxp.Underline) != 0;
if ((usemask & formatStruct.USE_STRIKEOUT) != 0 )
strikeout = (fs.attributes & libmxp.Strikeout) != 0;
if ((usemask & formatStruct.USE_FG) != 0 )
fgcolor = fs.fg;
if ((usemask & formatStruct.USE_BG) != 0 )
bgcolor = fs.bg;
if ((usemask & formatStruct.USE_FONT) != 0 )
curfont = fs.font;
if ((usemask & formatStruct.USE_SIZE) != 0 )
cursize = fs.size;
break;
}
case 15:
{
prevWindow = curWindow;
if (what.data != null)
curWindow = (String)what.data;
else
curWindow = "";
break;
}
};
}
private void commonAfterTagHandler()
{
//secure mode for one tag?
if (tempMode)
{
tempMode = false;
//set mode back to default mode
mode = defaultmode;
}
}
public void gotClosingTag(String name)
{
String nm = name.toLowerCase();
//hack, to prevent an error from being reported when </var> or end-of-flag comes
//we cannot simply test for </var> and friends and disable it then, because
//we could have the var tag inside some element
boolean oldInVar = inVar;
inVar = false;
commonTagHandler();
//restore the inVar variable...
inVar = oldInVar;
boolean okay = false;
while (!okay)
{
if (closingTags.isEmpty())
break; //last one closed...
//closingTags is a FIFO queue, tho technically it's a list
int last = closingTags.size()-1;
closingTag tag = closingTags.get(last);
closingTags.remove(last);
if (tag.name.equals(nm))
okay = true; //good
else
results.addToList (results.createWarning ("Had to auto-close tag " + tag.name +
", because closing tag </" + name + "> was received."));
closeTag (tag);
}
if (!okay)
results.addToList (results.createError ("Received unpaired closing tag </" + name + ">."));
commonAfterTagHandler();
}
//we treat flag as another tag - this is needed to allow correct flag closing even if the //appropriate closing tag wasn't sent by the MUD (auto-closing of flag)
public void gotFlag(boolean begin, String flag)
{
boolean setFlag = false; //is this a set-variable flag?
String f = flag.toLowerCase();
if ( f.indexOf("set ") == 0 )
setFlag = true;
//disable inVar and remember old value, if this is a set-flag
//this is needed to prevent error report in commonTagHandler()
boolean oldInVar = inVar;
if (setFlag) inVar = false;
commonTagHandler();
//restore inVar value
inVar = oldInVar;
//no -> inform about the flag
if (begin)
{
MXPResult res = results.createFlag (true, flag);
MXPResult res2 = createClosingResult (res);
results.addToList (res);
addClosingTag ("flag", res2, null);
//"set xxx" type of flag?
if (setFlag)
{
if (inVar) //in variable already
{
results.addToList (results.createError
("Got a set-flag, but I'm already in a variable definition!"));
return;
}
//we are now in a variable
inVar = true;
varName = f.substring(f.lastIndexOf(' ') + 1); //last word
varValue = "";
}
}
else
{
//closing set-flag...
if (inVar && setFlag)
{
results.addToList (results.createVariable (varName, varValue, false));
//send variable value, but no varname as in </var>
results.addToList (results.createText (varValue));
entities.addEntity (varName, varValue);
inVar = false;
varName = "";
varValue = "";
}
gotClosingTag ("flag");
}
//no commonAfterTagHandler() here - this ain't no real tag :D
}
//mxpResult handling
MXPResult createClosingResult (MXPResult what)
{
MXPResult res = null;
switch (what.type)
{
case 3:
{
flagStruct fs = (flagStruct) what.data;
res = results.createFlag (false, fs.name);
break;
}
case 5:
{
formatStruct fs = (formatStruct)what.data;
//usemask is the most relevant thing here - things not enabled there won't be applied,
//so we can place anything there
int usemask = fs.usemask;
int curattrib = (bold?1:0) * libmxp.Bold + (italic?1:0) * libmxp.Italic +
(underline?1:0) * libmxp.Underline + (strikeout?1:0) * libmxp.Strikeout;
String font = "";
if ( (usemask & formatStruct.USE_FONT) != 0 )
font = curfont;
res = results.createFormatting (usemask, curattrib, fgcolor, bgcolor, font, cursize);
break;
}
case 15: {
res = results.createSetWindow (curWindow);
break;
}
};
return res;
}
private void addClosingTag(String name, MXPResult res, List<MXPResult> res2)
{
closingTag ctag = new closingTag();
ctag.name = name;
ctag.closingresult = res;
ctag.closingresults = res2;
closingTags.add(ctag);
}
public void gotVariable(String name, String string, boolean b)
{
commonTagHandler();
//send the variable value
results.addToList(results.createVariable (name, string, b));
commonAfterTagHandler();
}
/**
* called upon the VAR tag
*/
public void gotVAR(String name)
{
commonTagHandler();
if (inVar)
{
results.addToList (results.createError ("Nested VAR tags are not allowed!"));
commonAfterTagHandler();
return;
}
//we are now in a variable
inVar = true;
varName = name;
varValue = "";
//create a closing result; the variable name shall be updated when the tag will be closed
addClosingTag("var", null, null);
commonAfterTagHandler();
}
public void gotBOLD()
{
commonTagHandler();
MXPResult res = results.createFormatting(formatStruct.USE_BOLD, libmxp.Bold,
MXPColors.noColor(), MXPColors.noColor(), "", 0);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("b", res2, null);
commonAfterTagHandler();
}
public void gotITALIC()
{
commonTagHandler();
MXPResult res = results.createFormatting (formatStruct.USE_ITALICS, libmxp.Italic,
MXPColors.noColor(), MXPColors.noColor(), "", 0);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("i", res2, null);
commonAfterTagHandler();
}
public void gotUNDERLINE()
{
commonTagHandler();
MXPResult res = results.createFormatting (formatStruct.USE_UNDERLINE, libmxp.Underline,
MXPColors.noColor(), MXPColors.noColor(), "", 0);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("u", res2, null);
commonAfterTagHandler();
}
public void gotSTRIKEOUT()
{
commonTagHandler();
MXPResult res = results.createFormatting (formatStruct.USE_STRIKEOUT, libmxp.Strikeout,
MXPColors.noColor(),
MXPColors.noColor(), "", 0);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("s", res2, null);
commonAfterTagHandler();
}
public Color bgColor()
{
return bgcolor;
}
public Color fgColor()
{
return fgcolor;
}
public void gotCOLOR(Color fg, Color bg)
{
commonTagHandler();
MXPResult res = results.createFormatting (formatStruct.USE_FG | formatStruct.USE_BG,
0, fg, bg, "", 0);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("c", res2, null);
commonAfterTagHandler();
}
public void gotHIGH()
{
commonTagHandler();
Color color = fgcolor;
//High color is computed by adding 128 to each attribute...
//This is a very primitive way of doing it, and it's probably insufficient. We'll see.
int r,g,b;
r = (color.getRed() < 128) ? (color.getRed() + 128) : 255;
g = (color.getGreen() < 128) ? (color.getGreen() + 128) : 255;
b = (color.getBlue() < 128) ? (color.getBlue() + 128) : 255;
color = new Color(r,g,b);
MXPResult res = results.createFormatting (formatStruct.USE_FG, 0,
color, MXPColors.noColor(), "", 0);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("h", res2, null);
commonAfterTagHandler();
}
public String fontFace()
{
return curfont;
}
public int fontSize()
{
return cursize;
}
public void gotFONT(String face, int size, Color fg, Color bg)
{
commonTagHandler();
MXPResult res = results.createFormatting (formatStruct.USE_FG | formatStruct.USE_BG |
formatStruct.USE_FONT | formatStruct.USE_SIZE, 0, fg, bg,
face, size);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("font", res2, null);
commonAfterTagHandler();
}
public void gotP()
{
commonTagHandler();
//we're now in a paragraph
inParagraph = true;
addClosingTag ("p", null, null);
//no reporting to the client
commonAfterTagHandler();
}
public void gotBR() {
commonTagHandler();
//inform the client that we got a newline (but no mode changes shall occur)
results.addToList (results.createText ("\r\n"));
commonAfterTagHandler();
}
public void gotNOBR() {
commonTagHandler();
//next new-line is to be ignored
ignoreNextNewLine = true;
//no reporting to client
commonAfterTagHandler();
}
public void gotSBR() {
commonTagHandler();
//soft-break is represented as 0x1F
results.addToList (results.createText ("\u001f"));
commonAfterTagHandler();
}
public void gotA(String href, String hint, String expire) {
commonTagHandler();
inLink = true;
isALink = true;
linkText = "";
MXPResult res = results.createLink (expire, href, "", hint);
addClosingTag ("a", res, null);
commonAfterTagHandler();
}
public void gotSEND(String command, String hint, boolean prompt, String expire)
{
commonTagHandler();
inLink = true;
isALink = false;
linkText = "";
gotmap = false;
String cmd = stripANSI (command);
lastcmd = cmd;
MXPResult res = results.createSendLink (expire, cmd, "", hint, prompt,
(command.indexOf("|") < 0) ? false : true);
addClosingTag ("send", res, null);
commonAfterTagHandler();
}
public void gotEXPIRE(String name)
{
commonTagHandler();
results.addToList (results.createExpire(name));
commonAfterTagHandler();
}
public void gotVERSION()
{
commonTagHandler();
//this is to be sent...
results.addToList (results.createSendThis ("\u001b[1z<VERSION MXP=" + mxpVersion + " CLIENT=" +
clientName + " VERSION=" + clientVersion + ">\r\n"));
commonAfterTagHandler();
}
public void gotHtag(int which)
{
if ((which < 1) || (which > 6)) //BUG!!!
{
commonAfterTagHandler();
return;
}
commonTagHandler();
int idx = which - 1;
MXPResult res = results.createFormatting (formatStruct.USE_ALL, Hattribs[idx], Hfg[idx], Hbg[idx],
Hfont[idx], Hsize[idx]);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
String ct = "h" + (idx+1);
addClosingTag (ct, res2, null);
commonAfterTagHandler();
}
public void gotHR()
{
commonTagHandler();
results.addToList (results.createHorizLine ());
commonAfterTagHandler();
}
public void gotSMALL()
{
commonTagHandler();
//SMALL means 3/4 of standard size :)
MXPResult res = results.createFormatting (formatStruct.USE_SIZE, 0, MXPColors.noColor(),
MXPColors.noColor(), "", defaultsize * 3/4);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("small", res2, null);
commonAfterTagHandler();
}
public void gotTT()
{
commonTagHandler();
MXPResult res = results.createFormatting (formatStruct.USE_FONT, 0,
MXPColors.noColor(), MXPColors.noColor(), ttFont, 0);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
addClosingTag ("tt", res2, null);
commonAfterTagHandler();
}
public void gotSOUND(String fname, int vol, int count, int priority, String type, String url)
{
commonTagHandler();
results.addToList (results.createSound (true, fname, vol, count, priority, false, type, url));
commonAfterTagHandler();
}
public void gotMUSIC(String fname, int vol, int count, boolean contifrereq, String type,
String url)
{
commonTagHandler();
results.addToList (results.createSound (false, fname, vol, count, 0, contifrereq, type, url));
commonAfterTagHandler();
}
public void gotGAUGE(String entity, String maxentity, String caption, Color color)
{
commonTagHandler();
results.addToList (results.createGauge (entity, maxentity, caption, color));
commonAfterTagHandler();
}
public void gotDEST(String name, int x, int y, boolean feol, boolean feof)
{
commonTagHandler();
String nm = name.toLowerCase();
boolean nmExists = (frames.containsKey(nm));
if (!nmExists)
{
results.addToList(results.createError(
"Received a request to redirect to non-existing window " + nm));
return;
}
MXPResult res = results.createSetWindow(name);
MXPResult res2 = createClosingResult (res);
applyResult (res);
results.addToList (res);
int _x = x;
int _y = y;
if ((y >= 0) && (x < 0)) _x = 0;
if ((_x >= 0) && (_y >= 0))
results.addToList (results.createMoveCursor (_x, _y));
List<MXPResult> ls = null;
//erase AFTER displaying text
if (feol || feof)
{
ls = new ArrayList<MXPResult>();
ls.add(res2);
res2 = results.createEraseText(feof);
}
//closing tag...
addClosingTag ("dest", res2, ls);
commonAfterTagHandler();
}
public void gotSTAT(String entity, String max, String caption)
{
commonTagHandler();
results.addToList (results.createStat(entity, max, caption));
commonAfterTagHandler();
}
public int computeCoord(String coord, boolean isX, boolean inWindow )
{
int retval = Integer.parseInt(coord);
int len = coord.length();
char ch = coord.charAt(len - 1);
if (ch == 'c') retval *= (isX ? fX : fY);
if (ch == '%') retval = retval * (inWindow ? (isX ? wX : wY) : (isX ? sX : sY)) / 100;
return retval;
}
public void gotFRAME(String name, String action, String title,
boolean internal, String align, int left, int top, int width,
int height, boolean scrolling, boolean floating) {
commonTagHandler();
if (name.isEmpty())
{
results.addToList (results.createError ("Got FRAME tag without frame name!"));
commonAfterTagHandler();
return;
}
String nm = name.toLowerCase();
String act = action.toLowerCase();
String alg = align.toLowerCase();
String tt = title;
//name is the default title
if (tt.isEmpty())
tt = name;
//align
libmxp.alignType at = libmxp.alignType.Top;
if (!align.isEmpty())
{
boolean alignok = false;
if (align.equals("left")) { at = libmxp.alignType.Left; alignok = true; }
if (align.equals("right")) { at = libmxp.alignType.Right; alignok = true; }
if (align.equals("top")) { at = libmxp.alignType.Top; alignok = true; }
if (align.equals("bottom")) { at = libmxp.alignType.Bottom; alignok = true; }
if (!alignok)
results.addToList (results.createError ("Received FRAME tag with unknown ALIGN option!"));
}
//does the list of frames contain frame with name nm?
boolean nmExists = frames.containsKey(nm);
if (act.equals("open"))
{
if (nmExists)
{
results.addToList (results.createError ("Received request to create an existing frame!"));
commonAfterTagHandler();
return;
}
//cannot create _top or _previous
if ((nm.equals("_top")) || (nm.equals("_previous")))
{
results.addToList (results.createError ("Received request to create a frame with name " +
nm + ", which is invalid!"));
commonAfterTagHandler();
return;
}
if (internal)
{
//false for internal windows... value not used as of now, but it may be used later...
frames.put(nm, false);
results.addToList (results.createInternalWindow (nm, tt, at, scrolling));
}
else
{
//true for normal windows... value not used as of now, but it may be used later...
frames.put(nm, true);
results.addToList (results.createWindow (nm, tt, left, top, width, height,
scrolling, floating));
}
}
if (act.equals("close"))
{
if (nmExists)
{
frames.remove(nm);
results.addToList (results.createCloseWindow (nm));
}
else
results.addToList (results.createError
("Received request to close a non-existing frame!"));
}
if (act.equals("redirect"))
{
//if the frame exists, or if the name is either _top or _previous, we redirect to that window
if ((nm.equals("_top")) || (nm.equals("_previous")) || nmExists)
redirectTo (nm);
else
{
//create that window
if (internal)
{
//false for internal windows... value not used as of now, but it may be used later...
frames.put(nm,false);
results.addToList (results.createInternalWindow (nm, tt, at, scrolling));
}
else
{
//true for normal windows... value not used as of now, but it may be used later...
frames.put(nm,true);
results.addToList (results.createWindow (nm, tt, left, top, width, height,
scrolling, floating));
}
//then redirect to it
redirectTo (nm);
}
}
commonAfterTagHandler();
}
private void redirectTo(String nm)
{
nm = nm.toLowerCase();
String emptystring = "";
MXPResult res = null;
if (nm.equals("_top"))
res = results.createSetWindow(emptystring);
else
if (nm.equals("_previous"))
res = results.createSetWindow (prevWindow);
else
if (frames.containsKey(nm))
res = results.createSetWindow (nm);
else
res = results.createError ("Received request to redirect to non-existing window " + nm);
//apply result - will update info about previous window and so...
applyResult (res);
results.addToList (res);
}
public void gotRELOCATE(String hostname, int port)
{
commonTagHandler();
results.addToList (results.createRelocate (hostname, port));
commonAfterTagHandler();
}
public void gotUSER()
{
commonTagHandler();
results.addToList (results.createSendLogin (true));
commonAfterTagHandler();
}
public void gotPASSWORD()
{
commonTagHandler();
results.addToList (results.createSendLogin (false));
commonAfterTagHandler();
}
public void gotIMAGE(String fname, String url, String type, int height, int width,
int hspace, int vspace, String align, boolean ismap)
{
commonTagHandler();
//align
String alg = align.toLowerCase();
alignType at = alignType.Top;
if (!align.isEmpty())
{
boolean alignok = false;
if (align.equals("left")) { at = alignType.Left; alignok = true; }
if (align.equals("right")) { at = alignType.Right; alignok = true; }
if (align.equals("top")) { at = alignType.Top; alignok = true; }
if (align.equals("bottom")) { at = alignType.Bottom; alignok = true; }
if (align.equals("middle")) { at = alignType.Middle; alignok = true; }
if (!alignok)
results.addToList (results.createError ("Received IMAGE tag with unknown ALIGN option!"));
}
if (gotmap)
results.addToList (results.createError ("Received multiple image maps in one SEND tag!"));
if (ismap)
{
if (inLink && (!isALink))
{
results.addToList (results.createImageMap(lastcmd));
lastcmd = "";
gotmap = true;
}
else
results.addToList (results.createError ("Received an image map with no SEND tag!"));
}
results.addToList (results.createImage (fname, url, type, height, width, hspace, vspace, at));
commonAfterTagHandler();
}
public void gotNewLine()
{
//got a newline char - close outstanding entities, if any (unless we're in LOCKED mode)
if (mode != mxpMode.lockedMode)
{
String t = entities.expandEntities ("", true);
if (!t.isEmpty())
gotText (t, false);
}
//was temp-secure mode?
if (tempMode)
{
tempMode = false;
mode = defaultmode;
results.addToList (results.createError ("Temp-secure line tag followed by a newline!"));
}
//leaving secure mode?
wasSecureMode = false;
if ((mode == mxpMode.secureMode) && (defaultmode != mxpMode.secureMode))
wasSecureMode = true;
//ending line in OPEN mode - close all tags!
if (mode == mxpMode.openMode)
closeAllTags ();
//is we're in SECURE mode, some tags may need to be closed...
//line ended inside a link
if (inLink)
{
inLink = false;
isALink = false;
linkText = "";
results.addToList (results.createError ("Received an unterminated link!"));
}
if (inVar)
{
inVar = false;
results.addToList (results.createError ("Received an unterminated VAR tag!"));
varValue = "";
}
//should next newline be ignored?
if (ignoreNextNewLine)
{
ignoreNextNewLine = false;
return;
}
//if we're in a paragraph, don't report the new-line either
if (inParagraph)
return;
//set mode back to default mode
mode = defaultmode;
//neither NOBR nor P - report newline
results.addToList (results.createText ("\r\n"));
}
public void gotLineTag(int number)
{
//got a line tag - close outstanding entities, if any (unless we're in LOCKED mode)
if (mode != mxpMode.lockedMode)
{
String t = entities.expandEntities ("", true);
if (!t.isEmpty())
gotText (t, false);
}
//leaving secure mode
if (wasSecureMode && (number != 1))
closeAllTags ();
wasSecureMode = false;
if (number < 0) return;
if (number > 99) return;
if (number >= 10)
results.addToList (results.createLineTag(number));
else
{
switch (number) {
case 0:
setMXPMode(mxpMode.openMode);
break;
case 1:
setMXPMode (mxpMode.secureMode);
break;
case 2:
setMXPMode (mxpMode.lockedMode);
break;
case 3:
closeAllTags ();
//default mode remains the same...
setMXPMode (mxpMode.openMode);
reset ();
break;
case 4:
setMXPMode (mxpMode.secureMode);
tempMode = true;
break;
case 5:
setMXPMode (mxpMode.openMode);
defaultmode = mxpMode.openMode;
break;
case 6:
setMXPMode (mxpMode.secureMode);
defaultmode = mxpMode.secureMode;
break;
case 7:
setMXPMode (mxpMode.lockedMode);
defaultmode = mxpMode.lockedMode;
break;
default:
results.addToList (results.createWarning ("Received unrecognized line tag."));
break;
};
}
}
private void setMXPMode(mxpMode m)
{
mode = m;
tempMode = false;
wasSecureMode = false;
//if we start in LOCKED mode and mode change occurs, we set default mode
//to OPEN, so that we are compatible with the spec...
if (initiallyLocked)
{
initiallyLocked = false;
defaultmode = mxpMode.openMode;
}
}
public void setDefaultText(String font, int size, boolean _bold,
boolean _italic, boolean _underline, boolean _strikeout, Color fg,
Color bg)
{
if (curfont == defaultfont) curfont = font;
defaultfont = font;
if (cursize == defaultsize) cursize = size;
defaultsize = size;
int curattrib = (bold?1:0) * libmxp.Bold + (italic?1:0) * libmxp.Italic +
(underline?1:0) * libmxp.Underline + (strikeout?1:0) * libmxp.Strikeout;
int newattribs = (_bold?1:0) * libmxp.Bold + (_italic?1:0) * libmxp.Italic +
(_underline?1:0) * libmxp.Underline + (_strikeout?1:0) * libmxp.Strikeout;
if (curattrib == defaultattribs)
{
bold = _bold;
italic = _italic;
underline = _underline;
strikeout = _strikeout;
}
defaultattribs = newattribs;
if (fgcolor == defaultfg) fgcolor = fg;
defaultfg = fg;
if (bgcolor == defaultbg) bgcolor = bg;
defaultbg = bg;
}
public void setHeaderParams(int which, String font, int size,
boolean _bold, boolean _italic, boolean _underline,
boolean _strikeout, Color fg, Color bg)
{
//invalid H-num?
if ((which < 1) || (which > 6))
return;
Hfont[which - 1] = font;
Hsize[which - 1] = size;
int newattribs = (_bold?1:0) * libmxp.Bold + (_italic?1:0) * libmxp.Italic +
(_underline?1:0) * libmxp.Underline + (_strikeout?1:0) * libmxp.Strikeout;
Hattribs[which - 1] = newattribs;
Hfg[which - 1] = fg;
Hbg[which - 1] = bg;
}
public void setNonProportFont(String font)
{
ttFont = font;
}
public void setClient(String name, String version)
{
clientName = name;
clientVersion = version;
}
public void supportsLink (boolean supports)
{
suplink = supports;
}
public void supportsGauge (boolean supports)
{
supgauge = supports;
}
public void supportsStatus (boolean supports)
{
supstatus = supports;
}
public void supportsSound (boolean supports)
{
supsound = supports;
}
public void supportsFrame (boolean supports)
{
supframe = supports;
}
public void supportsImage (boolean supports)
{
supimage = supports;
}
public void supportsRelocate (boolean supports)
{
suprelocate = supports;
}
public void switchToOpen ()
{
mode = mxpMode.openMode;
defaultmode = mxpMode.openMode;
initiallyLocked = false;
//not we conform to MXP spec... use with care - only affects non-MXP MUDs, where it allows
//open tags - MUDs supporting MXP are NOT affected
}
public void setScreenProps (int sx, int sy, int wx, int wy, int fx, int fy)
{
sX = sx;
sY = sy;
wX = wx;
wY = wy;
fX = fx;
fY = fy;
}
}