--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/javax/swing/text/html/FormView.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,932 @@
+/*
+ * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package javax.swing.text.html;
+
+import java.net.*;
+import java.io.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+
+/**
+ * Component decorator that implements the view interface
+ * for form elements, <input>, <textarea>,
+ * and <select>. The model for the component is stored
+ * as an attribute of the the element (using StyleConstants.ModelAttribute),
+ * and is used to build the component of the view. The type
+ * of the model is assumed to of the type that would be set by
+ * <code>HTMLDocument.HTMLReader.FormAction</code>. If there are
+ * multiple views mapped over the document, they will share the
+ * embedded component models.
+ * <p>
+ * The following table shows what components get built
+ * by this view.
+ * <table summary="shows what components get built by this view">
+ * <tr>
+ * <th>Element Type</th>
+ * <th>Component built</th>
+ * </tr>
+ * <tr>
+ * <td>input, type button</td>
+ * <td>JButton</td>
+ * </tr>
+ * <tr>
+ * <td>input, type checkbox</td>
+ * <td>JCheckBox</td>
+ * </tr>
+ * <tr>
+ * <td>input, type image</td>
+ * <td>JButton</td>
+ * </tr>
+ * <tr>
+ * <td>input, type password</td>
+ * <td>JPasswordField</td>
+ * </tr>
+ * <tr>
+ * <td>input, type radio</td>
+ * <td>JRadioButton</td>
+ * </tr>
+ * <tr>
+ * <td>input, type reset</td>
+ * <td>JButton</td>
+ * </tr>
+ * <tr>
+ * <td>input, type submit</td>
+ * <td>JButton</td>
+ * </tr>
+ * <tr>
+ * <td>input, type text</td>
+ * <td>JTextField</td>
+ * </tr>
+ * <tr>
+ * <td>select, size > 1 or multiple attribute defined</td>
+ * <td>JList in a JScrollPane</td>
+ * </tr>
+ * <tr>
+ * <td>select, size unspecified or 1</td>
+ * <td>JComboBox</td>
+ * </tr>
+ * <tr>
+ * <td>textarea</td>
+ * <td>JTextArea in a JScrollPane</td>
+ * </tr>
+ * <tr>
+ * <td>input, type file</td>
+ * <td>JTextField</td>
+ * </tr>
+ * </table>
+ *
+ * @author Timothy Prinzing
+ * @author Sunita Mani
+ */
+public class FormView extends ComponentView implements ActionListener {
+
+ /**
+ * If a value attribute is not specified for a FORM input element
+ * of type "submit", then this default string is used.
+ *
+ * @deprecated As of 1.3, value now comes from UIManager property
+ * FormView.submitButtonText
+ */
+ @Deprecated
+ public static final String SUBMIT = new String("Submit Query");
+ /**
+ * If a value attribute is not specified for a FORM input element
+ * of type "reset", then this default string is used.
+ *
+ * @deprecated As of 1.3, value comes from UIManager UIManager property
+ * FormView.resetButtonText
+ */
+ @Deprecated
+ public static final String RESET = new String("Reset");
+
+ /**
+ * Document attribute name for storing POST data. JEditorPane.getPostData()
+ * uses the same name, should be kept in sync.
+ */
+ final static String PostDataProperty = "javax.swing.JEditorPane.postdata";
+
+ /**
+ * Used to indicate if the maximum span should be the same as the
+ * preferred span. This is used so that the Component's size doesn't
+ * change if there is extra room on a line. The first bit is used for
+ * the X direction, and the second for the y direction.
+ */
+ private short maxIsPreferred;
+
+ /**
+ * Creates a new FormView object.
+ *
+ * @param elem the element to decorate
+ */
+ public FormView(Element elem) {
+ super(elem);
+ }
+
+ /**
+ * Create the component. This is basically a
+ * big switch statement based upon the tag type
+ * and html attributes of the associated element.
+ */
+ protected Component createComponent() {
+ AttributeSet attr = getElement().getAttributes();
+ HTML.Tag t = (HTML.Tag)
+ attr.getAttribute(StyleConstants.NameAttribute);
+ JComponent c = null;
+ Object model = attr.getAttribute(StyleConstants.ModelAttribute);
+ if (t == HTML.Tag.INPUT) {
+ c = createInputComponent(attr, model);
+ } else if (t == HTML.Tag.SELECT) {
+
+ if (model instanceof OptionListModel) {
+
+ JList list = new JList((ListModel) model);
+ int size = HTML.getIntegerAttributeValue(attr,
+ HTML.Attribute.SIZE,
+ 1);
+ list.setVisibleRowCount(size);
+ list.setSelectionModel((ListSelectionModel)model);
+ c = new JScrollPane(list);
+ } else {
+ c = new JComboBox((ComboBoxModel) model);
+ maxIsPreferred = 3;
+ }
+ } else if (t == HTML.Tag.TEXTAREA) {
+ JTextArea area = new JTextArea((Document) model);
+ int rows = HTML.getIntegerAttributeValue(attr,
+ HTML.Attribute.ROWS,
+ 1);
+ area.setRows(rows);
+ int cols = HTML.getIntegerAttributeValue(attr,
+ HTML.Attribute.COLS,
+ 20);
+ maxIsPreferred = 3;
+ area.setColumns(cols);
+ c = new JScrollPane(area,
+ JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+ JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+ }
+
+ if (c != null) {
+ c.setAlignmentY(1.0f);
+ }
+ return c;
+ }
+
+
+ /**
+ * Creates a component for an <INPUT> element based on the
+ * value of the "type" attribute.
+ *
+ * @param set of attributes associated with the <INPUT> element.
+ * @param model the value of the StyleConstants.ModelAttribute
+ * @return the component.
+ */
+ private JComponent createInputComponent(AttributeSet attr, Object model) {
+ JComponent c = null;
+ String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
+
+ if (type.equals("submit") || type.equals("reset")) {
+ String value = (String)
+ attr.getAttribute(HTML.Attribute.VALUE);
+ if (value == null) {
+ if (type.equals("submit")) {
+ value = UIManager.getString("FormView.submitButtonText");
+ } else {
+ value = UIManager.getString("FormView.resetButtonText");
+ }
+ }
+ JButton button = new JButton(value);
+ if (model != null) {
+ button.setModel((ButtonModel)model);
+ button.addActionListener(this);
+ }
+ c = button;
+ maxIsPreferred = 3;
+ } else if (type.equals("image")) {
+ String srcAtt = (String) attr.getAttribute(HTML.Attribute.SRC);
+ JButton button;
+ try {
+ URL base = ((HTMLDocument)getElement().getDocument()).getBase();
+ URL srcURL = new URL(base, srcAtt);
+ Icon icon = new ImageIcon(srcURL);
+ button = new JButton(icon);
+ } catch (MalformedURLException e) {
+ button = new JButton(srcAtt);
+ }
+ if (model != null) {
+ button.setModel((ButtonModel)model);
+ button.addMouseListener(new MouseEventListener());
+ }
+ c = button;
+ maxIsPreferred = 3;
+ } else if (type.equals("checkbox")) {
+ c = new JCheckBox();
+ if (model != null) {
+ ((JCheckBox)c).setModel((JToggleButton.ToggleButtonModel) model);
+ }
+ maxIsPreferred = 3;
+ } else if (type.equals("radio")) {
+ c = new JRadioButton();
+ if (model != null) {
+ ((JRadioButton)c).setModel((JToggleButton.ToggleButtonModel)model);
+ }
+ maxIsPreferred = 3;
+ } else if (type.equals("text")) {
+ int size = HTML.getIntegerAttributeValue(attr,
+ HTML.Attribute.SIZE,
+ -1);
+ JTextField field;
+ if (size > 0) {
+ field = new JTextField();
+ field.setColumns(size);
+ }
+ else {
+ field = new JTextField();
+ field.setColumns(20);
+ }
+ c = field;
+ if (model != null) {
+ field.setDocument((Document) model);
+ }
+ field.addActionListener(this);
+ maxIsPreferred = 3;
+ } else if (type.equals("password")) {
+ JPasswordField field = new JPasswordField();
+ c = field;
+ if (model != null) {
+ field.setDocument((Document) model);
+ }
+ int size = HTML.getIntegerAttributeValue(attr,
+ HTML.Attribute.SIZE,
+ -1);
+ field.setColumns((size > 0) ? size : 20);
+ field.addActionListener(this);
+ maxIsPreferred = 3;
+ } else if (type.equals("file")) {
+ JTextField field = new JTextField();
+ if (model != null) {
+ field.setDocument((Document)model);
+ }
+ int size = HTML.getIntegerAttributeValue(attr, HTML.Attribute.SIZE,
+ -1);
+ field.setColumns((size > 0) ? size : 20);
+ JButton browseButton = new JButton(UIManager.getString
+ ("FormView.browseFileButtonText"));
+ Box box = Box.createHorizontalBox();
+ box.add(field);
+ box.add(Box.createHorizontalStrut(5));
+ box.add(browseButton);
+ browseButton.addActionListener(new BrowseFileAction(
+ attr, (Document)model));
+ c = box;
+ maxIsPreferred = 3;
+ }
+ return c;
+ }
+
+
+ /**
+ * Determines the maximum span for this view along an
+ * axis. For certain components, the maximum and preferred span are the
+ * same. For others this will return the value
+ * returned by Component.getMaximumSize along the
+ * axis of interest.
+ *
+ * @param axis may be either View.X_AXIS or View.Y_AXIS
+ * @return the span the view would like to be rendered into >= 0.
+ * Typically the view is told to render into the span
+ * that is returned, although there is no guarantee.
+ * The parent may choose to resize or break the view.
+ * @exception IllegalArgumentException for an invalid axis
+ */
+ public float getMaximumSpan(int axis) {
+ switch (axis) {
+ case View.X_AXIS:
+ if ((maxIsPreferred & 1) == 1) {
+ super.getMaximumSpan(axis);
+ return getPreferredSpan(axis);
+ }
+ return super.getMaximumSpan(axis);
+ case View.Y_AXIS:
+ if ((maxIsPreferred & 2) == 2) {
+ super.getMaximumSpan(axis);
+ return getPreferredSpan(axis);
+ }
+ return super.getMaximumSpan(axis);
+ default:
+ break;
+ }
+ return super.getMaximumSpan(axis);
+ }
+
+
+ /**
+ * Responsible for processeing the ActionEvent.
+ * If the element associated with the FormView,
+ * has a type of "submit", "reset", "text" or "password"
+ * then the action is processed. In the case of a "submit"
+ * the form is submitted. In the case of a "reset"
+ * the form is reset to its original state.
+ * In the case of "text" or "password", if the
+ * element is the last one of type "text" or "password",
+ * the form is submitted. Otherwise, focus is transferred
+ * to the next component in the form.
+ *
+ * @param evt the ActionEvent.
+ */
+ public void actionPerformed(ActionEvent evt) {
+ Element element = getElement();
+ StringBuffer dataBuffer = new StringBuffer();
+ HTMLDocument doc = (HTMLDocument)getDocument();
+ AttributeSet attr = element.getAttributes();
+
+ String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
+
+ if (type.equals("submit")) {
+ getFormData(dataBuffer);
+ submitData(dataBuffer.toString());
+ } else if (type.equals("reset")) {
+ resetForm();
+ } else if (type.equals("text") || type.equals("password")) {
+ if (isLastTextOrPasswordField()) {
+ getFormData(dataBuffer);
+ submitData(dataBuffer.toString());
+ } else {
+ getComponent().transferFocus();
+ }
+ }
+ }
+
+
+ /**
+ * This method is responsible for submitting the form data.
+ * A thread is forked to undertake the submission.
+ */
+ protected void submitData(String data) {
+ Element form = getFormElement();
+ AttributeSet attrs = form.getAttributes();
+ HTMLDocument doc = (HTMLDocument) form.getDocument();
+ URL base = doc.getBase();
+
+ String target = (String) attrs.getAttribute(HTML.Attribute.TARGET);
+ if (target == null) {
+ target = "_self";
+ }
+
+ String method = (String) attrs.getAttribute(HTML.Attribute.METHOD);
+ if (method == null) {
+ method = "GET";
+ }
+ method = method.toLowerCase();
+ boolean isPostMethod = method.equals("post");
+ if (isPostMethod) {
+ storePostData(doc, target, data);
+ }
+
+ String action = (String) attrs.getAttribute(HTML.Attribute.ACTION);
+ URL actionURL;
+ try {
+ actionURL = (action == null)
+ ? new URL(base.getProtocol(), base.getHost(),
+ base.getPort(), base.getFile())
+ : new URL(base, action);
+ if (!isPostMethod) {
+ String query = data.toString();
+ actionURL = new URL(actionURL + "?" + query);
+ }
+ } catch (MalformedURLException e) {
+ actionURL = null;
+ }
+ final JEditorPane c = (JEditorPane) getContainer();
+ HTMLEditorKit kit = (HTMLEditorKit) c.getEditorKit();
+
+ FormSubmitEvent formEvent = null;
+ if (!kit.isAutoFormSubmission() || doc.isFrameDocument()) {
+ FormSubmitEvent.MethodType methodType = isPostMethod
+ ? FormSubmitEvent.MethodType.POST
+ : FormSubmitEvent.MethodType.GET;
+ formEvent = new FormSubmitEvent(
+ FormView.this, HyperlinkEvent.EventType.ACTIVATED,
+ actionURL, form, target, methodType, data);
+
+ }
+ // setPage() may take significant time so schedule it to run later.
+ final FormSubmitEvent fse = formEvent;
+ final URL url = actionURL;
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ if (fse != null) {
+ c.fireHyperlinkUpdate(fse);
+ } else {
+ try {
+ c.setPage(url);
+ } catch (IOException e) {
+ UIManager.getLookAndFeel().provideErrorFeedback(c);
+ }
+ }
+ }
+ });
+ }
+
+ private void storePostData(HTMLDocument doc, String target, String data) {
+
+ /* POST data is stored into the document property named by constant
+ * PostDataProperty from where it is later retrieved by method
+ * JEditorPane.getPostData(). If the current document is in a frame,
+ * the data is initially put into the toplevel (frameset) document
+ * property (named <PostDataProperty>.<Target frame name>). It is the
+ * responsibility of FrameView which updates the target frame
+ * to move data from the frameset document property into the frame
+ * document property.
+ */
+
+ Document propDoc = doc;
+ String propName = PostDataProperty;
+
+ if (doc.isFrameDocument()) {
+ // find the top-most JEditorPane holding the frameset view.
+ FrameView.FrameEditorPane p =
+ (FrameView.FrameEditorPane) getContainer();
+ FrameView v = p.getFrameView();
+ JEditorPane c = v.getOutermostJEditorPane();
+ if (c != null) {
+ propDoc = c.getDocument();
+ propName += ("." + target);
+ }
+ }
+
+ propDoc.putProperty(propName, data);
+ }
+
+ /**
+ * MouseEventListener class to handle form submissions when
+ * an input with type equal to image is clicked on.
+ * A MouseListener is necessary since along with the image
+ * data the coordinates associated with the mouse click
+ * need to be submitted.
+ */
+ protected class MouseEventListener extends MouseAdapter {
+
+ public void mouseReleased(MouseEvent evt) {
+ String imageData = getImageData(evt.getPoint());
+ imageSubmit(imageData);
+ }
+ }
+
+ /**
+ * This method is called to submit a form in response
+ * to a click on an image -- an <INPUT> form
+ * element of type "image".
+ *
+ * @param imageData the mouse click coordinates.
+ */
+ protected void imageSubmit(String imageData) {
+
+ StringBuffer dataBuffer = new StringBuffer();
+ Element elem = getElement();
+ HTMLDocument hdoc = (HTMLDocument)elem.getDocument();
+ getFormData(dataBuffer);
+ if (dataBuffer.length() > 0) {
+ dataBuffer.append('&');
+ }
+ dataBuffer.append(imageData);
+ submitData(dataBuffer.toString());
+ return;
+ }
+
+ /**
+ * Extracts the value of the name attribute
+ * associated with the input element of type
+ * image. If name is defined it is encoded using
+ * the URLEncoder.encode() method and the
+ * image data is returned in the following format:
+ * name + ".x" +"="+ x +"&"+ name +".y"+"="+ y
+ * otherwise,
+ * "x="+ x +"&y="+ y
+ *
+ * @param point associated with the mouse click.
+ * @return the image data.
+ */
+ private String getImageData(Point point) {
+
+ String mouseCoords = point.x + ":" + point.y;
+ int sep = mouseCoords.indexOf(':');
+ String x = mouseCoords.substring(0, sep);
+ String y = mouseCoords.substring(++sep);
+ String name = (String) getElement().getAttributes().getAttribute(HTML.Attribute.NAME);
+
+ String data;
+ if (name == null || name.equals("")) {
+ data = "x="+ x +"&y="+ y;
+ } else {
+ name = URLEncoder.encode(name);
+ data = name + ".x" +"="+ x +"&"+ name +".y"+"="+ y;
+ }
+ return data;
+ }
+
+
+ /**
+ * The following methods provide functionality required to
+ * iterate over a the elements of the form and in the case
+ * of a form submission, extract the data from each model
+ * that is associated with each form element, and in the
+ * case of reset, reinitialize the each model to its
+ * initial state.
+ */
+
+
+ /**
+ * Returns the Element representing the <code>FORM</code>.
+ */
+ private Element getFormElement() {
+ Element elem = getElement();
+ while (elem != null) {
+ if (elem.getAttributes().getAttribute
+ (StyleConstants.NameAttribute) == HTML.Tag.FORM) {
+ return elem;
+ }
+ elem = elem.getParentElement();
+ }
+ return null;
+ }
+
+ /**
+ * Iterates over the
+ * element hierarchy, extracting data from the
+ * models associated with the relevant form elements.
+ * "Relevant" means the form elements that are part
+ * of the same form whose element triggered the submit
+ * action.
+ *
+ * @param buffer the buffer that contains that data to submit
+ * @param targetElement the element that triggered the
+ * form submission
+ */
+ void getFormData(StringBuffer buffer) {
+ Element formE = getFormElement();
+ if (formE != null) {
+ ElementIterator it = new ElementIterator(formE);
+ Element next;
+
+ while ((next = it.next()) != null) {
+ if (isControl(next)) {
+ String type = (String)next.getAttributes().getAttribute
+ (HTML.Attribute.TYPE);
+
+ if (type != null && type.equals("submit") &&
+ next != getElement()) {
+ // do nothing - this submit isnt the trigger
+ } else if (type == null || !type.equals("image")) {
+ // images only result in data if they triggered
+ // the submit and they require that the mouse click
+ // coords be appended to the data. Hence its
+ // processing is handled by the view.
+ loadElementDataIntoBuffer(next, buffer);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Loads the data
+ * associated with the element into the buffer.
+ * The format in which data is appended depends
+ * on the type of the form element. Essentially
+ * data is loaded in name/value pairs.
+ *
+ */
+ private void loadElementDataIntoBuffer(Element elem, StringBuffer buffer) {
+
+ AttributeSet attr = elem.getAttributes();
+ String name = (String)attr.getAttribute(HTML.Attribute.NAME);
+ if (name == null) {
+ return;
+ }
+ String value = null;
+ HTML.Tag tag = (HTML.Tag)elem.getAttributes().getAttribute
+ (StyleConstants.NameAttribute);
+
+ if (tag == HTML.Tag.INPUT) {
+ value = getInputElementData(attr);
+ } else if (tag == HTML.Tag.TEXTAREA) {
+ value = getTextAreaData(attr);
+ } else if (tag == HTML.Tag.SELECT) {
+ loadSelectData(attr, buffer);
+ }
+
+ if (name != null && value != null) {
+ appendBuffer(buffer, name, value);
+ }
+ }
+
+
+ /**
+ * Returns the data associated with an <INPUT> form
+ * element. The value of "type" attributes is
+ * used to determine the type of the model associated
+ * with the element and then the relevant data is
+ * extracted.
+ */
+ private String getInputElementData(AttributeSet attr) {
+
+ Object model = attr.getAttribute(StyleConstants.ModelAttribute);
+ String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
+ String value = null;
+
+ if (type.equals("text") || type.equals("password")) {
+ Document doc = (Document)model;
+ try {
+ value = doc.getText(0, doc.getLength());
+ } catch (BadLocationException e) {
+ value = null;
+ }
+ } else if (type.equals("submit") || type.equals("hidden")) {
+ value = (String) attr.getAttribute(HTML.Attribute.VALUE);
+ if (value == null) {
+ value = "";
+ }
+ } else if (type.equals("radio") || type.equals("checkbox")) {
+ ButtonModel m = (ButtonModel)model;
+ if (m.isSelected()) {
+ value = (String) attr.getAttribute(HTML.Attribute.VALUE);
+ if (value == null) {
+ value = "on";
+ }
+ }
+ } else if (type.equals("file")) {
+ Document doc = (Document)model;
+ String path;
+
+ try {
+ path = doc.getText(0, doc.getLength());
+ } catch (BadLocationException e) {
+ path = null;
+ }
+ if (path != null && path.length() > 0) {
+ value = path;
+/*
+
+ try {
+ Reader reader = new BufferedReader(new FileReader(path));
+ StringBuffer buffer = new StringBuffer();
+ char[] cBuff = new char[1024];
+ int read;
+
+ try {
+ while ((read = reader.read(cBuff)) != -1) {
+ buffer.append(cBuff, 0, read);
+ }
+ } catch (IOException ioe) {
+ buffer = null;
+ }
+ try {
+ reader.close();
+ } catch (IOException ioe) {}
+ if (buffer != null) {
+ value = buffer.toString();
+ }
+ } catch (IOException ioe) {}
+*/
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Returns the data associated with the <TEXTAREA> form
+ * element. This is done by getting the text stored in the
+ * Document model.
+ */
+ private String getTextAreaData(AttributeSet attr) {
+ Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
+ try {
+ return doc.getText(0, doc.getLength());
+ } catch (BadLocationException e) {
+ return null;
+ }
+ }
+
+
+ /**
+ * Loads the buffer with the data associated with the Select
+ * form element. Basically, only items that are selected
+ * and have their name attribute set are added to the buffer.
+ */
+ private void loadSelectData(AttributeSet attr, StringBuffer buffer) {
+
+ String name = (String)attr.getAttribute(HTML.Attribute.NAME);
+ if (name == null) {
+ return;
+ }
+ Object m = attr.getAttribute(StyleConstants.ModelAttribute);
+ if (m instanceof OptionListModel) {
+ OptionListModel model = (OptionListModel)m;
+
+ for (int i = 0; i < model.getSize(); i++) {
+ if (model.isSelectedIndex(i)) {
+ Option option = (Option) model.getElementAt(i);
+ appendBuffer(buffer, name, option.getValue());
+ }
+ }
+ } else if (m instanceof ComboBoxModel) {
+ ComboBoxModel model = (ComboBoxModel)m;
+ Option option = (Option)model.getSelectedItem();
+ if (option != null) {
+ appendBuffer(buffer, name, option.getValue());
+ }
+ }
+ }
+
+ /**
+ * Appends name / value pairs into the
+ * buffer. Both names and values are encoded using the
+ * URLEncoder.encode() method before being added to the
+ * buffer.
+ */
+ private void appendBuffer(StringBuffer buffer, String name, String value) {
+ if (buffer.length() > 0) {
+ buffer.append('&');
+ }
+ String encodedName = URLEncoder.encode(name);
+ buffer.append(encodedName);
+ buffer.append('=');
+ String encodedValue = URLEncoder.encode(value);
+ buffer.append(encodedValue);
+ }
+
+ /**
+ * Returns true if the Element <code>elem</code> represents a control.
+ */
+ private boolean isControl(Element elem) {
+ return elem.isLeaf();
+ }
+
+ /**
+ * Iterates over the element hierarchy to determine if
+ * the element parameter, which is assumed to be an
+ * <INPUT> element of type password or text, is the last
+ * one of either kind, in the form to which it belongs.
+ */
+ boolean isLastTextOrPasswordField() {
+ Element parent = getFormElement();
+ Element elem = getElement();
+
+ if (parent != null) {
+ ElementIterator it = new ElementIterator(parent);
+ Element next;
+ boolean found = false;
+
+ while ((next = it.next()) != null) {
+ if (next == elem) {
+ found = true;
+ }
+ else if (found && isControl(next)) {
+ AttributeSet elemAttr = next.getAttributes();
+
+ if (HTMLDocument.matchNameAttribute
+ (elemAttr, HTML.Tag.INPUT)) {
+ String type = (String)elemAttr.getAttribute
+ (HTML.Attribute.TYPE);
+
+ if ("text".equals(type) || "password".equals(type)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Resets the form
+ * to its initial state by reinitializing the models
+ * associated with each form element to their initial
+ * values.
+ *
+ * param elem the element that triggered the reset
+ */
+ void resetForm() {
+ Element parent = getFormElement();
+
+ if (parent != null) {
+ ElementIterator it = new ElementIterator(parent);
+ Element next;
+
+ while((next = it.next()) != null) {
+ if (isControl(next)) {
+ AttributeSet elemAttr = next.getAttributes();
+ Object m = elemAttr.getAttribute(StyleConstants.
+ ModelAttribute);
+ if (m instanceof TextAreaDocument) {
+ TextAreaDocument doc = (TextAreaDocument)m;
+ doc.reset();
+ } else if (m instanceof PlainDocument) {
+ try {
+ PlainDocument doc = (PlainDocument)m;
+ doc.remove(0, doc.getLength());
+ if (HTMLDocument.matchNameAttribute
+ (elemAttr, HTML.Tag.INPUT)) {
+ String value = (String)elemAttr.
+ getAttribute(HTML.Attribute.VALUE);
+ if (value != null) {
+ doc.insertString(0, value, null);
+ }
+ }
+ } catch (BadLocationException e) {
+ }
+ } else if (m instanceof OptionListModel) {
+ OptionListModel model = (OptionListModel) m;
+ int size = model.getSize();
+ for (int i = 0; i < size; i++) {
+ model.removeIndexInterval(i, i);
+ }
+ BitSet selectionRange = model.getInitialSelection();
+ for (int i = 0; i < selectionRange.size(); i++) {
+ if (selectionRange.get(i)) {
+ model.addSelectionInterval(i, i);
+ }
+ }
+ } else if (m instanceof OptionComboBoxModel) {
+ OptionComboBoxModel model = (OptionComboBoxModel) m;
+ Option option = model.getInitialSelection();
+ if (option != null) {
+ model.setSelectedItem(option);
+ }
+ } else if (m instanceof JToggleButton.ToggleButtonModel) {
+ boolean checked = ((String)elemAttr.getAttribute
+ (HTML.Attribute.CHECKED) != null);
+ JToggleButton.ToggleButtonModel model =
+ (JToggleButton.ToggleButtonModel)m;
+ model.setSelected(checked);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * BrowseFileAction is used for input type == file. When the user
+ * clicks the button a JFileChooser is brought up allowing the user
+ * to select a file in the file system. The resulting path to the selected
+ * file is set in the text field (actually an instance of Document).
+ */
+ private class BrowseFileAction implements ActionListener {
+ private AttributeSet attrs;
+ private Document model;
+
+ BrowseFileAction(AttributeSet attrs, Document model) {
+ this.attrs = attrs;
+ this.model = model;
+ }
+
+ public void actionPerformed(ActionEvent ae) {
+ // PENDING: When mime support is added to JFileChooser use the
+ // accept value of attrs.
+ JFileChooser fc = new JFileChooser();
+ fc.setMultiSelectionEnabled(false);
+ if (fc.showOpenDialog(getContainer()) ==
+ JFileChooser.APPROVE_OPTION) {
+ File selected = fc.getSelectedFile();
+
+ if (selected != null) {
+ try {
+ if (model.getLength() > 0) {
+ model.remove(0, model.getLength());
+ }
+ model.insertString(0, selected.getPath(), null);
+ } catch (BadLocationException ble) {}
+ }
+ }
+ }
+ }
+}