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 org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.datadictionary.parse.BeanTag;
20  import org.kuali.rice.krad.datadictionary.parse.BeanTagAttribute;
21  import org.kuali.rice.krad.datadictionary.parse.BeanTags;
22  import org.kuali.rice.krad.uif.CssConstants;
23  import org.kuali.rice.krad.uif.component.Component;
24  import org.kuali.rice.krad.uif.container.Container;
25  import org.kuali.rice.krad.uif.container.Group;
26  import org.kuali.rice.krad.uif.view.View;
27  
28  import java.util.ArrayList;
29  import java.util.List;
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 GridLayoutManager extends LayoutManagerBase {
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 GridLayoutManager() {
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       * @see org.kuali.rice.krad.uif.layout.LayoutManagerBase#performFinalize(org.kuali.rice.krad.uif.view.View,
80       *      java.lang.Object, org.kuali.rice.krad.uif.container.Container)
81       */
82      @Override
83      public void performFinalize(View view, Object model, Container container) {
84          super.performFinalize(view, model, container);
85  
86          if (suppressLineWrapping) {
87              numberOfColumns = container.getItems().size();
88          }
89  
90          for (Component component : container.getItems()) {
91              if (this instanceof TableLayoutManager) {
92                  component.addCellCssClass("uif-gridLayoutCell");
93              }
94              setCellAttributes(component);
95          }
96      }
97  
98      /**
99       * Moves the width, align, and valign settings of the component to the corresponding cell properties (if not
100      * already configured)
101      * 
102      * @param component instance to adjust settings for
103      */
104     protected void setCellAttributes(Component component) {
105         if (StringUtils.isNotBlank(component.getWidth()) && StringUtils.isBlank(component.getCellWidth())) {
106             component.setCellWidth(component.getWidth());
107             component.setWidth("");
108         }
109 
110         if (StringUtils.isNotBlank(component.getAlign()) && !StringUtils.contains(component.getCellStyle(),
111                 CssConstants.TEXT_ALIGN)) {
112             if (component.getCellStyle() == null) {
113                 component.setCellStyle("");
114             }
115 
116             component.setCellStyle(component.getCellStyle() + CssConstants.TEXT_ALIGN + component.getAlign() + ";");
117             component.setAlign("");
118         }
119 
120         if (StringUtils.isNotBlank(component.getValign()) && !StringUtils.contains(component.getCellStyle(),
121                 CssConstants.VERTICAL_ALIGN)) {
122             if (component.getCellStyle() == null) {
123                 component.setCellStyle("");
124             }
125 
126             component.setCellStyle(
127                     component.getCellStyle() + CssConstants.VERTICAL_ALIGN + component.getValign() + ";");
128             component.setValign("");
129         }
130     }
131 
132     /**
133      * @see LayoutManagerBase#getSupportedContainer()
134      */
135     @Override
136     public Class<? extends Container> getSupportedContainer() {
137         return Group.class;
138     }
139 
140     /**
141      * Indicates the number of columns that should make up one row of data
142      *
143      * <p>
144      * If the item count is greater than the number of columns, a new row will
145      * be created to render the remaining items (and so on until all items are
146      * placed).
147      * </p>
148      *
149      * <p>
150      * Note this does not include any generated columns by the layout manager,
151      * so the final column count could be greater (if label fields are
152      * separate).
153      * </p>
154      *
155      * @return int
156      */
157     @BeanTagAttribute(name = "numberOfColumns")
158     public int getNumberOfColumns() {
159         return this.numberOfColumns;
160     }
161 
162     /**
163      * Setter for the number of columns (each row)
164      *
165      * @param numberOfColumns
166      */
167     public void setNumberOfColumns(int numberOfColumns) {
168         this.numberOfColumns = numberOfColumns;
169     }
170 
171     /**
172      * Indicates whether the number of columns for the table data should match
173      * the number of fields given in the container's items list (so that each
174      * field takes up one column without wrapping), this overrides the configured
175      * numberOfColumns
176      *
177      * <p>
178      * If set to true during the initialize phase the number of columns will be
179      * set to the size of the container's field list, if false the configured
180      * number of columns is used
181      * </p>
182      *
183      * @return true if the column count should match the container's
184      *         field count, false to use the configured number of columns
185      */
186     @BeanTagAttribute(name = "suppressLineWrapping")
187     public boolean isSuppressLineWrapping() {
188         return this.suppressLineWrapping;
189     }
190 
191     /**
192      * Setter for the suppressLineWrapping indicator
193      *
194      * @param suppressLineWrapping
195      */
196     public void setSuppressLineWrapping(boolean suppressLineWrapping) {
197         this.suppressLineWrapping = suppressLineWrapping;
198     }
199 
200     /**
201      * Indicates whether alternating row styles should be applied
202      *
203      * <p>
204      * Indicator to layout manager templates to apply alternating row styles.
205      * See the configured template for the actual style classes used
206      * </p>
207      *
208      * @return true if alternating styles should be applied, false if
209      *         all rows should have the same style
210      */
211     @BeanTagAttribute(name = "applyAlternatingRowStyles")
212     public boolean isApplyAlternatingRowStyles() {
213         return this.applyAlternatingRowStyles;
214     }
215 
216     /**
217      * Setter for the alternating row styles indicator
218      *
219      * @param applyAlternatingRowStyles
220      */
221     public void setApplyAlternatingRowStyles(boolean applyAlternatingRowStyles) {
222         this.applyAlternatingRowStyles = applyAlternatingRowStyles;
223     }
224 
225     /**
226      * Indicates whether the manager should default the cell widths
227      *
228      * <p>
229      * If true, the manager will set the cell width by equally dividing by the
230      * number of columns
231      * </p>
232      *
233      * @return true if default cell widths should be applied, false if
234      *         no defaults should be applied
235      */
236     @BeanTagAttribute(name = "applyDefaultCellWidths")
237     public boolean isApplyDefaultCellWidths() {
238         return this.applyDefaultCellWidths;
239     }
240 
241     /**
242      * Setter for the default cell width indicator
243      *
244      * @param applyDefaultCellWidths
245      */
246     public void setApplyDefaultCellWidths(boolean applyDefaultCellWidths) {
247         this.applyDefaultCellWidths = applyDefaultCellWidths;
248     }
249 
250     /**
251      * Indicates whether the first cell of each row should be rendered as a header cell (th)
252      *
253      * <p>
254      * When this flag is turned on, the first cell for each row will be rendered as a header cell. If
255      * {@link #isRenderAlternatingHeaderColumns()} is false, the remaining cells for the row will be rendered
256      * as data cells, else they will alternate between cell headers
257      * </p>
258      *
259      * @return true if first cell of each row should be rendered as a header cell
260      */
261     @BeanTagAttribute(name = "renderRowFirstCellHeader")
262     public boolean isRenderRowFirstCellHeader() {
263         return renderRowFirstCellHeader;
264     }
265 
266     /**
267      * Setter for render first row column as header indicator
268      *
269      * @param renderRowFirstCellHeader
270      */
271     public void setRenderRowFirstCellHeader(boolean renderRowFirstCellHeader) {
272         this.renderRowFirstCellHeader = renderRowFirstCellHeader;
273     }
274 
275     /**
276      * Indicates whether the first row of items rendered should all be rendered as table header (th) cells
277      *
278      * <p>
279      * Generally when using a grid layout all the cells will be tds or alternating th/td (with the label in the
280      * th cell). However in some cases it might be desired to display the labels in one row as table header cells (th)
281      * followed by a row with the corresponding fields in td cells. When this is enabled this type of layout is
282      * possible
283      * </p>
284      *
285      * @return true if first row should be rendered as header cells
286      */
287     @BeanTagAttribute(name = "renderFirstRowHeader")
288     public boolean isRenderFirstRowHeader() {
289         return renderFirstRowHeader;
290     }
291 
292     /**
293      * Setter for the first row as header indicator
294      *
295      * @param renderFirstRowHeader
296      */
297     public void setRenderFirstRowHeader(boolean renderFirstRowHeader) {
298         this.renderFirstRowHeader = renderFirstRowHeader;
299     }
300 
301     /**
302      * Indicates whether header columns (th for tables) should be rendered for
303      * every other item (alternating)
304      *
305      * <p>
306      * If true the first cell of each row will be rendered as an header, with
307      * every other cell in the row as a header
308      * </p>
309      *
310      * @return true if alternating headers should be rendered, false if not
311      */
312     @BeanTagAttribute(name = "renderAlternatingHeaderColumns")
313     public boolean isRenderAlternatingHeaderColumns() {
314         return this.renderAlternatingHeaderColumns;
315     }
316 
317     /**
318      * Setter for the render alternating header columns indicator
319      *
320      * @param renderAlternatingHeaderColumns
321      */
322     public void setRenderAlternatingHeaderColumns(boolean renderAlternatingHeaderColumns) {
323         this.renderAlternatingHeaderColumns = renderAlternatingHeaderColumns;
324     }
325 
326     /**
327      * The list of styles for each row
328      *
329      * <p>
330      * Each entry in the list gives the style for the row with the same index. This style will be added the the <tr>
331      * tag
332      * when the table rows are rendered in the grid.tag. This is used to store the styles for newly added lines and
333      * other special cases like the add item row.
334      * </p>
335      *
336      * @return list of styles for the rows
337      */
338     @BeanTagAttribute(name = "rowCssClasses", type = BeanTagAttribute.AttributeType.LISTVALUE)
339     public List<String> getRowCssClasses() {
340         return rowCssClasses;
341     }
342 
343     /**
344      * Setter for the list that stores the css style names of each row
345      *
346      * @param rowCssClasses
347      */
348     public void setRowCssClasses(List<String> rowCssClasses) {
349         this.rowCssClasses = rowCssClasses;
350     }
351 }