001 /*
002 * Copyright 1998-2006 Sun Microsystems, Inc. All Rights Reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation. Sun designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Sun in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022 * CA 95054 USA or visit www.sun.com if you need additional information or
023 * have any questions.
024 */
025 package javax.swing.text.html;
026
027 import java.net.*;
028 import java.io.*;
029 import java.awt.*;
030 import java.awt.event.*;
031 import java.util.*;
032 import javax.swing.*;
033 import javax.swing.event.*;
034 import javax.swing.text.*;
035
036 /**
037 * Component decorator that implements the view interface
038 * for form elements, <input>, <textarea>,
039 * and <select>. The model for the component is stored
040 * as an attribute of the the element (using StyleConstants.ModelAttribute),
041 * and is used to build the component of the view. The type
042 * of the model is assumed to of the type that would be set by
043 * <code>HTMLDocument.HTMLReader.FormAction</code>. If there are
044 * multiple views mapped over the document, they will share the
045 * embedded component models.
046 * <p>
047 * The following table shows what components get built
048 * by this view.
049 * <table summary="shows what components get built by this view">
050 * <tr>
051 * <th>Element Type</th>
052 * <th>Component built</th>
053 * </tr>
054 * <tr>
055 * <td>input, type button</td>
056 * <td>JButton</td>
057 * </tr>
058 * <tr>
059 * <td>input, type checkbox</td>
060 * <td>JCheckBox</td>
061 * </tr>
062 * <tr>
063 * <td>input, type image</td>
064 * <td>JButton</td>
065 * </tr>
066 * <tr>
067 * <td>input, type password</td>
068 * <td>JPasswordField</td>
069 * </tr>
070 * <tr>
071 * <td>input, type radio</td>
072 * <td>JRadioButton</td>
073 * </tr>
074 * <tr>
075 * <td>input, type reset</td>
076 * <td>JButton</td>
077 * </tr>
078 * <tr>
079 * <td>input, type submit</td>
080 * <td>JButton</td>
081 * </tr>
082 * <tr>
083 * <td>input, type text</td>
084 * <td>JTextField</td>
085 * </tr>
086 * <tr>
087 * <td>select, size > 1 or multiple attribute defined</td>
088 * <td>JList in a JScrollPane</td>
089 * </tr>
090 * <tr>
091 * <td>select, size unspecified or 1</td>
092 * <td>JComboBox</td>
093 * </tr>
094 * <tr>
095 * <td>textarea</td>
096 * <td>JTextArea in a JScrollPane</td>
097 * </tr>
098 * <tr>
099 * <td>input, type file</td>
100 * <td>JTextField</td>
101 * </tr>
102 * </table>
103 *
104 * @author Timothy Prinzing
105 * @author Sunita Mani
106 * @version 1.35 05/05/07
107 */
108 public class FormView extends ComponentView implements ActionListener {
109
110 /**
111 * If a value attribute is not specified for a FORM input element
112 * of type "submit", then this default string is used.
113 *
114 * @deprecated As of 1.3, value now comes from UIManager property
115 * FormView.submitButtonText
116 */
117 @Deprecated
118 public static final String SUBMIT = new String("Submit Query");
119 /**
120 * If a value attribute is not specified for a FORM input element
121 * of type "reset", then this default string is used.
122 *
123 * @deprecated As of 1.3, value comes from UIManager UIManager property
124 * FormView.resetButtonText
125 */
126 @Deprecated
127 public static final String RESET = new String("Reset");
128
129 /**
130 * Document attribute name for storing POST data. JEditorPane.getPostData()
131 * uses the same name, should be kept in sync.
132 */
133 final static String PostDataProperty = "javax.swing.JEditorPane.postdata";
134
135 /**
136 * Used to indicate if the maximum span should be the same as the
137 * preferred span. This is used so that the Component's size doesn't
138 * change if there is extra room on a line. The first bit is used for
139 * the X direction, and the second for the y direction.
140 */
141 private short maxIsPreferred;
142
143 /**
144 * Creates a new FormView object.
145 *
146 * @param elem the element to decorate
147 */
148 public FormView(Element elem) {
149 super (elem);
150 }
151
152 /**
153 * Create the component. This is basically a
154 * big switch statement based upon the tag type
155 * and html attributes of the associated element.
156 */
157 protected Component createComponent() {
158 AttributeSet attr = getElement().getAttributes();
159 HTML.Tag t = (HTML.Tag) attr
160 .getAttribute(StyleConstants.NameAttribute);
161 JComponent c = null;
162 Object model = attr.getAttribute(StyleConstants.ModelAttribute);
163 if (t == HTML.Tag.INPUT) {
164 c = createInputComponent(attr, model);
165 } else if (t == HTML.Tag.SELECT) {
166
167 if (model instanceof OptionListModel) {
168
169 JList list = new JList((ListModel) model);
170 int size = HTML.getIntegerAttributeValue(attr,
171 HTML.Attribute.SIZE, 1);
172 list.setVisibleRowCount(size);
173 list.setSelectionModel((ListSelectionModel) model);
174 c = new JScrollPane(list);
175 } else {
176 c = new JComboBox((ComboBoxModel) model);
177 maxIsPreferred = 3;
178 }
179 } else if (t == HTML.Tag.TEXTAREA) {
180 JTextArea area = new JTextArea((Document) model);
181 int rows = HTML.getIntegerAttributeValue(attr,
182 HTML.Attribute.ROWS, 1);
183 area.setRows(rows);
184 int cols = HTML.getIntegerAttributeValue(attr,
185 HTML.Attribute.COLS, 20);
186 maxIsPreferred = 3;
187 area.setColumns(cols);
188 c = new JScrollPane(area,
189 JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
190 JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
191 }
192
193 if (c != null) {
194 c.setAlignmentY(1.0f);
195 }
196 return c;
197 }
198
199 /**
200 * Creates a component for an <INPUT> element based on the
201 * value of the "type" attribute.
202 *
203 * @param set of attributes associated with the <INPUT> element.
204 * @param model the value of the StyleConstants.ModelAttribute
205 * @return the component.
206 */
207 private JComponent createInputComponent(AttributeSet attr,
208 Object model) {
209 JComponent c = null;
210 String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
211
212 if (type.equals("submit") || type.equals("reset")) {
213 String value = (String) attr
214 .getAttribute(HTML.Attribute.VALUE);
215 if (value == null) {
216 if (type.equals("submit")) {
217 value = UIManager
218 .getString("FormView.submitButtonText");
219 } else {
220 value = UIManager
221 .getString("FormView.resetButtonText");
222 }
223 }
224 JButton button = new JButton(value);
225 if (model != null) {
226 button.setModel((ButtonModel) model);
227 button.addActionListener(this );
228 }
229 c = button;
230 maxIsPreferred = 3;
231 } else if (type.equals("image")) {
232 String srcAtt = (String) attr
233 .getAttribute(HTML.Attribute.SRC);
234 JButton button;
235 try {
236 URL base = ((HTMLDocument) getElement().getDocument())
237 .getBase();
238 URL srcURL = new URL(base, srcAtt);
239 Icon icon = new ImageIcon(srcURL);
240 button = new JButton(icon);
241 } catch (MalformedURLException e) {
242 button = new JButton(srcAtt);
243 }
244 if (model != null) {
245 button.setModel((ButtonModel) model);
246 button.addMouseListener(new MouseEventListener());
247 }
248 c = button;
249 maxIsPreferred = 3;
250 } else if (type.equals("checkbox")) {
251 c = new JCheckBox();
252 if (model != null) {
253 ((JCheckBox) c)
254 .setModel((JToggleButton.ToggleButtonModel) model);
255 }
256 maxIsPreferred = 3;
257 } else if (type.equals("radio")) {
258 c = new JRadioButton();
259 if (model != null) {
260 ((JRadioButton) c)
261 .setModel((JToggleButton.ToggleButtonModel) model);
262 }
263 maxIsPreferred = 3;
264 } else if (type.equals("text")) {
265 int size = HTML.getIntegerAttributeValue(attr,
266 HTML.Attribute.SIZE, -1);
267 JTextField field;
268 if (size > 0) {
269 field = new JTextField();
270 field.setColumns(size);
271 } else {
272 field = new JTextField();
273 field.setColumns(20);
274 }
275 c = field;
276 if (model != null) {
277 field.setDocument((Document) model);
278 }
279 field.addActionListener(this );
280 maxIsPreferred = 3;
281 } else if (type.equals("password")) {
282 JPasswordField field = new JPasswordField();
283 c = field;
284 if (model != null) {
285 field.setDocument((Document) model);
286 }
287 int size = HTML.getIntegerAttributeValue(attr,
288 HTML.Attribute.SIZE, -1);
289 field.setColumns((size > 0) ? size : 20);
290 field.addActionListener(this );
291 maxIsPreferred = 3;
292 } else if (type.equals("file")) {
293 JTextField field = new JTextField();
294 if (model != null) {
295 field.setDocument((Document) model);
296 }
297 int size = HTML.getIntegerAttributeValue(attr,
298 HTML.Attribute.SIZE, -1);
299 field.setColumns((size > 0) ? size : 20);
300 JButton browseButton = new JButton(UIManager
301 .getString("FormView.browseFileButtonText"));
302 Box box = Box.createHorizontalBox();
303 box.add(field);
304 box.add(Box.createHorizontalStrut(5));
305 box.add(browseButton);
306 browseButton.addActionListener(new BrowseFileAction(attr,
307 (Document) model));
308 c = box;
309 maxIsPreferred = 3;
310 }
311 return c;
312 }
313
314 /**
315 * Determines the maximum span for this view along an
316 * axis. For certain components, the maximum and preferred span are the
317 * same. For others this will return the value
318 * returned by Component.getMaximumSize along the
319 * axis of interest.
320 *
321 * @param axis may be either View.X_AXIS or View.Y_AXIS
322 * @return the span the view would like to be rendered into >= 0.
323 * Typically the view is told to render into the span
324 * that is returned, although there is no guarantee.
325 * The parent may choose to resize or break the view.
326 * @exception IllegalArgumentException for an invalid axis
327 */
328 public float getMaximumSpan(int axis) {
329 switch (axis) {
330 case View.X_AXIS:
331 if ((maxIsPreferred & 1) == 1) {
332 super .getMaximumSpan(axis);
333 return getPreferredSpan(axis);
334 }
335 return super .getMaximumSpan(axis);
336 case View.Y_AXIS:
337 if ((maxIsPreferred & 2) == 2) {
338 super .getMaximumSpan(axis);
339 return getPreferredSpan(axis);
340 }
341 return super .getMaximumSpan(axis);
342 default:
343 break;
344 }
345 return super .getMaximumSpan(axis);
346 }
347
348 /**
349 * Responsible for processeing the ActionEvent.
350 * If the element associated with the FormView,
351 * has a type of "submit", "reset", "text" or "password"
352 * then the action is processed. In the case of a "submit"
353 * the form is submitted. In the case of a "reset"
354 * the form is reset to its original state.
355 * In the case of "text" or "password", if the
356 * element is the last one of type "text" or "password",
357 * the form is submitted. Otherwise, focus is transferred
358 * to the next component in the form.
359 *
360 * @param evt the ActionEvent.
361 */
362 public void actionPerformed(ActionEvent evt) {
363 Element element = getElement();
364 StringBuffer dataBuffer = new StringBuffer();
365 HTMLDocument doc = (HTMLDocument) getDocument();
366 AttributeSet attr = element.getAttributes();
367
368 String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
369
370 if (type.equals("submit")) {
371 getFormData(dataBuffer);
372 submitData(dataBuffer.toString());
373 } else if (type.equals("reset")) {
374 resetForm();
375 } else if (type.equals("text") || type.equals("password")) {
376 if (isLastTextOrPasswordField()) {
377 getFormData(dataBuffer);
378 submitData(dataBuffer.toString());
379 } else {
380 getComponent().transferFocus();
381 }
382 }
383 }
384
385 /**
386 * This method is responsible for submitting the form data.
387 * A thread is forked to undertake the submission.
388 */
389 protected void submitData(String data) {
390 Element form = getFormElement();
391 AttributeSet attrs = form.getAttributes();
392 HTMLDocument doc = (HTMLDocument) form.getDocument();
393 URL base = doc.getBase();
394
395 String target = (String) attrs
396 .getAttribute(HTML.Attribute.TARGET);
397 if (target == null) {
398 target = "_self";
399 }
400
401 String method = (String) attrs
402 .getAttribute(HTML.Attribute.METHOD);
403 if (method == null) {
404 method = "GET";
405 }
406 method = method.toLowerCase();
407 boolean isPostMethod = method.equals("post");
408 if (isPostMethod) {
409 storePostData(doc, target, data);
410 }
411
412 String action = (String) attrs
413 .getAttribute(HTML.Attribute.ACTION);
414 URL actionURL;
415 try {
416 actionURL = (action == null) ? new URL(base.getProtocol(),
417 base.getHost(), base.getPort(), base.getFile())
418 : new URL(base, action);
419 if (!isPostMethod) {
420 String query = data.toString();
421 actionURL = new URL(actionURL + "?" + query);
422 }
423 } catch (MalformedURLException e) {
424 actionURL = null;
425 }
426 final JEditorPane c = (JEditorPane) getContainer();
427 HTMLEditorKit kit = (HTMLEditorKit) c.getEditorKit();
428
429 FormSubmitEvent formEvent = null;
430 if (!kit.isAutoFormSubmission() || doc.isFrameDocument()) {
431 FormSubmitEvent.MethodType methodType = isPostMethod ? FormSubmitEvent.MethodType.POST
432 : FormSubmitEvent.MethodType.GET;
433 formEvent = new FormSubmitEvent(FormView.this ,
434 HyperlinkEvent.EventType.ACTIVATED, actionURL,
435 form, target, methodType, data);
436
437 }
438 // setPage() may take significant time so schedule it to run later.
439 final FormSubmitEvent fse = formEvent;
440 final URL url = actionURL;
441 SwingUtilities.invokeLater(new Runnable() {
442 public void run() {
443 if (fse != null) {
444 c.fireHyperlinkUpdate(fse);
445 } else {
446 try {
447 c.setPage(url);
448 } catch (IOException e) {
449 UIManager.getLookAndFeel()
450 .provideErrorFeedback(c);
451 }
452 }
453 }
454 });
455 }
456
457 private void storePostData(HTMLDocument doc, String target,
458 String data) {
459
460 /* POST data is stored into the document property named by constant
461 * PostDataProperty from where it is later retrieved by method
462 * JEditorPane.getPostData(). If the current document is in a frame,
463 * the data is initially put into the toplevel (frameset) document
464 * property (named <PostDataProperty>.<Target frame name>). It is the
465 * responsibility of FrameView which updates the target frame
466 * to move data from the frameset document property into the frame
467 * document property.
468 */
469
470 Document propDoc = doc;
471 String propName = PostDataProperty;
472
473 if (doc.isFrameDocument()) {
474 // find the top-most JEditorPane holding the frameset view.
475 FrameView.FrameEditorPane p = (FrameView.FrameEditorPane) getContainer();
476 FrameView v = p.getFrameView();
477 JEditorPane c = v.getOutermostJEditorPane();
478 if (c != null) {
479 propDoc = c.getDocument();
480 propName += ("." + target);
481 }
482 }
483
484 propDoc.putProperty(propName, data);
485 }
486
487 /**
488 * MouseEventListener class to handle form submissions when
489 * an input with type equal to image is clicked on.
490 * A MouseListener is necessary since along with the image
491 * data the coordinates associated with the mouse click
492 * need to be submitted.
493 */
494 protected class MouseEventListener extends MouseAdapter {
495
496 public void mouseReleased(MouseEvent evt) {
497 String imageData = getImageData(evt.getPoint());
498 imageSubmit(imageData);
499 }
500 }
501
502 /**
503 * This method is called to submit a form in response
504 * to a click on an image -- an <INPUT> form
505 * element of type "image".
506 *
507 * @param imageData the mouse click coordinates.
508 */
509 protected void imageSubmit(String imageData) {
510
511 StringBuffer dataBuffer = new StringBuffer();
512 Element elem = getElement();
513 HTMLDocument hdoc = (HTMLDocument) elem.getDocument();
514 getFormData(dataBuffer);
515 if (dataBuffer.length() > 0) {
516 dataBuffer.append('&');
517 }
518 dataBuffer.append(imageData);
519 submitData(dataBuffer.toString());
520 return;
521 }
522
523 /**
524 * Extracts the value of the name attribute
525 * associated with the input element of type
526 * image. If name is defined it is encoded using
527 * the URLEncoder.encode() method and the
528 * image data is returned in the following format:
529 * name + ".x" +"="+ x +"&"+ name +".y"+"="+ y
530 * otherwise,
531 * "x="+ x +"&y="+ y
532 *
533 * @param point associated with the mouse click.
534 * @return the image data.
535 */
536 private String getImageData(Point point) {
537
538 String mouseCoords = point.x + ":" + point.y;
539 int sep = mouseCoords.indexOf(':');
540 String x = mouseCoords.substring(0, sep);
541 String y = mouseCoords.substring(++sep);
542 String name = (String) getElement().getAttributes()
543 .getAttribute(HTML.Attribute.NAME);
544
545 String data;
546 if (name == null || name.equals("")) {
547 data = "x=" + x + "&y=" + y;
548 } else {
549 name = URLEncoder.encode(name);
550 data = name + ".x" + "=" + x + "&" + name + ".y" + "=" + y;
551 }
552 return data;
553 }
554
555 /**
556 * The following methods provide functionality required to
557 * iterate over a the elements of the form and in the case
558 * of a form submission, extract the data from each model
559 * that is associated with each form element, and in the
560 * case of reset, reinitialize the each model to its
561 * initial state.
562 */
563
564 /**
565 * Returns the Element representing the <code>FORM</code>.
566 */
567 private Element getFormElement() {
568 Element elem = getElement();
569 while (elem != null) {
570 if (elem.getAttributes().getAttribute(
571 StyleConstants.NameAttribute) == HTML.Tag.FORM) {
572 return elem;
573 }
574 elem = elem.getParentElement();
575 }
576 return null;
577 }
578
579 /**
580 * Iterates over the
581 * element hierarchy, extracting data from the
582 * models associated with the relevant form elements.
583 * "Relevant" means the form elements that are part
584 * of the same form whose element triggered the submit
585 * action.
586 *
587 * @param buffer the buffer that contains that data to submit
588 * @param targetElement the element that triggered the
589 * form submission
590 */
591 void getFormData(StringBuffer buffer) {
592 Element formE = getFormElement();
593 if (formE != null) {
594 ElementIterator it = new ElementIterator(formE);
595 Element next;
596
597 while ((next = it.next()) != null) {
598 if (isControl(next)) {
599 String type = (String) next.getAttributes()
600 .getAttribute(HTML.Attribute.TYPE);
601
602 if (type != null && type.equals("submit")
603 && next != getElement()) {
604 // do nothing - this submit isnt the trigger
605 } else if (type == null || !type.equals("image")) {
606 // images only result in data if they triggered
607 // the submit and they require that the mouse click
608 // coords be appended to the data. Hence its
609 // processing is handled by the view.
610 loadElementDataIntoBuffer(next, buffer);
611 }
612 }
613 }
614 }
615 }
616
617 /**
618 * Loads the data
619 * associated with the element into the buffer.
620 * The format in which data is appended depends
621 * on the type of the form element. Essentially
622 * data is loaded in name/value pairs.
623 *
624 */
625 private void loadElementDataIntoBuffer(Element elem,
626 StringBuffer buffer) {
627
628 AttributeSet attr = elem.getAttributes();
629 String name = (String) attr.getAttribute(HTML.Attribute.NAME);
630 if (name == null) {
631 return;
632 }
633 String value = null;
634 HTML.Tag tag = (HTML.Tag) elem.getAttributes().getAttribute(
635 StyleConstants.NameAttribute);
636
637 if (tag == HTML.Tag.INPUT) {
638 value = getInputElementData(attr);
639 } else if (tag == HTML.Tag.TEXTAREA) {
640 value = getTextAreaData(attr);
641 } else if (tag == HTML.Tag.SELECT) {
642 loadSelectData(attr, buffer);
643 }
644
645 if (name != null && value != null) {
646 appendBuffer(buffer, name, value);
647 }
648 }
649
650 /**
651 * Returns the data associated with an <INPUT> form
652 * element. The value of "type" attributes is
653 * used to determine the type of the model associated
654 * with the element and then the relevant data is
655 * extracted.
656 */
657 private String getInputElementData(AttributeSet attr) {
658
659 Object model = attr.getAttribute(StyleConstants.ModelAttribute);
660 String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
661 String value = null;
662
663 if (type.equals("text") || type.equals("password")) {
664 Document doc = (Document) model;
665 try {
666 value = doc.getText(0, doc.getLength());
667 } catch (BadLocationException e) {
668 value = null;
669 }
670 } else if (type.equals("submit") || type.equals("hidden")) {
671 value = (String) attr.getAttribute(HTML.Attribute.VALUE);
672 if (value == null) {
673 value = "";
674 }
675 } else if (type.equals("radio") || type.equals("checkbox")) {
676 ButtonModel m = (ButtonModel) model;
677 if (m.isSelected()) {
678 value = (String) attr
679 .getAttribute(HTML.Attribute.VALUE);
680 if (value == null) {
681 value = "on";
682 }
683 }
684 } else if (type.equals("file")) {
685 Document doc = (Document) model;
686 String path;
687
688 try {
689 path = doc.getText(0, doc.getLength());
690 } catch (BadLocationException e) {
691 path = null;
692 }
693 if (path != null && path.length() > 0) {
694 value = path;
695 /*
696
697 try {
698 Reader reader = new BufferedReader(new FileReader(path));
699 StringBuffer buffer = new StringBuffer();
700 char[] cBuff = new char[1024];
701 int read;
702
703 try {
704 while ((read = reader.read(cBuff)) != -1) {
705 buffer.append(cBuff, 0, read);
706 }
707 } catch (IOException ioe) {
708 buffer = null;
709 }
710 try {
711 reader.close();
712 } catch (IOException ioe) {}
713 if (buffer != null) {
714 value = buffer.toString();
715 }
716 } catch (IOException ioe) {}
717 */
718 }
719 }
720 return value;
721 }
722
723 /**
724 * Returns the data associated with the <TEXTAREA> form
725 * element. This is done by getting the text stored in the
726 * Document model.
727 */
728 private String getTextAreaData(AttributeSet attr) {
729 Document doc = (Document) attr
730 .getAttribute(StyleConstants.ModelAttribute);
731 try {
732 return doc.getText(0, doc.getLength());
733 } catch (BadLocationException e) {
734 return null;
735 }
736 }
737
738 /**
739 * Loads the buffer with the data associated with the Select
740 * form element. Basically, only items that are selected
741 * and have their name attribute set are added to the buffer.
742 */
743 private void loadSelectData(AttributeSet attr, StringBuffer buffer) {
744
745 String name = (String) attr.getAttribute(HTML.Attribute.NAME);
746 if (name == null) {
747 return;
748 }
749 Object m = attr.getAttribute(StyleConstants.ModelAttribute);
750 if (m instanceof OptionListModel) {
751 OptionListModel model = (OptionListModel) m;
752
753 for (int i = 0; i < model.getSize(); i++) {
754 if (model.isSelectedIndex(i)) {
755 Option option = (Option) model.getElementAt(i);
756 appendBuffer(buffer, name, option.getValue());
757 }
758 }
759 } else if (m instanceof ComboBoxModel) {
760 ComboBoxModel model = (ComboBoxModel) m;
761 Option option = (Option) model.getSelectedItem();
762 if (option != null) {
763 appendBuffer(buffer, name, option.getValue());
764 }
765 }
766 }
767
768 /**
769 * Appends name / value pairs into the
770 * buffer. Both names and values are encoded using the
771 * URLEncoder.encode() method before being added to the
772 * buffer.
773 */
774 private void appendBuffer(StringBuffer buffer, String name,
775 String value) {
776 if (buffer.length() > 0) {
777 buffer.append('&');
778 }
779 String encodedName = URLEncoder.encode(name);
780 buffer.append(encodedName);
781 buffer.append('=');
782 String encodedValue = URLEncoder.encode(value);
783 buffer.append(encodedValue);
784 }
785
786 /**
787 * Returns true if the Element <code>elem</code> represents a control.
788 */
789 private boolean isControl(Element elem) {
790 return elem.isLeaf();
791 }
792
793 /**
794 * Iterates over the element hierarchy to determine if
795 * the element parameter, which is assumed to be an
796 * <INPUT> element of type password or text, is the last
797 * one of either kind, in the form to which it belongs.
798 */
799 boolean isLastTextOrPasswordField() {
800 Element parent = getFormElement();
801 Element elem = getElement();
802
803 if (parent != null) {
804 ElementIterator it = new ElementIterator(parent);
805 Element next;
806 boolean found = false;
807
808 while ((next = it.next()) != null) {
809 if (next == elem) {
810 found = true;
811 } else if (found && isControl(next)) {
812 AttributeSet elemAttr = next.getAttributes();
813
814 if (HTMLDocument.matchNameAttribute(elemAttr,
815 HTML.Tag.INPUT)) {
816 String type = (String) elemAttr
817 .getAttribute(HTML.Attribute.TYPE);
818
819 if ("text".equals(type)
820 || "password".equals(type)) {
821 return false;
822 }
823 }
824 }
825 }
826 }
827 return true;
828 }
829
830 /**
831 * Resets the form
832 * to its initial state by reinitializing the models
833 * associated with each form element to their initial
834 * values.
835 *
836 * param elem the element that triggered the reset
837 */
838 void resetForm() {
839 Element parent = getFormElement();
840
841 if (parent != null) {
842 ElementIterator it = new ElementIterator(parent);
843 Element next;
844
845 while ((next = it.next()) != null) {
846 if (isControl(next)) {
847 AttributeSet elemAttr = next.getAttributes();
848 Object m = elemAttr
849 .getAttribute(StyleConstants.ModelAttribute);
850 if (m instanceof TextAreaDocument) {
851 TextAreaDocument doc = (TextAreaDocument) m;
852 doc.reset();
853 } else if (m instanceof PlainDocument) {
854 try {
855 PlainDocument doc = (PlainDocument) m;
856 doc.remove(0, doc.getLength());
857 if (HTMLDocument.matchNameAttribute(
858 elemAttr, HTML.Tag.INPUT)) {
859 String value = (String) elemAttr
860 .getAttribute(HTML.Attribute.VALUE);
861 if (value != null) {
862 doc.insertString(0, value, null);
863 }
864 }
865 } catch (BadLocationException e) {
866 }
867 } else if (m instanceof OptionListModel) {
868 OptionListModel model = (OptionListModel) m;
869 int size = model.getSize();
870 for (int i = 0; i < size; i++) {
871 model.removeIndexInterval(i, i);
872 }
873 BitSet selectionRange = model
874 .getInitialSelection();
875 for (int i = 0; i < selectionRange.size(); i++) {
876 if (selectionRange.get(i)) {
877 model.addSelectionInterval(i, i);
878 }
879 }
880 } else if (m instanceof OptionComboBoxModel) {
881 OptionComboBoxModel model = (OptionComboBoxModel) m;
882 Option option = model.getInitialSelection();
883 if (option != null) {
884 model.setSelectedItem(option);
885 }
886 } else if (m instanceof JToggleButton.ToggleButtonModel) {
887 boolean checked = ((String) elemAttr
888 .getAttribute(HTML.Attribute.CHECKED) != null);
889 JToggleButton.ToggleButtonModel model = (JToggleButton.ToggleButtonModel) m;
890 model.setSelected(checked);
891 }
892 }
893 }
894 }
895 }
896
897 /**
898 * BrowseFileAction is used for input type == file. When the user
899 * clicks the button a JFileChooser is brought up allowing the user
900 * to select a file in the file system. The resulting path to the selected
901 * file is set in the text field (actually an instance of Document).
902 */
903 private class BrowseFileAction implements ActionListener {
904 private AttributeSet attrs;
905 private Document model;
906
907 BrowseFileAction(AttributeSet attrs, Document model) {
908 this .attrs = attrs;
909 this .model = model;
910 }
911
912 public void actionPerformed(ActionEvent ae) {
913 // PENDING: When mime support is added to JFileChooser use the
914 // accept value of attrs.
915 JFileChooser fc = new JFileChooser();
916 fc.setMultiSelectionEnabled(false);
917 if (fc.showOpenDialog(getContainer()) == JFileChooser.APPROVE_OPTION) {
918 File selected = fc.getSelectedFile();
919
920 if (selected != null) {
921 try {
922 if (model.getLength() > 0) {
923 model.remove(0, model.getLength());
924 }
925 model.insertString(0, selected.getPath(), null);
926 } catch (BadLocationException ble) {
927 }
928 }
929 }
930 }
931 }
932 }
|