001    /**
002     * Copyright 2010 The Kuali Foundation Licensed under the
003     * Educational Community License, Version 2.0 (the "License"); you may
004     * not use this file except in compliance with the License. You may
005     * obtain a copy of the License at
006     *
007     * http://www.osedu.org/licenses/ECL-2.0
008     *
009     * Unless required by applicable law or agreed to in writing,
010     * software distributed under the License is distributed on an "AS IS"
011     * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
012     * or implied. See the License for the specific language governing
013     * permissions and limitations under the License.
014     */
015    
016    package org.kuali.student.common.ui.client.widgets.focus;
017    
018    import java.util.HashMap;
019    import java.util.Map;
020    
021    import com.google.gwt.event.dom.client.BlurEvent;
022    import com.google.gwt.event.dom.client.BlurHandler;
023    import com.google.gwt.event.dom.client.FocusEvent;
024    import com.google.gwt.event.dom.client.FocusHandler;
025    import com.google.gwt.event.dom.client.HasBlurHandlers;
026    import com.google.gwt.event.dom.client.HasFocusHandlers;
027    import com.google.gwt.event.shared.GwtEvent;
028    import com.google.gwt.event.shared.HandlerManager;
029    import com.google.gwt.event.shared.HandlerRegistration;
030    import com.google.gwt.event.shared.HasHandlers;
031    import com.google.gwt.user.client.Command;
032    import com.google.gwt.user.client.DeferredCommand;
033    import com.google.gwt.user.client.ui.Widget;
034    
035    public class FocusGroup implements HasBlurHandlers, HasFocusHandlers, HasHandlers {
036            private static class SyntheticBlurEvent extends BlurEvent {
037                    public SyntheticBlurEvent() {
038                            // do nothing
039                    }
040            }
041    
042            private static class SyntheticFocusEvent extends FocusEvent {
043                    public SyntheticFocusEvent() {
044                            // do nothing
045                    }
046            }
047    
048            private final HandlerManager handlers;
049            private final Map<Widget, Boolean> focusTrackers = new HashMap<Widget, Boolean>();
050            private boolean focusEventPending = false;
051            private boolean suppressed = false;
052            
053            private boolean focused = false;
054    
055            private final Command checkFocusState = new Command() {
056                    @Override
057                    public void execute() {
058                            focusEventPending = false;
059                            boolean nowFocused = false;
060                            for (final Boolean b : focusTrackers.values()) {
061                                    if (b) {
062                                            nowFocused = true;
063                                            break;
064                                    }
065                            }
066    
067                            if (!suppressed && (focused ^ nowFocused)) {
068                                    if (nowFocused) {
069                                            // we weren't focused, but now we are
070                                            //                                      FocusEvent.fireNativeEvent(lastFocusEvent, FocusGroup.this);
071                                            handlers.fireEvent(new SyntheticFocusEvent());
072                                    } else {
073                                            // we were focused, now we aren't
074                                            //                                      BlurEvent.fireNativeEvent(lastBlurEvent, FocusGroup.this);
075                                            handlers.fireEvent(new SyntheticBlurEvent());
076                                    }
077                            }
078    
079                            focused = nowFocused;
080                    }
081            };
082    
083            public FocusGroup(final Widget parentWidget) {
084                    this.handlers = new HandlerManager(parentWidget);
085            }
086    
087            @Override
088            public HandlerRegistration addBlurHandler(final BlurHandler handler) {
089                    return handlers.addHandler(BlurEvent.getType(), handler);
090            }
091    
092            @Override
093            public HandlerRegistration addFocusHandler(final FocusHandler handler) {
094                    return handlers.addHandler(FocusEvent.getType(), handler);
095            }
096    
097            public void addWidget(final Widget w) {
098                    if (w instanceof HasBlurHandlers) {
099                            ((HasBlurHandlers) w).addBlurHandler(new BlurHandler() {
100                                    @Override
101                                    public void onBlur(final BlurEvent event) {
102                                            focusTrackers.put(w, false);
103                                            queueCheckFocusState();
104                                    }
105                            });
106                    }
107                    if (w instanceof HasFocusHandlers) {
108                            ((HasFocusHandlers) w).addFocusHandler(new FocusHandler() {
109                                    @Override
110                                    public void onFocus(final FocusEvent event) {
111                                            focusTrackers.put(w, true);
112                                            queueCheckFocusState();
113                                    }
114                            });
115                    }
116            }
117    
118            @Override
119            public void fireEvent(final GwtEvent<?> event) {
120                    handlers.fireEvent(event);
121            }
122    
123            private void queueCheckFocusState() {
124                    if (!focusEventPending) {
125                            focusEventPending = true;
126                            DeferredCommand.addCommand(checkFocusState);
127                    }
128            }
129    
130            public boolean isSuppressed() {
131                    return suppressed;
132            }
133    
134            public void setSuppressed(boolean suppressed) {
135                    this.suppressed = suppressed;
136            }
137            
138            
139    }