View Javadoc

1   /**
2    * This file Copyright (c) 2013 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 info.magnolia.cms.i18n.MessagesUtil;
37  import info.magnolia.ui.vaadin.icon.Icon;
38  import info.magnolia.ui.workbench.ContentView.ViewType;
39  import info.magnolia.ui.workbench.definition.ContentPresenterDefinition;
40  
41  import java.io.Serializable;
42  import java.util.EnumMap;
43  import java.util.Map;
44  
45  import org.apache.commons.lang.StringUtils;
46  
47  import com.vaadin.data.Property;
48  import com.vaadin.event.FieldEvents;
49  import com.vaadin.shared.ui.MarginInfo;
50  import com.vaadin.ui.Button;
51  import com.vaadin.ui.Button.ClickEvent;
52  import com.vaadin.ui.Component;
53  import com.vaadin.ui.CssLayout;
54  import com.vaadin.ui.NativeButton;
55  import com.vaadin.ui.TextField;
56  import com.vaadin.ui.VerticalLayout;
57  import com.vaadin.ui.themes.BaseTheme;
58  
59  /**
60   * Implementation of the workbench view.
61   */
62  public class WorkbenchViewImpl extends VerticalLayout implements WorkbenchView, Serializable {
63  
64      private final CssLayout toolBar = new CssLayout();
65  
66      private final CssLayout viewModes = new CssLayout();
67  
68      private final CssLayout searchBox = new CssLayout();
69  
70      private TextField searchField;
71  
72      private Button clearSearchBoxButton;
73  
74      private Icon searchIcon;
75  
76      private Icon searchArrow;
77  
78      private StatusBarView statusBar;
79  
80      private Map<ViewType, ContentView> contentViews = new EnumMap<ViewType, ContentView>(ViewType.class);
81  
82      private Map<ViewType, Button> contentViewsButton = new EnumMap<ViewType, Button>(ViewType.class);
83  
84      private ViewType currentViewType = ViewType.TREE;
85  
86      /**
87       * for going back from search view if search expression is empty.
88       */
89      private ViewType previousViewType = currentViewType;
90  
91      private final Property.ValueChangeListener searchFieldListener = new Property.ValueChangeListener() {
92  
93          @Override
94          public void valueChange(Property.ValueChangeEvent event) {
95              listener.onSearch(searchField.getValue().toString());
96  
97              boolean hasSearchContent = !searchField.getValue().isEmpty();
98              if (hasSearchContent) {
99                  searchBox.addStyleName("has-content");
100             } else {
101                 searchBox.removeStyleName("has-content");
102             }
103             searchField.focus();
104         }
105     };
106 
107     private WorkbenchView.Listener listener;
108 
109     public WorkbenchViewImpl(){
110 
111         setSizeFull();
112         setMargin(new MarginInfo(true, false, false, true));
113         addStyleName("workbench");
114 
115         viewModes.setStyleName("view-modes");
116 
117         clearSearchBoxButton = new Button();
118         clearSearchBoxButton.setStyleName("m-closebutton");
119         clearSearchBoxButton.addStyleName("icon-delete-search");
120         clearSearchBoxButton.addStyleName("searchbox-clearbutton");
121         clearSearchBoxButton.addClickListener(new Button.ClickListener() {
122 
123             @Override
124             public void buttonClick(ClickEvent event) {
125                 searchField.setValue("");
126             }
127         });
128 
129         searchIcon = new Icon("search");
130         searchIcon.addStyleName("searchbox-icon");
131 
132         searchArrow = new Icon("arrow2_s");
133         searchArrow.addStyleName("searchbox-arrow");
134 
135         searchField = buildSearchField();
136 
137         searchBox.setVisible(false);
138         searchBox.addComponent(searchField);
139         searchBox.addComponent(clearSearchBoxButton);
140         searchBox.addComponent(searchIcon);
141         searchBox.addComponent(searchArrow);
142         searchBox.setStyleName("searchbox");
143 
144         toolBar.addStyleName("toolbar");
145         toolBar.setWidth(100, Unit.PERCENTAGE);
146         toolBar.addComponent(viewModes);
147         toolBar.addComponent(searchBox);
148 
149         addComponent(toolBar);
150         setExpandRatio(toolBar, 0);
151 
152 
153     }
154 
155     @Override
156     public void setSearchQuery(String query) {
157         if (searchField == null) {
158             return;
159         }
160         // turn off value change listener, so that presenter does not think there was user input and searches again
161         searchField.removeValueChangeListener(searchFieldListener);
162         if (StringUtils.isNotBlank(query)) {
163             searchField.setValue(query);
164             searchField.focus();
165         } else {
166             searchField.setValue("");
167             searchBox.removeStyleName("has-content");
168         }
169         searchField.addValueChangeListener(searchFieldListener);
170 
171     }
172 
173     @Override
174     public void addContentView(ViewType viewType, ContentView view, ContentPresenterDefinition contentViewDefintion) {
175         contentViews.put(viewType, view);
176 
177         if (viewType.equals(ViewType.SEARCH)) {
178             // do not add a button for search
179             return;
180         }
181         if (viewType.equals(ViewType.LIST)) {
182             searchBox.setVisible(true);
183         }
184 
185         // set button
186         Button button = buildButton(viewType, contentViewDefintion.getIcon(), contentViewDefintion.isActive());
187         contentViewsButton.put(viewType, button);
188         viewModes.addComponent(button);
189         // set active
190         if (contentViewDefintion.isActive()) {
191             currentViewType = previousViewType = viewType;
192         }
193     }
194 
195     @Override
196     public void setViewType(ViewType type) {
197         removeComponent(getSelectedView().asVaadinComponent());
198         final Component c = contentViews.get(type).asVaadinComponent();
199         addComponent(c, 1); // between tool bar and status bar
200         setExpandRatio(c, 1);
201 
202         if (type != ViewType.SEARCH) {
203             previousViewType = type;
204             setSearchQuery(null);
205         }
206         setViewTypeStyling(type);
207 
208         currentViewType = type;
209     }
210 
211     private void fireViewTypeChangedEvent(ViewType viewType) {
212         this.listener.onViewTypeChanged(viewType);
213     }
214 
215     @Override
216     public void setStatusBarView(StatusBarView statusBar) {
217         Component c = statusBar.asVaadinComponent();
218         if (this.statusBar == null) {
219             addComponent(c, getComponentCount()); // add last
220         } else {
221             replaceComponent(this.statusBar.asVaadinComponent(), c);
222         }
223         setExpandRatio(c, 0);
224         this.statusBar = statusBar;
225     }
226 
227     @Override
228     public ContentView getSelectedView() {
229         return contentViews.get(currentViewType);
230     }
231 
232     @Override
233     public Component asVaadinComponent() {
234         return this;
235     }
236 
237     @Override
238     public void setListener(WorkbenchView.Listener listener) {
239         this.listener = listener;
240     }
241 
242     private Button buildButton(final ViewType viewType, final String icon, final boolean active) {
243         NativeButton button = new NativeButton(null, new Button.ClickListener() {
244             @Override
245             public void buttonClick(Button.ClickEvent event) {
246                 fireViewTypeChangedEvent(viewType);
247             }
248         });
249         button.setStyleName(BaseTheme.BUTTON_LINK);
250 
251         button.setHtmlContentAllowed(true);
252         button.setCaption("<span class=\"" + icon + "\"></span><span class=\"view-type-arrow view-type-arrow-" + viewType.getText() + " icon-arrow2_n\"></span>");
253 
254         if (active) {
255             button.addStyleName("active");
256         }
257         return button;
258     }
259 
260     private void setViewTypeStyling(final ViewType viewType) {
261         for (Map.Entry<ViewType, Button> entry : contentViewsButton.entrySet()) {
262             entry.getValue().removeStyleName("active");
263             if (entry.getKey().equals(viewType)) {
264                 entry.getValue().addStyleName("active");
265             }
266         }
267         // search is a list view
268         if (viewType.equals(ContentView.ViewType.SEARCH) && contentViewsButton.containsKey(ContentView.ViewType.LIST)) {
269             contentViewsButton.get(ContentView.ViewType.LIST).addStyleName("active");
270         }
271     }
272 
273     private TextField buildSearchField() {
274         final TextField field = new TextField();
275         final String inputPrompt = MessagesUtil.getWithDefault("toolbar.search.prompt", "Search");
276 
277         field.setInputPrompt(inputPrompt);
278         field.setSizeUndefined();
279         field.addStyleName("searchfield");
280 
281         // TextField has to be immediate to fire value changes when pressing Enter, avoiding ShortcutListener overkill.
282         field.setImmediate(true);
283         field.addListener(searchFieldListener);
284 
285         field.addFocusListener(new FieldEvents.FocusListener() {
286             @Override
287             public void focus(FieldEvents.FocusEvent event) {
288                 // put the cursor at the end of the field
289                 TextField tf = (TextField) event.getSource();
290                 tf.setCursorPosition(tf.getValue().length());
291             }
292         });
293 
294         // No blur handler.
295 
296         return field;
297     }
298 
299     @Override
300     public void setMultiselect(boolean multiselect) {
301         for (ViewType type : contentViews.keySet()) {
302             contentViews.get(type).setMultiselect(multiselect);
303         }
304     }
305 }