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 }