View Javadoc
1   /**
2    * This file Copyright (c) 2013-2015 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.ui.framework.overlay;
35  
36  import info.magnolia.objectfactory.Classes;
37  import info.magnolia.ui.api.overlay.AlertCallback;
38  import info.magnolia.ui.api.overlay.ConfirmationCallback;
39  import info.magnolia.ui.api.overlay.MessageStyleType;
40  import info.magnolia.ui.api.overlay.NotificationCallback;
41  import info.magnolia.ui.api.overlay.OverlayCloser;
42  import info.magnolia.ui.api.overlay.OverlayLayer;
43  import info.magnolia.ui.api.view.View;
44  import info.magnolia.ui.vaadin.dialog.BaseDialog;
45  import info.magnolia.ui.vaadin.dialog.ConfirmationDialog;
46  import info.magnolia.ui.vaadin.dialog.ConfirmationDialog.ConfirmationEvent;
47  import info.magnolia.ui.vaadin.dialog.LightDialog;
48  import info.magnolia.ui.vaadin.dialog.Notification;
49  import info.magnolia.ui.vaadin.icon.CompositeIcon;
50  
51  import com.vaadin.event.LayoutEvents;
52  import com.vaadin.event.ShortcutAction;
53  import com.vaadin.event.ShortcutListener;
54  import com.vaadin.server.Sizeable.Unit;
55  import com.vaadin.shared.ui.label.ContentMode;
56  import com.vaadin.ui.Button;
57  import com.vaadin.ui.Button.ClickEvent;
58  import com.vaadin.ui.Button.ClickListener;
59  import com.vaadin.ui.CssLayout;
60  import com.vaadin.ui.HorizontalLayout;
61  import com.vaadin.ui.Label;
62  import com.vaadin.ui.Panel;
63  import com.vaadin.ui.themes.BaseTheme;
64  
65  /**
66   * Provides implementations for most OverlayLayer methods.
67   */
68  public abstract class OverlayPresenter implements OverlayLayer {
69  
70      public static final int TIMEOUT_SECONDS_DEFAULT = 3;
71  
72      /**
73       * Opens an overlay with default strong modality level.
74       */
75      @Override
76      public OverlayCloser openOverlay(View view) {
77          return openOverlay(view, ModalityLevel.STRONG);
78      }
79  
80      /**
81       * Opens an alert dialog of given {@link MessageStyleType type}, with given body.
82       * 
83       * @param type the message level, i.e. INFO, WARNING or ERROR
84       * @param body the alert dialog's body as a magnolia {@link View}; alternatively one may wrap any Vaadin component as a View using {@link ViewAdapter}
85       * @param okButton the OK button text
86       * @param callback the callback to execute when the OK button is pressed, or when the dialog is closed
87       */
88      @Override
89      public void openAlert(MessageStyleType type, View body, String okButton, AlertCallback callback) {
90          openAlert(type, null, body, okButton, callback);
91      }
92  
93      /**
94       * Opens an alert dialog of given {@link MessageStyleType type}, with given title and body.
95       *
96       * @param type the message level, i.e. INFO, WARNING or ERROR
97       * @param title the alert dialog's title
98       * @param body the alert dialog's text body
99       * @param okButton the OK button text
100      * @param callback the callback to execute when the OK button is pressed, or when the dialog is closed
101      */
102     @Override
103     public void openAlert(MessageStyleType type, String title, String body, String okButton, AlertCallback callback) {
104         openAlert(type, title, new ViewAdapter(new Label(body, ContentMode.HTML)), okButton, callback);
105     }
106 
107     /**
108      * Opens an alert dialog of given {@link MessageStyleType type}, with given title and body.
109      *
110      * @param type the message level, i.e. INFO, WARNING or ERROR
111      * @param title the alert dialog's title
112      * @param body the alert dialog's body as a magnolia {@link View}; alternatively one may wrap any Vaadin component as a View using {@link ViewAdapter}
113      * @param okButton the OK button text
114      * @param callback the callback to execute when the OK button is pressed, or when the dialog is closed
115      */
116     public void openAlert(MessageStyleType type, String title, View body, String okButton, final AlertCallback callback) {
117 
118         final BaseDialog dialog = new LightDialog();
119         dialog.addStyleName(type.getCssClass());
120         dialog.addStyleName("alert");
121 
122         dialog.setCaption(title);
123         CompositeIcon icon = (CompositeIcon) Classes.getClassFactory().newInstance(type.getIconClass());
124         icon.setStyleName("dialog-icon");
125         dialog.setHeaderToolbar(icon);
126         dialog.showCloseButton();
127 
128         dialog.setContent(body.asVaadinComponent());
129 
130         Panel shortcutPanel = new Panel();
131         shortcutPanel.setStyleName("shortcut-panel");
132         shortcutPanel.setHeight(100, Unit.PERCENTAGE);
133         shortcutPanel.setWidth(100, Unit.PERCENTAGE);
134         shortcutPanel.setContent(dialog);
135 
136         final OverlayCloser overlayCloser = openOverlay(new ViewAdapter(shortcutPanel), ModalityLevel.LIGHT);
137         final ShortcutListener escapeShortcut = new ShortcutListener("Escape shortcut", ShortcutAction.KeyCode.ESCAPE, null) {
138             @Override
139             public void handleAction(Object sender, Object target) {
140                 callback.onOk();
141                 dialog.closeSelf();
142             }
143         };
144         shortcutPanel.addShortcutListener(escapeShortcut);
145         addOkHandler(dialog, okButton, overlayCloser, callback);
146         dialog.addDialogCloseHandler(createCloseHandler(overlayCloser));
147     }
148 
149     private void addOkHandler(BaseDialog dialog, String okButtonText, final OverlayCloser overlayCloser, final AlertCallback cb) {
150         CssLayout footer = new CssLayout();
151         footer.setWidth(100, Unit.PERCENTAGE);
152         footer.addStyleName("v-align-right");
153         Button okButton = new Button(okButtonText, new ClickListener() {
154             @Override
155             public void buttonClick(ClickEvent event) {
156                 cb.onOk();
157                 overlayCloser.close();
158             }
159         });
160         okButton.focus();
161         footer.addComponent(okButton);
162         dialog.setFooterToolbar(footer);
163     }
164 
165     /**
166      * Opens a confirmation dialog of given {@link MessageStyleType type}, with given body.
167      * 
168      * @param type the message level, i.e. INFO, WARNING or ERROR
169      * @param body the confirmation dialog's body as a magnolia {@link View}; alternatively one may wrap any Vaadin component as a View using {@link ViewAdapter}
170      * @param confirmButton the confirm button text
171      * @param cancelButton the cancel button text
172      * @param cancelIsDefault whether the cancel button should be focused by default
173      * @param callback the callback to execute when the any button is pressed, or when the dialog is closed
174      */
175     @Override
176     public void openConfirmation(MessageStyleType type, View body, String confirmButton, String cancelButton, boolean cancelIsDefault, ConfirmationCallback callback) {
177         openConfirmation(type, null, body, confirmButton, cancelButton, cancelIsDefault, callback);
178     }
179 
180     /**
181      * Opens a confirmation dialog of given {@link MessageStyleType type}, with given title and body.
182      * 
183      * @param type the message level, i.e. INFO, WARNING or ERROR
184      * @param title the confirmation dialog's title
185      * @param body the confirmation dialog's body text
186      * @param confirmButton the confirm button text
187      * @param cancelButton the cancel button text
188      * @param cancelIsDefault whether the cancel button should be focused by default
189      * @param callback the callback to execute when the OK button is pressed, or when the dialog is closed
190      */
191     @Override
192     public void openConfirmation(MessageStyleType type, String title, String body, String confirmButton, String cancelButton, boolean cancelIsDefault, ConfirmationCallback callback) {
193         openConfirmation(type, title, new ViewAdapter(new Label(body, ContentMode.HTML)), confirmButton, cancelButton, cancelIsDefault, callback);
194     }
195 
196     /**
197      * Opens a confirmation dialog of given {@link MessageStyleType type}, with given title and body.
198      * 
199      * @param type the message level, i.e. INFO, WARNING or ERROR
200      * @param title the confirmation dialog's title
201      * @param body the confirmation dialog's body as a magnolia {@link View}; alternatively one may wrap any Vaadin component as a View using {@link ViewAdapter}
202      * @param confirmButton the confirm button text
203      * @param cancelButton the cancel button text
204      * @param cancelIsDefault whether the cancel button should be focused by default
205      * @param callback the callback to execute when the OK button is pressed, or when the dialog is closed
206      */
207     public void openConfirmation(MessageStyleType type, String title, View body, String confirmButton, String cancelButton, boolean cancelIsDefault, final ConfirmationCallback callback) {
208         final ConfirmationDialog dialog = new ConfirmationDialog(body.asVaadinComponent(), confirmButton, cancelButton, cancelIsDefault);
209         dialog.addStyleName(type.getCssClass());
210         dialog.addStyleName("confirmation");
211 
212         dialog.setCaption(title);
213         CompositeIcon icon = (CompositeIcon) Classes.getClassFactory().newInstance(type.getIconClass());
214         icon.setStyleName("dialog-icon");
215         dialog.setHeaderToolbar(icon);
216         dialog.showCloseButton();
217 
218         dialog.setContent(body.asVaadinComponent());
219 
220         Panel shortcutPanel = new Panel();
221         shortcutPanel.setStyleName("shortcut-panel");
222         shortcutPanel.setHeight(100, Unit.PERCENTAGE);
223         shortcutPanel.setWidth(100, Unit.PERCENTAGE);
224         shortcutPanel.setContent(dialog);
225 
226         final OverlayCloser overlayCloser = openOverlay(new ViewAdapter(shortcutPanel), ModalityLevel.LIGHT);
227 
228         final ShortcutListener escapeShortcut = new ShortcutListener("Escape shortcut", ShortcutAction.KeyCode.ESCAPE, null) {
229             @Override
230             public void handleAction(Object sender, Object target) {
231                 callback.onCancel();
232                 dialog.closeSelf();
233             }
234         };
235         shortcutPanel.addShortcutListener(escapeShortcut);
236         dialog.addConfirmationHandler(
237                 new ConfirmationDialog.ConfirmationEvent.Handler() {
238                     @Override
239                     public void onConfirmation(ConfirmationEvent event) {
240                         if (event.isConfirmed()) {
241                             callback.onSuccess();
242                         } else {
243                             callback.onCancel();
244                         }
245                         overlayCloser.close();
246                     }
247                 });
248         dialog.addDialogCloseHandler(createCloseHandler(overlayCloser));
249     }
250 
251     private BaseDialog.DialogCloseEvent.Handler createCloseHandler(final OverlayCloser overlayCloser) {
252         return new BaseDialog.DialogCloseEvent.Handler() {
253             @Override
254             public void onClose(BaseDialog.DialogCloseEvent event) {
255                 overlayCloser.close();
256             }
257         };
258     }
259 
260     /**
261      * Opens a notification of given {@link MessageStyleType type}, with given body; it can close automatically after a timeout.
262      */
263     @Override
264     public void openNotification(final MessageStyleType type, boolean doesTimeout, View viewToShow) {
265         final Notification notification = new Notification(type);
266         notification.setContent(viewToShow.asVaadinComponent());
267 
268         Panel shortcutPanel = new Panel();
269         shortcutPanel.setStyleName("shortcut-panel");
270         shortcutPanel.setWidth(null);
271         shortcutPanel.setContent(notification.asVaadinComponent());
272         final OverlayCloser closer = openOverlay(new ViewAdapter(shortcutPanel), ModalityLevel.NON_MODAL);
273 
274         final ShortcutListener escapeShortcut = new ShortcutListener("Escape shortcut", ShortcutAction.KeyCode.ESCAPE, null) {
275             @Override
276             public void handleAction(Object sender, Object target) {
277                 closer.close();
278             }
279         };
280         shortcutPanel.addShortcutListener(escapeShortcut);
281 
282         notification.addCloseButtonListener(new ClickListener() {
283             @Override
284             public void buttonClick(ClickEvent clickEvent) {
285                 closer.close();
286             }
287         });
288 
289         notification.addNotificationBodyClickListener(new LayoutEvents.LayoutClickListener() {
290             @Override
291             public void layoutClick(LayoutEvents.LayoutClickEvent layoutClickEvent) {
292                 closer.setCloseTimeout(-1);
293             }
294         });
295 
296         if (doesTimeout) {
297             closer.setCloseTimeout(TIMEOUT_SECONDS_DEFAULT);
298         }
299 
300     }
301 
302     /**
303      * Opens a notification of given {@link MessageStyleType type}, with given body text; it can close automatically after a timeout.
304      */
305     @Override
306     public void openNotification(MessageStyleType type, boolean doesTimeout, final String title) {
307         openNotification(type, doesTimeout, new ViewAdapter(new Label(title, ContentMode.HTML)));
308 
309     }
310 
311     /**
312      * Convenience method for presenting notification indicator with string content.
313      */
314     @Override
315     public void openNotification(MessageStyleType type, boolean doesTimeout, String title, String linkText, final NotificationCallback callback) {
316         HorizontalLayout layout = new HorizontalLayout();
317         layout.setSpacing(true);
318         layout.addComponent(new Label(title, ContentMode.HTML));
319 
320         Button button = new Button(linkText, new ClickListener() {
321             @Override
322             public void buttonClick(ClickEvent event) {
323                 callback.onLinkClicked();
324             }
325         });
326         button.setStyleName(BaseTheme.BUTTON_LINK);
327 
328         layout.addComponent(button);
329         openNotification(type, doesTimeout, new ViewAdapter(layout));
330     }
331 
332 }