View Javadoc

1   /**
2    * Copyright 2005-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krad.uif.layout;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.apache.commons.lang.StringUtils;
22  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
23  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
24  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
25  import org.kuali.rice.krad.uif.CssConstants;
26  import org.kuali.rice.krad.uif.component.Component;
27  import org.kuali.rice.krad.uif.container.Container;
28  import org.kuali.rice.krad.uif.container.Group;
29  
30  /**
31   * Layout manager that organizes its components in a table based grid
32   *
33   * <p>
34   * Items are laid out from left to right (with each item taking up one column)
35   * until the configured number of columns is reached. If the item count is
36   * greater than the number of columns, a new row will be created to render the
37   * remaining items (and so on until all items are placed). Labels for the fields
38   * can be pulled out (default) and rendered as a separate column. The manager
39   * also supports the column span and row span options for the field items. If
40   * not specified the default is 1.
41   * </p>
42   *
43   * @author Kuali Rice Team (rice.collab@kuali.org)
44   */
45  @BeanTags({@BeanTag(name = "gridLayout-bean", parent = "Uif-GridLayoutBase"),
46          @BeanTag(name = "twoColumnGridLayout-bean", parent = "Uif-TwoColumnGridLayout"),
47          @BeanTag(name = "fourColumnGridLayout-bean", parent = "Uif-FourColumnGridLayout"),
48          @BeanTag(name = "sixColumnGridLayout-bean", parent = "Uif-SixColumnGridLayout")})
49  public class GridLayoutManager extends LayoutManagerBase {
50      private static final long serialVersionUID = 1890011900375071128L;
51  
52      private int numberOfColumns;
53  
54      private boolean suppressLineWrapping;
55      private boolean applyAlternatingRowStyles;
56      private boolean applyDefaultCellWidths;
57      private boolean renderFirstRowHeader;
58      private boolean renderAlternatingHeaderColumns;
59      private boolean renderRowFirstCellHeader;
60  
61      private List<String> rowCssClasses;
62  
63      public GridLayoutManager() {
64          super();
65  
66          rowCssClasses = new ArrayList<String>();
67      }
68  
69      /**
70       * The following finalization is performed:
71       *
72       * <ul>
73       * <li>If suppressLineWrapping is true, sets the number of columns to the
74       * container's items list size</li>
75       * <li>Adjust the cell attributes for the container items</li>
76       * </ul>
77       *
78       * @see org.kuali.rice.krad.uif.layout.LayoutManagerBase#performFinalize(org.kuali.rice.krad.uif.view.View,
79       *      java.lang.Object, org.kuali.rice.krad.uif.container.Container)
80       */
81      @Override
82      public void performFinalize(Object model, Component component) {
83          super.performFinalize(model, component);
84          
85          Container container = (Container) component;
86  
87          if (suppressLineWrapping) {
88              numberOfColumns = container.getItems().size();
89          }
90  
91          for (Component item : container.getItems()) {
92              if (!(this instanceof TableLayoutManager)) {
93                  item.addWrapperCssClass("uif-gridLayoutCell");
94              }
95              setCellAttributes(item);
96          }
97      }
98  
99      /**
100      * Moves the width, align, and valign settings of the component to the corresponding cell properties (if not
101      * already configured)
102      *
103      * @param component instance to adjust settings for
104      */
105     protected void setCellAttributes(Component component) {
106         if (StringUtils.isNotBlank(component.getWidth()) && StringUtils.isBlank(component.getCellWidth())) {
107             component.setCellWidth(component.getWidth());
108             component.setWidth("");
109         }
110 
111         if (StringUtils.isNotBlank(component.getAlign()) && !StringUtils.contains(component.getWrapperStyle(),
112                 CssConstants.TEXT_ALIGN)) {
113             if (component.getWrapperStyle() == null) {
114                 component.setWrapperStyle("");
115             }
116 
117             component.setWrapperStyle(
118                     component.getWrapperStyle() + CssConstants.TEXT_ALIGN + component.getAlign() + ";");
119             component.setAlign("");
120         }
121 
122         if (StringUtils.isNotBlank(component.getValign()) && !StringUtils.contains(component.getWrapperStyle(),
123                 CssConstants.VERTICAL_ALIGN)) {
124             if (component.getWrapperStyle() == null) {
125                 component.setWrapperStyle("");
126             }
127 
128             component.setWrapperStyle(
129                     component.getWrapperStyle() + CssConstants.VERTICAL_ALIGN + component.getValign() + ";");
130             component.setValign("");
131         }
132     }
133 
134     /**
135      * @see LayoutManagerBase#getSupportedContainer()
136      */
137     @Override
138     public Class<? extends Container> getSupportedContainer() {
139         return Group.class;
140     }
141 
142     /**
143      * Indicates the number of columns that should make up one row of data
144      *
145      * <p>
146      * If the item count is greater than the number of columns, a new row will
147      * be created to render the remaining items (and so on until all items are
148      * placed).
149      * </p>
150      *
151      * <p>
152      * Note this does not include any generated columns by the layout manager,
153      * so the final column count could be greater (if label fields are
154      * separate).
155      * </p>
156      *
157      * @return int
158      */
159     @BeanTagAttribute(name = "numberOfColumns")
160     public int getNumberOfColumns() {
161         return this.numberOfColumns;
162     }
163 
164     /**
165      * Setter for the number of columns (each row)
166      *
167      * @param numberOfColumns
168      */
169     public void setNumberOfColumns(int numberOfColumns) {
170         this.numberOfColumns = numberOfColumns;
171     }
172 
173     /**
174      * Indicates whether the number of columns for the table data should match
175      * the number of fields given in the container's items list (so that each
176      * field takes up one column without wrapping), this overrides the configured
177      * numberOfColumns
178      *
179      * <p>
180      * If set to true during the initialize phase the number of columns will be
181      * set to the size of the container's field list, if false the configured
182      * number of columns is used
183      * </p>
184      *
185      * @return true if the column count should match the container's
186      *         field count, false to use the configured number of columns
187      */
188     @BeanTagAttribute(name = "suppressLineWrapping")
189     public boolean isSuppressLineWrapping() {
190         return this.suppressLineWrapping;
191     }
192 
193     /**
194      * Setter for the suppressLineWrapping indicator
195      *
196      * @param suppressLineWrapping
197      */
198     public void setSuppressLineWrapping(boolean suppressLineWrapping) {
199         this.suppressLineWrapping = suppressLineWrapping;
200     }
201 
202     /**
203      * Indicates whether alternating row styles should be applied
204      *
205      * <p>
206      * Indicator to layout manager templates to apply alternating row styles.
207      * See the configured template for the actual style classes used
208      * </p>
209      *
210      * @return true if alternating styles should be applied, false if
211      *         all rows should have the same style
212      */
213     @BeanTagAttribute(name = "applyAlternatingRowStyles")
214     public boolean isApplyAlternatingRowStyles() {
215         return this.applyAlternatingRowStyles;
216     }
217 
218     /**
219      * Setter for the alternating row styles indicator
220      *
221      * @param applyAlternatingRowStyles
222      */
223     public void setApplyAlternatingRowStyles(boolean applyAlternatingRowStyles) {
224         this.applyAlternatingRowStyles = applyAlternatingRowStyles;
225     }
226 
227     /**
228      * Indicates whether the manager should default the cell widths
229      *
230      * <p>
231      * If true, the manager will set the cell width by equally dividing by the
232      * number of columns
233      * </p>
234      *
235      * @return true if default cell widths should be applied, false if
236      *         no defaults should be applied
237      */
238     @BeanTagAttribute(name = "applyDefaultCellWidths")
239     public boolean isApplyDefaultCellWidths() {
240         return this.applyDefaultCellWidths;
241     }
242 
243     /**
244      * Setter for the default cell width indicator
245      *
246      * @param applyDefaultCellWidths
247      */
248     public void setApplyDefaultCellWidths(boolean applyDefaultCellWidths) {
249         this.applyDefaultCellWidths = applyDefaultCellWidths;
250     }
251 
252     /**
253      * Indicates whether the first cell of each row should be rendered as a header cell (th)
254      *
255      * <p>
256      * When this flag is turned on, the first cell for each row will be rendered as a header cell. If
257      * {@link #isRenderAlternatingHeaderColumns()} is false, the remaining cells for the row will be rendered
258      * as data cells, else they will alternate between cell headers
259      * </p>
260      *
261      * @return true if first cell of each row should be rendered as a header cell
262      */
263     @BeanTagAttribute(name = "renderRowFirstCellHeader")
264     public boolean isRenderRowFirstCellHeader() {
265         return renderRowFirstCellHeader;
266     }
267 
268     /**
269      * Setter for render first row column as header indicator
270      *
271      * @param renderRowFirstCellHeader
272      */
273     public void setRenderRowFirstCellHeader(boolean renderRowFirstCellHeader) {
274         this.renderRowFirstCellHeader = renderRowFirstCellHeader;
275     }
276 
277     /**
278      * Indicates whether the first row of items rendered should all be rendered as table header (th) cells
279      *
280      * <p>
281      * Generally when using a grid layout all the cells will be tds or alternating th/td (with the label in the
282      * th cell). However in some cases it might be desired to display the labels in one row as table header cells (th)
283      * followed by a row with the corresponding fields in td cells. When this is enabled this type of layout is
284      * possible
285      * </p>
286      *
287      * @return true if first row should be rendered as header cells
288      */
289     @BeanTagAttribute(name = "renderFirstRowHeader")
290     public boolean isRenderFirstRowHeader() {
291         return renderFirstRowHeader;
292     }
293 
294     /**
295      * Setter for the first row as header indicator
296      *
297      * @param renderFirstRowHeader
298      */
299     public void setRenderFirstRowHeader(boolean renderFirstRowHeader) {
300         this.renderFirstRowHeader = renderFirstRowHeader;
301     }
302 
303     /**
304      * Indicates whether header columns (th for tables) should be rendered for
305      * every other item (alternating)
306      *
307      * <p>
308      * If true the first cell of each row will be rendered as an header, with
309      * every other cell in the row as a header
310      * </p>
311      *
312      * @return true if alternating headers should be rendered, false if not
313      */
314     @BeanTagAttribute(name = "renderAlternatingHeaderColumns")
315     public boolean isRenderAlternatingHeaderColumns() {
316         return this.renderAlternatingHeaderColumns;
317     }
318 
319     /**
320      * Setter for the render alternating header columns indicator
321      *
322      * @param renderAlternatingHeaderColumns
323      */
324     public void setRenderAlternatingHeaderColumns(boolean renderAlternatingHeaderColumns) {
325         this.renderAlternatingHeaderColumns = renderAlternatingHeaderColumns;
326     }
327 
328     /**
329      * The list of styles for each row
330      *
331      * <p>
332      * Each entry in the list gives the style for the row with the same index. This style will be added the the <tr>
333      * tag
334      * when the table rows are rendered in the grid.tag. This is used to store the styles for newly added lines and
335      * other special cases like the add item row.
336      * </p>
337      *
338      * @return list of styles for the rows
339      */
340     @BeanTagAttribute(name = "rowCssClasses", type = BeanTagAttribute.AttributeType.LISTVALUE)
341     public List<String> getRowCssClasses() {
342         return rowCssClasses;
343     }
344 
345     /**
346      * Setter for the list that stores the css style names of each row
347      *
348      * @param rowCssClasses
349      */
350     public void setRowCssClasses(List<String> rowCssClasses) {
351         this.rowCssClasses = rowCssClasses;
352     }
353 
354     /**
355      * @see org.kuali.rice.krad.datadictionary.DictionaryBeanBase#copyProperties(Object)
356      */
357     @Override
358     protected <T> void copyProperties(T layoutManager) {
359         super.copyProperties(layoutManager);
360 
361         GridLayoutManager gridLayoutManagerCopy = (GridLayoutManager) layoutManager;
362 
363         gridLayoutManagerCopy.setNumberOfColumns(this.numberOfColumns);
364         gridLayoutManagerCopy.setSuppressLineWrapping(this.suppressLineWrapping);
365         gridLayoutManagerCopy.setApplyAlternatingRowStyles(this.applyAlternatingRowStyles);
366         gridLayoutManagerCopy.setApplyDefaultCellWidths(this.applyDefaultCellWidths);
367         gridLayoutManagerCopy.setRenderFirstRowHeader(this.renderFirstRowHeader);
368         gridLayoutManagerCopy.setRenderAlternatingHeaderColumns(this.renderAlternatingHeaderColumns);
369         gridLayoutManagerCopy.setRenderRowFirstCellHeader(this.renderRowFirstCellHeader);
370 
371         if (rowCssClasses != null) {
372             gridLayoutManagerCopy.setRowCssClasses(new ArrayList<String>(rowCssClasses));
373         }
374     }
375 }