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