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}