View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.component.layout;
23  
24  import java.util.Arrays;
25  import java.util.Collection;
26  import java.util.Collections;
27  import java.util.Map;
28  import java.util.logging.Logger;
29  
30  import javax.el.ValueExpression;
31  import javax.faces.application.ResourceDependency;
32  import javax.faces.component.UIComponent;
33  import javax.faces.component.UIComponentBase;
34  import javax.faces.component.behavior.ClientBehaviorHolder;
35  import javax.faces.context.FacesContext;
36  import javax.faces.context.ResponseWriter;
37  import javax.faces.event.AjaxBehaviorEvent;
38  import javax.faces.event.FacesEvent;
39  
40  import org.primefaces.component.api.Widget;
41  import org.primefaces.extensions.event.CloseEvent;
42  import org.primefaces.extensions.event.OpenEvent;
43  import org.primefaces.extensions.event.ResizeEvent;
44  import org.primefaces.util.Constants;
45  import org.primefaces.util.FastStringWriter;
46  import org.primefaces.util.LangUtils;
47  
48  /**
49   * <code>Layout</code> component.
50   *
51   * @author Oleg Varaksin / last modified by Melloware
52   * @since 0.2
53   */
54  @ResourceDependency(library = "primefaces", name = "jquery/jquery.js")
55  @ResourceDependency(library = "primefaces", name = "jquery/jquery-plugins.js")
56  @ResourceDependency(library = "primefaces", name = "core.js")
57  @ResourceDependency(library = "primefaces-extensions", name = "primefaces-extensions.js")
58  @ResourceDependency(library = "primefaces-extensions", name = "layout/layout.css")
59  @ResourceDependency(library = "primefaces-extensions", name = "layout/layout.js")
60  public class Layout extends UIComponentBase implements Widget, ClientBehaviorHolder {
61  
62      public static final String POSITION_SEPARATOR = "_";
63      public static final String STYLE_CLASS = "ui-layout-";
64      public static final String STYLE_CLASS_PANE = "ui-layout-unit ui-panel ui-widget ui-widget-content ui-corner-all";
65      public static final String STYLE_CLASS_PANE_WITH_SUBPANES = "ui-corner-all pe-layout-pane-withsubpanes";
66      public static final String STYLE_CLASS_PANE_HEADER = "ui-layout-unit-header ui-panel-titlebar ui-widget-header ui-corner-top pe-layout-pane-header ";
67      public static final String STYLE_CLASS_PANE_CONTENT = "ui-layout-unit-content pe-layout-pane-content ui-panel-content";
68      public static final String STYLE_CLASS_LAYOUT_CONTENT = "ui-layout-content ui-panel-content";
69  
70      public static final String PANE_POSITION_CENTER = "center";
71      public static final String PANE_POSITION_NORTH = "north";
72      public static final String PANE_POSITION_SOUTH = "south";
73      public static final String PANE_POSITION_WEST = "west";
74      public static final String PANE_POSITION_EAST = "east";
75  
76      public static final String COMPONENT_TYPE = "org.primefaces.extensions.component.Layout";
77      public static final String COMPONENT_FAMILY = "org.primefaces.extensions.component";
78      private static final String DEFAULT_RENDERER = "org.primefaces.extensions.component.LayoutRenderer";
79  
80      private static final Collection<String> EVENT_NAMES = Collections
81                  .unmodifiableCollection(Arrays.asList(OpenEvent.NAME, CloseEvent.NAME, ResizeEvent.NAME));
82      private static final Logger LOG = Logger.getLogger(Layout.class.getName());
83  
84      private ResponseWriter originalWriter;
85      private FastStringWriter fsw;
86      private boolean buildOptions;
87  
88      /**
89       * Properties that are tracked by state saving.
90       *
91       * @author Oleg Varaksin / last modified by Melloware
92       */
93      @SuppressWarnings("java:S115")
94      public enum PropertyKeys {
95          // @formatter:off
96        widgetVar,
97        fullPage,
98        options,
99        style,
100       styleClass,
101       state,
102       stateCookie,
103       togglerTip_open("Open"),
104       togglerTip_closed("Close"),
105       resizerTip("Resize"),
106       sliderTip("Slide"),
107       maskPanesEarly;
108       // @formatter:on
109 
110         private final String toString;
111 
112         PropertyKeys(final String toString) {
113             this.toString = toString;
114         }
115 
116         PropertyKeys() {
117             toString = null;
118         }
119 
120         @Override
121         public String toString() {
122             return ((toString != null) ? toString : super.toString());
123         }
124     }
125 
126     public Layout() {
127         setRendererType(DEFAULT_RENDERER);
128     }
129 
130     @Override
131     public String getFamily() {
132         return COMPONENT_FAMILY;
133     }
134 
135     public String getWidgetVar() {
136         return (String) getStateHelper().eval(PropertyKeys.widgetVar, null);
137     }
138 
139     public void setWidgetVar(final String widgetVar) {
140         getStateHelper().put(PropertyKeys.widgetVar, widgetVar);
141     }
142 
143     public boolean isFullPage() {
144         return (Boolean) getStateHelper().eval(PropertyKeys.fullPage, true);
145     }
146 
147     public void setFullPage(final boolean fullPage) {
148         getStateHelper().put(PropertyKeys.fullPage, fullPage);
149     }
150 
151     public Object getOptions() {
152         return getStateHelper().eval(PropertyKeys.options, null);
153     }
154 
155     public void setOptions(final Object options) {
156         getStateHelper().put(PropertyKeys.options, options);
157     }
158 
159     public String getStyle() {
160         return (String) getStateHelper().eval(PropertyKeys.style, null);
161     }
162 
163     public void setStyle(final String style) {
164         getStateHelper().put(PropertyKeys.style, style);
165     }
166 
167     public String getStyleClass() {
168         return (String) getStateHelper().eval(PropertyKeys.styleClass, null);
169     }
170 
171     public void setStyleClass(final String styleClass) {
172         getStateHelper().put(PropertyKeys.styleClass, styleClass);
173     }
174 
175     public String getState() {
176         return (String) getStateHelper().eval(PropertyKeys.state, null);
177     }
178 
179     public void setState(final String state) {
180         getStateHelper().put(PropertyKeys.state, state);
181     }
182 
183     public boolean isStateCookie() {
184         return (Boolean) getStateHelper().eval(PropertyKeys.stateCookie, false);
185     }
186 
187     public void setStateCookie(final boolean stateCookie) {
188         getStateHelper().put(PropertyKeys.stateCookie, stateCookie);
189     }
190 
191     public String getTogglerTipOpen() {
192         return (String) getStateHelper().eval(PropertyKeys.togglerTip_open, null);
193     }
194 
195     public void setTogglerTipOpen(final String togglerTipOpen) {
196         getStateHelper().put(PropertyKeys.togglerTip_open, togglerTipOpen);
197     }
198 
199     public String getTogglerTipClosed() {
200         return (String) getStateHelper().eval(PropertyKeys.togglerTip_closed, null);
201     }
202 
203     public void setTogglerTipClosed(final String togglerTipClosed) {
204         getStateHelper().put(PropertyKeys.togglerTip_closed, togglerTipClosed);
205     }
206 
207     public String getResizerTip() {
208         return (String) getStateHelper().eval(PropertyKeys.resizerTip, null);
209     }
210 
211     public void setResizerTip(final String resizerTip) {
212         getStateHelper().put(PropertyKeys.resizerTip, resizerTip);
213     }
214 
215     public String getSliderTip() {
216         return (String) getStateHelper().eval(PropertyKeys.sliderTip, null);
217     }
218 
219     public void setSliderTip(final String sliderTip) {
220         getStateHelper().put(PropertyKeys.sliderTip, sliderTip);
221     }
222 
223     public boolean isMaskPanesEarly() {
224         return (Boolean) getStateHelper().eval(PropertyKeys.maskPanesEarly, false);
225     }
226 
227     public void setMaskPanesEarly(final boolean maskPanesEarly) {
228         getStateHelper().put(PropertyKeys.maskPanesEarly, maskPanesEarly);
229     }
230 
231     @Override
232     public Collection<String> getEventNames() {
233         return EVENT_NAMES;
234     }
235 
236     @Override
237     public void processDecodes(final FacesContext fc) {
238         if (isSelfRequest(fc)) {
239             decode(fc);
240         }
241         else {
242             super.processDecodes(fc);
243         }
244     }
245 
246     @Override
247     public void processValidators(final FacesContext fc) {
248         if (!isSelfRequest(fc)) {
249             super.processValidators(fc);
250         }
251     }
252 
253     @Override
254     public void processUpdates(final FacesContext fc) {
255         if (!isSelfRequest(fc)) {
256             super.processUpdates(fc);
257         }
258 
259         final String state = fc.getExternalContext().getRequestParameterMap().get(getClientId(fc) + "_state");
260         if (LangUtils.isNotBlank(state)) {
261             final ValueExpression stateVE = getValueExpression(PropertyKeys.state.toString());
262             if (stateVE != null) {
263                 // save "state"
264                 stateVE.setValue(fc.getELContext(), state);
265                 getStateHelper().remove(PropertyKeys.state);
266             }
267         }
268     }
269 
270     @Override
271     public void queueEvent(final FacesEvent event) {
272         final FacesContext context = FacesContext.getCurrentInstance();
273 
274         if (isSelfRequest(context)) {
275             final Map<String, String> params = context.getExternalContext().getRequestParameterMap();
276             final String eventName = params.get(Constants.RequestParams.PARTIAL_BEHAVIOR_EVENT_PARAM);
277             final String clientId = getClientId(context);
278 
279             final AjaxBehaviorEvent behaviorEvent = (AjaxBehaviorEvent) event;
280             final LayoutPane pane = getLayoutPane(this, params.get(clientId + "_pane"));
281             if (pane == null) {
282                 LOG.warning("LayoutPane by request parameter '" + params.get(clientId + "_pane") + "' was not found");
283 
284                 return;
285             }
286 
287             if (OpenEvent.NAME.equals(eventName)) {
288                 final OpenEvent openEvent = new OpenEvent(pane, behaviorEvent.getBehavior());
289                 openEvent.setPhaseId(behaviorEvent.getPhaseId());
290                 super.queueEvent(openEvent);
291 
292                 return;
293             }
294             else if (CloseEvent.NAME.equals(eventName)) {
295                 final CloseEvent closeEvent = new CloseEvent(pane, behaviorEvent.getBehavior());
296                 closeEvent.setPhaseId(behaviorEvent.getPhaseId());
297                 super.queueEvent(closeEvent);
298 
299                 return;
300             }
301             else if (ResizeEvent.NAME.equals(eventName)) {
302                 final double width = Double.parseDouble(params.get(clientId + "_width"));
303                 final double height = Double.parseDouble(params.get(clientId + "_height"));
304 
305                 final ResizeEvent resizeEvent = new ResizeEvent(pane, behaviorEvent.getBehavior(), width, height);
306                 resizeEvent.setPhaseId(behaviorEvent.getPhaseId());
307                 super.queueEvent(resizeEvent);
308 
309                 return;
310             }
311         }
312 
313         super.queueEvent(event);
314     }
315 
316     public static LayoutPane getLayoutPane(final UIComponent component, final String combinedPosition) {
317         for (final UIComponent child : component.getChildren()) {
318             if (child instanceof LayoutPane) {
319                 if (((LayoutPane) child).getCombinedPosition().equals(combinedPosition)) {
320                     return (LayoutPane) child;
321                 }
322                 else {
323                     final LayoutPane pane = getLayoutPane(child, combinedPosition);
324                     if (pane != null) {
325                         return pane;
326                     }
327                 }
328             }
329         }
330 
331         return null;
332     }
333 
334     public ResponseWriter getOriginalWriter() {
335         return originalWriter;
336     }
337 
338     public void setOriginalWriter(final ResponseWriter originalWriter) {
339         this.originalWriter = originalWriter;
340     }
341 
342     public FastStringWriter getFastStringWriter() {
343         return fsw;
344     }
345 
346     public void setFastStringWriter(final FastStringWriter fsw) {
347         this.fsw = fsw;
348     }
349 
350     public boolean isBuildOptions() {
351         return buildOptions;
352     }
353 
354     public void setBuildOptions(final boolean buildOptions) {
355         this.buildOptions = buildOptions;
356     }
357 
358     public void removeOptions() {
359         getStateHelper().remove(PropertyKeys.options);
360     }
361 
362     public boolean isNested() {
363         return getParent() instanceof LayoutPane;
364     }
365 
366     public boolean isElementLayout() {
367         return !isNested() && !isFullPage();
368     }
369 
370     private boolean isSelfRequest(final FacesContext context) {
371         return getClientId(context)
372                     .equals(context.getExternalContext().getRequestParameterMap().get(
373                                 Constants.RequestParams.PARTIAL_SOURCE_PARAM));
374     }
375 
376 }