001: //** Copyright Statement ***************************************************
002: //The Salmon Open Framework for Internet Applications (SOFIA)
003: // Copyright (C) 1999 - 2002, Salmon LLC
004: //
005: // This program is free software; you can redistribute it and/or
006: // modify it under the terms of the GNU General Public License version 2
007: // as published by the Free Software Foundation;
008: //
009: // This program is distributed in the hope that it will be useful,
010: // but WITHOUT ANY WARRANTY; without even the implied warranty of
011: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: // GNU General Public License for more details.
013: //
014: // You should have received a copy of the GNU General Public License
015: // along with this program; if not, write to the Free Software
016: // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
017: //
018: // For more information please visit http://www.salmonllc.com
019: //** End Copyright Statement ***************************************************
020: package com.salmonllc.html;
021:
022: /////////////////////////
023: //$Archive: /SOFIA/SourceCode/com/salmonllc/html/HtmlFormComponent.java $
024: //$Author: Dan $
025: //$Revision: 39 $
026: //$Modtime: 10/01/04 10:08a $
027: /////////////////////////
028:
029: import java.io.PrintWriter;
030: import java.util.Vector;
031:
032: import com.salmonllc.html.events.ValueChangedEvent;
033: import com.salmonllc.html.events.ValueChangedListener;
034: import com.salmonllc.localizer.LanguagePreferences;
035: import com.salmonllc.localizer.LanguageResourceFinder;
036: import com.salmonllc.properties.Props;
037: import com.salmonllc.sql.DataStoreBuffer;
038: import com.salmonllc.sql.DataStoreException;
039: import com.salmonllc.util.MessageLog;
040:
041: /**
042: * This type is the base class for all Html Form Controls
043: */
044: public abstract class HtmlFormComponent extends HtmlComponent {
045: public static final int FORCE_CASE_NONE = 0;
046: public static final int FORCE_CASE_UPPER = 1;
047: public static final int FORCE_CASE_LOWER = 2;
048:
049: public static final int TAB_INDEX_DEFAULT = -1;
050: public static final int TAB_INDEX_NONE = -2;
051:
052: protected Vector _events;
053: protected Vector _listeners = null;
054:
055: protected String _value;
056: protected DataStoreBuffer _dsBuff = null;
057: protected int _dsColNo = -1;
058: protected boolean _enabled = true;
059: protected String _disabledFontStartTag;
060: protected String _disabledFontEndTag;
061: private boolean _focus = false;
062: private int _focusRow = -1;
063: private boolean _select = false;
064:
065: protected int _forceCase = FORCE_CASE_NONE;
066: private String _theme;
067:
068: private String _formatLocaleKey;
069: private boolean _updateLocale;
070: private String _displayFormat;
071:
072: /**
073: * HtmlFormComponent constructor.
074: * @param name java.lang.String
075: * @param p com.salmonllc.html.HtmlPage
076: */
077: public HtmlFormComponent(String name, HtmlPage p) {
078: this (name, null, p);
079: }
080:
081: /**
082: * HtmlFormComponent constructor
083: * @param name java.lang.String
084: * @param theme The theme to use for loading properties.
085: * @param p com.salmonllc.html.HtmlPage
086: */
087: public HtmlFormComponent(String name, String theme, HtmlPage p) {
088: super (name, p);
089: setTheme(theme);
090: }
091:
092: protected void addEvent(ValueChangedEvent e) {
093: if (_events == null)
094: _events = new Vector();
095:
096: _events.addElement(e);
097: }
098:
099: /**
100: * This method adds a listener the will be notified when the value in this component changes.
101: * @param l The listener to add.
102: */
103: public void addValueChangedListener(ValueChangedListener l) {
104: if (_listeners == null)
105: _listeners = new Vector();
106: //
107: int listenersSize = _listeners.size();
108: for (int i = 0; i < listenersSize; i++) {
109: if (((ValueChangedListener) _listeners.elementAt(i)) == l)
110: return;
111: }
112: _listeners.addElement(l);
113: }
114:
115: /**
116: * This method converts the text value according to case conversion if necessary.
117: */
118: protected void convertValue() {
119: if (_value != null) {
120: switch (_forceCase) {
121: case FORCE_CASE_NONE:
122: break;
123: case FORCE_CASE_UPPER:
124: _value = _value.toUpperCase();
125: break;
126: case FORCE_CASE_LOWER:
127: _value = _value.toLowerCase();
128: break;
129: }
130: }
131: }
132:
133: public boolean executeEvent(int eventType) throws Exception {
134: if (eventType != EVENT_OTHER)
135: return true;
136:
137: if (_events == null)
138: return true;
139:
140: int eventNo = 0;
141: while (_events.size() > eventNo) {
142: ValueChangedEvent e = (ValueChangedEvent) _events
143: .elementAt(0);
144:
145: boolean cont = true;
146: if (_listeners != null) {
147: for (int i = 0; i < _listeners.size(); i++) {
148: cont = ((ValueChangedListener) _listeners
149: .elementAt(i)).valueChanged(e);
150: if (!cont
151: && e.getAcceptValueInt() == ValueChangedEvent.PROCESSING_NOTENTERED)
152: return false;
153: }
154: }
155:
156: if (e.getAcceptValueInt() == ValueChangedEvent.PROCESSING_NOTENTERED) {
157: if (e.getAcceptValue())
158: setValue(e.getNewValue(), e.getRow());
159: else
160: removeEvent(eventNo);
161: } else {
162: if (e.getAcceptValueInt() == ValueChangedEvent.PROCESSING_COMMIT_CHANGE)
163: setValue(e.getNewValue(), e.getRow());
164: else if (e.getAcceptValueInt() == ValueChangedEvent.PROCESSING_MOVE_CHANGE_TO_TEMP) {
165: if (!setTempValue(e.getNewValue(), e.getRow(),
166: eventNo))
167: eventNo++;
168: } else if (e.getAcceptValueInt() == ValueChangedEvent.PROCESSING_KEEP_CHANGE_IN_QUEUE)
169: eventNo++;
170: else
171: removeEvent(eventNo);
172: }
173:
174: if (!cont)
175: return false;
176: }
177:
178: return true;
179: }
180:
181: protected int findEvent(int rowNo) {
182: int queueSize = getEventCount();
183: for (int i = queueSize - 1; i > -1; i--) {
184: ValueChangedEvent e = getEventAt(i);
185: if (e.getRow() == rowNo)
186: return i;
187: }
188:
189: return -1;
190: }
191:
192: abstract public void generateHTML(java.io.PrintWriter p, int row)
193: throws Exception;
194:
195: /**
196: * This method returns the number of the column in the data store that this component is bound to.
197: */
198: public int getColumnNumber() {
199: return _dsColNo;
200: }
201:
202: /**
203: * This method returns the datastore that this component is bound to
204: */
205: public DataStoreBuffer getBoundDataStore() {
206: return _dsBuff;
207: }
208:
209: /**
210: * Returns the end font tag for disabled mode.
211: * @return java.lang.String
212: */
213: public String getDisabledFontEndTag() {
214: return _disabledFontEndTag;
215: }
216:
217: /**
218: * Returns the font start tag for disabled mode.
219: * @return java.lang.String
220: */
221: public String getDisabledFontStartTag() {
222: return _disabledFontStartTag;
223: }
224:
225: /**
226: * Returns true if the component is enabled to respond to user input.
227: * @return boolean
228: */
229: public boolean getEnabled() {
230: return _enabled;
231: }
232:
233: protected ValueChangedEvent getEventAt(int index) {
234: return (ValueChangedEvent) _events.elementAt(index);
235: }
236:
237: protected int getEventCount() {
238: if (_events == null)
239: return 0;
240: return _events.size();
241: }
242:
243: /**
244: * This method returns the property theme for the component.
245: */
246: public String getTheme() {
247: return _theme;
248: }
249:
250: /**
251: * This method gets the value of the data in the component.
252: */
253: public String getValue() {
254: return getValue(-1);
255: }
256:
257: /**
258: * This method gets the value of the data in the component at the specified row number.
259: */
260: public String getValue(int rowNo) {
261:
262: return getValue(rowNo, false);
263:
264: }
265:
266: /**
267: * Gets the value in the datastore bound to this component
268: */
269: protected String getDataStoreValue(int rowNo) {
270: if (_dsBuff != null) {
271: try {
272: if (rowNo > -1)
273: return (_dsBuff.getFormattedString(rowNo, _dsColNo));
274: else
275: return (_dsBuff.getFormattedString(_dsColNo));
276: } catch (Exception e) {
277: }
278: }
279: return null;
280: }
281:
282: /**
283: * This method gets the value of the data in the component at the specified row number.
284: */
285: protected String getValue(int rowNo, boolean remove) {
286:
287: String retVal = _value;
288: Object temp;
289:
290: if (_dsBuff != null) {
291: try {
292: if (rowNo > -1)
293: retVal = _dsBuff
294: .getFormattedString(rowNo, _dsColNo);
295: else
296: retVal = _dsBuff.getFormattedString(_dsColNo);
297: } catch (Exception e) {
298: }
299: }
300:
301: int queueSize = getEventCount();
302: for (int i = queueSize - 1; i > -1; i--) {
303: ValueChangedEvent e = getEventAt(i);
304: if (e.getRow() == rowNo) {
305: temp = e.getNewValue();
306: if (temp == null)
307: retVal = null;
308: else
309: retVal = temp.toString();
310: if (remove)
311: removeEvent(i);
312: break;
313: }
314: }
315:
316: return retVal;
317: }
318:
319: public boolean isValid() {
320: return true;
321: }
322:
323: protected void removeEvent(int index) {
324: _events.removeElementAt(index);
325: }
326:
327: /**
328: * This method removes a listener from the list that will be notified if the text in the component changes.
329: * @param l The listener to remove.
330: */
331: public void removeValueChangedListener(ValueChangedListener l) {
332: if (_listeners == null)
333: return;
334:
335: for (int i = 0; i < _listeners.size(); i++) {
336: if (((ValueChangedListener) _listeners.elementAt(i)) == l) {
337: _listeners.removeElementAt(i);
338: return;
339: }
340: }
341: }
342:
343: /**
344: * This method will clear all pending events from the event queue for this component.
345: */
346: public void reset() {
347: if (_events != null) {
348: _events.setSize(0);
349: }
350: }
351:
352: /**
353: * Use this method to bind the component to a column in a DataStore.
354: * @param ds The datastore to bind to.
355: * @param columnNo The index of the column to bind to.
356: */
357: public void setColumn(DataStoreBuffer ds, int columnNo) {
358: if (columnNo < 0 || columnNo >= ds.getColumnCount()) {
359: String msg = "Column not in range columnNo=" + columnNo;
360: msg += "\n ds and column not set";
361: MessageLog.writeErrorMessage(msg, null, this );
362: return;
363: }
364:
365: _dsBuff = ds;
366: _dsColNo = columnNo;
367: }
368:
369: /**
370: * Use this method to bind the component to a column in a DataStore.
371: * @param ds The datastore to bind to.
372: * @param columnName The name of the column to bind to.
373: */
374: public void setColumn(DataStoreBuffer ds, String columnName) {
375: int colIndex = ds.getColumnIndex(columnName);
376: if (colIndex < 0) {
377: MessageLog.writeErrorMessage("UNMATCHED columnName="
378: + columnName, null, this );
379: }
380: setColumn(ds, colIndex);
381: }
382:
383: /**
384: * Sets the font end tag for disabled mode.
385: * @param tag java.lang.String
386: */
387: public void setDisabledFontEndTag(String tag) {
388: _disabledFontEndTag = tag;
389: }
390:
391: /**
392: * Sets the font tag for disabled mode.
393: * @param tag java.lang.String
394: */
395: public void setDisabledFontStartTag(String tag) {
396: _disabledFontStartTag = tag;
397: }
398:
399: /**
400: * Enables or disables the ability of this component to respond to user input.
401: * @param enabled boolean
402: */
403: public void setEnabled(boolean enabled) {
404: _enabled = enabled;
405: }
406:
407: /**
408: * This method will set the edit focus to this component.
409: */
410: public void setFocus() {
411: _focus = true;
412: _focusRow = -1;
413: _select = false;
414: }
415:
416: /**
417: * This method will set the edit focus to this component.
418: * If select is true it additionally highlights the input area of the component.
419: */
420: public void setFocus(boolean select) {
421: _focus = true;
422: _focusRow = -1;
423: _select = select;
424: }
425:
426: /**
427: * This method will set the edit focus to this component for a particular row in a datastore.
428: * If select is true it additionally highlights the input area of the component.
429: */
430: public void setFocus(int row, boolean select) {
431: _focus = true;
432: _focusRow = row;
433: _select = select;
434:
435: }
436:
437: /**
438: * This method will set the edit focus to this component for a particular row in a datastore.
439: */
440: public void setFocus(int row) {
441: _focus = true;
442: _focusRow = row;
443: _select = false;
444: }
445:
446: /**
447: * This method sets case-conversion mode for incoming text.
448: * @param mode int One of FORCE_CASE_NONE, FORCE_CASE_UPPER, FORCE_CASE_LOWER.
449: */
450: public void setForceCase(int mode) {
451: switch (mode) {
452: case FORCE_CASE_NONE:
453: case FORCE_CASE_UPPER:
454: case FORCE_CASE_LOWER:
455: break;
456: default:
457: // Ignore invalid values
458: return;
459: }
460: _forceCase = mode;
461: }
462:
463: /**
464: * This method sets the property theme for the component.
465: * @param theme The theme to use.
466: */
467: public void setTheme(String theme) {
468: Props props = getPage().getPageProperties();
469: _disabledFontStartTag = props.getThemeProperty(theme,
470: Props.FONT_DEFAULT + Props.TAG_START);
471: _disabledFontEndTag = props.getThemeProperty(theme,
472: Props.FONT_DEFAULT + Props.TAG_END);
473: String s = props.getThemeProperty(theme,
474: Props.TEXTEDIT_FORCE_CASE);
475: if (s != null) {
476: s = s.toLowerCase();
477: if (s.equals("upper"))
478: _forceCase = FORCE_CASE_UPPER;
479: else if (s.equals("lower"))
480: _forceCase = FORCE_CASE_LOWER;
481: }
482: _theme = theme;
483: }
484:
485: /**
486: * This method sets the value of the component.
487: */
488: public void setValue(String value) {
489: setValue(value, -1);
490: }
491:
492: /**
493: * This method sets the value of the component at the specified row number.
494: */
495: public void setValue(String value, int rowNo) {
496: _value = value;
497:
498: if (_dsBuff != null) {
499: try {
500: if (rowNo > -1)
501: _dsBuff.setFormattedString(rowNo, _dsColNo, value);
502: else
503: _dsBuff.setFormattedString(_dsColNo, value);
504: } catch (Exception e) {
505: try {
506: if (rowNo > -1)
507: _dsBuff.setAny(rowNo, _dsColNo, null);
508: else
509: _dsBuff.setAny(_dsColNo, null);
510: } catch (Exception ex) {
511: }
512: }
513: }
514:
515: int queueSize = getEventCount();
516: for (int i = queueSize - 1; i > -1; i--) {
517: ValueChangedEvent e = getEventAt(i);
518: if (e.getRow() == rowNo)
519: removeEvent(i);
520: }
521: }
522:
523: protected boolean setTempValue(String value, int rowNo, int eventNo) {
524: _value = value;
525: if (_dsBuff != null) {
526: try {
527: if (rowNo > -1)
528: _dsBuff.setTempValue(rowNo, _dsColNo, value);
529: else
530: _dsBuff.setTempValue(_dsColNo, value);
531: removeEvent(eventNo);
532: return true;
533: } catch (Exception e) {
534: MessageLog.writeErrorMessage("setTempValue", e, this );
535: }
536: }
537: return false;
538: }
539:
540: protected boolean valuesEqual(Object newValue, Object oldValue) {
541: if (newValue == null && oldValue != null)
542: return false;
543: else if (newValue != null && oldValue == null)
544: return false;
545: else if (newValue != null && oldValue != null)
546: if (!newValue.toString().trim().equals(
547: oldValue.toString().trim()))
548: return false;
549:
550: return true;
551: }
552:
553: protected void writeFocusScript(java.io.PrintWriter p, int row) {
554: if (!_focus)
555: return;
556:
557: if (!_enabled || !_visible) {
558: _focus = false;
559: return;
560: }
561:
562: String compToFocusName = null;
563: if (_focusRow == -1) {
564: compToFocusName = getFormString() + getFullName();
565: } else if (row == _focusRow) {
566: compToFocusName = getFormString() + getFullName() + "_"
567: + _focusRow;
568: }
569:
570: if (compToFocusName != null) {
571: getPage().writeScript(compToFocusName + ".focus();");
572: _focus = false;
573: if (_select) {
574: getPage().writeScript(compToFocusName + ".select();");
575: _select = false;
576: }
577: }
578:
579: }
580:
581: /**
582: * This method returns the name of the component.
583: */
584: public String toString() {
585: return "[name=" + super .toString() + ", value=" + getValue()
586: + "]";
587: }
588:
589: /**
590: * Returns the Locale key used for display format
591: */
592: public String getDisplayFormatLocaleKey() {
593: return _formatLocaleKey;
594: }
595:
596: /**
597: * Sets the Locale key used for display format
598: */
599: public void setDisplayFormatLocaleKey(String formatLocaleKey) {
600: _formatLocaleKey = formatLocaleKey;
601: _updateLocale = true;
602:
603: }
604:
605: /**
606: * Returns the Display format used by this component
607: */
608: public String getDisplayFormat() {
609: return _displayFormat;
610: }
611:
612: /**
613: * Sets the Display format used by this component. This only works if the component is bound to a Datastore column.
614: */
615: public void setDisplayFormat(String format) {
616: if (format != null) {
617: //Retrieve format string from page properties
618: Props props = getPage().getPageProperties();
619: String newFormat = props.getThemeProperty(_theme, format);
620: if (newFormat != null)
621: format = newFormat;
622: }
623: _displayFormat = format;
624: _updateLocale = true;
625:
626: }
627:
628: public boolean isEnabled() {
629: return _enabled;
630: }
631:
632: /**
633: * Updates the text and format for the current local
634: */
635: public void updateLocale() {
636: _updateLocale = true;
637: }
638:
639: protected void processLocaleInfo() {
640: if (_updateLocale) {
641: _updateLocale = false;
642: LanguagePreferences p = getPage().getLanguagePreferences();
643:
644: if (_displayFormat != null && _dsBuff != null
645: && _dsColNo != -1) {
646: try {
647: _dsBuff.setFormat(_dsColNo, _displayFormat);
648: } catch (DataStoreException e) {
649: MessageLog.writeErrorMessage("processLocaleInfo()",
650: e, this );
651: }
652: }
653:
654: if (_formatLocaleKey != null && _dsBuff != null
655: && _dsColNo != -1) {
656: String newFormat = LanguageResourceFinder.getResource(
657: getPage().getApplicationName(),
658: _formatLocaleKey, p);
659: if (newFormat != null) {
660: try {
661: _dsBuff.setFormat(_dsColNo, newFormat);
662: } catch (DataStoreException e) {
663: MessageLog.writeErrorMessage(
664: "processLocaleInfo()", e, this );
665: }
666: }
667: }
668: }
669: }
670:
671: /**
672: * Enables or disables the ability of this component to respond to user input.
673: * @param enabled boolean
674: * @param useDisableAttribute whether to use the HTML disable attribute or not
675: */
676: public void setEnabled(boolean enabled, boolean useDisableAttribute) {
677: _enabled = enabled;
678: setUseDisabledAttribute((useDisableAttribute) ? DISABLED_ATTRIBUTE_USE_ON_SUPPORTED_BROWSERS
679: : DISABLED_ATTRIBUTE_USE_NEVER);
680: }
681:
682: /**
683: * Generates a script for form components to automatically tab to the next field
684: * @param pw
685: */
686: protected void generateAutoTabJavaScript(PrintWriter pw) {
687: pw.println("<SCRIPT LANGUAGE=\"JavaScript\">");
688: pw
689: .println("<!-- Original: Cyanide_7 (leo7278@hotmail.com) -->");
690: pw
691: .println("<!-- Web Site: http://members.xoom.com/cyanide_7 -->");
692: pw
693: .println("<!-- This script and many more are available free online at -->");
694: pw
695: .println("<!-- The JavaScript Source!! http://javascript.internet.com -->");
696: pw.println("function " + getFullName()
697: + "_autoTab(input,len, e) {");
698: pw
699: .println(" var isNN = (navigator.appName.indexOf(\"Netscape\")!=-1);");
700: pw.println(" var keyCode = (isNN) ? e.which : e.keyCode; ");
701: pw
702: .println(" var filter = (isNN) ? [0,8,9] : [0,8,9,16,17,18,37,38,39,40,46];");
703: pw
704: .println(" if(input.value.length >= len && !containsElement(filter,keyCode)) {");
705: pw.println(" input.value = input.value.slice(0, len);");
706: pw
707: .println(" input.form[(getIndex(input)+1) % input.form.length].focus();");
708: pw.println(" }");
709: pw.println(" function containsElement(arr, ele) {");
710: pw.println(" var found = false, index = 0;");
711: pw.println(" while(!found && index < arr.length)");
712: pw.println(" if(arr[index] == ele)");
713: pw.println(" found = true;");
714: pw.println(" else");
715: pw.println(" index++;");
716: pw.println(" return found;");
717: pw.println(" }");
718: pw.println("");
719: pw.println(" function getIndex(input) {");
720: pw.println(" var index = -1, i = 0, found = false;");
721: pw.println(" while (i < input.form.length && index == -1)");
722: pw.println(" if (input.form[i] == input)");
723: pw.println(" index = i;");
724: pw.println(" else ");
725: pw.println(" i++;");
726: pw.println(" return index;");
727: pw.println(" }");
728: pw.println(" return true;");
729: pw.println("}");
730: pw.println("</script>");
731:
732: }
733:
734: /**
735: * Returns the name of this component would be referenced by if used in javascript code
736: */
737: public String getJavaScriptName() {
738: return getFormString() + getFullName();
739: }
740: }
|