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