View Javadoc
1   /**
2    * This file Copyright (c) 2012-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.dialog;
35  
36  import info.magnolia.i18nsystem.I18nizer;
37  import info.magnolia.i18nsystem.SimpleTranslator;
38  import info.magnolia.objectfactory.ComponentProvider;
39  import info.magnolia.ui.api.action.ActionDefinition;
40  import info.magnolia.ui.api.action.ActionExecutionException;
41  import info.magnolia.ui.api.action.ActionExecutor;
42  import info.magnolia.ui.api.app.AppContext;
43  import info.magnolia.ui.api.app.SubAppContext;
44  import info.magnolia.ui.api.context.UiContext;
45  import info.magnolia.ui.api.message.Message;
46  import info.magnolia.ui.api.message.MessageType;
47  import info.magnolia.ui.api.overlay.ConfirmationCallback;
48  import info.magnolia.ui.dialog.actionarea.ActionAreaPresenter;
49  import info.magnolia.ui.dialog.actionarea.ActionListener;
50  import info.magnolia.ui.dialog.actionarea.EditorActionAreaPresenter;
51  import info.magnolia.ui.dialog.actionarea.view.EditorActionAreaView;
52  import info.magnolia.ui.dialog.definition.DialogDefinition;
53  import info.magnolia.ui.vaadin.dialog.BaseDialog;
54  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
55  
56  import javax.inject.Inject;
57  
58  import net.sf.cglib.proxy.Enhancer;
59  
60  import org.slf4j.Logger;
61  import org.slf4j.LoggerFactory;
62  
63  import com.vaadin.event.ShortcutAction.KeyCode;
64  import com.vaadin.event.ShortcutAction.ModifierKey;
65  import com.vaadin.event.ShortcutListener;
66  import com.vaadin.server.WebBrowser;
67  import com.vaadin.ui.Panel;
68  import com.vaadin.ui.UI;
69  
70  /**
71   * Base implementation of {@link DialogPresenter}.
72   */
73  public class BaseDialogPresenter implements DialogPresenter, ActionListener {
74  
75      private Logger log = LoggerFactory.getLogger(getClass());
76  
77      private DialogView view;
78  
79      protected ComponentProvider componentProvider;
80  
81      private ActionExecutor executor;
82  
83      private EditorActionAreaPresenter editorActionAreaPresenter;
84  
85      private final I18nizer i18nizer;
86  
87      private final SimpleTranslator i18n;
88  
89      private UiContext uiContext;
90  
91      private DialogDefinition definition;
92  
93      @Inject
94      public BaseDialogPresenter(ComponentProvider componentProvider, ActionExecutor executor, DialogView view, I18nizer i18nizer, SimpleTranslator i18n) {
95          this.componentProvider = componentProvider;
96          this.executor = executor;
97          this.view = view;
98          this.i18nizer = i18nizer;
99          this.i18n = i18n;
100     }
101 
102     @Override
103     public DialogView getView() {
104         return view;
105     }
106 
107     @Override
108     public ActionAreaPresenter getActionArea() {
109         return editorActionAreaPresenter;
110     }
111 
112     @Override
113     public void closeDialog() {
114         view.close();
115     }
116 
117     @Override
118     public void addShortcut(final String actionName, final int keyCode, final int... modifiers) {
119 
120         view.addShortcut(new ShortcutListener(actionName, keyCode, modifiers) {
121             @Override
122             public void handleAction(Object sender, Object target) {
123                 executeAction(actionName, new Object[0]);
124             }
125         });
126     }
127 
128     @Override
129     public DialogView start(DialogDefinition definition, UiContext uiContext) {
130         this.uiContext = uiContext;
131         // ChooseDialogDefinition is already enhanced as it is obtained via ContentAppDescriptor.getChooseDialog() at ContentApp.openChooseDialog(..)
132         if (Enhancer.isEnhanced(definition.getClass())) {
133             this.definition = definition;
134         } else {
135             this.definition = i18nizer.decorate(definition);
136         }
137         this.editorActionAreaPresenter = componentProvider.newInstance(definition.getActionArea().getPresenterClass());
138         EditorActionAreaView editorActionAreaView = editorActionAreaPresenter.start(filterActions(), definition.getActionArea(), this, uiContext);
139 
140         // Set modifier key based on OS.
141         int osSpecificModifierKey;
142         UI ui = UI.getCurrent();
143         if (ui != null) {
144             WebBrowser browser = ui.getPage().getWebBrowser();
145             if (browser.isWindows()) {
146                 osSpecificModifierKey = ModifierKey.CTRL;
147             } else {
148                 // osx and linux
149                 osSpecificModifierKey = ModifierKey.META;
150             }
151 
152             if (definition.getActions().containsKey(BaseDialog.COMMIT_ACTION_NAME)) {
153                 addShortcut(BaseDialog.COMMIT_ACTION_NAME, KeyCode.S, osSpecificModifierKey);
154             }
155             if (definition.getActions().containsKey(BaseDialog.CANCEL_ACTION_NAME)) {
156                 addShortcut(BaseDialog.CANCEL_ACTION_NAME, KeyCode.W, osSpecificModifierKey);
157             }
158 
159         } else {
160             log.warn("The current Vaadin UI was null when starting {}, as a result dialog keyboard shortcuts will not work.", this);
161         }
162 
163         view.addShortcut(new CloseDialogAfterConfirmationShortcutListener(KeyCode.ESCAPE));
164         view.addShortcut(new CommitDialogShortcutListener(KeyCode.ENTER));
165 
166         this.view.setActionAreaView(editorActionAreaView);
167         return this.view;
168     }
169 
170     protected Iterable<ActionDefinition> filterActions() {
171         return getDefinition().getActions().values();
172     }
173 
174     protected Object[] getActionParameters(String actionName) {
175         return new Object[]{this};
176     }
177 
178     @Override
179     public void onActionFired(String actionName, Object... actionContextParams) {
180         executeAction(actionName, actionContextParams);
181     }
182 
183     protected void executeAction(String actionName, Object[] actionContextParams) {
184         Object[] providedParameters = getActionParameters(actionName);
185         Object[] combinedParameters = new Object[providedParameters.length + actionContextParams.length];
186         System.arraycopy(providedParameters, 0, combinedParameters, 0, providedParameters.length);
187         System.arraycopy(actionContextParams, 0, combinedParameters, providedParameters.length, actionContextParams.length);
188         try {
189             executor.execute(actionName, combinedParameters);
190         } catch (ActionExecutionException e) {
191             String exceptionStatement = i18n.translate("ui-dialog.actionexecutionerror.basemessage");
192             Message error = new Message(MessageType.ERROR, exceptionStatement, e.getMessage());
193             log.error(exceptionStatement, e);
194             if (uiContext instanceof AppContext) {
195                 ((AppContext) uiContext).sendLocalMessage(error);
196             } else if (uiContext instanceof SubAppContext) {
197                 ((SubAppContext) uiContext).getAppContext().sendLocalMessage(error);
198             }
199 
200         }
201     }
202 
203     protected DialogDefinition getDefinition() {
204         return definition;
205     }
206 
207     protected ActionExecutor getExecutor() {
208         return executor;
209     }
210 
211     protected I18nizer getI18nizer() {
212         return i18nizer;
213     }
214 
215     /**
216      * A shortcut listener used to close the dialog.
217      */
218     protected class CloseDialogShortcutListener extends ShortcutListener {
219 
220         public CloseDialogShortcutListener(int keyCode, int... modifierKey) {
221             super("", keyCode, modifierKey);
222         }
223 
224         @Override
225         public void handleAction(Object sender, Object target) {
226             closeDialog();
227         }
228     }
229 
230     /**
231      * A shortcut listener which opens a confirmation to confirm closing the dialog.
232      */
233     protected class CloseDialogAfterConfirmationShortcutListener extends ShortcutListener {
234 
235         public CloseDialogAfterConfirmationShortcutListener(int keyCode, int... modifierKey) {
236             super("", keyCode, modifierKey);
237         }
238 
239         @Override
240         public void handleAction(Object sender, Object target) {
241             uiContext.openConfirmation(
242                     MessageStyleTypeEnum.WARNING, i18n.translate("ui-dialog.closeConfirmation.title"), i18n.translate("ui-dialog.closeConfirmation.body"), i18n.translate("ui-dialog.closeConfirmation.confirmButton"), i18n.translate("ui-dialog.cancelButton"), false,
243                     new ConfirmationCallback() {
244                         @Override
245                         public void onSuccess() {
246                             closeDialog();
247                         }
248 
249                         @Override
250                         public void onCancel() {
251                             if (getView() instanceof Panel) {
252                                 ((Panel) getView()).focus();
253                             }
254                         }
255                     });
256         }
257     }
258 
259     /**
260      * A shortcut listener used to commit the dialog.
261      */
262     protected class CommitDialogShortcutListener extends ShortcutListener {
263 
264         public CommitDialogShortcutListener(int keyCode, int... modifierKey) {
265             super("", keyCode, modifierKey);
266         }
267 
268         @Override
269         public void handleAction(Object sender, Object target) {
270             // textareas are excluded on the client-side, see 'EnterFriendlyShortcutActionHandler', used in PanelConnector
271             executeAction(BaseDialog.COMMIT_ACTION_NAME, new Object[0]);
272         }
273     }
274 }