1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.renderkit.html.scarborough.standard.tag;
21
22 import org.apache.myfaces.tobago.component.Attributes;
23 import org.apache.myfaces.tobago.component.Facets;
24 import org.apache.myfaces.tobago.component.RendererTypes;
25 import org.apache.myfaces.tobago.component.UICommand;
26 import org.apache.myfaces.tobago.component.UIMenu;
27 import org.apache.myfaces.tobago.component.UIMenuCommand;
28 import org.apache.myfaces.tobago.component.UITab;
29 import org.apache.myfaces.tobago.component.UITabGroup;
30 import org.apache.myfaces.tobago.component.UIToolBar;
31 import org.apache.myfaces.tobago.context.Markup;
32 import org.apache.myfaces.tobago.event.TabChangeEvent;
33 import org.apache.myfaces.tobago.internal.component.AbstractUIPanelBase;
34 import org.apache.myfaces.tobago.internal.util.AccessKeyMap;
35 import org.apache.myfaces.tobago.layout.Measure;
36 import org.apache.myfaces.tobago.renderkit.LabelWithAccessKey;
37 import org.apache.myfaces.tobago.renderkit.LayoutComponentRendererBase;
38 import org.apache.myfaces.tobago.renderkit.css.Classes;
39 import org.apache.myfaces.tobago.renderkit.css.Position;
40 import org.apache.myfaces.tobago.renderkit.css.Style;
41 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
42 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
43 import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
44 import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
45 import org.apache.myfaces.tobago.renderkit.util.JQueryUtils;
46 import org.apache.myfaces.tobago.renderkit.util.RenderUtils;
47 import org.apache.myfaces.tobago.util.ComponentUtils;
48 import org.apache.myfaces.tobago.util.CreateComponentUtils;
49 import org.apache.myfaces.tobago.util.FacetUtils;
50 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 import javax.el.ValueExpression;
55 import javax.faces.application.Application;
56 import javax.faces.application.FacesMessage;
57 import javax.faces.component.UIComponent;
58 import javax.faces.context.FacesContext;
59 import java.io.IOException;
60 import java.util.List;
61 import java.util.Map;
62
63 public class TabGroupRenderer extends LayoutComponentRendererBase {
64
65 private static final Logger LOG = LoggerFactory.getLogger(TabGroupRenderer.class);
66
67 public static final String ACTIVE_INDEX_POSTFIX = ComponentUtils.SUB_SEPARATOR + "activeIndex";
68
69 @Override
70 public void decode(FacesContext facesContext, UIComponent component) {
71 if (ComponentUtils.isOutputOnly(component)) {
72 return;
73 }
74
75 int oldIndex = ((UITabGroup) component).getRenderedIndex();
76
77 String clientId = component.getClientId(facesContext);
78 final Map parameters = facesContext.getExternalContext().getRequestParameterMap();
79 String newValue = (String) parameters.get(clientId + ACTIVE_INDEX_POSTFIX);
80 try {
81 int activeIndex = Integer.parseInt(newValue);
82 if (activeIndex != oldIndex) {
83 TabChangeEvent event = new TabChangeEvent(component, oldIndex, activeIndex);
84 component.queueEvent(event);
85 }
86 } catch (NumberFormatException e) {
87 LOG.error("Can't parse activeIndex: '" + newValue + "'");
88 }
89 }
90
91 @Override
92 public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException {
93
94 UITabGroup tabGroup = (UITabGroup) uiComponent;
95
96 int activeIndex = ensureRenderedActiveIndex(facesContext, tabGroup);
97
98 final String clientId = tabGroup.getClientId(facesContext);
99 final String hiddenId = clientId + TabGroupRenderer.ACTIVE_INDEX_POSTFIX;
100 final String switchType = tabGroup.getSwitchType();
101 TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
102
103 writer.startElement(HtmlElements.DIV, null);
104 writer.writeIdAttribute(clientId);
105 writer.writeClassAttribute(Classes.create(tabGroup));
106 HtmlRendererUtils.writeDataAttributes(facesContext, writer, tabGroup);
107 writer.writeStyleAttribute(new Style(facesContext, tabGroup));
108 writer.writeAttribute(HtmlAttributes.SWITCHTYPE, switchType, false);
109
110 writer.startElement(HtmlElements.INPUT, null);
111 writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
112 writer.writeAttribute(HtmlAttributes.VALUE, activeIndex);
113 writer.writeNameAttribute(hiddenId);
114 writer.writeIdAttribute(hiddenId);
115 writer.endElement(HtmlElements.INPUT);
116
117 encodeHeader(facesContext, writer, tabGroup, activeIndex);
118
119 int index = 0;
120 for (UIComponent tab : tabGroup.getChildren()) {
121 if (tab instanceof UITab) {
122 if (tab.isRendered() && (UITabGroup.SWITCH_TYPE_CLIENT.equals(switchType) || index == activeIndex)) {
123 encodeContent(writer, facesContext, (UITab) tab, index);
124 }
125 index++;
126 }
127 }
128
129 writer.endElement(HtmlElements.DIV);
130 }
131
132 private int ensureRenderedActiveIndex(FacesContext context, UITabGroup tabGroup) {
133 int activeIndex = tabGroup.getSelectedIndex();
134
135 int index = -1;
136 int closestRenderedTabIndex = -1;
137 for (UIComponent tab : (List<UIComponent>) tabGroup.getChildren()) {
138 index++;
139 if (tab instanceof AbstractUIPanelBase) {
140 if (index == activeIndex) {
141 if (tab.isRendered()) {
142 return index;
143 } else if (closestRenderedTabIndex > -1) {
144 break;
145 }
146 }
147 if (tab.isRendered()) {
148 closestRenderedTabIndex = index;
149 if (index > activeIndex) {
150 break;
151 }
152 }
153 }
154 }
155 if (closestRenderedTabIndex == -1) {
156
157 closestRenderedTabIndex = 0;
158 }
159 final ValueExpression expression = tabGroup.getValueExpression(Attributes.SELECTED_INDEX);
160 if (expression != null) {
161 expression.setValue(context.getELContext(), closestRenderedTabIndex);
162 } else {
163 tabGroup.setSelectedIndex(closestRenderedTabIndex);
164 }
165 return closestRenderedTabIndex;
166 }
167
168 private void encodeHeader(
169 FacesContext facesContext, TobagoResponseWriter writer, UITabGroup tabGroup, int activeIndex)
170 throws IOException {
171
172 Measure width = tabGroup.getCurrentWidth();
173 Measure headerHeight = getResourceManager().getThemeMeasure(facesContext, tabGroup, "headerHeight");
174 Measure toolBarWidth = getResourceManager().getThemeMeasure(facesContext, tabGroup, "toolBarWidth");
175 Style header = new Style();
176 header.setPosition(Position.RELATIVE);
177 header.setWidth(width.subtractNotNegative(toolBarWidth));
178 header.setHeight(headerHeight);
179 writer.startElement(HtmlElements.DIV, tabGroup);
180 writer.writeClassAttribute(Classes.create(tabGroup, "header"));
181 writer.writeStyleAttribute(header);
182
183 writer.startElement(HtmlElements.DIV, tabGroup);
184 writer.writeClassAttribute(Classes.create(tabGroup, "headerInner"));
185
186 int index = 0;
187 for (UIComponent child : tabGroup.getChildren()) {
188 if (child instanceof UITab) {
189 UITab tab = (UITab) child;
190 if (tab.isRendered()) {
191 LabelWithAccessKey label = new LabelWithAccessKey(tab);
192 if (activeIndex == index) {
193 ComponentUtils.addCurrentMarkup(tab, Markup.SELECTED);
194 }
195 FacesMessage.Severity maxSeverity = ComponentUtils.getMaximumSeverityOfChildrenMessages(facesContext, tab);
196 if (maxSeverity != null) {
197 ComponentUtils.addCurrentMarkup(tab, ComponentUtils.markupOfSeverity(maxSeverity));
198 }
199 writer.startElement(HtmlElements.DIV, tab);
200 writer.writeClassAttribute(Classes.create(tab));
201 writer.writeAttribute(HtmlAttributes.TABGROUPINDEX, index);
202
203 writer.startElement(HtmlElements.A, tab);
204 if (!tab.isDisabled()) {
205 writer.writeAttribute(HtmlAttributes.HREF, "#", false);
206 }
207 final String tabId = tab.getClientId(facesContext);
208 writer.writeIdAttribute(tabId);
209 if (label.getAccessKey() != null) {
210 writer.writeAttribute(HtmlAttributes.ACCESSKEY, Character.toString(label.getAccessKey()), false);
211 }
212 if (label.getText() != null) {
213 HtmlRendererUtils.writeLabelWithAccessKey(writer, label);
214 } else {
215 writer.writeText(Integer.toString(index + 1));
216 }
217 writer.endElement(HtmlElements.A);
218
219 if (label.getAccessKey() != null) {
220 if (LOG.isWarnEnabled()
221 && !AccessKeyMap.addAccessKey(facesContext, label.getAccessKey())) {
222 LOG.warn("duplicated accessKey : " + label.getAccessKey());
223 }
224 HtmlRendererUtils.addClickAcceleratorKey(facesContext, tabId, label.getAccessKey());
225 }
226 writer.endElement(HtmlElements.DIV);
227 }
228 }
229 index++;
230 }
231 writer.endElement(HtmlElements.DIV);
232 Style body = new Style();
233 body.setWidth(width);
234 body.setHeight(tabGroup.getCurrentHeight().subtract(headerHeight));
235 writer.endElement(HtmlElements.DIV);
236 if (tabGroup.isShowNavigationBar()) {
237 UIToolBar toolBar = createToolBar(facesContext, tabGroup);
238 renderToolBar(facesContext, writer, tabGroup, toolBar);
239 }
240 }
241
242 private UIToolBar createToolBar(FacesContext facesContext, UITabGroup tabGroup) {
243 final String clientId = tabGroup.getClientId(facesContext);
244 Application application = facesContext.getApplication();
245
246
247 UICommand previous = (UICommand) application.createComponent(UICommand.COMPONENT_TYPE);
248 previous.setRendererType(null);
249 previous.getAttributes().put(Attributes.IMAGE, "image/tabPrev.gif");
250 previous.setOnclick("/**/");
251
252 UICommand next = (UICommand) application.createComponent(UICommand.COMPONENT_TYPE);
253 next.setRendererType(null);
254 next.getAttributes().put(Attributes.IMAGE, "image/tabNext.gif");
255 next.setOnclick("/**/");
256
257
258 UICommand all = (UICommand) CreateComponentUtils.createComponent(
259 facesContext, UICommand.COMPONENT_TYPE, null);
260 UIMenu menu = (UIMenu) CreateComponentUtils.createComponent(
261 facesContext, UIMenu.COMPONENT_TYPE, RendererTypes.MENU);
262 FacetUtils.setDropDownMenu(all, menu);
263 int index = 0;
264 for (UIComponent child : tabGroup.getChildren()) {
265 if (child instanceof UITab) {
266 UITab tab = (UITab) child;
267 if (tab.isRendered()) {
268 UIMenuCommand entry = (UIMenuCommand) CreateComponentUtils.createComponent(
269 facesContext, UIMenuCommand.COMPONENT_TYPE, RendererTypes.MENU_COMMAND, "entry-" + index);
270 LabelWithAccessKey label = new LabelWithAccessKey(tab);
271 entry.setLabel(label.getText());
272 if (tab.isDisabled()) {
273 entry.setDisabled(true);
274 } else {
275 entry.setOnclick(JQueryUtils.selectId(clientId)
276 + ".find('.tobago-tab[tabgroupindex=" + index + "]')"
277 + ".click();"
278 + "if (event.stopPropagation === undefined) { "
279 + " event.cancelBubble = true; "
280 + "} else { "
281 + " event.stopPropagation(); "
282 + "}");
283 }
284 menu.getChildren().add(entry);
285 }
286 index++;
287 }
288 }
289 UIToolBar toolBar = (UIToolBar) application.createComponent(UIToolBar.COMPONENT_TYPE);
290 toolBar.setId(facesContext.getViewRoot().createUniqueId());
291 toolBar.setRendererType("TabGroupToolBar");
292 toolBar.setTransient(true);
293 toolBar.getChildren().add(previous);
294 toolBar.getChildren().add(next);
295 toolBar.getChildren().add(all);
296 tabGroup.getFacets().put(Facets.TOOL_BAR, toolBar);
297 return toolBar;
298 }
299
300 private void renderToolBar(
301 FacesContext facesContext, TobagoResponseWriter writer, UITabGroup tabGroup, UIToolBar toolBar)
302 throws IOException {
303 writer.startElement(HtmlElements.DIV, null);
304 writer.writeClassAttribute(Classes.create(tabGroup, "toolBar"));
305 RenderUtils.encode(facesContext, toolBar);
306 writer.endElement(HtmlElements.DIV);
307 }
308
309 protected void encodeContent(TobagoResponseWriter writer, FacesContext facesContext, UITab tab, int index)
310 throws IOException {
311
312 if (tab.isDisabled()) {
313 return;
314 }
315
316 writer.startElement(HtmlElements.DIV, null);
317 writer.writeClassAttribute(Classes.create(tab, "content"));
318 writer.writeIdAttribute(tab.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + "content");
319
320 final Style style = new Style(facesContext, tab);
321 final Measure borderLeft = tab.getBorderLeft();
322 final Measure borderRight = tab.getBorderRight();
323 final Measure borderTop = tab.getBorderTop();
324 final Measure borderBottom = tab.getBorderBottom();
325 style.setWidth(style.getWidth().subtract(borderLeft).subtract(borderRight));
326 style.setHeight(style.getHeight().subtract(borderTop).subtract(borderBottom));
327 writer.writeStyleAttribute(style);
328 writer.writeAttribute(HtmlAttributes.TABGROUPINDEX, index);
329
330 RenderUtils.encodeChildren(facesContext, tab);
331
332 writer.endElement(HtmlElements.DIV);
333 }
334 }