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; } }