Hi Norm, Below is text from a CustomHead I wrote. It does header/splash/icon images for appfuse css-framework. Integrated to trails features I developed of course.
It will give you some insights for the shell derivative. I am a trails developer. So anything trails you might want to tailor. Can't help you with the menu. Best regards Ken in nashua Customhead.java ------------------------------------- package org.trails.demo.components; import java.util.Date; import java.util.Iterator; import org.apache.hivemind.HiveMind; import org.apache.tapestry.IAsset; import org.apache.tapestry.IMarkupWriter; import org.apache.tapestry.IPage; import org.apache.tapestry.IRender; import org.apache.tapestry.IRequestCycle; import org.apache.tapestry.IScript; import org.apache.tapestry.Tapestry; import org.apache.tapestry.annotations.Asset; import org.apache.tapestry.annotations.ComponentClass; import org.apache.tapestry.annotations.InjectObject; import org.apache.tapestry.annotations.InjectScript; import org.apache.tapestry.annotations.InjectState; import org.apache.tapestry.annotations.Parameter; import org.apache.tapestry.coerce.ObjectArrayToIteratorConverter; import org.apache.tapestry.engine.ILink; import org.apache.tapestry.html.Shell; import org.apache.tapestry.json.IJSONWriter; import org.apache.tapestry.spec.IApplicationSpecification; import org.trails.component.blob.BlobDownloadService; import org.trails.component.blob.TrailsBlobAsset; import org.trails.demo.AdminLayout; /** * Background Usage... * * <html jwcid="@Shell" title="Trails" stylesheet="ognl:assets.stylesheet"> * * The <html> tag is basically the shell to a page. It encompasses <head><body> * * @Shell is a tapestry component that intercepts <html> parameters and renders * according to it's semantics. We can do whatever we like in * @Shell according to it's defined parameters. * * This component is an extension to * @Shell. We instrument additional features with the main purpose of rendering * a custom <head> * * In short...This guy reaches into the database and applies logo, header and * background to default css within the <html> shell. internally, tedius * renderings will occur to override specific css-framework fragments and * styles. * * @author kenneth.colassi [EMAIL PROTECTED] * */ @ComponentClass(allowBody = true, allowInformalParameters = true) public abstract class CustomHead extends Shell { @InjectState("adminLayout") public abstract AdminLayout getLayout(); public abstract void setLayout(AdminLayout layout); @InjectObject("service:trails.core.BlobService") public abstract BlobDownloadService getDownloadService(); private static final String generatorContent = "Tapestry Application Framework, version " + Tapestry.VERSION; @Asset("/js/dojo-0.4.3/dojo.js") public abstract IAsset getDefaultDojoSource(); @Asset("/dojo-0.4.3/") public abstract IAsset getDefaultDojoPath(); @Asset("/tapestry/core.js") public abstract IAsset getDefaultTapestrySource(); @Asset("/tapestry/") public abstract IAsset getDefaultTapestryPath(); @Asset("/styles/tapestryskin/theme.css") public abstract IAsset getTheme(); /** * Injected via jwc - readonly, yes that means no setter */ public abstract IApplicationSpecification getApplicationSpecification(); /** * Injected via jwc - readonly, yes that means no setter */ @InjectScript("CustomHead.script") public abstract IScript getScript(); @Parameter(required = true, cache = true) public abstract IAsset getStylesheet(); public abstract void setStylesheet(IAsset stylesheet); @Parameter(required = true, cache = true) public abstract String getTitle(); public abstract void setTitle(String title); public abstract IRender getDelegate(); public abstract IRender getAjaxDelegate(); public void renderComponent(IJSONWriter writer, IRequestCycle cycle) { } @Override protected void prepareForRender(IRequestCycle cycle) { getLayout(); super.prepareForRender(cycle); } @Override protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) { long startTime = 0; boolean rewinding = cycle.isRewinding(); if (!rewinding) { startTime = System.currentTimeMillis(); writeDocType(writer, cycle); IPage page = getPage(); writer.comment("Application: " + getApplicationSpecification().getName()); writer.comment("Page: " + page.getPageName()); writer.comment("Generated: " + new Date()); writer.begin("html"); renderInformalParameters(writer, cycle); writer.println(); writer.begin("head"); writer.println(); writer.beginEmpty("meta"); writer.attribute("name", "generator"); writer.attribute("content", generatorContent); writer.println(); if (getRenderContentType()) { /** * This should not be necessary (the HTTP content type should be * sufficient), but some browsers require it for some reason */ writer.beginEmpty("meta"); writer.attribute("http-equiv", "Content-Type"); writer.attribute("content", writer.getContentType()); writer.println(); } if (getLayout() != null) { writer.begin("title"); writer.print(getLayout().getTitle()); writer.end(); // title writer.println(); } IAsset stylesheet = getStylesheet(); if (stylesheet != null) { writeStylesheetLink(writer, cycle, stylesheet.buildURL()); } ObjectArrayToIteratorConverter conv = new ObjectArrayToIteratorConverter(); if (getStylesheets() != null) { Iterator i = (Iterator) conv.convertValue((Object[]) getStylesheets()); if (i != null) { while (i.hasNext()) { stylesheet = (IAsset) i.next(); writeStylesheetLink(writer, cycle, stylesheet.buildURL()); } } } writeRefresh(writer, cycle); if (getLayout() != null && getLayout().getBackground().getFileName() != null) { writeStyleOverride(writer, cycle); } writer.end(); writer.flush(); } // Render the body, the actual page content renderBody(writer, cycle); if (!rewinding) { writer.end(); // html writer.println(); long endTime = System.currentTimeMillis(); writer.comment("Render time: ~ " + (endTime - startTime) + "ms"); } } private void writeDocType(IMarkupWriter writer, IRequestCycle cycle) { String doctype = getDoctype(); if (HiveMind.isNonBlank(doctype)) { writer.printRaw("<!DOCTYPE " + doctype + ">"); writer.println(); } } private void writeRefresh(IMarkupWriter writer, IRequestCycle cycle) { int refresh = getRefresh(); if (refresh <= 0) { return; } // Here comes the tricky part ... have to assemble a complete URL // for the current page. String pageName = cycle.getPage().getPageName(); ILink link = getPageService().getLink(false, new String[] { pageName }); StringBuffer buffer = new StringBuffer(); buffer.append(refresh); buffer.append("; URL="); buffer.append(link.getAbsoluteURL()); // Write out the <meta> tag writer.println(); writer.println(); writer.beginEmpty("meta"); writer.attribute("http-equiv", "Refresh"); writer.attribute("content", buffer.toString()); writer.println(); writer.println(); } private void writeStylesheetLink(IMarkupWriter writer, IRequestCycle cycle, String stylesheetBase) { writer.beginEmpty("link"); writer.attribute("rel", "stylesheet"); writer.attribute("type", "text/css"); writer.attribute("title", "don't select"); String sessionStrippedURL = getTheme().buildURL(); if (sessionStrippedURL.contains(";")) sessionStrippedURL = sessionStrippedURL.substring(0, sessionStrippedURL.indexOf(";")); writer.attribute("href", sessionStrippedURL); writer.println(); writer.println(); } private void writeStyleOverride(IMarkupWriter writer, IRequestCycle cycle) { writer.begin("style"); writer.attribute("type", "text/css"); writer.println(); writer.comment("Checkout these Custom <HEAD> widget...Style Overrides"); doBodyOverride(writer, cycle); doMainOverride(writer, cycle); doSplashOverride(writer, cycle); doPageOverride(writer, cycle); writer.end(); // style writer.println(); writer.println(); } private void writeJavaScript(IMarkupWriter writer, IRequestCycle cycle, IAsset javascript) { writer.beginEmpty("script"); writer.attribute("type", "text/javascript"); writer.attribute("src", javascript.buildURL()); writer.closeTag(); writer.println(); writer.println(); } private void doBodyOverride(IMarkupWriter writer, IRequestCycle cycle) { writer.println(); writer.printRaw("\tbody {"); writer.println(); writer.printRaw("\t\tpadding: 5;"); writer.println(); writer.printRaw("\t\tmargin: 5;"); writer.println(); String blobRenderString = "/BlobService.svc?class=" + getLayout().getClass().getCanonicalName() + "&contentType=" + getLayout().getBackground().getContentType().toString() + "&fileName=" + getLayout().getBackground().getFileName().toString() + "&id=" + getLayout().getId().toString() + "&property=background"; writer.printRaw("\t\tbackground: #fff url( " + blobRenderString + " ) top left repeat;"); writer.println(); writer.printRaw("\t}"); writer.println(); } private void doSplashOverride(IMarkupWriter writer, IRequestCycle cycle) { writer.println(); writer.printRaw("\tdiv#splash {"); writer.println(); String blobRenderString = "/BlobService.svc?class=" + getLayout().getClass().getCanonicalName() + "&contentType=" + getLayout().getSplash().getContentType().toString() + "&fileName=" + getLayout().getSplash().getFileName().toString() + "&id=" + getLayout().getId().toString() + "&property=splash"; writer.printRaw("\t\tbackground: #fff url( " + blobRenderString + " ) top left no-repeat;"); writer.println(); writer.printRaw("\t}"); writer.println(); } private void doMainOverride(IMarkupWriter writer, IRequestCycle cycle) { writer.println(); writer.printRaw("\tdiv#main {"); writer.println(); writer.printRaw("\t}"); writer.println(); } private void doPageOverride(IMarkupWriter writer, IRequestCycle cycle) { writer.println(); writer.printRaw("\tdiv#page {"); writer.println(); writer.println(); String blobRenderString = "/BlobService.svc?class=" + getLayout().getClass().getCanonicalName() + "&contentType=" + getLayout().getBackground().getContentType().toString() + "&fileName=" + getLayout().getBackground().getFileName().toString() + "&id=" + getLayout().getId().toString() + "&property=background"; writer.printRaw("\t}"); writer.println(); } private void doHeaderOverride(IMarkupWriter writer, IRequestCycle cycle) { writer.println(); writer.printRaw("\tdiv#header {"); writer.println(); writer.printRaw("\t\theight: 94px;"); writer.println(); writer.printRaw("\t\twidth: 740px;"); writer.println(); String blobRenderString = "/BlobService.svc?class=" + getLayout().getClass().getCanonicalName() + "&contentType=" + getLayout().getHeader().getContentType().toString() + "&fileName=" + getLayout().getHeader().getFileName().toString() + "&id=" + getLayout().getId().toString() + "&property=header"; writer.printRaw("\t\tbackground: #fff url( " + blobRenderString + " ) top left no-repeat;"); writer.println(); writer.printRaw("\t}"); writer.println(); } private void doHeaderImgOverride(IMarkupWriter writer, IRequestCycle cycle) { writer.println(); writer.printRaw("\tdiv#header img background {"); writer.println(); writer.printRaw("\t\twidth: 740px;"); writer.println(); writer.printRaw("\t\theight: 94px;"); writer.println(); writer.printRaw("\t}"); writer.println(); } public IAsset getHeaderAsset() { String contentType = getLayout().getHeader().getContentType(); String fileName = getLayout().getHeader().getFileName(); return new TrailsBlobAsset(getDownloadService(), AdminLayout.class.getName(), getLayout().getId().toString(), "header", contentType, fileName); } public IAsset getLogoAsset() { String contentType = getLayout().getLogo().getContentType(); String fileName = getLayout().getLogo().getFileName(); return new TrailsBlobAsset(getDownloadService(), AdminLayout.class.getName(), getLayout().getId().toString(), "logo", contentType, fileName); } } CustomHead.jwc ------------------------------------- <!DOCTYPE component-specification PUBLIC "-//Apache Software Foundation//Tapestry Specification 4.1//EN" "http://jakarta.apache.org/tapestry/dtd/Tapestry_4_1.dtd"> <component-specification class="org.trails.demo.components.CustomHead" allow-body="yes" allow-informal-parameters="no"> <inject property="applicationSpecification" object="infrastructure:applicationSpecification"/> </component-specification> CustomHead.html ------------------------------------- <span jwcid="$content$"> <span jwcid="customhead"/> </span> Border.html (shows custom head usage) ------------------------------------- <span jwcid="@ScriptIncludes" /> <html jwcid="[EMAIL PROTECTED]:CustomHead" title="ognl:layout.title" stylesheet="ognl:assets.stylesheet" > <body jwcid="@Body"> .... snip... _________________________________________________________________ Windows Live Hotmail and Microsoft Office Outlook – together at last. Get it now. http://office.microsoft.com/en-us/outlook/HA102225181033.aspx?pid=CL100626971033