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