View Javadoc
1   /**
2    * This file Copyright (c) 2012-2015 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.model.focus;
35  
36  import info.magnolia.ui.vaadin.gwt.client.editor.dom.MgnlArea;
37  import info.magnolia.ui.vaadin.gwt.client.editor.dom.MgnlComponent;
38  import info.magnolia.ui.vaadin.gwt.client.editor.dom.MgnlElement;
39  import info.magnolia.ui.vaadin.gwt.client.editor.dom.MgnlPage;
40  import info.magnolia.ui.vaadin.gwt.client.editor.event.ComponentStopMoveEvent;
41  import info.magnolia.ui.vaadin.gwt.client.editor.event.SelectElementEvent;
42  import info.magnolia.ui.vaadin.gwt.client.editor.model.Model;
43  
44  import com.google.gwt.dom.client.Element;
45  import com.google.web.bindery.event.shared.EventBus;
46  
47  /**
48   * Helper class to keep track of selected items. Welcome to the MindTwister.
49   */
50  public class FocusModelImpl implements FocusModel {
51  
52      private final Model model;
53      private final EventBus eventBus;
54  
55      private boolean rootSelected = false;
56  
57      public FocusModelImpl(EventBus eventBus, Model model) {
58          super();
59          this.eventBus = eventBus;
60          this.model = model;
61      }
62  
63      @Override
64      public void selectElement(Element element) {
65  
66  
67          MgnlElement mgnlElement = model.getMgnlElement(element);
68  
69          if (model.isMoving()) {
70              // cancel move if click was outside the moving components relatives or null
71              if (mgnlElement == null || !mgnlElement.isRelated(model.getSelectedComponent())) {
72                  eventBus.fireEvent(new ComponentStopMoveEvent(null, false));
73              }
74              return;
75          }
76  
77          MgnlPage page = null;
78          MgnlArea area = null;
79          MgnlComponent component = null;
80  
81          // if there is no mapping, we select the page
82          if (mgnlElement == null) {
83              page = model.getRootPage();
84          } else {
85              if (mgnlElement.isComponent()) {
86                  component = (MgnlComponent) mgnlElement;
87                  area = mgnlElement.getParentArea();
88              } else if (mgnlElement.isArea()) {
89                  area = (MgnlArea) mgnlElement;
90              }
91          }
92          // first set the component, then set the area. the selected component is used for setting
93          // the current area class.
94          setComponentSelection(component);
95          setAreaSelection(area);
96          setPageSelection(page);
97  
98          select(mgnlElement);
99      }
100 
101     @Override
102     public void init() {
103         for (MgnlArea root : model.getRootAreas()) {
104             root.setVisible(true);
105 
106             if(root.getComponents().isEmpty()) {
107                 root.setPlaceHolderVisible(true);
108             }
109         }
110 
111         setPageSelection(model.getRootPage());
112         select(model.getRootPage());
113     }
114 
115     /**
116      * Takes care of the selection of components. keeps track of last selected element and toggles
117      * the focus. If a null-value is passed it will reset the currently selected component.
118      *
119      * @param component the MgnlElement component, can be null.
120      */
121     private void setComponentSelection(MgnlComponent component) {
122         MgnlComponent currentComponent = model.getSelectedComponent();
123         if (currentComponent == component) {
124             return;
125         }
126         if (currentComponent != null) {
127                 currentComponent.removeFocus();
128         }
129         if (component != null) {
130             component.setFocus();
131         }
132         model.setSelectedComponent(component);
133 
134     }
135 
136     /**
137      * This method takes care of selecting and deselecting areas.
138      *
139      * @param area selected area, can be null.
140      */
141     private void setAreaSelection(MgnlArea area) {
142         MgnlArea selectedArea = model.getSelectedArea();
143         MgnlComponent currentComponent = model.getSelectedComponent();
144 
145         if (selectedArea != null) {
146 
147             selectedArea.removeFocus();
148 
149             // always reset current area selection unless area and current area are related
150             if (!selectedArea.isRelated(area)) {
151 
152                 toggleChildComponentVisibility(selectedArea, false);
153                 toggleAreaVisibility(selectedArea, false);
154             }
155 
156             // hide child components if area is an ascendant of current area or selectedArea is not
157             // a descendant
158             else if (selectedArea.getAscendants().contains(area) || (area != null && !area.getDescendants().contains(selectedArea))) {
159                 toggleChildComponentVisibility(selectedArea, false);
160             }
161         }
162 
163         // set focus on new selected area
164         if (area != null) {
165             toggleAreaVisibility(area, true);
166             toggleChildComponentVisibility(area, true);
167 
168             area.setFocus((currentComponent != null));
169         }
170         model.setSelectedArea(area);
171     }
172 
173     private void toggleAreaVisibility(MgnlArea area, boolean visible) {
174 
175         MgnlArea parentArea = area.getParentArea();
176         if (parentArea != null) {
177             toggleAreaVisibility(parentArea, visible);
178             toggleChildComponentVisibility(parentArea, visible);
179 
180         }
181         // root areas are always visible
182         if (!model.getRootAreas().contains(area)) {
183             area.setVisible(visible);
184         }
185 
186         toggleNestedAreasVisibility(area, visible);
187     }
188 
189     private void toggleChildComponentVisibility(MgnlArea area, boolean visible) {
190 
191         // do not hide empty root areas placeholder
192         if (!model.getRootAreas().contains(area) || !area.getComponents().isEmpty()) {
193             area.setPlaceHolderVisible(visible);
194         }
195 
196         // hide
197         if (!visible && !area.getComponents().isEmpty()) {
198             area.setPlaceHolderVisible(visible);
199         }
200 
201 
202         for (MgnlComponent component : area.getComponents()) {
203 
204             // toggle all child-components editbar visibility - does this case occur?
205             component.setVisible(visible);
206 
207 
208             // toggle all child-components-area visibility
209             for (MgnlArea childArea : component.getAreas()) {
210                 childArea.setVisible(visible);
211                 toggleNestedAreasVisibility(childArea, visible);
212             }
213         }
214     }
215 
216     private void toggleNestedAreasVisibility(MgnlArea area, boolean visible) {
217         for (MgnlArea childArea : area.getAreas()) {
218             childArea.setVisible(visible);
219             toggleNestedAreasVisibility(childArea, visible);
220         }
221     }
222 
223     private void setPageSelection(MgnlPage page) {
224         boolean visible = true;
225         if (page == null) {
226             visible = false;
227         }
228 
229         this.rootSelected = !this.rootSelected;
230         for (MgnlArea root : model.getRootAreas()) {
231             root.toggleInitFocus(visible);
232         }
233     }
234 
235     @Override
236     public void select(MgnlElement mgnlElement) {
237         mgnlElement = (mgnlElement != null) ? mgnlElement : model.getRootPage();
238         eventBus.fireEvent(new SelectElementEvent(mgnlElement.getTypedElement()));
239     }
240 
241 }