package talk; import java.awt.*; import java.lang.String; class ChatFrame extends Frame { int rowsShown = 10, colsShown = 60; TextArea outputText; TextField inputField; final int outputBufferHigh = 4096 + 2048; final int outputBufferLow = 4096; Client owner; ChatFrame(Client ownerArg) { super("Chat"); owner = ownerArg; int size = 10; Font font = new Font("Times Roman", Font.PLAIN, size); GridBagLayout gridbag = new GridBagLayout(); setLayout(gridbag); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.BOTH; c.gridwidth = GridBagConstraints.REMAINDER; c.gridheight = 1; c.weightx = 1.0; c.weighty = 2.0; c.gridx = 0; c.gridy = 0; outputText = new TextArea(rowsShown, colsShown); outputText.setEditable(false); outputText.setFont(font); gridbag.setConstraints(outputText, c); add(outputText); c.fill = GridBagConstraints.HORIZONTAL; c.gridx = 0; c.gridy = 1; c.gridheight = 1; c.weightx = 1.0; c.weighty = 0.0; inputField = new TextField(); inputField.setEditable(true); gridbag.setConstraints(inputField, c); add(inputField); inputField.requestFocus(); outputText.setText(""); inputField.setText(""); pack(); show(); } // handleEvent is an important method; all events in // this frame come to this method first. We want to // catch WINDOW_DESTROY events, which occur when the // user tries to close the frame. For all other events, // it is very important to call the original version // of handleEvent. Otherwise, action() and other event // handling methods will never be called! public boolean handleEvent(Event evt) { if (evt.id == Event.WINDOW_DESTROY) { // Tell the applet to get rid of us // and hang up. owner.disconnect("Not Connected."); // Return true; we have completely // processed this event. return true; } // We didn't handle this event here, so call the // version of handleEvent in the parent class. // In Java, the syntax for this is "super.methodName". return super.handleEvent(evt); } public boolean action(Event evt, Object arg) { // Text fields send an action event when // the user presses ENTER. In that case, the // 'arg' argument is a String object containing // the text of the field. Handy, isn't it? if (evt.target == (Object) inputField) { inputLine((String) arg); return true; } return false; } void inputLine(String arg) { // The user has entered a line of text. // Pass it to the owner. owner.inputLine(arg); // Now clear the text entry field. if (inputField != null) { inputField.setText(""); } } void outputLine(String l) { // Our task here is to break the line of text // into separate lines (word wrapping), since // the TextArea component cannot word wrap for us. // Otherwise users would have to scroll horizontally, // which is very unpleasant to do often. // Find the length of the line int len = l.length(); // The number of columns we want per line int w = colsShown; // While the string is too long for one line... while (len > w) { int sp; // Consider the string up to the maximum # of columns String sub = l.substring(0, w); // And look for the last space in that string sp = sub.lastIndexOf(' '); if (sp == -1) { // If there are no spaces at all, // then snap the line in half right // at the max # of columns. // Insert a line break... outputText.appendText("\n"); // Insert the new line... outputText.appendText(sub); // And break it off. l = l.substring(w); // Subtract from the length. len -= w; } else { // There is a space, so break politely // at the space. // Insert a line break... outputText.appendText("\n"); // Insert the new line... String untilSpace = sub.substring(0, sp); outputText.appendText(untilSpace); // And break it off, *after* the space // (thus the + 1). l = l.substring(sp + 1); // Subtract the length of the new line // from the total length... len -= sp; // And subtract one more for the space, // which has been discarded. len --; } } // Now that the line is short enough, if there's // any left, we can just add it right in. if (len > 0) { outputText.appendText("\n"); outputText.appendText(l); } // Delete any excess scrollback. deleteOldScrollback(); // And scroll to the end. scrollOutputToEnd(); } void deleteOldScrollback() { // Our task here is to remove the oldest // text in the output window if there's more // than a certain amount of data there. // Get the length of the output window int len = outputText.getText().length(); int remove = 0; // Get the text in the output window String text = outputText.getText(); // If we have more than outputBufferHigh characters // of text, then we want to throw out text until // we get below outputBufferLow characters. // This strategy avoids the need to throw out // text on every single call. // If we are above the high-water mark... if (len > outputBufferHigh) { // While we are above the low-water mark... while (len > outputBufferLow) { // We want to split cleanly at a line break. // So keep removing complete lines of text // on each pass, until we are under the // low water mark. // Find a carriage return... int cr = text.indexOf('\n'); // If there are no carriage returns, which // isn't very likely, then just hack off // exactly as many bytes as needed to // reach the low-water mark. Add the // number of bytes removed to 'remove', // subtract it from the length, and // break out of the loop. if (cr == -1) { remove += len - outputBufferLow; len -= remove; break; } // If there is a carriage return, which there // will be, then break off the text after // the carriage return. Add the // number of characters removed to 'remove', // and subtract it from the length. remove += (cr + 1); text = text.substring(0, cr + 1); len -= (cr + 1); } } // Now, this is the clever part: instead of actually // setting new text for the entire TextArea, we will // just replace the first 'remove' bytes in the // existing text with an empty string. We do this // because it isn't "jumpy" and doesn't annoy the user. if (remove > 0) { outputText.replaceText("", 0, remove); } } void scrollOutputToEnd() { // Our task here is to scroll to the end // of the TextArea containing the output // from the server. A little-known fact // about java text components: the caret // is always at the end of the selection, // so by selecting zero characters at the // very end, we scroll to the end. int len = outputText.getText().length(); outputText.select(len, len); } }