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.workbench;
35
36 import info.magnolia.i18nsystem.SimpleTranslator;
37 import info.magnolia.ui.vaadin.extension.ShortcutProtector;
38 import info.magnolia.ui.vaadin.icon.Icon;
39 import info.magnolia.ui.workbench.definition.ContentPresenterDefinition;
40 import info.magnolia.ui.workbench.list.ListPresenterDefinition;
41 import info.magnolia.ui.workbench.search.SearchPresenterDefinition;
42 import info.magnolia.ui.workbench.tree.TreePresenterDefinition;
43 import info.magnolia.ui.workbench.tree.TreeView;
44
45 import java.io.Serializable;
46 import java.util.Arrays;
47 import java.util.HashMap;
48 import java.util.Map;
49
50 import javax.inject.Inject;
51
52 import org.apache.commons.lang3.StringUtils;
53
54 import com.vaadin.data.Property;
55 import com.vaadin.event.FieldEvents;
56 import com.vaadin.event.ShortcutAction;
57 import com.vaadin.event.ShortcutAction.KeyCode;
58 import com.vaadin.event.ShortcutListener;
59 import com.vaadin.shared.ui.MarginInfo;
60 import com.vaadin.ui.Button;
61 import com.vaadin.ui.Button.ClickEvent;
62 import com.vaadin.ui.Component;
63 import com.vaadin.ui.CssLayout;
64 import com.vaadin.ui.NativeButton;
65 import com.vaadin.ui.Panel;
66 import com.vaadin.ui.TextField;
67 import com.vaadin.ui.VerticalLayout;
68 import com.vaadin.ui.themes.BaseTheme;
69
70
71
72
73 public class WorkbenchViewImpl extends VerticalLayout implements WorkbenchView, Serializable {
74
75 private final CssLayout toolBar = new CssLayout();
76
77 private final CssLayout viewModes = new CssLayout();
78
79 private final CssLayout searchBox = new CssLayout();
80
81 protected final Panel keyboardEventPanel;
82
83 private TextField searchField;
84
85 private Button clearSearchBoxButton;
86
87 private Icon searchIcon;
88
89 private Icon searchArrow;
90
91 private StatusBarView statusBar;
92
93 private Map<String, ContentView> contentViews = new HashMap<String, ContentView>();
94
95 private Map<String, Button> contentViewsButton = new HashMap<String, Button>();
96
97 private String currentViewType = TreePresenterDefinition.VIEW_TYPE;
98
99
100
101
102 private String previousViewType = currentViewType;
103
104 private final Property.ValueChangeListener searchFieldListener = new Property.ValueChangeListener() {
105
106 @Override
107 public void valueChange(Property.ValueChangeEvent event) {
108 listener.onSearch(searchField.getValue().toString());
109
110 boolean hasSearchContent = !searchField.getValue().isEmpty();
111 if (hasSearchContent) {
112 searchBox.addStyleName("has-content");
113 } else {
114 searchBox.removeStyleName("has-content");
115 }
116 searchField.focus();
117 }
118 };
119
120 private WorkbenchView.Listener listener;
121 private final SimpleTranslator i18n;
122
123 @Inject
124 public WorkbenchViewImpl(SimpleTranslator i18n) {
125 this.i18n = i18n;
126
127 setSizeFull();
128 setMargin(new MarginInfo(true, false, false, true));
129 addStyleName("workbench");
130
131 viewModes.setStyleName("view-modes");
132
133 clearSearchBoxButton = new Button();
134 clearSearchBoxButton.setStyleName("m-closebutton");
135 clearSearchBoxButton.addStyleName("icon-delete-search");
136 clearSearchBoxButton.addStyleName("searchbox-clearbutton");
137 clearSearchBoxButton.addClickListener(new Button.ClickListener() {
138
139 @Override
140 public void buttonClick(ClickEvent event) {
141 searchField.setValue("");
142 }
143 });
144
145 searchIcon = new Icon("search");
146 searchIcon.addStyleName("searchbox-icon");
147
148 searchArrow = new Icon("arrow2_s");
149 searchArrow.addStyleName("searchbox-arrow");
150
151 searchField = buildSearchField();
152
153 searchBox.setVisible(false);
154 searchBox.addComponent(searchField);
155 searchBox.addComponent(clearSearchBoxButton);
156 searchBox.addComponent(searchIcon);
157 searchBox.addComponent(searchArrow);
158 searchBox.setStyleName("searchbox");
159
160 toolBar.addStyleName("toolbar");
161 toolBar.setWidth(100, Unit.PERCENTAGE);
162 toolBar.addComponent(viewModes);
163 toolBar.addComponent(searchBox);
164
165 addComponent(toolBar);
166 setExpandRatio(toolBar, 0);
167
168 keyboardEventPanel = new Panel();
169 keyboardEventPanel.setSizeFull();
170 keyboardEventPanel.addStyleName("keyboard-panel");
171 addComponent(keyboardEventPanel, 1);
172 setExpandRatio(keyboardEventPanel, 1);
173
174 bindKeyboardHandlers();
175 }
176
177 public void bindKeyboardHandlers() {
178
179 final ShortcutListener enterShortcut = new ShortcutListener("Enter shortcut", ShortcutAction.KeyCode.ENTER, null) {
180 @Override
181 public void handleAction(Object sender, Object target) {
182 getSelectedView().onShortcutKey(ShortcutAction.KeyCode.ENTER, null);
183 }
184 };
185 keyboardEventPanel.addShortcutListener(enterShortcut);
186
187 final ShortcutListener deleteShortcut = new ShortcutListener("Delete shortcut", ShortcutAction.KeyCode.DELETE, null) {
188 @Override
189 public void handleAction(Object sender, Object target) {
190 getSelectedView().onShortcutKey(ShortcutAction.KeyCode.DELETE, null);
191 }
192 };
193
194
195
196 }
197
198 @Override
199 public void setSearchQuery(String query) {
200 if (searchField == null) {
201 return;
202 }
203
204 searchField.removeValueChangeListener(searchFieldListener);
205 if (StringUtils.isNotBlank(query)) {
206 searchField.setValue(query);
207 searchField.focus();
208 } else {
209 searchField.setValue("");
210 searchBox.removeStyleName("has-content");
211 }
212 searchField.addValueChangeListener(searchFieldListener);
213
214 }
215
216 @Override
217 public void addContentView(String viewType, ContentView view, ContentPresenterDefinition contentViewDefintion) {
218 contentViews.put(viewType, view);
219
220 if (view instanceof TreeView) {
221 ((TreeView) view).setActionManager(keyboardEventPanel);
222 }
223
224
225 if (contentViews.containsKey(ListPresenterDefinition.VIEW_TYPE) && contentViews.containsKey(SearchPresenterDefinition.VIEW_TYPE)) {
226 searchBox.setVisible(true);
227 }
228
229 if (contentViewDefintion instanceof SearchPresenterDefinition) {
230
231 return;
232 }
233
234
235 Button button = buildButton(viewType, contentViewDefintion.getIcon(), contentViewDefintion.isActive());
236 contentViewsButton.put(viewType, button);
237 viewModes.addComponent(button);
238
239 if (contentViewDefintion.isActive()) {
240 currentViewType = previousViewType = viewType;
241 }
242 }
243
244 @Override
245 public void setViewType(String type) {
246
247 final Component c = contentViews.get(type).asVaadinComponent();
248
249
250 keyboardEventPanel.setContent(c);
251
252 if (type != SearchPresenterDefinition.VIEW_TYPE) {
253 previousViewType = type;
254 setSearchQuery(null);
255 }
256 setViewTypeStyling(type);
257
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 Component c = statusBar.asVaadinComponent();
268 if (this.statusBar == null) {
269 addComponent(c, getComponentCount());
270 } else {
271 replaceComponent(this.statusBar.asVaadinComponent(), c);
272 }
273 setExpandRatio(c, 0);
274 this.statusBar = statusBar;
275 }
276
277 @Override
278 public ContentView getSelectedView() {
279 return contentViews.get(currentViewType);
280 }
281
282 @Override
283 public Component asVaadinComponent() {
284 return this;
285 }
286
287 @Override
288 public void setListener(WorkbenchView.Listener listener) {
289 this.listener = listener;
290 }
291
292 private Button buildButton(final String viewType, final String icon, final boolean active) {
293 NativeButton button = new NativeButton(null, new Button.ClickListener() {
294 @Override
295 public void buttonClick(Button.ClickEvent event) {
296 fireViewTypeChangedEvent(viewType);
297 }
298 });
299 button.setStyleName(BaseTheme.BUTTON_LINK);
300
301 button.setHtmlContentAllowed(true);
302 button.setCaption("<span class=\"" + icon + "\"></span><span class=\"view-type-arrow view-type-arrow-" + viewType + " icon-arrow2_n\"></span>");
303
304 if (active) {
305 button.addStyleName("active");
306 }
307 return button;
308 }
309
310 private void setViewTypeStyling(final String viewType) {
311 for (Map.Entry<String, Button> entry : contentViewsButton.entrySet()) {
312 entry.getValue().removeStyleName("active");
313 if (entry.getKey().equals(viewType)) {
314 entry.getValue().addStyleName("active");
315 }
316 }
317
318 if (viewType.equals(SearchPresenterDefinition.VIEW_TYPE) && contentViews.containsKey(ListPresenterDefinition.VIEW_TYPE)) {
319 contentViewsButton.get(ListPresenterDefinition.VIEW_TYPE).addStyleName("active");
320 }
321 }
322
323 private TextField buildSearchField() {
324 final TextField field = new TextField();
325 ShortcutProtector.extend(field, Arrays.asList(KeyCode.ENTER));
326 final String inputPrompt = i18n.translate("toolbar.search.prompt");
327
328 field.setInputPrompt(inputPrompt);
329 field.setSizeUndefined();
330 field.addStyleName("searchfield");
331
332
333 field.setImmediate(true);
334 field.addValueChangeListener(searchFieldListener);
335
336 field.addFocusListener(new FieldEvents.FocusListener() {
337 @Override
338 public void focus(FieldEvents.FocusEvent event) {
339
340 TextField tf = (TextField) event.getSource();
341 tf.setCursorPosition(tf.getValue().length());
342 }
343 });
344
345
346
347 return field;
348 }
349
350 @Override
351 public void setMultiselect(boolean multiselect) {
352 for (String type : contentViews.keySet()) {
353 contentViews.get(type).setMultiselect(multiselect);
354 }
355 }
356 }