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.context.MgnlContext;
38  import info.magnolia.ui.api.overlay.ConfirmationCallback;
39  import info.magnolia.ui.framework.AdmincentralNodeTypes;
40  import info.magnolia.ui.api.shell.Shell;
41  import info.magnolia.ui.vaadin.integration.jcr.AbstractJcrNodeAdapter;
42  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
43  
44  import org.apache.commons.lang.StringUtils;
45  
46  import com.vaadin.data.Property;
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 {
67  
68      private HorizontalLayout root = new HorizontalLayout();
69      private String location;
70      private String title;
71      private String group;
72      private String relPath;
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      /**
90       * Sets this fav as unselected and non editable, that is at its initial state.
91       */
92      public void reset() {
93          setEditable(false);
94          setSelected(false);
95      }
96  
97      private void setEditable(boolean editable) {
98          this.editable = editable;
99          String icon = "icon-tick";
100         if (editable) {
101             titleField.addStyleName("editable");
102             titleField.focus();
103             titleField.selectAll();
104         } else {
105             icon = "icon-edit";
106             titleField.removeStyleName("editable");
107             // pending changes are reverted
108             titleField.setValue(title);
109         }
110         titleField.setReadOnly(!editable);
111         editButton.setCaption("<span class=\"" + icon + "\"></span>");
112     }
113 
114     private void setSelected(boolean selected) {
115         this.selected = selected;
116         if (selected) {
117             addStyleName("selected");
118         } else {
119             removeStyleName("selected");
120         }
121         titleField.setReadOnly(true);
122         editButton.setVisible(selected);
123         editButton.setCaption("<span class=\"icon-edit\"></span>");
124         removeButton.setVisible(selected);
125     }
126 
127     private void construct(final AbstractJcrNodeAdapter favorite, final FavoritesView.Listener listener) {
128         addStyleName("favorites-entry");
129         setSizeUndefined();
130         root.setSizeUndefined();
131 
132         this.enterKeyShortcutListener = new EnterKeyShortcutListener(listener);
133         this.escapeKeyShortcutListener = new EscapeKeyShortcutListener();
134 
135         final String nodeName = favorite.getNodeName();
136         this.location = favorite.getItemProperty(AdmincentralNodeTypes.Favorite.URL).getValue().toString();
137         this.title = favorite.getItemProperty(AdmincentralNodeTypes.Favorite.TITLE).getValue().toString();
138         this.relPath = nodeName;
139         try {
140             this.group = MgnlContext.doInSystemContext(new MgnlContext.Op<String, Throwable>() {
141 
142                 @Override
143                 public String exec() throws Throwable {
144                     final Property<?> property = favorite.getItemProperty(AdmincentralNodeTypes.Favorite.GROUP);
145                     if (property != null) {
146                         return property.getValue().toString();
147                     }
148                     return null;
149                 }
150             });
151         } catch (Throwable e) {
152             throw new RuntimeException(e);
153         }
154 
155         if (StringUtils.isNotBlank(this.group)) {
156             this.relPath = this.group + "/" + nodeName;
157         }
158 
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 
172         titleField = new TextField();
173         titleField.setValue(title);
174         titleField.setReadOnly(true);
175 
176         titleField.addFocusListener(new FocusListener() {
177 
178             @Override
179             public void focus(FocusEvent event) {
180                 iconLabel.removeShortcutListener(enterKeyShortcutListener);
181                 titleField.addShortcutListener(enterKeyShortcutListener);
182                 titleField.addShortcutListener(escapeKeyShortcutListener);
183             }
184         });
185 
186         titleField.addBlurListener(new BlurListener() {
187 
188             @Override
189             public void blur(BlurEvent event) {
190                 titleField.removeShortcutListener(enterKeyShortcutListener);
191                 titleField.removeShortcutListener(escapeKeyShortcutListener);
192             }
193         });
194 
195         root.addComponent(titleField);
196 
197         editButton = new NativeButton();
198         editButton.setHtmlContentAllowed(true);
199         editButton.setCaption("<span class=\"icon-edit\"></span>");
200         editButton.addStyleName("favorite-action");
201         editButton.addClickListener(new ClickListener() {
202 
203             @Override
204             public void buttonClick(ClickEvent event) {
205                 if (selected && !editable) {
206                     setEditable(true);
207                     return;
208                 }
209                 doEditTitle(listener);
210             }
211         });
212         editButton.setVisible(false);
213         root.addComponent(editButton);
214 
215         removeButton = new NativeButton();
216         removeButton.setHtmlContentAllowed(true);
217         removeButton.setCaption("<span class=\"icon-trash\"></span>");
218         removeButton.addStyleName("favorite-action");
219         removeButton.addClickListener(new ClickListener() {
220 
221             @Override
222             public void buttonClick(ClickEvent event) {
223                 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() {
224 
225                     @Override
226                     public void onSuccess() {
227                         listener.removeFavorite(relPath);
228                     }
229 
230                     @Override
231                     public void onCancel() {
232                         // no op
233                     }
234                 });
235             }
236         });
237         removeButton.setVisible(false);
238         root.addComponent(removeButton);
239 
240         root.addLayoutClickListener(new LayoutClickListener() {
241 
242             @Override
243             public void layoutClick(LayoutClickEvent event) {
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     private void doEditTitle(final FavoritesView.Listener listener) {
266         if (StringUtils.isBlank(titleField.getValue())) {
267             shell.openNotification(MessageStyleTypeEnum.ERROR, true, MessagesUtil.get("favorites.title.required"));
268             return;
269         }
270 
271         boolean titleHasChanged = !title.equals(titleField.getValue());
272         if (editable && titleHasChanged) {
273             listener.editFavorite(relPath, titleField.getValue());
274         }
275         setEditable(false);
276     }
277 
278     private class EnterKeyShortcutListener extends ShortcutListener {
279         private FavoritesView.Listener listener;
280 
281         public EnterKeyShortcutListener(final FavoritesView.Listener listener) {
282             super("", KeyCode.ENTER, null);
283             this.listener = listener;
284         }
285 
286         @Override
287         public void handleAction(Object sender, Object target) {
288             if (editable) {
289                 doEditTitle(listener);
290             } else {
291                 setEditable(true);
292             }
293         }
294     }
295 
296     private class EscapeKeyShortcutListener extends ShortcutListener {
297 
298         public EscapeKeyShortcutListener() {
299             super("", KeyCode.ESCAPE, null);
300         }
301 
302         @Override
303         public void handleAction(Object sender, Object target) {
304             reset();
305         }
306     }
307 }