1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.ui.vaadin.gwt.client.magnoliashell.shell;
35
36 import info.magnolia.ui.vaadin.gwt.client.jquerywrapper.JQueryCallback;
37 import info.magnolia.ui.vaadin.gwt.client.jquerywrapper.JQueryWrapper;
38 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.ShellState;
39 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.ShellMessageWidget;
40 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.ShellMessageWidget.MessageType;
41 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.VInfoMessage;
42 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.VShellErrorMessage;
43 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.shellmessage.VWarningMessage;
44 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.animation.JQueryAnimation;
45 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.widget.AppsViewportWidget;
46 import info.magnolia.ui.vaadin.gwt.client.magnoliashell.viewport.widget.ViewportWidget;
47 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.Fragment;
48 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.ShellAppType;
49 import info.magnolia.ui.vaadin.gwt.client.shared.magnoliashell.ViewportType;
50
51 import java.util.EnumMap;
52 import java.util.Map;
53
54 import com.google.gwt.core.client.Scheduler;
55 import com.google.gwt.core.client.Scheduler.ScheduledCommand;
56 import com.google.gwt.dom.client.Element;
57 import com.google.gwt.event.dom.client.KeyCodes;
58 import com.google.gwt.event.dom.client.KeyPressEvent;
59 import com.google.gwt.event.dom.client.KeyPressHandler;
60 import com.google.gwt.regexp.shared.RegExp;
61 import com.google.gwt.user.client.DOM;
62 import com.google.gwt.user.client.Window;
63 import com.google.gwt.user.client.ui.FocusPanel;
64 import com.google.gwt.user.client.ui.RootPanel;
65 import com.google.gwt.user.client.ui.Widget;
66 import com.googlecode.mgwt.ui.client.widget.touch.TouchPanel;
67 import com.vaadin.client.WidgetUtil;
68
69
70
71
72 public class MagnoliaShellViewImpl extends TouchPanel implements MagnoliaShellView {
73
74 public static final String CLASS_NAME = "v-magnolia-shell";
75
76 public static final String VIEWPORT_SLOT_CLASS_NAME = "v-shell-viewport-slot";
77
78 private final Map<ViewportType, ViewportWidget> viewports = new EnumMap<ViewportType, ViewportWidget>(ViewportType.class);
79
80 private final ShellAppLauncher mainAppLauncher;
81
82 private ShellMessageWidget lowPriorityMessage;
83
84 private ShellMessageWidget hiPriorityMessage;
85
86 private Presenter presenter;
87
88 private JQueryAnimation/magnoliashell/viewport/animation/JQueryAnimation.html#JQueryAnimation">JQueryAnimation viewportShifter = new JQueryAnimation();
89
90 private final Element viewportSlot = DOM.createDiv();
91
92
93
94 private static final RegExp GROOVY_CONSOLE_URL_FRAGMENT_REGEX = RegExp.compile("(app:groovy:)(console|;)");
95
96
97 private final FocusPanel blurHelper = new FocusPanel();
98
99 public MagnoliaShellViewImpl() {
100 super();
101 this.mainAppLauncher = new ShellAppLauncher();
102 getElement().setClassName(CLASS_NAME);
103 viewportSlot.setClassName(VIEWPORT_SLOT_CLASS_NAME);
104 add(mainAppLauncher, getElement());
105
106
107 getElement().appendChild(viewportSlot);
108 viewportShifter.addCallback(new JQueryCallback() {
109 @Override
110 public void execute(JQueryWrapper query) {
111 presenter.updateViewportLayout(appViewport());
112 }
113 });
114
115 initKeyboardShortcutSupport();
116 }
117
118
119
120
121
122
123 protected void initKeyboardShortcutSupport() {
124
125 add(blurHelper, getElement());
126
127 KeyPressHandler keyboardShortcutHandler = new KeyPressHandler() {
128
129 @Override
130 public void onKeyPress(KeyPressEvent event) {
131
132
133 if (isFocusedElementAnInputField()) {
134 return;
135 }
136
137
138 if (event.isAnyModifierKeyDown()) {
139 return;
140 }
141
142 if (isAppInFocusGroovyConsole()) {
143 return;
144 }
145
146 char c = event.getCharCode();
147
148 switch (c) {
149
150
151 case '1':
152 mainAppLauncher.toggleShellApp(ShellAppType.APPLAUNCHER);
153 break;
154 case '2':
155 mainAppLauncher.toggleShellApp(ShellAppType.PULSE);
156 break;
157 case '3':
158 mainAppLauncher.toggleShellApp(ShellAppType.FAVORITE);
159 break;
160
161
162 case '9':
163
164 if (appViewport().readyForAppSwipeOrShortcutNavigation()) {
165 ShellState.get().setAppStarting();
166 appViewport().goToPreviousApp();
167 }
168 break;
169 case '0':
170
171 if (appViewport().readyForAppSwipeOrShortcutNavigation()) {
172 ShellState.get().setAppStarting();
173 appViewport().goToNextApp();
174 }
175 break;
176
177 default:
178
179 }
180 }
181 };
182 RootPanel.get().addDomHandler(keyboardShortcutHandler, KeyPressEvent.getType());
183
184
185
186
187
188
189 KeyPressHandler escapeKeyPressHandler = new KeyPressHandler() {
190 @Override
191 public void onKeyPress(KeyPressEvent event) {
192 int code = event.getNativeEvent().getKeyCode();
193 if (code == KeyCodes.KEY_ESCAPE) {
194 blurHelper.getElement().focus();
195 }
196 }
197 };
198 RootPanel.get().addDomHandler(escapeKeyPressHandler, KeyPressEvent.getType());
199 }
200
201
202
203
204 protected boolean isFocusedElementAnInputField() {
205 final Element focused = WidgetUtil.getFocusedElement();
206 String tagName = focused.getTagName();
207
208 if ("input".equalsIgnoreCase(tagName) || "select".equalsIgnoreCase(tagName) || "textarea".equalsIgnoreCase(tagName)) {
209 return true;
210 }
211
212 Element el = focused;
213 boolean isContentEditable = false;
214 final String contenteditableAttrName = "contenteditable";
215 while (!isContentEditable && el != null) {
216
217 isContentEditable = el.hasAttribute(contenteditableAttrName) && !el.getAttribute(contenteditableAttrName).equalsIgnoreCase("false");
218 el = el.getParentElement();
219 }
220
221 return isContentEditable;
222 }
223
224
225 protected AppsViewportWidget appViewport() {
226 return (AppsViewportWidget) viewports.get(ViewportType.APP);
227 }
228
229 protected void replaceWidget(final Widget oldWidget, final Widget newWidget) {
230 if (oldWidget != newWidget) {
231 if (oldWidget != null) {
232 remove(oldWidget);
233 }
234 }
235 if (getWidgetIndex(newWidget) < 0) {
236 add(newWidget, viewportSlot);
237 }
238 }
239
240 @Override
241 public void setPresenter(Presenter presenter) {
242 this.presenter = presenter;
243 mainAppLauncher.setListener(presenter);
244 }
245
246 @Override
247 public void showMessage(MessageType type, String topic, String message, String id) {
248 final ShellMessageWidget msg;
249 switch (type) {
250 case WARNING:
251 msg = new VWarningMessage(this, topic, message, id);
252 if (lowPriorityMessage != null && getWidgetIndex(lowPriorityMessage) != -1) {
253 lowPriorityMessage.hide();
254 }
255 lowPriorityMessage = msg;
256 break;
257 case INFO:
258 msg = new VInfoMessage(this, topic, message, id);
259 if (lowPriorityMessage != null && getWidgetIndex(lowPriorityMessage) != -1) {
260 lowPriorityMessage.hide();
261 }
262 lowPriorityMessage = msg;
263 break;
264 case ERROR:
265 msg = new VShellErrorMessage(this, topic, message, id);
266 if (hiPriorityMessage != null && getWidgetIndex(hiPriorityMessage) != -1) {
267 hiPriorityMessage.hide();
268 }
269 hiPriorityMessage = msg;
270 break;
271 default:
272 msg = null;
273 }
274 Scheduler.get().scheduleDeferred(new ScheduledCommand() {
275
276 @Override
277 public void execute() {
278 if (msg != null) {
279 add(msg, getElement());
280 }
281 }
282 });
283 }
284
285 @Override
286 public void hideAllMessages() {
287 if (hiPriorityMessage != null && getWidgetIndex(hiPriorityMessage) != -1) {
288 hiPriorityMessage.hideWithoutTransition();
289 }
290 if (lowPriorityMessage != null && getWidgetIndex(lowPriorityMessage) != -1) {
291 lowPriorityMessage.hideWithoutTransition();
292 }
293
294 hiPriorityMessage = null;
295 lowPriorityMessage = null;
296 }
297
298 @Override
299 public void updateViewport(ViewportWidget viewport, ViewportType type) {
300 ViewportWidget oldViewport = viewports.get(type);
301 if (oldViewport != viewport) {
302 replaceWidget(oldViewport, viewport);
303 viewports.put(type, viewport);
304 }
305 }
306
307 @Override
308 public void shiftViewportsVertically(int shiftPx, boolean animated) {
309 viewportShifter.setProperty("top", mainAppLauncher.getOffsetHeight() + shiftPx);
310 if (shiftPx == 0 || shiftPx == 60) {
311 viewportShifter.clearTopAfterThisAnimation();
312 }
313 viewportShifter.run(animated ? 300 : 0, viewportSlot);
314 }
315
316 @Override
317 public void setShellAppIndication(ShellAppType type, int indication) {
318 mainAppLauncher.setIndication(type, indication);
319 }
320
321 @Override
322 public void closeMessageEager(final String id) {
323 presenter.removeMessage(id);
324 }
325
326 @Override
327 public void navigateToMessageDetails(String id) {
328 presenter.activateShellApp(Fragment.fromString("shell:pulse:messages/" + id));
329 }
330
331 @Override
332 public void updateShellDivet() {
333 mainAppLauncher.updateDivet();
334 }
335
336 @Override
337 public void openOverlayOnWidget(Widget overlayWidget, Widget overlayParent) {
338 add(overlayWidget, overlayParent.getElement());
339 }
340
341 @Override
342 public void onShellAppStarting(ShellAppType type) {
343 mainAppLauncher.activateControl(type);
344 }
345
346 @Override
347 public void onAppStarting() {
348 mainAppLauncher.deactivateControls();
349 }
350
351 @Override
352 public void setUserMenu(Widget widget) {
353 mainAppLauncher.setUserMenu(widget);
354 }
355
356 @Override
357 public void setSticker(Widget widget) {
358 mainAppLauncher.setSticker(widget);
359 }
360
361 @Override
362 public void onLoad() {
363 super.onLoad();
364 presenter.initHistory();
365 }
366
367 @Override
368 public boolean hasOverlay(Widget widget) {
369 return getWidgetIndex(widget) != -1;
370 }
371
372
373
374
375 private boolean isAppInFocusGroovyConsole() {
376 String hash = Window.Location.getHash();
377 return GROOVY_CONSOLE_URL_FRAGMENT_REGEX.test(hash);
378 }
379
380 }