001: /*
002: * Copyright (c) 2004 JETA Software, Inc. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without modification,
005: * are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of JETA Software nor the names of its contributors may
015: * be used to endorse or promote products derived from this software without
016: * specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
021: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
022: * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
023: * INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
024: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
025: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
026: * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: */
029:
030: package com.jeta.forms.gui.focus;
031:
032: import java.awt.Component;
033: import java.awt.Container;
034: import java.util.ArrayList;
035: import java.util.Collection;
036: import java.util.HashMap;
037: import java.util.HashSet;
038: import java.util.Iterator;
039: import java.util.LinkedHashSet;
040:
041: import javax.swing.JScrollBar;
042: import javax.swing.JScrollPane;
043: import javax.swing.JTabbedPane;
044: import javax.swing.LayoutFocusTraversalPolicy;
045:
046: import com.jeta.forms.components.panel.FormPanel;
047: import com.jeta.forms.gui.common.FormUtils;
048: import com.jeta.forms.gui.form.FormComponent;
049: import com.jeta.forms.gui.form.FormContainerComponent;
050: import com.jeta.forms.gui.form.GridComponent;
051: import com.jeta.forms.gui.form.StandardComponent;
052: import com.jeta.forms.store.memento.CompositeFocusKey;
053: import com.jeta.forms.store.memento.ContainerFocusKey;
054: import com.jeta.forms.store.memento.FocusKey;
055: import com.jeta.forms.store.memento.FocusPolicyMemento;
056: import com.jeta.forms.store.memento.FormCellFocusKey;
057: import com.jeta.open.support.EmptyCollection;
058:
059: /**
060: * This class is responsible for handling the focus policy for a form. This
061: * class is currently not being used since focus is not supported in the
062: * designer.
063: *
064: * @author Jeff Tassin
065: */
066: public class FormFocusManager {
067: /**
068: * The form whose focus policy we are managing.
069: */
070: private FormComponent m_form;
071:
072: /**
073: * The list of components (Component objects )in their proper focus
074: * ordering.
075: */
076: private ArrayList m_focus_list;
077:
078: /**
079: * ctor
080: */
081: public FormFocusManager(FormComponent fc) {
082: /**
083: * now set the focusCycleRoot to false for any child forms.
084: */
085: disableChildFocusCycleRoots(fc);
086: fc.setFocusCycleRoot(true);
087:
088: m_form = fc;
089:
090: /**
091: * First get all valid components from the stored focus policy. This
092: * list was created when the developer set the focus policy in the
093: * designer and stored the result.
094: */
095: ArrayList stored_focus_set = buildStoredFocusList(fc);
096:
097: /**
098: * Now get the default focus policy which is assigned by Swing.
099: */
100: LinkedHashSet default_focus_set = buildDefaultFocusPolicy(fc);
101:
102: /**
103: * Now we want to reconcile what has been stored versus the default
104: * policy because the form may have changed since the last time the
105: * focus policy was saved.
106: */
107:
108: /**
109: * now remove any elements from the stored policy that are not found in
110: * the default focus policy
111: */
112: Iterator iter = stored_focus_set.iterator();
113: while (iter.hasNext()) {
114: Component comp = (Component) iter.next();
115: if (!default_focus_set.contains(comp)) {
116: iter.remove();
117: }
118: }
119:
120: /** used for quick lookups */
121: HashSet stored_lookup = new HashSet();
122: stored_lookup.addAll(stored_focus_set);
123:
124: /**
125: * now iterate over the default focus policy. Any components not found
126: * in the stored focus policy are added at the default position
127: */
128: Component prev_comp = null;
129: iter = default_focus_set.iterator();
130: while (iter.hasNext()) {
131: Component comp = (Component) iter.next();
132: if (!stored_lookup.contains(comp)) {
133: stored_lookup.add(comp);
134: if (prev_comp == null) {
135: stored_focus_set.add(0, comp);
136: } else {
137: int pos = stored_focus_set.indexOf(prev_comp);
138: assert (pos >= 0);
139: if (pos >= 0) {
140: stored_focus_set.add(pos + 1, comp);
141: }
142: }
143: }
144: prev_comp = comp;
145: }
146:
147: /** now the stored_focus_set has the correct focus order */
148: m_focus_list = stored_focus_set;
149:
150: }
151:
152: /**
153: * ctor
154: */
155: public FormFocusManager(FormComponent rootForm, Collection focusList) {
156: m_form = rootForm;
157: m_focus_list = new ArrayList(focusList);
158: }
159:
160: /**
161: * Builds a list of components (Component) for a form that are ordered in
162: * the default focus order.
163: */
164: public static LinkedHashSet buildDefaultFocusPolicy(FormComponent fc) {
165: System.out.println("buildDefaultFocusPolicy form: "
166: + fc.getId());
167:
168: final FormComponent theform = fc;
169: LinkedHashSet default_policy = new LinkedHashSet();
170: LayoutFocusTraversalPolicy policy = new LayoutFocusTraversalPolicy() {
171: protected boolean accept(Component aComponent) {
172: if (aComponent instanceof StandardComponent) {
173: if (((StandardComponent) aComponent)
174: .getBeanDelegate() == null)
175: return false;
176: }
177:
178: if (aComponent == theform)
179: return super .accept(aComponent);
180:
181: if (aComponent instanceof FormComponent) {
182: if (((FormComponent) aComponent).isTopLevelForm())
183: return false;
184: }
185:
186: if (aComponent instanceof JTabbedPane)
187: return true;
188:
189: if (aComponent != null) {
190: /**
191: * handle the case for embedded focus cycle roots such as
192: * JTabbedPane forms
193: */
194: Container cc = aComponent.getParent();
195: while (cc != null && cc != theform) {
196: if (cc instanceof FormContainerComponent) {
197: return false;
198: }
199:
200: cc = cc.getParent();
201: }
202: }
203: return super .accept(aComponent);
204: }
205: };
206: Component comp = policy.getFirstComponent(fc);
207: Component last_comp = policy.getLastComponent(fc);
208: while (true) {
209:
210: /**
211: * Don't add scroll pane in design mode since the scroll bars might
212: * not be visible
213: */
214: if (FormUtils.isDesignMode()) {
215: if (!(comp instanceof JScrollPane)
216: && !(comp instanceof JScrollBar)) {
217: default_policy.add(comp);
218: }
219: } else {
220: default_policy.add(comp);
221: }
222:
223: if (comp == last_comp)
224: break;
225:
226: System.out.println("FormFocusManager.getComponentAfter: "
227: + comp.getClass());
228: comp = policy.getComponentAfter(fc, comp);
229: }
230:
231: return default_policy;
232: }
233:
234: /**
235: * Builds an list of components that are ordered in the focus order
236: * previously set by a user for a given form. The form may have changed
237: * (components might have been deleted or moved), so we need to delete those
238: * components from the focus order that don't match the current state of the
239: * form.
240: */
241: ArrayList buildStoredFocusList(FormComponent form) {
242: ArrayList focus_list = new ArrayList();
243: FocusPolicyMemento memento = form.getFocusPolicy();
244: if (memento != null) {
245: Collection fkeys = memento.getFocusPolicyKeys();
246: Iterator iter = fkeys.iterator();
247: while (iter.hasNext()) {
248: FocusKey fkey = (FocusKey) iter.next();
249: Component comp = fkey.getComponent(form);
250: if (comp != null) {
251: focus_list.add(comp);
252: }
253: }
254: }
255: return focus_list;
256: }
257:
258: /**
259: * Builds the set of FocusKey objects for all focusable components on the
260: * form. A FocusKey allows us to store a reference to a focusable component
261: * and later find that component when a form has been de-serialized. We
262: * don't use the component name to reference the component because we don't
263: * want to keep track of when the user changes the name.
264: */
265: public void buildFocusKeys(HashSet currentFocusSet,
266: HashMap focus_key_map, FormComponent form,
267: CompositeFocusKey compositeKey) {
268: for (int row = 1; row <= form.getRowCount(); row++) {
269: for (int col = 1; col <= form.getColumnCount(); col++) {
270: GridComponent gc = form.getGridComponent(col, row);
271: if (gc instanceof StandardComponent) {
272: Component comp = gc.getBeanDelegate();
273: if (comp != null) {
274: if (currentFocusSet.contains(comp)) {
275: CompositeFocusKey ckey = (CompositeFocusKey) compositeKey
276: .clone();
277: ckey.add(new FormCellFocusKey(row, col,
278: comp));
279: focus_key_map.put(comp, ckey);
280: } else {
281: /**
282: * This comp must be a container that contains
283: * components that are in the focus policy This can
284: * happen for Java Beans that are also panels which
285: * contain other components.
286: */
287: if (comp instanceof Container) {
288: CompositeFocusKey ckey = (CompositeFocusKey) compositeKey
289: .clone();
290: ckey.add(new FormCellFocusKey(row, col,
291: comp));
292: buildContainerFocusKeys(
293: currentFocusSet, focus_key_map,
294: (Container) comp, ckey);
295: } else {
296: // ignore because this could be a component like
297: // a JLabel which does not need focus
298: }
299: }
300: }
301: } else if (gc instanceof FormComponent) {
302: FormComponent childform = (FormComponent) gc;
303: CompositeFocusKey ckey = (CompositeFocusKey) compositeKey
304: .clone();
305: ckey.add(new FormCellFocusKey(row, col, gc));
306: buildFocusKeys(currentFocusSet, focus_key_map,
307: childform, ckey);
308: } else {
309: if (gc != null) {
310: System.out
311: .println("FormFocusManager.buildDefaultPolicyFailed found unknown comp: "
312: + gc.getClass());
313: }
314: }
315: }
316: }
317: }
318:
319: /**
320: * Builds a container focus key
321: */
322: public void buildContainerFocusKeys(HashSet currentFocusSet,
323: HashMap focus_key_map, Container container,
324: CompositeFocusKey compositeKey) {
325: for (int index = 0; index < container.getComponentCount(); index++) {
326: Component comp = container.getComponent(index);
327: if (currentFocusSet.contains(comp)) {
328: CompositeFocusKey ckey = (CompositeFocusKey) compositeKey
329: .clone();
330: ckey.add(new ContainerFocusKey(index, comp));
331: focus_key_map.put(comp, ckey);
332: } else {
333: // this comp must be a container that contains components that
334: // are in the focus policy
335: if (comp instanceof Container) {
336: CompositeFocusKey ckey = (CompositeFocusKey) compositeKey
337: .clone();
338: ckey.add(new ContainerFocusKey(index, comp));
339: buildContainerFocusKeys(currentFocusSet,
340: focus_key_map, (Container) comp, ckey);
341: }
342: }
343: }
344: }
345:
346: /**
347: * @return true if the current focus list contains the given component
348: */
349: public boolean contains(Component comp) {
350: return m_focus_list.contains(comp);
351: }
352:
353: /**
354: * Sets the focusCycleRoot to false for any child forms.
355: */
356: private void disableChildFocusCycleRoots(Container cc) {
357: if (cc == null)
358: return;
359:
360: for (int index = 0; index < cc.getComponentCount(); index++) {
361: Component comp = cc.getComponent(index);
362: if (comp instanceof StandardComponent
363: || comp instanceof FormPanel) {
364: ((Container) comp).setFocusCycleRoot(false);
365: disableChildFocusCycleRoots((Container) comp);
366: } else if (comp instanceof Container) {
367: disableChildFocusCycleRoots((Container) comp);
368: }
369: }
370: }
371:
372: /**
373: * @return the focus policy memento that represents the current focus
374: * ordering for this manager
375: */
376: public FocusPolicyMemento getFocusPolicyMemento() {
377: HashSet current_focus_set = new HashSet();
378: current_focus_set.addAll(m_focus_list);
379:
380: HashMap focus_key_map = new HashMap();
381: CompositeFocusKey cfk = new CompositeFocusKey();
382: /** first build location keys for all components in the the form */
383: buildFocusKeys(current_focus_set, focus_key_map, m_form, cfk);
384:
385: FocusPolicyMemento memento = new FocusPolicyMemento();
386:
387: /** now iterate over the focus list and get the corresponding focus key */
388: Iterator iter = m_focus_list.iterator();
389: while (iter.hasNext()) {
390: Component comp = (Component) iter.next();
391: FocusKey fkey = (FocusKey) focus_key_map.get(comp);
392: if (fkey != null)
393: memento.addFocusKey(fkey);
394: }
395:
396: return memento;
397: }
398:
399: /**
400: * @return the component at the index in the current focus list
401: */
402: public Component getComponent(int index) {
403: if (index < 0 || index >= m_focus_list.size())
404: return null;
405:
406: return (Component) m_focus_list.get(index);
407: }
408:
409: /**
410: * @return the number of components in the focus list
411: */
412: public int getComponentCount() {
413: return m_focus_list.size();
414: }
415:
416: public Collection getFocusList() {
417: if (m_focus_list == null)
418: return EmptyCollection.getInstance();
419: else
420: return m_focus_list;
421: }
422:
423: /**
424: * Checks that all focuskeys reference the correct components
425: */
426: public void validateFocusKeys(FormComponent root,
427: FocusPolicyMemento memento) {
428: Iterator iter = memento.getFocusPolicyKeys().iterator();
429: while (iter.hasNext()) {
430: FocusKey fkey = (FocusKey) iter.next();
431: Component comp = fkey.getComponent(root);
432: assert (comp != null);
433: System.out.print("Focuskey validated: ");
434: fkey.print();
435: System.out.println(" comp: " + comp.getClass());
436: }
437: }
438:
439: }
|