View Javadoc
1   /**
2    * This file Copyright (c) 2013-2018 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.vaadin.gwt.client.editor.dom;
35  
36  import info.magnolia.cms.security.operations.OperationPermissionDefinition;
37  import info.magnolia.jcr.util.NodeTypes;
38  import info.magnolia.ui.vaadin.gwt.client.editor.event.ComponentActionEvent;
39  import info.magnolia.ui.vaadin.gwt.client.editor.event.ComponentStartMoveEvent;
40  import info.magnolia.ui.vaadin.gwt.client.editor.event.ComponentStopMoveEvent;
41  import info.magnolia.ui.vaadin.gwt.client.editor.event.EditComponentEvent;
42  import info.magnolia.ui.vaadin.gwt.client.editor.event.SortComponentEvent;
43  import info.magnolia.ui.vaadin.gwt.client.shared.AreaElement;
44  import info.magnolia.ui.vaadin.gwt.client.shared.ComponentElement;
45  import info.magnolia.ui.vaadin.gwt.client.widget.controlbar.ComponentBar;
46  import info.magnolia.ui.vaadin.gwt.client.widget.controlbar.listener.ComponentListener;
47  import info.magnolia.ui.vaadin.gwt.client.widget.dnd.MoveWidget;
48  
49  import java.util.Iterator;
50  import java.util.LinkedList;
51  import java.util.List;
52  import java.util.Map;
53  
54  import com.google.gwt.dom.client.Element;
55  import com.google.gwt.event.shared.EventBus;
56  import com.google.gwt.event.shared.HandlerRegistration;
57  import com.google.gwt.user.client.DOM;
58  
59  /**
60   * Represents a component inside the {@link CmsNode}-tree.
61   * Implements a listener interface for the associated {@link info.magnolia.ui.vaadin.gwt.client.widget.controlbar.ComponentBar}.
62   * Handles DnD and move Events for components and provides wrapper functions used by the {@link info.magnolia.ui.vaadin.gwt.client.editor.model.focus.FocusModel}.
63   */
64  public class MgnlComponent extends MgnlElement implements ComponentListener {
65  
66      private final EventBus eventBus;
67      private List<HandlerRegistration> handlers = new LinkedList<HandlerRegistration>();
68  
69      public MgnlComponent(MgnlElement parent, EventBus eventBus) {
70          super(parent);
71          this.eventBus = eventBus;
72      }
73  
74      @Override
75      public ComponentElement getTypedElement() {
76          ComponentElement component = new ComponentElement(getAttribute("workspace"), getAttribute("path"), getAttribute("dialog"));
77          boolean inherited = Boolean.parseBoolean(getAttribute("inherited"));
78  
79          boolean isWritable = hasEditButton();
80          component.setWritable(isWritable);
81  
82          boolean isEditable = isEditable() && isWritable;
83          component.setEditable(isEditable);
84  
85          boolean deletable = isDeletable() && !inherited;
86          component.setDeletable(deletable);
87  
88          boolean moveable = isMovable();
89          component.setMoveable(moveable);
90  
91          component.setDuplicatable(isDuplicatable());
92  
93          if(getParent() instanceof MgnlArea) {
94             component.setAvailableTemplates(((MgnlArea) getParent()).getTypedElement().getAvailableComponents());
95          }
96  
97          return component;
98      }
99  
100     boolean isDuplicatable() {
101         MgnlArea parentArea = getParentArea();
102         if (parentArea != null) {
103             return !parentArea.isTypeSingle() && !parentArea.isMaxComponentsReached();
104         }
105         return false;
106     }
107 
108     private boolean isDeletable() {
109         boolean deletable = true;
110         if (getAttributes().containsKey(OperationPermissionDefinition.DELETABLE)) {
111             deletable = Boolean.parseBoolean(getAttribute(OperationPermissionDefinition.DELETABLE));
112         }
113         return deletable;
114     }
115 
116     private boolean isEditable() {
117         if (isInherited()) {
118             return false;
119         }
120         String editableAttr = getAttribute("editable");
121         return editableAttr == null || Boolean.parseBoolean(editableAttr);
122     }
123 
124     @Override
125     public void edit() {
126         eventBus.fireEvent(new EditComponentEvent(getTypedElement()));
127     }
128 
129     @Override
130     public void onAction(String actionName, Map<String, String> parameters) {
131         eventBus.fireEvent(new ComponentActionEvent(actionName, getTypedElement(), parameters));
132     }
133 
134     /**
135      * Fires a {@link SortComponentEvent} with the parent {@link AreaElement}.
136      * Sets the source component this component and the target to the component passed with the {@link ComponentStopMoveEvent}.
137      */
138     private void sortComponent(MgnlComponent target) {
139         MgnlArea area = getParentArea();
140         if (area != null) {
141             // area.onDragStart(true);
142 
143             AreaElement areaElement = area.getTypedElement();
144             areaElement.setSourceComponent(getTypedElement());
145             areaElement.setTargetComponent(target.getTypedElement());
146             areaElement.setSortOrder(getSortOrder(target));
147 
148             SortComponentEvent sortComponentEvent = new SortComponentEvent(areaElement);
149             eventBus.fireEvent(sortComponentEvent);
150         }
151     }
152 
153     public String getLabel() {
154         return getAttribute("label");
155     }
156 
157     public boolean hasEditButton() {
158         boolean inherited = Boolean.parseBoolean(getAttribute("inherited"));
159         boolean hasDialog = getAttribute("dialog") != null && !"".equals(getAttribute("dialog"));
160         boolean writable = true;
161         if (getAttributes().containsKey(OperationPermissionDefinition.WRITABLE)) {
162             writable = Boolean.parseBoolean(getAttribute(OperationPermissionDefinition.WRITABLE));
163         }
164         return writable && hasDialog && !inherited;
165     }
166 
167     public int getActivationStatus() {
168         if (containsAttribute(ACTIVATION_STATUS_KEY)) {
169             return Integer.parseInt(getAttribute(ACTIVATION_STATUS_KEY));
170         }
171         return NodeTypes.Activatable.ACTIVATION_STATUS_NOT_ACTIVATED;
172     }
173 
174     public boolean isMovable() {
175         boolean inherited = Boolean.parseBoolean(getAttribute("inherited"));
176         MgnlArea parentArea = this.getParentArea();
177         boolean parentAreaIsTypeSingle = parentArea != null && "single".equals(parentArea.getAttribute("type"));
178         boolean movable = true;
179         if (getAttributes().containsKey(OperationPermissionDefinition.MOVEABLE)) {
180             movable = Boolean.parseBoolean(getAttribute(OperationPermissionDefinition.MOVEABLE));
181         }
182         return movable && !inherited && !parentAreaIsTypeSingle;
183     }
184 
185     /**
186      * Callback for {@link ComponentBar} when starting a drag or move event. Depending on whether it is a drag or a move
187      * it will either notify the server by firing a {@link ComponentStartMoveEvent} or register the handlers in {@link #doStartMove(boolean)}.
188      *
189      * @param isDrag whether we are dragging the component or moving it
190      */
191     @Override
192     public void onMoveStart(boolean isDrag) {
193         if (isDrag) {
194             doStartMove(isDrag);
195         } else {
196             eventBus.fireEvent(new ComponentStartMoveEvent());
197         }
198     }
199 
200     /**
201      * Registers the sibling components as move targets and registers a handler for {@link ComponentStopMoveEvent} on the source component which will call {@link #sortComponent(MgnlComponent)}.
202      *
203      * @param isDrag whether we are dragging the component or moving it
204      */
205     public void doStartMove(boolean isDrag) {
206         setMoveSource(true);
207 
208         for (MgnlComponent component : getSiblingComponents()) {
209             component.registerMoveTarget(isDrag);
210         }
211 
212         handlers.add(eventBus.addHandler(ComponentStopMoveEvent.TYPE, new ComponentStopMoveEvent.ComponentStopMoveEventHandler() {
213             @Override
214             public void onStop(ComponentStopMoveEvent componentStopMoveEvent) {
215 
216                 setMoveSource(false);
217 
218                 Iterator<HandlerRegistration> it = handlers.iterator();
219                 while (it.hasNext()) {
220                     it.next().removeHandler();
221                     it.remove();
222                 }
223                 MgnlComponent target = componentStopMoveEvent.getTargetComponent();
224                 if (target != null) {
225                     sortComponent(target);
226                 }
227             }
228         }));
229     }
230 
231     /**
232      * Callback for {@link ComponentBar} targets when a move or drag event is dropped on or moved to this target.
233      * Fires {@link ComponentStopMoveEvent} to notify the system. Holds itself as payload for handling by the source,
234      * see handler registered in {@link #doStartMove}.
235      */
236     @Override
237     public void onMoveStop() {
238         eventBus.fireEvent(new ComponentStopMoveEvent(this, false));
239     }
240 
241     /**
242      * Callback for {@link ComponentBar} source when a drag is stopped.
243      * Fires {@link ComponentStopMoveEvent} to notify the system about the cancel. Will cause target components to
244      * unregister themselves as targets.
245      *
246      * @see #unregisterMoveTarget(boolean)
247      */
248     @Override
249     public void onMoveCancel() {
250         eventBus.fireEvent(new ComponentStopMoveEvent(null, false));
251     }
252 
253     /**
254      * Registers a {@link MgnlComponent} as a target.
255      * Registers the ui events for move or DnD on {@link ComponentBar} and adds an handler for {@link ComponentStopMoveEvent}.
256      */
257     private void registerMoveTarget(final boolean isDrag) {
258         setMoveTarget(true);
259 
260         if (isDrag) {
261             registerDragAndDropHandlers();
262         } else {
263             setDraggable(false);
264             registerMoveHandlers();
265         }
266         handlers.add(eventBus.addHandler(ComponentStopMoveEvent.TYPE, new ComponentStopMoveEvent.ComponentStopMoveEventHandler() {
267             @Override
268             public void onStop(ComponentStopMoveEvent componentMoveEvent) {
269                 unregisterMoveTarget(isDrag);
270                 setMoveOver(false);
271             }
272         }));
273     }
274 
275     /**
276      * Unregisters a {@link MgnlComponent} as a target.
277      * Removes the ui event handlers for move or DnD on {@link ComponentBar} and removes the handler for {@link ComponentStopMoveEvent}.
278      */
279     private void unregisterMoveTarget(boolean isDrag) {
280         setMoveTarget(false);
281 
282         if (isDrag) {
283             unregisterDragAndDropHandlers();
284         } else {
285             setDraggable(true);
286             unregisterMoveHandlers();
287         }
288 
289         Iterator<HandlerRegistration> it = handlers.iterator();
290         while (it.hasNext()) {
291             it.next().removeHandler();
292             it.remove();
293         }
294     }
295 
296     private void registerMoveHandlers() {
297         if (getControlBar() != null) {
298             getControlBar().registerMoveHandlers();
299         }
300     }
301 
302     private void unregisterMoveHandlers() {
303         if (getControlBar() != null) {
304             getControlBar().unregisterMoveHandlers();
305         }
306     }
307 
308     private void registerDragAndDropHandlers() {
309         if (getControlBar() != null) {
310             getControlBar().registerDragAndDropHandlers();
311         }
312     }
313 
314     private void unregisterDragAndDropHandlers() {
315         if (getControlBar() != null) {
316             getControlBar().unregisterDragAndDropHandlers();
317         }
318     }
319 
320     private void setDraggable(boolean draggable) {
321         if (getControlBar() != null) {
322             getControlBar().setDraggable(draggable);
323         }
324     }
325 
326     public void setVisible(boolean visible) {
327         if (getControlBar() != null) {
328             getControlBar().setVisible(visible);
329         }
330     }
331 
332     public void removeFocus() {
333         if (getControlBar() != null) {
334             getControlBar().removeFocus();
335         }
336     }
337 
338     public void setFocus() {
339         if (getControlBar() != null) {
340             getControlBar().setFocus(false);
341         }
342     }
343 
344     public void setMoveTarget(boolean moveTarget) {
345         if (getControlBar() != null) {
346             getControlBar().setMoveTarget(moveTarget);
347         }
348     }
349 
350     public void setMoveOver(boolean moveTarget) {
351         if (getControlBar() != null) {
352             getControlBar().setMoveOver(moveTarget);
353         }
354     }
355 
356     private void setMoveSource(boolean source) {
357         if (getControlBar() != null) {
358             getControlBar().setMoveSource(source);
359         }
360     }
361 
362     @Override
363     protected ComponentBar getControlBar() {
364         return (ComponentBar) super.getControlBar();
365     }
366 
367     private String getSortOrder(MgnlComponent target) {
368 
369         int xTarget = target.getControlBar().getAbsoluteLeft();
370         int yTarget = target.getControlBar().getAbsoluteTop();
371         int xThis = getControlBar().getAbsoluteLeft();
372         int yThis = getControlBar().getAbsoluteTop();
373 
374         boolean isDragUp = yThis > yTarget;
375         boolean isDragDown = !isDragUp;
376         boolean isDragLeft = xThis > xTarget;
377         boolean isDragRight = !isDragLeft;
378 
379         String order = null;
380 
381         if (isDragUp || isDragLeft) {
382             order = "before";
383         } else if (isDragDown || isDragRight) {
384             order = "after";
385         }
386         return order;
387     }
388 
389     private List<MgnlComponent> getSiblingComponents() {
390         List<MgnlComponent> siblings = new LinkedList<MgnlComponent>();
391         MgnlArea area = getParentArea();
392         for (MgnlComponent component : area.getComponents()) {
393             if (component != this) {
394                 siblings.add(component);
395             }
396         }
397         return siblings;
398     }
399 
400     public MoveWidget getMoveWidget() {
401         Element element = DOM.clone(getControlBar().getElement(), true);
402         return new MoveWidget(element, getWidth(), getHeight());
403     }
404 
405     private int getHeight() {
406         if (getControlBar() != null) {
407             return getControlBar().getOffsetHeight();
408         }
409         return 0;
410     }
411 
412     private int getWidth() {
413         if (getControlBar() != null) {
414             return getControlBar().getOffsetWidth();
415         }
416         return 0;
417     }
418 }