On Tue, Dec 9, 2008 at 1:03 AM, Jeanne Waldman <[EMAIL PROTECTED]> wrote: > What about Step A - requirements? We should list out the requirements we > have so we can make sure we aren't missing anything. There's been a lot of > work in getting the skinning to work with portals, so we'll need that to > still work and we will probably want to redesign it since it's kind of lame > the way it works right now.
+1 good point, Jeanne, we don't want to see that work destroyed. -M > > From my experience coming up with a good API takes a lot of time. I'll try > to find some time to read your proposal at least. > > Jeanne > > Simon Lessard wrote, On 12/8/2008 6:16 AM PT: >> >> I would prefer a roadmap like: >> >> 1. Design a modular API that fits Trinidad's needs with minimal >> compatibility loss. >> 2. In parallel >> 1. Start implementing the API defined in 1. as a myfaces >> extension, possibily along with a ResourceHandler for JSF 1.2 >> 2. Propose the API to JSF 2.0 EG >> 3. If the API make it the the spec then >> 1. Convert the extension to fit the new API (even if target >> is 1.2 it should be possible but in a different package) >> 2. Integrate the API with the corect package naming to the >> 2.0 branch (obviously) >> 4. In the API doesn't get in the spec, then create a 2.0 branch of >> the skin extension >> 5. Convert Trinidad to the new extension >> 6. Integrate the extension in Tomahawk and Tobago >> >> >> So, for now, I would really really like to get comments from the community >> on the API I proposed as well as potential solutions for the pendings. >> Otherwise I'll have to propose it in its current form and that would prevent >> the API to be reviewed by the MyFaces community and that would be a shame >> imho. As I mentioned before, time is running really short and the fact that >> 2.0 is in public review phase alone might prevent the addition of such core >> API in the spec so the faster we can design a great API, the more chances >> there is to see it standardized. >> >> >> Regards, >> >> ~ Simon >> >> On Mon, Dec 8, 2008 at 4:22 AM, Gerhard Petracek >> <[EMAIL PROTECTED] <mailto:[EMAIL PROTECTED]>> wrote: >> >> hello paul, >> >> ok - let's continue with the case you mentioned. >> >> we could: >> - use the same approach of the myfaces shared module also for the >> shared skinning code >> - provide skinning as an independent jar file (it's easier for >> other projects/custom components to re-use it) >> >> regards, >> gerhard >> >> >> >> 2008/12/8 Paul Rivera <[EMAIL PROTECTED] >> <mailto:[EMAIL PROTECTED]>> >> >> Hi, >> >> It's good to hear that there's effort done to make skinning >> standardized and part of JSF by Simon. Is there anything that >> the current skinning project we have at >> http://code.google.com/p/myfaces-csi/ can contribute to this? >> >> In the event that Simon's ideas don't get integrated into JSF >> 2.0, I'd like to bring back the discussion to the skinning >> project that we now have in the link mentioned above. >> >> Overall, I think the community agrees with the idea of having >> an independent skinning project that is basically derived from >> trinidad code. I'd like to hear your opinion on the project >> layout described in >> http://code.google.com/p/myfaces-csi/wiki/MyfacesSkinsProposal. >> >> What concerns me most here is that the skins project and >> trinidad share a lot of classes that it is hard to get a clean >> separation. You can check shared-impl for those classes. >> >> Our current layout has a shared module (shared-api, >> shared-impl). This module contains code shared by both >> skinning and trinidad-core. We can probably move some of the >> utility classes into myfaces-commons-utils. But other classes >> there might not be such a good fit into myfaces-commons-utils. >> >> We started this project with the most immediate goal of giving >> tomahawk skinning support and eventually have trinidad use >> this project. We've taken trinidad skinning code and now it >> works with tomahawk and trinidad-core. In the event that this >> project gets accepted into myfaces, integration with tomahawk >> is not a big problem. I am actually more worried with the >> changes we have to make in trinidad-core. This is why I'd >> like to propose as a roadmap: >> - finish the skinning project and make the necessary changes >> in trinidad-core without adding new features yet to the >> current set that trinidad has. There will be big changes in >> trinidad-core that need to be done at the same time. >> - once we verify that everything is working, we can then add >> the desired new features/changes to the skinning api. In this >> phase, we can implement the new features/changes to the api in >> small and more manageable increments. >> >> While Simon is in the process of submitting his proposal, I'd >> like to work on these issues and anything else you think might >> be a problem for the skinning project or the transition with >> trinidad. >> >> Best Regards, >> Paul Rivera >> >> >> --- On *Sat, 12/6/08, Gerhard Petracek >> /<[EMAIL PROTECTED] >> <mailto:[EMAIL PROTECTED]>>/* wrote: >> >> From: Gerhard Petracek <[EMAIL PROTECTED] >> <mailto:[EMAIL PROTECTED]>> >> Subject: Re: [Skinning] Independent skinning myfaces >> subproject >> To: "MyFaces Development" <[email protected] >> <mailto:[email protected]>> >> Date: Saturday, December 6, 2008, 12:42 PM >> >> >> hello simon, >> >> no it's just because of ie version < 7 like it is >> mentioned in the trinidad skinning documentation. >> >> regards, >> gerhard >> >> >> >> 2008/12/6 Simon Lessard <[EMAIL PROTECTED] >> <mailto:[EMAIL PROTECTED]>> >> >> Hi Gerhard, >> >> Trinidad already has this and it would be the same >> with what I propose. Assume: <tr:tree >> styleClass="MyClass"/> >> >> Then switch the selectors to: tr|tree.MyClass >> >> As far as I can tell this is enough to cover every >> possibilities without burdening the framework with an >> extra attribute. Unless you can come up with a counter >> example not involving IE6's shitty CSS support. >> >> >> ~ Simon >> >> >> On Sat, Dec 6, 2008 at 2:19 PM, Gerhard Petracek >> <[EMAIL PROTECTED] >> <mailto:[EMAIL PROTECTED]>> wrote: >> >> hello simon, >> >> in my opinion andrew brought up an interesting >> question. have you also thought about multiple >> selectors for the same component? >> >> i repeat the example i mentioned before: >> >> >> <tr:tree/> ... uses default selector >> <tr:tree selectorId="myId"/> ... uses selector >> with "myId" to use a different selector >> >> regards, >> gerhard >> >> >> >> 2008/12/6 Simon Lessard <[EMAIL PROTECTED] >> <mailto:[EMAIL PROTECTED]>> >> >> Hi all, >> >> Since I completed the proposition by memory >> yesterday I forgot some important parts. Also, >> I thought more about some possibilities I >> rejected during the night and reconsidered my >> those. So here is v2. Among other thing I >> integrated skin extends, removed selector >> redirection, removed some other PENDING and >> simplified 3.1 suggestion since I discarded >> redirection: >> >> *Modified classes* >> >> public abstract class Application >> { >> public String getDefaultSkinFamily(); >> public void setDefaultSkinFamily(String >> skinFamily); >> } >> >> public final class FactoryFinder >> { >> public static final String SKIN_FACTORY; >> } >> >> public class UIViewRoot >> { >> public String getSkinFamily(); >> public void setSkinFamily(String skinFamily); >> } >> >> >> *Modified methods* >> >> public abstract class ViewHandler >> { >> /** >> * Same as before but also copy skin family. >> */ >> public abstract UIViewRoot >> createView(FacesContext context, String viewId); >> } >> >> >> *New classes* >> >> public abstract class Icon extends Resource >> { >> /** >> * Gets the key within the skin's >> ResourceBundle to the description of this >> icon. The description can be used >> * as an alternate text for the icon to >> improve accessibility for example. >> */ >> public abstract String getDescriptionKey(); >> /** >> * Gets the name of this icon. >> */ >> public abstract String getName(); >> >> /** >> * Gets the effective style class of this >> icon within the skin. >> */ >> public abstract String getStyleClass(); >> } >> >> public abstract class Skin >> { >> public abstract Resource >> getContentResource(FacesContext context); >> >> public abstract Icon getIcon(FacesContext >> context, String iconName); >> >> public abstract Object >> getProperty(FacesContext context, String >> propertyName); >> >> /** >> * Get the ResourceBundle for this skin in >> the current locale. >> */ >> public abstract ResourceBundle >> getResourceBundle(FacesContext context); >> >> public abstract SkinNode >> getSkinNode(FacesContext context, SkinSelector >> selector); >> >> public String getStyleClass(FacesContext >> context, SkinSelector selector) >> { >> SkinNode node = getSkinNode(context, >> selector); >> return node == null ? null : >> node.getStyleClass(); >> } >> } >> >> public class SkinException extends FacesException >> { >> // Provides all 4 common Exception >> constructors >> } >> >> public abstract class SkinFactory implements >> FacesWrapper<SkinFactory> >> { >> public abstract void addSkin(String >> skinFamily, String renderKitId, Skin skin); >> public abstract Skin getSkin(String >> skinFamily, String renderKitId); >> public abstract Collection<String> >> getSkinFamilies(); >> public SkinFactory getWrapped() >> { >> return null; >> } >> } >> >> public class SkinInitializationException >> extends SkinException >> { >> // Provides all 4 common Exception >> constructors >> } >> >> /** >> * Implementations of this interface are in >> charge of creating Skin instances in an >> implementation specific manner. >> */ >> public interface SkinLoader >> { >> /** >> * Creates a new <code>Skin</code> >> instance the specified skinFamily and >> parameters. There's no garanteed as of what >> * parameters will be present in the Map >> argument and the creation should fails if >> those parameters are not correct >> * for the specific implementation. >> * >> * @throws SkinInitializationException if >> the provided parameters are not sufficient to >> create the new <code>Skin</code> >> * instance >> */ >> public Skin loadSkin(Skin parentSkin, >> String resourceBundle, Map<String, String> >> parameters); >> } >> >> public abstract class SkinNode >> { >> public abstract String getStyleClass(); >> >> public abstract Map<String, Object> >> getProperties(); >> >> /** >> * Gets a server side property of this >> node. Server side properties are not visible >> by the client. >> */ >> public Object getProperty(String propertyName) >> { >> return getProperties().get(propertyName); >> } >> >> /** >> * Gets a read-only version of the >> client-side properties of this node. In HTML, >> those represent CSS properties. >> * >> * It's valid to place those properties in >> a SoftRenderece for memory footprint safety. >> However, this >> * method must be able to reload them if >> needed >> * those properties should be placed in >> memory-aware renference >> */ >> public abstract Map<String, Object> >> getStyleProperties(); >> >> public Object getStyleProperty(String >> propertyName) >> { >> return >> getStyleProperties().get(propertyName); >> } >> } >> >> public class SkinSelector implements >> Comparable<SkinSelector> >> { >> /** >> * Finds the SkinSelector with the >> specified name or return <code>null</code> if >> it doesn't exists. This >> * method should be called by Renderers >> during their initialization in order to cache >> the SkinSelector instances >> * they'll be using to increase overall >> performance of the Skin module. >> */ >> public static SkinSelector >> getInstance(String selectorName); >> >> /** >> * Finds the SkinSelector with the >> specified name or create a new one if it >> doesn't exists and <code>create</code> >> * argument is <code>true</code>. This >> method should be used mainly by SkinLoader in >> order to register a new >> * selector. >> */ >> public static SkinSelector >> getInstance(String selectorName, boolean create); >> >> private static int nextOrdinal = 0; >> >> private int ordinal; >> private String name; >> private >> SkinSelector(String name) >> { >> assert name != null; >> synchronized >> (SkinSelector.class) >> { >> ordinal = nextOrdinal++; >> } >> >> this.name <http://this.name> = name; >> } >> >> public boolean equals(Object obj) >> { >> // Sufficient because ordinal is unique >> return o == this; >> } >> public int hashCode() >> { >> return ordinal(); >> } >> >> public String name() >> { >> return name; >> } >> >> public int ordinal() >> { >> return ordinal; >> } >> } >> >> *New config >> * >> <application> >> <default-skin-family/> >> </application> >> <skin> >> <skin-family/> >> <render-kit-id/> >> <description/> >> <extends> >> <skin-family/> >> <render-kit-id/> >> </extends> >> <skin-loader-class/> >> <resource-bundle/> >> <skin-loader-parameters> >> <map-entries/> >> </skin-loader-parameters> >> </skin> >> * >> * >> PENDING: Should it instead be a >> getTranslatedResource instead of >> getResourceBundle within the Skin class? >> >> PENDING: Should the skin family and render kit >> id passed to the loader or is that irrelevant? >> >> PENDING: With the proposed API, Trinidad >> aliases wouldn't be able to be used across >> skin additions as it would be aggregated in a >> CompositeSkin instance and thus in multiple >> different documents, anyone has a better idea >> than CompositeSkin for this? Maybe stardadize >> aliases somehow and have CompositeSkin >> aggregate the content in a single document >> instead of delegating to its fragments? >> >> PENDING: Should Skin.getIcon uses an optimized >> key as well? Doesn't seem required since there >> are much less icons in a page than HTML >> elements, thus the gain wouldn't worth the >> trouble imho. >> >> PENDING: The Resource API might have to be >> revisited in order to recognize the Resource >> returned by Skin.getContentResource so that >> ResourceHandler can identify Resource requests >> for the skin content and thus locate the given >> skin, get its Resource insteance and return >> the content with the standard algorithm. I >> believe a reserved library name could be used >> for that, the resource name is an harder issue >> though since the skin identifier is a tuple >> atm and defining a reserved separator >> character would bring limitation to the render >> kit ids and skin family values. Someone could >> come up with a better suggestion for that maybe? >> >> PENDING: Skin.getStyleProperties could maybe >> offer the option to return a mutable Map as an >> optional feature, but then would requires the >> Skin to >> regenerate the style document (CSS). However >> this can get hellish with composite skin. The >> node would need to have access to its >> container Skin and there should be a >> SkinModificationEvent/Listener capability >> added to the Skin class. >> >> >> Regards, >> >> ~ Simon >> >> >> On Fri, Dec 5, 2008 at 1:21 PM, Simon Lessard >> <[EMAIL PROTECTED] >> <mailto:[EMAIL PROTECTED]>> wrote: >> >> I agree with most of these statements, but >> I would start even higher than that, like >> defining the base interfaces and access >> classes. >> >> For instance, I think it's safe to assume >> that a Skin class or interface should >> exists. Let assume all interfaces for now >> as it takes less characters. Now, from >> where and how should the Skin be available >> for a given FacesContext. Personally I >> believe a Skin should be linked to a >> RenderKit (or a common base RenderKit >> class for all MyFaces extensions if not in >> the standard), but let assume work on the >> API for now for the sake of the discussion. >> >> So for now we have: >> >> public interface Skin >> { >> public String getResourceBundle(); >> } >> >> public abstract class RenderKit >> { >> public abstract Skin >> getSkin(FacesContext context); >> } >> >> So far so good, but now that opens some >> issues that will themselves explode into more: >> >> 1. Should the way the RenderKit locates >> the current Skin standardized? >> 2. How do we define the contract >> ensuring that the Skin get rendered >> on the page? >> 3. What other functionalities should be >> standarized in the Skin interface? >> >> *Issue 1: Current skin localization* >> Personally I chose yes to that question >> since it allow a standardized way to >> define skins. Let use Trinidad's >> skin-family notion here, but with the way >> renderKitId is defined, that's the view >> root and a factory implementing decorator >> pattern. So we add >> >> public class UIViewRoot >> { >> public String getSkinFamily(); >> public void setSkinFamily(String >> skinFamily); >> } >> >> public abstract class SkinFactory >> implements FacesWrapper<SkinFactory> >> { >> public abstract void addSkin(String >> skinFamily, String renderKitId, Skin skin); >> public abstract Skin getSkin(String >> skinFamily, String renderKitId); >> public abstract Collection<String> >> getSkinFamilies(); >> public SkinFactory getWrapped() >> { >> return null; >> } >> } >> >> *Sub-issue 1.1: Default SkinFactory >> implementation behavior* >> Additional Exceptions >> public class SkinException extends >> FacesException >> { >> // Provides all 4 common Exception >> constructors >> } >> >> public class SkinInitializationException >> extends SkinException >> { >> // Provides all 4 common Exception >> constructors >> } >> >> Additional interface >> /** >> * Implementations of this interface are >> in charge of creating Skin instances in an >> implementation specific manner. >> */ >> public interface SkinLoader >> { >> /** >> * Creates a new <code>Skin</code> >> instance the specified skinFamily and >> parameters. There's no garanteed as of what >> * parameters will be present in the >> Map argument and the creation should fails >> if those parameters are not correct >> * for the specific implementation. >> * >> * @throws SkinInitializationException >> if the provided parameter are not enough >> to create the <code>Skin</code> instance >> */ >> public Skin loadSkin(String >> skinFamily, String resourceBundle, >> Map<String, String> parameters); >> } >> >> Addition to faces-config.xml: >> <application> >> <default-skin-family/> >> </application> >> <skin> >> <skin-family/> >> <render-kit-id/> >> <description/> >> <skin-loader-class/> >> <resource-bundle/> >> <skin-loader-parameters> >> <map-entries/> >> </skin-loader-parameters> >> </skin> >> >> Note that this solution makes >> RenderKit.getSkin more an utility method >> than anything, except if an extension >> decides to override it (would probably be >> the casew ith Trinidad, at least to start >> to use trinidad-config.xml, or later to >> add .pda or .desktop to the current >> skinFamily). >> >> Also, the framework should probably >> provide some implementations of SkinLoader >> out-of-the-box, most likely >> ClassSkinLoader expecting a skinClass >> parameter and a CSSSkinLoader receiving a >> cssResourcePath parameter. (Trinidad would >> mostl ikely provide an XSSSkinLoader for >> example) >> >> *Issue 2: Skin content rendering* >> How should rendering be handled? Should it >> be handled as an added ResourceDependency >> just before rendering or as an additional >> contract for the default HtmlRenderer for >> HtmlHead? The latter sounds better, but >> how should it be implemented? target would >> obviously be "head", but then a public >> Resource Skin.getContentResource() method >> could be required and this might imply >> some changes to the ResourceHandler >> specification so that it can locate the >> Skin instance in order to be able to serve >> the Resource's content (effective CSS >> content) to the agent. I like the Resource >> version, but I have more thinking to do >> about how to implement this, maybe >> involving a reserved resourceIdentifier >> structure/syntax >> >> *Issue 3: Skin features* >> Additional class >> public abstract class Icon extends Resource >> { >> /** >> * Gets the key within the skin's >> ResourceBundle to the description of this >> icon. The description can be used >> * as an alternate text for the icon >> to improve accessibility for example. >> */ >> public abstract String >> getDescriptionKey(); >> /** >> * Gets the name of this icon. >> */ >> public abstract String getName(); >> >> /** >> * Gets the effective style class of >> this icon within the skin. >> */ >> public abstract String getStyleClass(); >> } >> >> public interface Skin >> { >> public Icon getIcon(String iconName); >> >> public String getProperty(String >> propertyKey); >> >> public String getResourceBundle(); >> >> public String getStyleClass(String >> selectorKey); >> } >> >> *Sub-issue 3.1: style class / property / >> icon access strategy* >> Trinidad uses String keys to access style >> classes within the skin. That strategy is >> very flexible, but alas very slow, thanks >> to String.hashCode() being linear and >> uncached. So, even if String.intern() is >> called on all selectors within a given >> skin, the gain will only be on >> String.equals, making it O(1) instead of >> O(n). Even if this is better than nothing, >> especially it there's a collision, the >> overall complexity of looking up a >> selector remains O(n) with the length of >> the key. So, the overall cost of skinning >> with this strategy is averageLengthOfKey * >> averageAmountOfComponentPerPage * >> averageAmountOfHtmlElementPerComponent >> (assuming each element gets its own style >> class which is required for maximum >> customizability). Another, but a little >> bit more complex, solution is also >> possible using a "dynamic enum" alike to >> PropertyKey in Trinidad. So I'd like >> something like: >> >> public abstract class SkinKeyPart >> { >> public String name(); >> >> public int ordinal(); >> } >> >> public class Namespace extends SkinKeyPart >> { >> /** >> * Find or create the Namespace >> instance with the specified name. >> */ >> public static Namespace >> getInstance(String name); >> } >> >> public class ComponentKey extends SkinKeyPart >> { >> /** >> * Find or create the ComponentKey >> instance with the specified name. >> */ >> public static ComponentKey >> getInstance(String name); >> } >> >> public class ComponentPartKey extends >> SkinKeyPart >> { >> /** >> * Find or create the ComponentPartKey >> instance with the specified name. >> */ >> public static ComponentPartKey >> getInstance(String name); >> } >> >> public class ComponentStateKey extends >> SkinKeyPart >> { >> /** >> * Find or create the >> ComponentStateKey instance with the >> specified name. >> */ >> public static ComponentPartKey >> getInstance(String name); >> } >> >> public abstract class SkinNode >> { >> public abstract String[] >> getStyleClasses(); >> public abstract String >> getProperty(String propertyName); >> public abstract Icon getIcon(String >> iconName); >> public abstract SkinNode >> getSubPart(ComponentPartKey partKey); >> } >> >> public interface Skin >> { >> /** >> * Still exists to get properties >> global to the whole skin if someone ever >> need that feature >> */ >> public String getProperty(String >> propertyName); >> >> /** >> * No change here >> */ >> public String getResourceBundle(); >> >> public String getSkinNode(Namespace >> ns, ComponentKey component); >> >> public String getSkinNode(Namespace >> ns, ComponentKey component, >> ComponentStateKey state); >> >> public String getSkinNode(Namespace >> ns, ComponentKey component, >> ComponentStateKey... states); >> } >> >> That solution has the advantage of being >> very fast, actually a real O(1) for every >> node access assuming the Renderer gathers >> the keys it needs during instanciation. >> Then the "root" SkinNode could probably be >> located using a base Renderer class and >> passed to a custom encoding method >> (Trinidad's encodeAll for example). Of >> course, the skin loaders would have to >> create skin instances structuring the >> selectors accordingly. A base class for >> Skin with such feature should probably be >> provided if this solution is wanted in >> order to make new implementation of Skin >> easier to create. >> >> *PENDIND issues:* >> PENDING: Should the path leverage the >> Resource API instead and receive a >> library-name, library-version, >> resource-name and resource-version instead? >> >> PENDING: A CompositeSkin class should >> probably be provided and most likely >> usable from the configuration, should it >> be using a specific loader with parameters >> or have a fully-fledged tag like >> <composite-skin> for example? The use of >> such skin implementation would be for >> users using multiple libraries, each with >> very different RenderKit implementations. >> A composite RenderKit class could also be >> useful here. >> >> PENDING: Should Trinidad's SkinAddition >> notion be considered? SkinAddition >> provides a way to add skin elements to an >> existing skin based on its id. It would be >> possible to leverage that by using the >> skin-family/render-kit-id tuple instead. >> Another option would be to simply have the >> default SkinFactory implementation to >> automatically agreggate the Skin instances >> with the same skin-family and >> render-kit-id into a CompositeSkin instance. >> >> PENDING: Should additional metadata be >> added to <renderer> tag to define the >> supported skin selector for tooling >> purpose? This point is dependant on the >> content of the Skin interface and the >> strategy chosen to access the >> properties/style class of a given >> component and/or one of its sub-element. >> >> PENDING: Should the effective CSS >> properties stay accessible in memory or >> otherwise from the Skin instance? If so, >> should they be modifiable? I tend toward >> no for both for performance issues. Of >> course this could be also left as an >> optional operation of the interface, >> throwing UnsupportedOperationException in >> most case but at least leaving the >> possibility for special implementations >> requiring it. This point also impact issue >> 2 in some ways. >> >> PENDING: About Icon, it should probably >> also leverage the Resource API, how should >> that be implemented? >> >> PENDING: Icon's styleClass, should the >> instance directly return an effective >> class or should it rather return a >> selector that would then be looked up from >> the Skin? I prefer the latter, but then >> there's the Stirng issue so maybe the icon >> would have to return an optimized selector >> key as mentioned in 3.1 >> >> PENDING: Solution to issue 3.1, although >> very fast and flexible, can be an linkage >> nightmare. For example, a node for >> component "inputText" with states >> "disabled" and "required" should be able >> to link to the component part node for >> part "label" of "inputText" without any >> state defined if needed. The performance >> gain of 3.1 is great, the implementation >> complexity isn't. Still, personally I >> don't have any problem throwing complexity >> at implementor's head if the users can >> feel the difference. Another option would >> be to remove the "state" complexity part >> and have a predefined style class syntax >> for those. That solution is what Trinidad >> does. However, this solution is not as >> flexible as it doesn't allow to switch >> Icon dynamically depending on the >> component's state. >> >> PENDING: Solution 3.1 don't allow state to >> be applied on the component's sub parts. >> Most of the time it's not an issue, but we >> have one such use case with Trinidad's >> train component where the train itself >> doesn't have a state, but each of its >> station (sub-part) can be >> visited/unvisited and/or selected and/or >> disabled. If 3.1 is kept, should it be >> tweaked even more to allow a sub-part >> state on a per part basis as well? This >> makes the linkage a nightma > > ... > > [Message clipped] -- Matthias Wessendorf blog: http://matthiaswessendorf.wordpress.com/ sessions: http://www.slideshare.net/mwessendorf twitter: http://twitter.com/mwessendorf
