View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 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.workbench;
35  
36  import com.vaadin.ui.Alignment;
37  import com.vaadin.ui.Button;
38  import com.vaadin.ui.Component;
39  import com.vaadin.ui.CssLayout;
40  import com.vaadin.ui.HorizontalLayout;
41  import com.vaadin.ui.Label;
42  import com.vaadin.ui.Panel;
43  import com.vaadin.ui.VerticalLayout;
44  import info.magnolia.i18nsystem.SimpleTranslator;
45  import info.magnolia.icons.MagnoliaIcons;
46  import info.magnolia.ui.api.view.View;
47  import info.magnolia.ui.framework.ioc.AdmincentralFlavour;
48  import info.magnolia.ui.theme.ResurfaceTheme;
49  import info.magnolia.ui.workbench.contenttool.ContentToolDefinition;
50  import info.magnolia.ui.workbench.definition.ContentPresenterDefinition;
51  import info.magnolia.ui.workbench.list.ListPresenterDefinition;
52  import info.magnolia.ui.workbench.search.SearchPresenterDefinition;
53  import info.magnolia.ui.workbench.tree.TreeView;
54  
55  import java.io.Serializable;
56  import java.util.HashMap;
57  import java.util.Map;
58  
59  import com.vaadin.event.ShortcutAction;
60  import com.vaadin.event.ShortcutListener;
61  
62  /**
63   * Implementation of the workbench view.
64   */
65  public class WorkbenchViewImpl extends VerticalLayout implements WorkbenchView, Serializable {
66  
67      protected final HorizontalLayout toolBar = new HorizontalLayout();
68  
69      private final CssLayout filterBar = new CssLayout();
70  
71      private final HorizontalLayout viewModes = new HorizontalLayout();
72  
73      protected final Panel keyboardEventPanel;
74  
75      private StatusBarView statusBar;
76  
77      private Map<String, ContentView> contentViews = new HashMap<String, ContentView>();
78  
79      private Map<String, Button> contentViewsButton = new HashMap<String, Button>();
80  
81      private String currentViewType;
82  
83      private WorkbenchView.Listener listener;
84  
85      private Label title;
86  
87      private Button toggleButton;
88  
89      public WorkbenchViewImpl() {
90          setSizeFull();
91          addStyleName("workbench");
92          setSpacing(false);
93          setMargin(false);
94  
95          HorizontalLayout toolBarWrapper = new HorizontalLayout();
96          toolBarWrapper.addStyleName("toolbar");
97          toolBarWrapper.setWidth(100.0F, Unit.PERCENTAGE);
98          toolBarWrapper.setSpacing(true);
99          toolBarWrapper.setDefaultComponentAlignment(Alignment.BOTTOM_LEFT);
100 
101         title = new Label();
102         title.addStyleName("heading-1");
103         title.setVisible(false);
104         toolBarWrapper.addComponent(title);
105         toolBarWrapper.setExpandRatio(title, 0);
106 
107         Label spacer = new Label();
108         toolBarWrapper.addComponent(spacer);
109         toolBarWrapper.setExpandRatio(spacer, 1f);
110 
111         toolBar.addStyleName("tools");
112         toolBar.setSpacing(true);
113 
114         createFilterToggle();
115 
116         viewModes.setStyleName("view-modes");
117         viewModes.setSpacing(true);
118         toolBar.addComponent(viewModes);
119         toolBar.setComponentAlignment(viewModes, Alignment.BOTTOM_RIGHT);
120 
121         toolBarWrapper.addComponent(toolBar);
122         toolBarWrapper.setComponentAlignment(toolBar, Alignment.BOTTOM_RIGHT);
123         toolBarWrapper.setExpandRatio(toolBar, 0);
124 
125         addComponent(toolBarWrapper);
126         setExpandRatio(toolBarWrapper, 0.0F);
127 
128         addComponent(filterBar);
129         setExpandRatio(filterBar, 0.0F);
130         filterBar.addStyleName("filter-bar");
131         filterBar.setWidth(100, Unit.PERCENTAGE);
132         filterBar.setVisible(false);
133 
134         this.keyboardEventPanel = new Panel();
135         this.keyboardEventPanel.setSizeFull();
136         this.keyboardEventPanel.addStyleName("keyboard-panel");
137         addComponent(keyboardEventPanel, 2);
138         setExpandRatio(keyboardEventPanel, 1.0F);
139 
140         bindKeyboardHandlers();
141     }
142 
143     private void createFilterToggle() {
144         toggleButton = new Button(MagnoliaIcons.SEARCH_RESULT_FILTERS);
145         toggleButton.addStyleNames(ResurfaceTheme.BUTTON_ICON);
146         toggleButton.addStyleName("filter-toggle");
147         toggleButton.setVisible(false);
148 
149         toolBar.addComponent(toggleButton);
150         toolBar.setComponentAlignment(toggleButton, Alignment.BOTTOM_RIGHT);
151 
152         toggleButton.addClickListener(event -> {
153             boolean filterVisible = filterBar.isVisible();
154 
155             filterBar.setVisible(!filterVisible);
156 
157             if (!filterVisible) {
158                 toggleButton.addStyleName("active");
159             } else {
160                 toggleButton.removeStyleName("active");
161             }
162         });
163     }
164 
165     /**
166      * @deprecated since 5.4.3. Use default constructor instead.
167      */
168     @Deprecated
169     public WorkbenchViewImpl(SimpleTranslator i18n) {
170         this();
171     }
172 
173     public void bindKeyboardHandlers() {
174 
175         final ShortcutListener enterShortcut = new ShortcutListener("Enter shortcut", ShortcutAction.KeyCode.ENTER, null) {
176             @Override
177             public void handleAction(Object sender, Object target) {
178                 getSelectedView().onShortcutKey(KeyCode.ENTER, null);
179             }
180         };
181         keyboardEventPanel.addShortcutListener(enterShortcut);
182 
183         final ShortcutListener deleteShortcut = new ShortcutListener("Delete shortcut", ShortcutAction.KeyCode.DELETE, null) {
184             @Override
185             public void handleAction(Object sender, Object target) {
186                 getSelectedView().onShortcutKey(KeyCode.DELETE, null);
187             }
188         };
189         // MGNLUI-2106 disable the delete shortcut until we apply it without disrupting inplace-editing
190         // keyboardEventPanel.addShortcutListener(deleteShortcut);
191     }
192 
193     @Override
194     public void setSearchQuery(String query) {
195         if (listener != null) {
196             listener.onSearchQueryChange(query);
197         }
198     }
199 
200 
201     /**
202      * Adds a content view by given view type, content view and content view definition.
203      * @deprecated since 5.4.3. Use addContentView( viewType, view, viewTypeIcon) instead. Interface method will become deprecated on magnolia-ui 5.5.
204      */
205     @Deprecated
206     @Override
207     public void addContentView(String viewType, ContentView view, ContentPresenterDefinition contentViewDefintion) {
208         addContentView(viewType, view, contentViewDefintion.getIcon());
209     }
210 
211     /**
212      * Adds a content view by given view type, content view and view type icon.
213      */
214     public void addContentView(String viewType, ContentView view, String viewTypeIcon) {
215         contentViews.put(viewType, view);
216 
217         if (view instanceof TreeView) {
218             ((TreeView) view).setActionManager(keyboardEventPanel);
219         }
220 
221         // display search-box only if both list and search content presenters are configured
222         if (contentViews.containsKey(ListPresenterDefinition.VIEW_TYPE) && contentViews.containsKey(SearchPresenterDefinition.VIEW_TYPE)) {
223             int contentToolsCount = toolBar.getComponentCount();
224             if (contentToolsCount > 1) { // components > 1 because first component in the toolbar is switcher between tree/list view
225                 toolBar.getComponent(contentToolsCount - 1).setVisible(true);
226             }
227         }
228 
229         if (SearchPresenterDefinition.VIEW_TYPE.equals(viewType)) {
230             // do not add a view-type button for search
231             return;
232         }
233 
234         // set button
235         Button button;
236         if (!AdmincentralFlavour.get().isM5()) {
237             button = new Button(MagnoliaIcons.forCssClass(viewTypeIcon).orElse(MagnoliaIcons.VIEW_LIST),
238                     clickEvent -> fireViewTypeChangedEvent(viewType));
239         } else {
240             button = buildButton(viewType, viewTypeIcon);
241         }
242         button.addStyleName(ResurfaceTheme.BUTTON_ICON);
243         contentViewsButton.put(viewType, button);
244         viewModes.addComponent(button);
245         viewModes.setVisible(contentViews.size() > 1);
246     }
247 
248     @Override
249     public void setViewType(String type) {
250         final Component c = contentViews.get(type).asVaadinComponent();
251 
252         keyboardEventPanel.setContent(c);
253 
254         if (SearchPresenterDefinition.VIEW_TYPE.equals(currentViewType) && !SearchPresenterDefinition.VIEW_TYPE.equals(type)) {
255             setSearchQuery(null);
256         }
257         setViewTypeStyling(type);
258         currentViewType = type;
259     }
260 
261     private void fireViewTypeChangedEvent(String viewType) {
262         this.listener.onViewTypeChanged(viewType);
263     }
264 
265     @Override
266     public void setStatusBarView(StatusBarView statusBar) {
267         if (AdmincentralFlavour.get().isM5()) {
268             Component c = statusBar.asVaadinComponent();
269             if (this.statusBar == null) {
270                 addComponent(c, getComponentCount()); // add last
271             } else {
272                 replaceComponent(this.statusBar.asVaadinComponent(), c);
273             }
274             setExpandRatio(c, 0);
275         }
276         this.statusBar = statusBar;
277     }
278 
279     @Override
280     public View getStatusBarView() {
281         return this.statusBar;
282     }
283 
284     @Override
285     public ContentView getSelectedView() {
286         return contentViews.get(currentViewType);
287     }
288 
289     @Override
290     public Component asVaadinComponent() {
291         return this;
292     }
293 
294     @Override
295     public void setListener(WorkbenchView.Listener listener) {
296         this.listener = listener;
297     }
298 
299     @Override
300     public void setTitle(String title) {
301         this.title.setValue(title);
302         this.title.setVisible(true);
303     }
304 
305     @Deprecated
306     private Button buildButton(final String viewType, final String icon) {
307         Button button = new Button();
308         button.addClickListener((Button.ClickListener) event -> fireViewTypeChangedEvent(viewType));
309         button.setCaptionAsHtml(true);
310         button.setCaption("<span class=\"" + icon + "\"></span>");
311         return button;
312     }
313 
314     private void setViewTypeStyling(final String viewType) {
315         for (Map.Entry<String, Button> entry : contentViewsButton.entrySet()) {
316             entry.getValue().removeStyleName("active");
317             if (entry.getKey().equals(viewType)) {
318                 entry.getValue().addStyleName("active");
319             }
320         }
321         // search is a list view
322         if (viewType.equals(SearchPresenterDefinition.VIEW_TYPE) && contentViews.containsKey(ListPresenterDefinition.VIEW_TYPE)) {
323             contentViewsButton.get(ListPresenterDefinition.VIEW_TYPE).addStyleName("active");
324         }
325     }
326 
327     @Override
328     public void setMultiselect(boolean multiselect) {
329         for (String type : contentViews.keySet()) {
330             contentViews.get(type).setMultiselect(multiselect);
331         }
332     }
333 
334     @Override
335     public void addContentTool(View view) {
336         addContentTool(view, ContentToolDefinition.Alignment.RIGHT, 0);
337     }
338 
339     @Override
340     public void addContentTool(View view, ContentToolDefinition.Alignment alignment, float expandRatio) {
341         final Component toolComponent = view.asVaadinComponent();
342 
343         toolComponent.addStyleName("content-tool");
344 
345         toolBar.addComponent(toolComponent, 0);
346         toolBar.setExpandRatio(toolComponent, expandRatio);
347 
348         Alignment vaadinAlignment;
349         switch (alignment) {
350         case RIGHT:
351             vaadinAlignment = Alignment.MIDDLE_RIGHT;
352             break;
353         case LEFT:
354             vaadinAlignment = Alignment.MIDDLE_LEFT;
355             break;
356         case CENTER:
357             vaadinAlignment = Alignment.MIDDLE_CENTER;
358             break;
359         default:
360             vaadinAlignment = Alignment.MIDDLE_RIGHT;
361         }
362 
363         toolBar.setComponentAlignment(toolComponent, vaadinAlignment);
364     }
365 
366     @Override
367     public void addFilterComponent(View view) {
368         final Component filterComponent = view.asVaadinComponent();
369 
370         filterComponent.addStyleName("content-filter");
371         filterBar.addComponent(filterComponent);
372         toggleButton.setVisible(true);
373     }
374 }