001    package org.gwtbootstrap3.client.ui;
002    
003    /*
004     * #%L
005     * GwtBootstrap3
006     * %%
007     * Copyright (C) 2013 GwtBootstrap3
008     * %%
009     * Licensed under the Apache License, Version 2.0 (the "License");
010     * you may not use this file except in compliance with the License.
011     * You may obtain a copy of the License at
012     * 
013     *      http://www.apache.org/licenses/LICENSE-2.0
014     * 
015     * Unless required by applicable law or agreed to in writing, software
016     * distributed under the License is distributed on an "AS IS" BASIS,
017     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018     * See the License for the specific language governing permissions and
019     * limitations under the License.
020     * #L%
021     */
022    
023    import org.gwtbootstrap3.client.shared.event.ModalHiddenEvent;
024    import org.gwtbootstrap3.client.shared.event.ModalHiddenHandler;
025    import org.gwtbootstrap3.client.shared.event.ModalHideEvent;
026    import org.gwtbootstrap3.client.shared.event.ModalHideHandler;
027    import org.gwtbootstrap3.client.shared.event.ModalShowEvent;
028    import org.gwtbootstrap3.client.shared.event.ModalShowHandler;
029    import org.gwtbootstrap3.client.shared.event.ModalShownEvent;
030    import org.gwtbootstrap3.client.shared.event.ModalShownHandler;
031    import org.gwtbootstrap3.client.ui.base.helper.StyleHelper;
032    import org.gwtbootstrap3.client.ui.base.modal.ModalContent;
033    import org.gwtbootstrap3.client.ui.base.modal.ModalDialog;
034    import org.gwtbootstrap3.client.ui.constants.Attributes;
035    import org.gwtbootstrap3.client.ui.constants.ModalBackdrop;
036    import org.gwtbootstrap3.client.ui.constants.Styles;
037    import org.gwtbootstrap3.client.ui.html.Div;
038    
039    import com.google.gwt.dom.client.Element;
040    import com.google.gwt.user.client.Event;
041    import com.google.gwt.user.client.ui.RootPanel;
042    import com.google.gwt.user.client.ui.Widget;
043    import com.google.web.bindery.event.shared.HandlerRegistration;
044    
045    /**
046     * Modal dialog.
047     * <p/>
048     * <h3>UiBinder example</h3>
049     * <p/>
050     * <pre>
051     * {@code
052     *     <b:Modal title="Important information" b:id="modal1">
053     *         <b:ModalBody>
054     *             <g:HTML>Lorem ipsum...</g:HTML>
055     *         </b:ModalBody>
056     *         <b:ModalFooter>
057     *             <b:Button type="PRIMARY">Do something</b:Button>
058     *             <b:Button type="DANGER" dismiss="MODAL">Close</b:Button>
059     *         </b:ModalFooter>
060     *     </b:Modal>
061     *     <b:Button target="#modal1" toggle="MODAL">Show modal</b:Button>
062     * }
063     * </pre>
064     * <p/>
065     * It's also possible to specify a custom modal header:
066     * <p/>
067     * <pre>
068     * {@code
069     *     <b:Modal>
070     *         <b:ModalHeader>
071     *             <g:HTML>
072     *                 <h4>Custom header</h4>
073     *             </g:HTML>
074     *         </b:ModalHeader>
075     *         ...
076     *     </b:Modal>
077     * }
078     * </pre>
079     *
080     * @author Sven Jacobs
081     * @author Joshua Godi
082     * @see ModalHeader
083     * @see ModalBody
084     * @see ModalFooter
085     * @see org.gwtbootstrap3.client.shared.event.ModalShowEvent
086     * @see org.gwtbootstrap3.client.shared.event.ModalShownEvent
087     * @see org.gwtbootstrap3.client.shared.event.ModalHideEvent
088     * @see org.gwtbootstrap3.client.shared.event.ModalHiddenEvent
089     */
090    public class Modal extends Div implements IsClosable {
091    
092        private final static String TOGGLE = "toggle";
093        private final static String HIDE = "hide";
094        private final static String SHOW = "show";
095    
096        private final ModalContent content = new ModalContent();
097        private final ModalDialog dialog = new ModalDialog();
098        private ModalHeader header = new ModalHeader();
099    
100        private boolean hideOtherModals = false;
101    
102        public Modal() {
103            setStyleName(Styles.MODAL);
104    
105            content.add(header);
106            dialog.add(content);
107    
108            add(dialog);
109        }
110    
111        @Override
112        public void setWidth(final String width) {
113            dialog.setWidth(width);
114        }
115    
116        public void setSize(ModalSize size) {
117            StyleHelper.addUniqueEnumStyleName(dialog, ModalSize.class, size);
118        }
119    
120        @Override
121        protected void onLoad() {
122            super.onLoad();
123            bindJavaScriptEvents(getElement());
124        }
125    
126        @Override
127        public void add(final Widget w) {
128            // User can supply own ModalHeader
129            if (w instanceof ModalHeader) {
130                header.removeFromParent();
131                header = (ModalHeader) w;
132            }
133    
134            if (w instanceof ModalComponent) {
135                content.add(w);
136            } else {
137                super.add(w);
138            }
139        }
140    
141        @Override
142        public void setTitle(final String title) {
143            header.setTitle(title);
144        }
145    
146        @Override
147        public void setClosable(final boolean closable) {
148            header.setClosable(closable);
149        }
150    
151        @Override
152        public boolean isClosable() {
153            return header.isClosable();
154        }
155    
156        /**
157         * If set to true, when the modal is shown it will force hide all other modals
158         *
159         * @param hideOtherModals - true to force hide other modals, false to keep them shown
160         */
161        public void setHideOtherModals(final boolean hideOtherModals) {
162            this.hideOtherModals = hideOtherModals;
163        }
164    
165        /**
166         * If set to true, will remove the modal from the DOM completely and unbind any events to the modal
167         *
168         * @param removeOnHide - true to remove modal and unbind events on hide, false to keep it in the DOM
169         */
170        public void setRemoveOnHide(final boolean removeOnHide) {
171            if (removeOnHide) {
172                addHiddenHandler(new ModalHiddenHandler() {
173                    @Override
174                    public void onHidden(final ModalHiddenEvent evt) {
175                        unbindAllHandlers(getElement());
176                        removeFromParent();
177                    }
178                });
179            }
180        }
181    
182        /**
183         * If set Modal will fade in/out.
184         *
185         * @param fade If {@code true} modal will fade in/out
186         */
187        public void setFade(final boolean fade) {
188            if (fade) {
189                addStyleName(Styles.FADE);
190            } else {
191                removeStyleName(Styles.FADE);
192            }
193        }
194    
195        /**
196         * Sets backdrop of modal.
197         *
198         * @param backdrop Backdrop of modal
199         * @see org.gwtbootstrap3.client.ui.constants.ModalBackdrop
200         */
201        public void setDataBackdrop(final ModalBackdrop backdrop) {
202            if (backdrop != null) {
203                getElement().setAttribute(Attributes.DATA_BACKDROP, backdrop.getBackdrop());
204            } else {
205                getElement().removeAttribute(Attributes.DATA_BACKDROP);
206            }
207        }
208    
209        public void setDataKeyboard(final boolean keyboard) {
210            getElement().setAttribute(Attributes.DATA_KEYBOARD, Boolean.toString(keyboard));
211    
212            // tabindex must be set to -1 for ESC key to work
213            if (keyboard) {
214                getElement().setAttribute(Attributes.TABINDEX, "-1");
215            }
216        }
217    
218        public void toggle() {
219            modal(getElement(), TOGGLE);
220        }
221    
222        public void show() {
223            checkIsAttached();
224            modal(getElement(), SHOW);
225        }
226    
227        public void hide() {
228            modal(getElement(), HIDE);
229        }
230    
231        public HandlerRegistration addShowHandler(final ModalShowHandler modalShowHandler) {
232            return addHandler(modalShowHandler, ModalShowEvent.getType());
233        }
234    
235        public HandlerRegistration addShownHandler(final ModalShownHandler modalShownHandler) {
236            return addHandler(modalShownHandler, ModalShownEvent.getType());
237        }
238    
239        public HandlerRegistration addHideHandler(final ModalHideHandler modalHideHandler) {
240            return addHandler(modalHideHandler, ModalHideEvent.getType());
241        }
242    
243        public HandlerRegistration addHiddenHandler(final ModalHiddenHandler modalHiddenHandler) {
244            return addHandler(modalHiddenHandler, ModalHiddenEvent.getType());
245        }
246    
247        /**
248         * Can be override by subclasses to handle Modal's "show" event however it's
249         * recommended to add an event handler to the modal.
250         *
251         * @param evt Event
252         * @see org.gwtbootstrap3.client.shared.event.ModalShowEvent
253         */
254        protected void onShow(final Event evt) {
255            if (hideOtherModals) {
256                hideOtherModals();
257            }
258            fireEvent(new ModalShowEvent(this, evt));
259        }
260    
261        /**
262         * Can be override by subclasses to handle Modal's "shown" event however
263         * it's recommended to add an event handler to the modal.
264         *
265         * @param evt Event
266         * @see org.gwtbootstrap3.client.shared.event.ModalShownEvent
267         */
268        protected void onShown(final Event evt) {
269            fireEvent(new ModalShownEvent(this, evt));
270        }
271    
272        /**
273         * Can be override by subclasses to handle Modal's "hide" event however it's
274         * recommended to add an event handler to the modal.
275         *
276         * @param evt Event
277         * @see org.gwtbootstrap3.client.shared.event.ModalHideEvent
278         */
279        protected void onHide(final Event evt) {
280            fireEvent(new ModalHideEvent(this, evt));
281        }
282    
283        /**
284         * Can be override by subclasses to handle Modal's "hidden" event however
285         * it's recommended to add an event handler to the modal.
286         *
287         * @param evt Event
288         * @see org.gwtbootstrap3.client.shared.event.ModalHiddenEvent
289         */
290        protected void onHidden(final Event evt) {
291            fireEvent(new ModalHiddenEvent(this, evt));
292        }
293    
294        private void checkIsAttached() {
295            if (!this.isAttached()) {
296                RootPanel.get().add(this);
297            }
298        }
299    
300        private native void bindJavaScriptEvents(final Element e) /*-{
301            var target = this;
302            var $modal = $wnd.jQuery(e);
303    
304            $modal.on('show.bs.modal', function (evt) {
305                target.@org.gwtbootstrap3.client.ui.Modal::onShow(Lcom/google/gwt/user/client/Event;)(evt);
306            });
307    
308            $modal.on('shown.bs.modal', function (evt) {
309                target.@org.gwtbootstrap3.client.ui.Modal::onShown(Lcom/google/gwt/user/client/Event;)(evt);
310            });
311    
312            $modal.on('hide.bs.modal', function (evt) {
313                target.@org.gwtbootstrap3.client.ui.Modal::onHide(Lcom/google/gwt/user/client/Event;)(evt);
314            });
315    
316            $modal.on('hidden.bs.modal', function (evt) {
317                target.@org.gwtbootstrap3.client.ui.Modal::onHidden(Lcom/google/gwt/user/client/Event;)(evt);
318            });
319        }-*/;
320    
321        private native void modal(final Element e, final String arg) /*-{
322            $wnd.jQuery(e).modal(arg);
323        }-*/;
324    
325        // Will iterate over all the modals, if they are visible it will hide them
326        private native void hideOtherModals() /*-{
327            $wnd.jQuery('.modal.in').modal('hide');
328        }-*/;
329    
330        // Unbinds all the handlers
331        private native void unbindAllHandlers(final Element e) /*-{
332            var $e = $wnd.jQuery(e);
333            $e.off('show.bs.modal');
334            $e.off('shown.bs.modal');
335            $e.off('hide.bs.modal');
336            $e.off('hidden.bs.modal');
337        }-*/;
338    }