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.api.overlay.ConfirmationCallback;
39  import info.magnolia.ui.api.shell.Shell;
40  import info.magnolia.ui.framework.AdmincentralNodeTypes;
41  import info.magnolia.ui.vaadin.integration.jcr.AbstractJcrNodeAdapter;
42  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
43  
44  import java.util.Iterator;
45  import java.util.Map;
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.ui.Button.ClickEvent;
57  import com.vaadin.ui.Button.ClickListener;
58  import com.vaadin.ui.Component;
59  import com.vaadin.ui.CssLayout;
60  import com.vaadin.ui.DragAndDropWrapper.DragStartMode;
61  import com.vaadin.ui.NativeButton;
62  import com.vaadin.ui.TextField;
63  
64  /**
65   * Favorite group.
66   */
67  public final class FavoritesGroup extends CssLayout {
68  
69      private TextField titleField;
70      private NativeButton editButton;
71      private NativeButton removeButton;
72      private String title;
73      private String relPath;
74      private boolean editable;
75      private boolean selected;
76      private CssLayout wrapper;
77      private EnterKeyShortcutListener enterKeyShortcutListener;
78      private EscapeKeyShortcutListener escapeKeyShortcutListener;
79      private Shell shell;
80  
81      /**
82       * Creates an empty placeholder group.
83       */
84      public FavoritesGroup() {
85          addStyleName("no-group");
86      }
87  
88      public FavoritesGroup(final AbstractJcrNodeAdapter favoritesGroup, final FavoritesView.Listener listener, final Shell shell) {
89          this.shell = shell;
90  
91          addStyleName("favorites-group");
92          construct(favoritesGroup, listener);
93  
94          final Map<String, AbstractJcrNodeAdapter> nodeAdapters = favoritesGroup.getChildren();
95  
96          for (String key : nodeAdapters.keySet()) {
97              final AbstractJcrNodeAdapter fav = nodeAdapters.get(key);
98              final FavoritesEntry favEntry = new FavoritesEntry(fav, listener, shell);
99              favEntry.setGroup(this.relPath);
100             final EntryDragAndDropWrapper wrapper = new EntryDragAndDropWrapper(favEntry, listener);
101             favEntry.addEditingListener(new EditingListener() {
102 
103                 @Override
104                 public void onEdit(EditingEvent event) {
105                     if (event.isEditing()) {
106                         wrapper.setDragStartMode(DragStartMode.NONE);
107                     } else {
108                         wrapper.setDragStartMode(DragStartMode.WRAPPER);
109                     }
110 
111                 }
112             });
113             addComponent(wrapper);
114         }
115     }
116 
117     public String getRelPath() {
118         return this.relPath;
119     }
120 
121     /**
122      * Sets this group and all of its fav entries (if any) as unselected and non editable, that is at their initial state.
123      */
124     public void reset() {
125         // skip it if this group is a placeholder for no group fav entries, as it has no title
126         if (titleField != null) {
127             setEditable(false);
128             setSelected(false);
129         }
130         Iterator<Component> components = iterator();
131         while (components.hasNext()) {
132             Component component = components.next();
133             if (component instanceof EntryDragAndDropWrapper) {
134                 component = ((EntryDragAndDropWrapper) component).getWrappedComponent();
135             }
136             if(component instanceof FavoritesEntry) {
137                 FavoritesEntry fav = (FavoritesEntry) component;
138                 fav.reset();
139             }
140         }
141     }
142 
143     private void setEditable(boolean editable) {
144         this.editable = editable;
145         String icon = "icon-tick";
146         if (editable) {
147             titleField.addStyleName("editable");
148             titleField.focus();
149             titleField.selectAll();
150         } else {
151             icon = "icon-edit";
152             // discard pending changes
153             titleField.setValue(title);
154             titleField.removeStyleName("editable");
155         }
156         titleField.setReadOnly(!editable);
157         editButton.setCaption("<span class=\"" + icon + "\"></span>");
158     }
159 
160     private void setSelected(boolean selected) {
161         this.selected = selected;
162         if (selected) {
163             wrapper.addStyleName("selected");
164         } else {
165             wrapper.removeStyleName("selected");
166         }
167         titleField.setReadOnly(true);
168         editButton.setVisible(selected);
169         editButton.setCaption("<span class=\"icon-edit\"></span>");
170         removeButton.setVisible(selected);
171     }
172 
173     private void construct(final AbstractJcrNodeAdapter favoritesGroup, final FavoritesView.Listener listener) {
174         wrapper = new CssLayout();
175         wrapper.addStyleName("favorites-group-title");
176 
177         this.enterKeyShortcutListener = new EnterKeyShortcutListener(listener);
178         this.escapeKeyShortcutListener = new EscapeKeyShortcutListener();
179 
180         this.relPath = favoritesGroup.getNodeName();
181         this.title = favoritesGroup.getItemProperty(AdmincentralNodeTypes.Favorite.TITLE).getValue().toString();
182 
183         titleField = new TextField();
184         titleField.setValue(title);
185         titleField.setReadOnly(true);
186         titleField.addFocusListener(new FocusListener() {
187 
188             @Override
189             public void focus(FocusEvent event) {
190                 titleField.addShortcutListener(enterKeyShortcutListener);
191                 titleField.addShortcutListener(escapeKeyShortcutListener);
192             }
193         });
194 
195         titleField.addBlurListener(new BlurListener() {
196 
197             @Override
198             public void blur(BlurEvent event) {
199                 titleField.removeShortcutListener(enterKeyShortcutListener);
200                 titleField.removeShortcutListener(escapeKeyShortcutListener);
201             }
202         });
203 
204         wrapper.addComponent(titleField);
205 
206         editButton = new NativeButton();
207         editButton.setHtmlContentAllowed(true);
208         editButton.setCaption("<span class=\"icon-edit\"></span>");
209         editButton.addStyleName("favorite-action");
210         editButton.addClickListener(new ClickListener() {
211 
212             @Override
213             public void buttonClick(ClickEvent event) {
214                 if (selected && !editable) {
215                     setEditable(true);
216                     return;
217                 }
218                 doEditTitle(listener);
219             }
220         });
221         editButton.setVisible(false);
222         wrapper.addComponent(editButton);
223 
224         removeButton = new NativeButton();
225         removeButton.setHtmlContentAllowed(true);
226         removeButton.setCaption("<span class=\"icon-trash\"></span>");
227         removeButton.addStyleName("favorite-action");
228         removeButton.addClickListener(new ClickListener() {
229 
230             @Override
231             public void buttonClick(ClickEvent event) {
232                 shell.openConfirmation(MessageStyleTypeEnum.WARNING, MessagesUtil.get("favorites.group.confirmation.title"), MessagesUtil.get("confirmation.cannot.undo"), MessagesUtil.get("confirmation.delete.yes"), MessagesUtil.get("confirmation.no"), true, new ConfirmationCallback() {
233 
234                     @Override
235                     public void onSuccess() {
236                         listener.removeGroup(relPath);
237                     }
238 
239                     @Override
240                     public void onCancel() {
241                         // no op
242                     }
243                 });
244             }
245         });
246         removeButton.setVisible(false);
247         wrapper.addComponent(removeButton);
248 
249         addLayoutClickListener(new LayoutClickListener() {
250 
251             @Override
252             public void layoutClick(LayoutClickEvent event) {
253 
254                 if (event.getClickedComponent() == titleField) {
255                     if (!editable) {
256                         setSelected(!selected);
257                     }
258                 }
259             }
260         });
261 
262         addComponent(new GroupDragAndDropWrapper(wrapper, listener, this));
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         boolean titleHasChanged = !title.equals(titleField.getValue());
271         if (editable && titleHasChanged) {
272             listener.editGroup(relPath, titleField.getValue());
273         }
274         setEditable(false);
275     }
276 
277     private class EnterKeyShortcutListener extends ShortcutListener {
278         private FavoritesView.Listener listener;
279 
280         public EnterKeyShortcutListener(final FavoritesView.Listener listener) {
281             super("", KeyCode.ENTER, null);
282             this.listener = listener;
283         }
284 
285         @Override
286         public void handleAction(Object sender, Object target) {
287             if (editable) {
288                 doEditTitle(listener);
289             } else {
290                 setEditable(true);
291             }
292         }
293     }
294 
295     private class EscapeKeyShortcutListener extends ShortcutListener {
296 
297         public EscapeKeyShortcutListener() {
298             super("", KeyCode.ESCAPE, null);
299         }
300 
301         @Override
302         public void handleAction(Object sender, Object target) {
303             reset();
304         }
305     }
306 }