001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.client; 029 030import org.opencms.gwt.client.util.CmsDomUtil; 031import org.opencms.ui.shared.components.CmsBreadCrumbState; 032import org.opencms.util.CmsStringUtil; 033 034import com.google.gwt.core.client.JavaScriptObject; 035import com.google.gwt.core.client.Scheduler; 036import com.google.gwt.core.client.Scheduler.ScheduledCommand; 037import com.google.gwt.dom.client.Element; 038import com.google.gwt.dom.client.NodeList; 039import com.google.gwt.dom.client.Style; 040import com.google.gwt.event.logical.shared.ResizeEvent; 041import com.google.gwt.event.logical.shared.ResizeHandler; 042import com.google.gwt.event.shared.HandlerRegistration; 043import com.google.gwt.user.client.Window; 044import com.google.gwt.user.client.ui.HTML; 045import com.google.gwt.user.client.ui.RootPanel; 046import com.google.gwt.user.client.ui.Widget; 047import com.vaadin.client.communication.StateChangeEvent; 048import com.vaadin.client.ui.AbstractComponentConnector; 049import com.vaadin.shared.ui.Connect; 050import com.vaadin.shared.ui.Connect.LoadStyle; 051 052/** 053 * Bread crumb component connector.<p> 054 */ 055@Connect(value = org.opencms.ui.components.CmsBreadCrumb.class, loadStyle = LoadStyle.EAGER) 056public class CmsBreadCrumbConnector extends AbstractComponentConnector implements ResizeHandler { 057 058 /** The dynamic style element id. */ 059 private static final String DYNAMIC_STYLE_ID = "breadcrumbstyle"; 060 061 /** The style rule. */ 062 private static JavaScriptObject m_maxWidthRule; 063 064 /** The serial version id. */ 065 private static final long serialVersionUID = -8483069041782419156L; 066 067 /** The widget style name. */ 068 private static final String STYLE_NAME = "o-tools-breadcrumb"; 069 070 /** 071 * @see com.vaadin.client.ui.AbstractComponentConnector#getState() 072 */ 073 @Override 074 public CmsBreadCrumbState getState() { 075 076 return (CmsBreadCrumbState)super.getState(); 077 } 078 079 /** 080 * @see com.vaadin.client.ui.AbstractComponentConnector#getWidget() 081 */ 082 @Override 083 public HTML getWidget() { 084 085 return (HTML)super.getWidget(); 086 } 087 088 /** 089 * @see com.google.gwt.event.logical.shared.ResizeHandler#onResize(com.google.gwt.event.logical.shared.ResizeEvent) 090 */ 091 public void onResize(ResizeEvent event) { 092 093 updateMaxWidth(); 094 } 095 096 /** 097 * @see com.vaadin.client.ui.AbstractComponentConnector#onStateChanged(com.vaadin.client.communication.StateChangeEvent) 098 */ 099 @Override 100 public void onStateChanged(StateChangeEvent stateChangeEvent) { 101 102 super.onStateChanged(stateChangeEvent); 103 104 getWidget().setHTML(getBreadCrumbHtml(getState().getEntries())); 105 if (RootPanel.getBodyElement().isOrHasChild(getWidget().getElement())) { 106 // only if attached 107 updateMaxWidth(); 108 } 109 } 110 111 /** 112 * @see com.vaadin.client.ui.AbstractComponentConnector#createWidget() 113 */ 114 @Override 115 protected Widget createWidget() { 116 117 HTML widget = new HTML() { 118 119 private HandlerRegistration m_handlerReg; 120 121 @Override 122 protected void onAttach() { 123 124 super.onAttach(); 125 m_handlerReg = Window.addResizeHandler(CmsBreadCrumbConnector.this); 126 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 127 128 public void execute() { 129 130 updateMaxWidth(); 131 } 132 }); 133 } 134 135 @Override 136 protected void onDetach() { 137 138 super.onDetach(); 139 m_handlerReg.removeHandler(); 140 } 141 }; 142 widget.setStyleName(STYLE_NAME); 143 return widget; 144 } 145 146 /** 147 * Generates the bread crumb HTML for the given entries.<p> 148 * 149 * @param breadCrumbEntries the bread crub entries 150 * 151 * @return the generated HTML 152 */ 153 protected String getBreadCrumbHtml(String[][] breadCrumbEntries) { 154 155 StringBuffer buffer = new StringBuffer(); 156 buffer.append("<div>"); 157 if ((breadCrumbEntries != null)) { 158 for (String[] entry : breadCrumbEntries) { 159 appendBreadCrumbEntry(buffer, entry[0], entry[1]); 160 } 161 } 162 buffer.append("</div>"); 163 return buffer.toString(); 164 } 165 166 /** 167 * Updates the entry max-width according to the available space.<p> 168 */ 169 void updateMaxWidth() { 170 171 Element base = getWidget().getElement(); 172 int availableWidth = base.getOffsetWidth(); 173 int requiredWidth = 0; 174 NodeList<Element> children = CmsDomUtil.querySelectorAll("div > a, div > span", base); 175 for (int i = 0; i < children.getLength(); i++) { 176 Element child = children.getItem(i); 177 Style style = child.getFirstChildElement().getStyle(); 178 style.setProperty("maxWidth", "none"); 179 requiredWidth += child.getOffsetWidth(); 180 style.clearProperty("maxWidth"); 181 } 182 if (requiredWidth > availableWidth) { 183 int padding = 30 + ((children.getLength() - 1) * 35); 184 int maxWidth = (availableWidth - padding) / children.getLength(); 185 setMaxWidth(maxWidth + "px"); 186 } else { 187 setMaxWidth("none"); 188 } 189 } 190 191 /** 192 * Appends a bread crumb entry.<p> 193 * 194 * @param buffer the string buffer to append to 195 * @param target the target state 196 * @param label the entry label 197 */ 198 private void appendBreadCrumbEntry(StringBuffer buffer, String target, String label) { 199 200 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(target)) { 201 buffer.append("<a href=\"#!").append(target).append( 202 "\" title=\"" + CmsDomUtil.escapeXml(label) + "\"><span>").append(label).append("</span></a>"); 203 } else { 204 buffer.append( 205 "<span class=\"o-tools-breadcrumb-active\" title=\"" 206 + CmsDomUtil.escapeXml(label) 207 + "\"><span>").append(label).append("</span></span>"); 208 } 209 } 210 211 /** 212 * Sets the max-width style property.<p> 213 * 214 * @param maxWidth the max-width value 215 */ 216 private native void setMaxWidth(String maxWidth)/*-{ 217 if (@org.opencms.ui.client.CmsBreadCrumbConnector::m_maxWidthRule == null) { 218 var style = $wnd.document 219 .getElementById(@org.opencms.ui.client.CmsBreadCrumbConnector::DYNAMIC_STYLE_ID); 220 if (style == null) { 221 style = $wnd.document.createElement("style"); 222 style 223 .setAttribute("id", 224 @org.opencms.ui.client.CmsBreadCrumbConnector::DYNAMIC_STYLE_ID) 225 style.appendChild(window.document.createTextNode("")); 226 $wnd.document.head.appendChild(style); 227 } 228 style.sheet 229 .insertRule( 230 ".opencms .o-tools-breadcrumb > div > a span, .opencms .o-tools-breadcrumb > div > span span {}", 231 0); 232 var rules = style.sheet.cssRules ? style.sheet.cssRules 233 : style.sheet.rules; 234 @org.opencms.ui.client.CmsBreadCrumbConnector::m_maxWidthRule = rules[0]; 235 } 236 @org.opencms.ui.client.CmsBreadCrumbConnector::m_maxWidthRule.style.maxWidth = maxWidth; 237 }-*/; 238}