View Javadoc

1   /**
2    * This file Copyright (c) 2013-2014 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.admincentral.shellapp.favorites;
35  
36  import info.magnolia.i18nsystem.SimpleTranslator;
37  import info.magnolia.ui.admincentral.shellapp.favorites.EditingEvent.EditingListener;
38  import info.magnolia.ui.admincentral.shellapp.favorites.EditingEvent.EditingNotifier;
39  import info.magnolia.ui.admincentral.shellapp.favorites.SelectedEvent.SelectedListener;
40  import info.magnolia.ui.admincentral.shellapp.favorites.SelectedEvent.SelectedNotifier;
41  import info.magnolia.ui.api.overlay.ConfirmationCallback;
42  import info.magnolia.ui.api.shell.Shell;
43  import info.magnolia.ui.framework.AdmincentralNodeTypes;
44  import info.magnolia.ui.vaadin.integration.jcr.AbstractJcrNodeAdapter;
45  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
46  
47  import org.apache.commons.lang.StringUtils;
48  
49  import com.vaadin.event.FieldEvents.BlurEvent;
50  import com.vaadin.event.FieldEvents.BlurListener;
51  import com.vaadin.event.FieldEvents.FocusEvent;
52  import com.vaadin.event.FieldEvents.FocusListener;
53  import com.vaadin.event.LayoutEvents.LayoutClickEvent;
54  import com.vaadin.event.LayoutEvents.LayoutClickListener;
55  import com.vaadin.event.ShortcutListener;
56  import com.vaadin.shared.ui.label.ContentMode;
57  import com.vaadin.ui.Button.ClickEvent;
58  import com.vaadin.ui.Button.ClickListener;
59  import com.vaadin.ui.CustomComponent;
60  import com.vaadin.ui.HorizontalLayout;
61  import com.vaadin.ui.Label;
62  import com.vaadin.ui.NativeButton;
63  import com.vaadin.ui.TextField;
64  
65  /**
66   * FavoritesEntry.
67   */
68  public final class FavoritesEntry extends CustomComponent implements EditingNotifier, SelectedNotifier {
69  
70      private HorizontalLayout root = new HorizontalLayout();
71      private String location;
72      private String title;
73      private String group = null;
74      private String nodename;
75      private TextField titleField;
76      private NativeButton editButton;
77      private NativeButton removeButton;
78      private boolean editable;
79      private boolean selected;
80      private EnterKeyShortcutListener enterKeyShortcutListener;
81      private EscapeKeyShortcutListener escapeKeyShortcutListener;
82      private Shell shell;
83      private final SimpleTranslator i18n;
84  
85      public FavoritesEntry(final AbstractJcrNodeAdapter favorite, final FavoritesView.Listener listener, final Shell shell, SimpleTranslator i18n) {
86          super();
87          this.shell = shell;
88          this.i18n = i18n;
89          construct(favorite, listener);
90      }
91  
92      public String getRelPath() {
93          return StringUtils.isBlank(group) ? this.nodename : this.group + "/" + this.nodename;
94      }
95  
96      public String getNodename() {
97          return this.nodename;
98      }
99  
100     public void setGroup(String group) {
101         this.group = group;
102     }
103 
104     public String getGroup() {
105         return this.group;
106     }
107 
108     /**
109      * Sets this fav as unselected and non editable, that is at its initial state.
110      */
111     public void reset() {
112         setEditable(false);
113         setSelected(false);
114     }
115 
116     private void setEditable(boolean editable) {
117         this.editable = editable;
118         String icon = "icon-tick";
119         if (editable) {
120             titleField.addStyleName("editable");
121             titleField.focus();
122             titleField.selectAll();
123         } else {
124             icon = "icon-edit";
125             titleField.removeStyleName("editable");
126             // pending changes are reverted
127             titleField.setValue(title);
128         }
129         titleField.setReadOnly(!editable);
130         editButton.setCaption("<span class=\"" + icon + "\"></span>");
131         fireEvent(new EditingEvent(this, editable));
132     }
133 
134     private void setSelected(boolean selected) {
135         this.selected = selected;
136         if (selected) {
137             addStyleName("selected");
138             fireEvent(new SelectedEvent(this));
139         } else {
140             removeStyleName("selected");
141         }
142         titleField.setReadOnly(true);
143         editButton.setVisible(selected);
144         editButton.setCaption("<span class=\"icon-edit\"></span>");
145         removeButton.setVisible(selected);
146     }
147 
148     private void construct(final AbstractJcrNodeAdapter favorite, final FavoritesView.Listener listener) {
149         addStyleName("favorites-entry");
150         setSizeUndefined();
151         root.setSizeUndefined();
152 
153         this.enterKeyShortcutListener = new EnterKeyShortcutListener(listener);
154         this.escapeKeyShortcutListener = new EscapeKeyShortcutListener();
155 
156         this.nodename = favorite.getNodeName();
157         this.location = favorite.getItemProperty(AdmincentralNodeTypes.Favorite.URL).getValue().toString();
158         this.title = favorite.getItemProperty(AdmincentralNodeTypes.Favorite.TITLE).getValue().toString();
159 
160         String icon = "icon-app";
161         if (favorite.getItemProperty(AdmincentralNodeTypes.Favorite.ICON).getValue() != null) {
162             icon = favorite.getItemProperty(AdmincentralNodeTypes.Favorite.ICON).getValue().toString();
163         }
164 
165         final Label iconLabel = new Label();
166         iconLabel.setValue("<span class=\"" + icon + "\"></span>");
167         iconLabel.setStyleName("icon");
168         iconLabel.setContentMode(ContentMode.HTML);
169         root.addComponent(iconLabel);
170 
171         titleField = new TextField();
172         titleField.setValue(title);
173         titleField.setReadOnly(true);
174 
175         titleField.addFocusListener(new FocusListener() {
176 
177             @Override
178             public void focus(FocusEvent event) {
179                 iconLabel.removeShortcutListener(enterKeyShortcutListener);
180                 titleField.addShortcutListener(enterKeyShortcutListener);
181                 titleField.addShortcutListener(escapeKeyShortcutListener);
182             }
183         });
184 
185         titleField.addBlurListener(new BlurListener() {
186 
187             @Override
188             public void blur(BlurEvent event) {
189                 titleField.removeShortcutListener(enterKeyShortcutListener);
190                 titleField.removeShortcutListener(escapeKeyShortcutListener);
191             }
192         });
193 
194         root.addComponent(titleField);
195 
196         editButton = new NativeButton();
197         editButton.setHtmlContentAllowed(true);
198         editButton.setCaption("<span class=\"icon-edit\"></span>");
199         editButton.addStyleName("favorite-action");
200         editButton.addClickListener(new ClickListener() {
201 
202             @Override
203             public void buttonClick(ClickEvent event) {
204                 if (selected && !editable) {
205                     setEditable(true);
206                     return;
207                 }
208                 doEditTitle(listener);
209             }
210         });
211         editButton.setVisible(false);
212         root.addComponent(editButton);
213 
214         removeButton = new NativeButton();
215         removeButton.setHtmlContentAllowed(true);
216         removeButton.setCaption("<span class=\"icon-trash\"></span>");
217         removeButton.addStyleName("favorite-action");
218         removeButton.addClickListener(new ClickListener() {
219 
220             @Override
221             public void buttonClick(ClickEvent event) {
222                 shell.openConfirmation(MessageStyleTypeEnum.WARNING, i18n.translate("confirmation.delete.title.generic"), i18n.translate("confirmation.cannot.undo"), i18n.translate("confirmation.delete.yes"), i18n.translate("confirmation.no"), true, new ConfirmationCallback() {
223 
224                     @Override
225                     public void onSuccess() {
226                         listener.removeFavorite(getRelPath());
227                     }
228 
229                     @Override
230                     public void onCancel() {
231                         // no op
232                     }
233                 });
234             }
235         });
236         removeButton.setVisible(false);
237         root.addComponent(removeButton);
238 
239         root.addLayoutClickListener(new LayoutClickListener() {
240 
241             @Override
242             public void layoutClick(LayoutClickEvent event) {
243 
244                 if (event.getClickedComponent() == titleField && !editable) {
245                     if (event.isDoubleClick()) {
246                         // TODO fgrilli commented out as, besides making the text editable, it also goes to the saved location
247                         // See MGNLUI-1317
248                         // setEditable(true);
249                     } else {
250                         listener.goToLocation(location);
251                     }
252                 } else if (event.getClickedComponent() == iconLabel) {
253                     setSelected(!selected);
254                     setEditable(false);
255                     if (selected) {
256                         iconLabel.addShortcutListener(enterKeyShortcutListener);
257                     }
258                 }
259             }
260         });
261 
262         setCompositionRoot(root);
263     }
264 
265     @Override
266     public void addEditingListener(EditingListener listener) {
267         addListener("onEdit", EditingEvent.class, listener, EditingEvent.EDITING_METHOD);
268     }
269 
270     @Override
271     public void removeEditingListener(EditingListener listener) {
272         removeListener(EditingEvent.class, listener, EditingEvent.EDITING_METHOD);
273     }
274 
275     @Override
276     public void addSelectedListener(SelectedListener listener) {
277         addListener("onSelected", SelectedEvent.class, listener, SelectedEvent.SELECTED_METHOD);
278     }
279 
280     @Override
281     public void removeSelectedListener(SelectedListener listener) {
282         removeListener(SelectedEvent.class, listener, SelectedEvent.SELECTED_METHOD);
283     }
284 
285     private void doEditTitle(final FavoritesView.Listener listener) {
286         if (StringUtils.isBlank(titleField.getValue())) {
287             shell.openNotification(MessageStyleTypeEnum.ERROR, true, i18n.translate("favorites.title.required"));
288             return;
289         }
290 
291         boolean titleHasChanged = !title.equals(titleField.getValue());
292         if (editable && titleHasChanged) {
293             listener.editFavorite(getRelPath(), titleField.getValue());
294         }
295         setEditable(false);
296     }
297 
298     private class EnterKeyShortcutListener extends ShortcutListener {
299         private FavoritesView.Listener listener;
300 
301         public EnterKeyShortcutListener(final FavoritesView.Listener listener) {
302             super("", KeyCode.ENTER, null);
303             this.listener = listener;
304         }
305 
306         @Override
307         public void handleAction(Object sender, Object target) {
308             if (editable) {
309                 doEditTitle(listener);
310             } else {
311                 setEditable(true);
312             }
313         }
314     }
315 
316     private class EscapeKeyShortcutListener extends ShortcutListener {
317 
318         public EscapeKeyShortcutListener() {
319             super("", KeyCode.ESCAPE, null);
320         }
321 
322         @Override
323         public void handleAction(Object sender, Object target) {
324             reset();
325         }
326     }
327 
328 }