ISIS-537: moved impl of LinksSelectorPanelAbstract (.java) to subclasses.

Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/b1677ca8
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/b1677ca8
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/b1677ca8

Branch: refs/heads/master
Commit: b1677ca880b7a27a831ecf84e387952aca12a241
Parents: 3b80b5e
Author: Dan Haywood <[email protected]>
Authored: Sun Nov 9 11:57:48 2014 +0000
Committer: Dan Haywood <[email protected]>
Committed: Mon Nov 10 10:21:35 2014 +0000

----------------------------------------------------------------------
 .../CollectionContentsLinksSelectorPanel.java   | 408 ++++++++++++++++---
 .../links/EntityLinksSelectorPanel.java         | 352 +++++++++++++++-
 .../links/LinksSelectorPanelAbstract.java       |  40 +-
 3 files changed, 724 insertions(+), 76 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/b1677ca8/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
----------------------------------------------------------------------
diff --git 
a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
 
b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
index 81f7ece..096903c 100644
--- 
a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
+++ 
b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/collectioncontents/selector/links/CollectionContentsLinksSelectorPanel.java
@@ -19,26 +19,52 @@
 
 package 
org.apache.isis.viewer.wicket.ui.components.collectioncontents.selector.links;
 
-import java.util.List;
+import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
 
+import java.util.ArrayList;
+import java.util.List;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
 import com.google.common.collect.Lists;
-
+import org.apache.wicket.AttributeModifier;
 import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
 import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.AbstractLink;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.model.IModel;
-
+import org.apache.wicket.model.Model;
 import org.apache.isis.applib.annotation.Render.Type;
+import org.apache.isis.core.commons.lang.StringExtensions;
 import org.apache.isis.core.metamodel.facets.members.render.RenderFacet;
 import org.apache.isis.core.metamodel.spec.feature.OneToManyAssociation;
+import org.apache.isis.viewer.wicket.model.hints.IsisUiHintEvent;
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
+import org.apache.isis.viewer.wicket.model.hints.UiHintPathSignificant;
+import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
+import org.apache.isis.viewer.wicket.model.links.LinksProvider;
 import org.apache.isis.viewer.wicket.model.models.EntityCollectionModel;
+import org.apache.isis.viewer.wicket.ui.CollectionContentsAsFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
+import 
org.apache.isis.viewer.wicket.ui.components.additionallinks.AdditionalLinksPanel;
 import 
org.apache.isis.viewer.wicket.ui.components.collection.CollectionCountProvider;
 import org.apache.isis.viewer.wicket.ui.components.collection.CollectionPanel;
 import 
org.apache.isis.viewer.wicket.ui.components.collectioncontents.ajaxtable.CollectionContentsAsAjaxTablePanelFactory;
 import 
org.apache.isis.viewer.wicket.ui.components.collectioncontents.unresolved.CollectionContentsAsUnresolvedPanelFactory;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+import org.apache.isis.viewer.wicket.ui.panels.PanelUtil;
 import 
org.apache.isis.viewer.wicket.ui.selector.links.LinksSelectorPanelAbstract;
+import org.apache.isis.viewer.wicket.ui.util.Components;
+import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
+import org.apache.isis.viewer.wicket.ui.util.CssClassRemover;
 
 /**
  * Provides a list of links for selecting other views that support
@@ -49,29 +75,317 @@ import 
org.apache.isis.viewer.wicket.ui.selector.links.LinksSelectorPanelAbstrac
  * Most of the heavy lifting is factored out into the superclass,
  * {@link LinksSelectorPanelAbstract}.
  */
-public class CollectionContentsLinksSelectorPanel extends 
LinksSelectorPanelAbstract<EntityCollectionModel> implements 
CollectionCountProvider {
+public class CollectionContentsLinksSelectorPanel
+        extends PanelAbstract<EntityCollectionModel> implements 
UiHintPathSignificant,  CollectionCountProvider {
 
     private static final long serialVersionUID = 1L;
 
+    private static final String INVISIBLE_CLASS = 
"link-selector-panel-invisible";
+    private static final int MAX_NUM_UNDERLYING_VIEWS = 10;
+
+    private static final String ID_ADDITIONAL_LINKS = "additionalLinks";
+
+    private static final String ID_VIEWS = "views";
+    private static final String ID_VIEW_LIST = "viewList";
+    private static final String ID_VIEW_LINK = "viewLink";
+    private static final String ID_VIEW_ITEM = "viewItem";
+    private static final String ID_VIEW_ITEM_TITLE = "viewItemTitle";
+    private static final String ID_VIEW_ITEM_ICON = "viewItemIcon";
+
+    private static final String UIHINT_VIEW = "view";
+    private static final String ID_VIEW_BUTTON_TITLE = "viewButtonTitle";
+    private static final String ID_VIEW_BUTTON_ICON = "viewButtonIcon";
+
+    private final ComponentType componentType;
+    private final String underlyingIdPrefix;
+
+    private ComponentFactory selectedComponentFactory;
+    protected Component selectedComponent;
+
+    /**
+     * May be <tt>null</tt>, depending upon the model implementation.
+     */
+    protected WebMarkupContainer additionalLinks;
+
     public CollectionContentsLinksSelectorPanel(final String id, final 
EntityCollectionModel model, final ComponentFactory factory) {
-        super(id, ComponentType.COLLECTION_CONTENTS.toString(), model, 
factory);
+        super(id, model);
+        this.underlyingIdPrefix = ComponentType.COLLECTION_CONTENTS.toString();
+        this.componentType = factory.getComponentType();
     }
 
-    /* (non-Javadoc)
-     * @see 
org.apache.isis.viewer.wicket.ui.selector.links.LinksSelectorPanelAbstract#onInitialize()
+    /**
+     * Build UI only after added to parent.
      */
-    @Override
     public void onInitialize() {
         super.onInitialize();
+        ComponentFactory componentFactory = 
getComponentFactoryRegistry().findComponentFactoryElseFailFast(getComponentType(),
 getModel());
+        addAdditionalLinks(getModel());
+        addUnderlyingViews(underlyingIdPrefix, getModel(), componentFactory);
         applyCssVisibility(additionalLinks, selectedComponent instanceof 
CollectionCountProvider);
     }
-    
-    @Override
-    protected EntityCollectionModel dummyOf(EntityCollectionModel model) {
-        return model.asDummy();
+
+
+    protected void addAdditionalLinks(final EntityCollectionModel model) {
+        if(!(model instanceof LinksProvider)) {
+            permanentlyHide(ID_ADDITIONAL_LINKS);
+            return;
+        }
+        LinksProvider linksProvider = (LinksProvider) model;
+        List<LinkAndLabel> links = linksProvider.getLinks();
+
+        addAdditionalLinks(this, links);
+    }
+
+    protected void addAdditionalLinks(MarkupContainer markupContainer, 
List<LinkAndLabel> links) {
+        if(links == null || links.isEmpty()) {
+            Components.permanentlyHide(markupContainer, ID_ADDITIONAL_LINKS);
+            return;
+        }
+        links = Lists.newArrayList(links); // copy, to serialize any lazy 
evaluation
+
+        additionalLinks = new AdditionalLinksPanel(ID_ADDITIONAL_LINKS, links);
+        markupContainer.addOrReplace(additionalLinks);
+    }
+
+    private void addUnderlyingViews(final String underlyingIdPrefix, final 
EntityCollectionModel model, final ComponentFactory factory) {
+        final List<ComponentFactory> componentFactories = 
findOtherComponentFactories(model, factory);
+
+        final int selected = honourViewHintElseDefault(componentFactories, 
model);
+
+        final CollectionContentsLinksSelectorPanel selectorPanel = this;
+
+        // create all, hide the one not selected
+        final Component[] underlyingViews = new 
Component[MAX_NUM_UNDERLYING_VIEWS];
+        int i = 0;
+        final EntityCollectionModel emptyModel = dummyOf(model);
+        for (ComponentFactory componentFactory : componentFactories) {
+            final String underlyingId = underlyingIdPrefix + "-" + i;
+
+            Component underlyingView = 
componentFactory.createComponent(underlyingId,i==selected? model: emptyModel);
+            underlyingViews[i++] = underlyingView;
+            selectorPanel.addOrReplace(underlyingView);
+        }
+
+        // hide any unused placeholders
+        while(i<MAX_NUM_UNDERLYING_VIEWS) {
+            String underlyingId = underlyingIdPrefix + "-" + i;
+            permanentlyHide(underlyingId);
+            i++;
+        }
+
+        // selector
+        if (componentFactories.size() <= 1) {
+            permanentlyHide(ID_VIEWS);
+        } else {
+            final Model<ComponentFactory> componentFactoryModel = new 
Model<>();
+
+            selectorPanel.selectedComponentFactory = 
componentFactories.get(selected);
+            
componentFactoryModel.setObject(selectorPanel.selectedComponentFactory);
+
+            final WebMarkupContainer views = new WebMarkupContainer(ID_VIEWS);
+
+            final Label viewButtonTitle = new Label(ID_VIEW_BUTTON_TITLE, 
"Hidden");
+            views.addOrReplace(viewButtonTitle);
+
+            final Label viewButtonIcon = new Label(ID_VIEW_BUTTON_ICON, "");
+            views.addOrReplace(viewButtonIcon);
+
+            final WebMarkupContainer container = new 
WebMarkupContainer(ID_VIEW_LIST);
+
+            views.addOrReplace(container);
+            views.setOutputMarkupId(true);
+
+            this.setOutputMarkupId(true);
+
+            final ListView<ComponentFactory> listView = new 
ListView<ComponentFactory>(ID_VIEW_ITEM, componentFactories) {
+
+                private static final long serialVersionUID = 1L;
+
+                @Override
+                protected void populateItem(ListItem<ComponentFactory> item) {
+
+                    final int underlyingViewNum = item.getIndex();
+
+                    final ComponentFactory componentFactory = 
item.getModelObject();
+                    final AbstractLink link = new AjaxLink<Void>(ID_VIEW_LINK) 
{
+                        private static final long serialVersionUID = 1L;
+                        @Override
+                        public void onClick(AjaxRequestTarget target) {
+                            CollectionContentsLinksSelectorPanel 
linksSelectorPanel = CollectionContentsLinksSelectorPanel.this;
+                            
linksSelectorPanel.setViewHintAndBroadcast(underlyingViewNum, target);
+
+                            final EntityCollectionModel dummyModel = 
dummyOf(model);
+                            for(int i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
+                                final Component component = underlyingViews[i];
+                                if(component == null) {
+                                    continue;
+                                }
+                                final boolean isSelected = i == 
underlyingViewNum;
+                                applyCssVisibility(component, isSelected);
+                                component.setDefaultModel(isSelected? model: 
dummyModel);
+                            }
+
+                            selectorPanel.selectedComponentFactory = 
componentFactory;
+                            selectorPanel.selectedComponent = 
underlyingViews[underlyingViewNum];
+                            selectorPanel.onSelect(target);
+                            target.add(selectorPanel, views);
+                        }
+
+                        @Override
+                        protected void onComponentTag(ComponentTag tag) {
+                            super.onComponentTag(tag);
+                            Buttons.fixDisabledState(this, tag);
+                        }
+                    };
+
+                    IModel<String> title = nameFor(componentFactory);
+                    Label viewItemTitleLabel = new Label(ID_VIEW_ITEM_TITLE, 
title);
+                    link.add(viewItemTitleLabel);
+
+                    Label viewItemIcon = new Label(ID_VIEW_ITEM_ICON, "");
+                    link.add(viewItemIcon);
+
+                    boolean isEnabled = componentFactory != 
selectorPanel.selectedComponentFactory;
+                    if (!isEnabled) {
+                        viewButtonTitle.setDefaultModel(title);
+                        IModel<String> cssClass = 
cssClassFor(componentFactory, viewButtonIcon);
+                        viewButtonIcon.add(AttributeModifier.replace("class", 
"ViewLinkItem " + cssClass.getObject()));
+                        link.setVisible(false);
+                    } else {
+                        IModel<String> cssClass = 
cssClassFor(componentFactory, viewItemIcon);
+                        viewItemIcon.add(new CssClassAppender(cssClass));
+                    }
+
+                    item.add(link);
+                }
+
+                private IModel<String> cssClassFor(final ComponentFactory 
componentFactory, Label viewIcon) {
+                    IModel<String> cssClass = null;
+                    if (componentFactory instanceof 
CollectionContentsAsFactory) {
+                        CollectionContentsAsFactory 
collectionContentsAsFactory = (CollectionContentsAsFactory) componentFactory;
+                        cssClass = collectionContentsAsFactory.getCssClass();
+                        viewIcon.setDefaultModelObject("");
+                        viewIcon.setEscapeModelStrings(true);
+                    }
+                    if (cssClass == null) {
+                        String name = componentFactory.getName();
+                        cssClass = 
Model.of(StringExtensions.asLowerDashed(name));
+                        // Small hack: if there is no specific CSS class then 
we assume that background-image is used
+                        // the span.ViewItemLink should have some content to 
show it
+                        // FIX: find a way to do this with CSS (width and 
height don't seems to help)
+                        
viewIcon.setDefaultModelObject("&#160;&#160;&#160;&#160;&#160;");
+                        viewIcon.setEscapeModelStrings(false);
+                    }
+                    return cssClass;
+                }
+
+                private IModel<String> nameFor(final ComponentFactory 
componentFactory) {
+                    IModel<String> name = null;
+                    if (componentFactory instanceof 
CollectionContentsAsFactory) {
+                        CollectionContentsAsFactory 
collectionContentsAsFactory = (CollectionContentsAsFactory) componentFactory;
+                        name = collectionContentsAsFactory.getTitleLabel();
+                    }
+                    if (name == null) {
+                        name = Model.of(componentFactory.getName());
+                    }
+                    return name;
+                }
+            };
+            container.add(listView);
+            addOrReplace(views);
+        }
+
+        for(i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
+            Component component = underlyingViews[i];
+            if(component != null) {
+                if(i != selected) {
+                    component.add(new CssClassAppender(INVISIBLE_CLASS));
+                } else {
+                    selectedComponent = component;
+                }
+            }
+        }
+    }
+
+
+
+    protected void setViewHintAndBroadcast(int viewNum, AjaxRequestTarget 
target) {
+        final UiHintContainer uiHintContainer = getUiHintContainer();
+        if(uiHintContainer == null) {
+            return;
+        }
+        uiHintContainer.setHint(CollectionContentsLinksSelectorPanel.this, 
UIHINT_VIEW, ""+viewNum);
+        send(getPage(), Broadcast.EXACT, new IsisUiHintEvent(uiHintContainer, 
target));
     }
 
-    
+    /**
+     * Iterates up the component hierarchy looking for a parent
+     * {@link 
org.apache.isis.viewer.wicket.ui.components.collection.CollectionPanel}, and if 
so adds to ajax target so that it'll
+     * be repainted.
+     *
+     * <p>
+     * Yeah, agreed, it's a little bit hacky doing it this way, because it 
bakes
+     * in knowledge that this component is created, somehow, by a parent 
{@link org.apache.isis.viewer.wicket.ui.components.collection.CollectionPanel}.
+     * Perhaps it could be refactored to use a more general purpose observer 
pattern?
+     *
+     * <p>
+     * In fact, I've since discovered that Wicket has an event bus, which is 
used by the
+     * {@link UiHintContainer hinting mechanism}.  So this ought to be 
relatively easy to do.
+     */
+    protected void onSelect(AjaxRequestTarget target) {
+        Component component = this;
+        while(component != null) {
+            if(component instanceof CollectionPanel) {
+                CollectionPanel collectionPanel = (CollectionPanel) component;
+                boolean hasCount = collectionPanel.hasCount();
+                if(hasCount) {
+                    collectionPanel.updateLabel(target);
+                }
+                if(additionalLinks != null) {
+                    applyCssVisibility(additionalLinks, hasCount);
+                }
+                return;
+            }
+            component = component.getParent();
+        }
+    }
+
+
+    protected static void applyCssVisibility(final Component component, final 
boolean visible) {
+        if(component == null) {
+            return;
+        }
+        AttributeModifier modifier = visible ? new 
CssClassRemover(INVISIBLE_CLASS) : new CssClassAppender(INVISIBLE_CLASS);
+        component.add(modifier);
+    }
+
+    protected int honourViewHintElseDefault(final List<ComponentFactory> 
componentFactories, final IModel<?> model) {
+        // honour hints ...
+        final UiHintContainer hintContainer = getUiHintContainer();
+        if(hintContainer != null) {
+            String viewStr = hintContainer.getHint(this, UIHINT_VIEW);
+            if(viewStr != null) {
+                try {
+                    int view = Integer.parseInt(viewStr);
+                    if(view >= 0 && view < componentFactories.size()) {
+                        return view;
+                    }
+                } catch(NumberFormatException ex) {
+                    // ignore
+                }
+            }
+        }
+
+        // ... else default
+        int initialFactory = determineInitialFactory(componentFactories, 
model);
+        if(hintContainer != null) {
+            hintContainer.setHint(this, UIHINT_VIEW, ""+initialFactory);
+            // don't broadcast (no AjaxRequestTarget, still configuring 
initial setup)
+        }
+        return initialFactory;
+    }
+
+
     /**
      * return the index of {@link CollectionContentsAsUnresolvedPanelFactory 
unresolved panel} if present and not eager loading;
      * else the index of {@link CollectionContentsAsAjaxTablePanelFactory ajax 
table} if present,
@@ -92,11 +406,44 @@ public class CollectionContentsLinksSelectorPanel extends 
LinksSelectorPanelAbst
         return 0;
     }
 
-    @Override
+    private List<ComponentFactory> findOtherComponentFactories(final 
EntityCollectionModel model, final ComponentFactory ignoreFactory) {
+        final List<ComponentFactory> componentFactories = 
getComponentFactoryRegistry().findComponentFactories(componentType, model);
+        ArrayList<ComponentFactory> otherFactories = 
Lists.newArrayList(Collections2.filter(componentFactories, new 
Predicate<ComponentFactory>() {
+            @Override
+            public boolean apply(final ComponentFactory input) {
+                return input != ignoreFactory;
+            }
+        }));
+        return ordered(otherFactories);
+    }
+
     protected List<ComponentFactory> ordered(List<ComponentFactory> 
componentFactories) {
         return orderAjaxTableToEnd(componentFactories);
     }
 
+
+    @Override
+    public void renderHead(final IHeaderResponse response) {
+        super.renderHead(response);
+        PanelUtil.renderHead(response, LinksSelectorPanelAbstract.class);
+    }
+
+
+
+
+
+
+
+    /**
+     * Ask for a dummy (empty) {@link Model} to pass into those components 
that are rendered but will be
+     * made invisible using CSS styling.
+     */
+    protected EntityCollectionModel dummyOf(EntityCollectionModel model) {
+        return model.asDummy();
+    }
+
+    
+
     static List<ComponentFactory> orderAjaxTableToEnd(List<ComponentFactory> 
componentFactories) {
         int ajaxTableIdx = findAjaxTable(componentFactories);
         if(ajaxTableIdx>=0) {
@@ -134,38 +481,6 @@ public class CollectionContentsLinksSelectorPanel extends 
LinksSelectorPanelAbst
         return renderFacet != null && renderFacet.value() == Type.EAGERLY;
     }
 
-    /**
-     * Iterates up the component hierarchy looking for a parent
-     * {@link CollectionPanel}, and if so adds to ajax target so that it'll
-     * be repainted.
-     * 
-     * <p>
-     * Yeah, agreed, it's a little bit hacky doing it this way, because it 
bakes
-     * in knowledge that this component is created, somehow, by a parent 
{@link CollectionPanel}.
-     * Perhaps it could be refactored to use a more general purpose observer 
pattern?
-     * 
-     * <p>
-     * In fact, I've since discovered that Wicket has an event bus, which is 
used by the 
-     * {@link UiHintContainer hinting mechanism}.  So this ought to be 
relatively easy to do.
-     */
-    protected void onSelect(AjaxRequestTarget target) {
-        super.onSelect(target);
-        Component component = this;
-        while(component != null) {
-            if(component instanceof CollectionPanel) {
-                CollectionPanel collectionPanel = (CollectionPanel) component;
-                boolean hasCount = collectionPanel.hasCount();
-                if(hasCount) {
-                    collectionPanel.updateLabel(target);
-                }
-                if(additionalLinks != null) {
-                    applyCssVisibility(additionalLinks, hasCount);
-                }
-                return;
-            }
-            component = component.getParent();
-        }
-    }
 
     @Override
     public Integer getCount() {
@@ -177,5 +492,4 @@ public class CollectionContentsLinksSelectorPanel extends 
LinksSelectorPanelAbst
         }
     }
 
-
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/b1677ca8/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.java
----------------------------------------------------------------------
diff --git 
a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.java
 
b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.java
index 378f277..7cab8d8 100644
--- 
a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.java
+++ 
b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/entity/selector/links/EntityLinksSelectorPanel.java
@@ -19,15 +19,45 @@
 
 package org.apache.isis.viewer.wicket.ui.components.entity.selector.links;
 
-import java.util.List;
+import de.agilecoders.wicket.core.markup.html.bootstrap.button.Buttons;
 
+import java.util.ArrayList;
+import java.util.List;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import org.apache.wicket.AttributeModifier;
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.ajax.markup.html.AjaxLink;
+import org.apache.wicket.event.Broadcast;
+import org.apache.wicket.markup.ComponentTag;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.link.AbstractLink;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
 import org.apache.wicket.model.IModel;
-
+import org.apache.wicket.model.Model;
+import org.apache.isis.core.commons.lang.StringExtensions;
+import org.apache.isis.viewer.wicket.model.hints.IsisUiHintEvent;
 import org.apache.isis.viewer.wicket.model.hints.UiHintContainer;
+import org.apache.isis.viewer.wicket.model.hints.UiHintPathSignificant;
+import org.apache.isis.viewer.wicket.model.links.LinkAndLabel;
+import org.apache.isis.viewer.wicket.model.links.LinksProvider;
 import org.apache.isis.viewer.wicket.model.models.EntityModel;
+import org.apache.isis.viewer.wicket.ui.CollectionContentsAsFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.ComponentType;
+import 
org.apache.isis.viewer.wicket.ui.components.additionallinks.AdditionalLinksPanel;
+import org.apache.isis.viewer.wicket.ui.panels.PanelAbstract;
+import org.apache.isis.viewer.wicket.ui.panels.PanelUtil;
 import 
org.apache.isis.viewer.wicket.ui.selector.links.LinksSelectorPanelAbstract;
+import org.apache.isis.viewer.wicket.ui.util.Components;
+import org.apache.isis.viewer.wicket.ui.util.CssClassAppender;
+import org.apache.isis.viewer.wicket.ui.util.CssClassRemover;
 
 /**
  * Provides a list of links for selecting other views that support
@@ -37,20 +67,47 @@ import 
org.apache.isis.viewer.wicket.ui.selector.links.LinksSelectorPanelAbstrac
  * Most of the heavy lifting is factored out into the superclass,
  * {@link LinksSelectorPanelAbstract}.
  */
-public class EntityLinksSelectorPanel extends 
LinksSelectorPanelAbstract<EntityModel> {
+public class EntityLinksSelectorPanel extends PanelAbstract<EntityModel> 
implements UiHintPathSignificant {
+
 
     private static final long serialVersionUID = 1L;
 
+    private static final String INVISIBLE_CLASS = 
"link-selector-panel-invisible";
+    private static final int MAX_NUM_UNDERLYING_VIEWS = 10;
+
+    private static final String ID_ADDITIONAL_LINKS = "additionalLinks";
+    public static final String ID_ADDITIONAL_LINK = "additionalLink";
+
+    private static final String ID_VIEWS = "views";
+    private static final String ID_VIEW_LIST = "viewList";
+    private static final String ID_VIEW_LINK = "viewLink";
+    private static final String ID_VIEW_ITEM = "viewItem";
+    private static final String ID_VIEW_ITEM_TITLE = "viewItemTitle";
+    private static final String ID_VIEW_ITEM_ICON = "viewItemIcon";
+
+    private static final String UIHINT_VIEW = "view";
+    private static final String ID_VIEW_BUTTON_TITLE = "viewButtonTitle";
+    private static final String ID_VIEW_BUTTON_ICON = "viewButtonIcon";
+
+    private final ComponentType componentType;
+    private final String underlyingIdPrefix;
+
+    private ComponentFactory selectedComponentFactory;
+    protected Component selectedComponent;
+
+    /**
+     * May be <tt>null</tt>, depending upon the model implementation.
+     */
+    protected WebMarkupContainer additionalLinks;
+
+
     public EntityLinksSelectorPanel(final String id, final EntityModel model, 
final ComponentFactory factory) {
-        super(id, ComponentType.ENTITY.toString(), model, factory);
+        super(id, model);
+        this.underlyingIdPrefix = ComponentType.ENTITY.toString();
+        this.componentType = factory.getComponentType();
     }
 
-    @Override
-    protected EntityModel dummyOf(EntityModel model) {
-        return model;
-    }
 
-    @Override
     protected int determineInitialFactory(List<ComponentFactory> 
componentFactories, IModel<?> model) {
         return 0;
     }
@@ -61,4 +118,281 @@ public class EntityLinksSelectorPanel extends 
LinksSelectorPanelAbstract<EntityM
         return null;
     }
 
+
+    /**
+     * Build UI only after added to parent.
+     */
+    public void onInitialize() {
+        super.onInitialize();
+        ComponentFactory componentFactory = 
getComponentFactoryRegistry().findComponentFactoryElseFailFast(getComponentType(),
 getModel());
+        addAdditionalLinks(getModel());
+        addUnderlyingViews(underlyingIdPrefix, getModel(), componentFactory);
+    }
+
+    protected void addAdditionalLinks(final EntityModel model) {
+        if(!(model instanceof LinksProvider)) {
+            permanentlyHide(ID_ADDITIONAL_LINKS);
+            return;
+        }
+        LinksProvider linksProvider = (LinksProvider) model;
+        List<LinkAndLabel> links = linksProvider.getLinks();
+
+        addAdditionalLinks(this, links);
+    }
+
+    protected void addAdditionalLinks(MarkupContainer markupContainer, 
List<LinkAndLabel> links) {
+        if(links == null || links.isEmpty()) {
+            Components.permanentlyHide(markupContainer, ID_ADDITIONAL_LINKS);
+            return;
+        }
+        links = Lists.newArrayList(links); // copy, to serialize any lazy 
evaluation
+
+        additionalLinks = new AdditionalLinksPanel(ID_ADDITIONAL_LINKS, links);
+        markupContainer.addOrReplace(additionalLinks);
+    }
+
+    private void addUnderlyingViews(final String underlyingIdPrefix, final 
EntityModel model, final ComponentFactory factory) {
+        final List<ComponentFactory> componentFactories = 
findOtherComponentFactories(model, factory);
+
+        final int selected = honourViewHintElseDefault(componentFactories, 
model);
+
+        final EntityLinksSelectorPanel selectorPanel = this;
+
+        // create all, hide the one not selected
+        final Component[] underlyingViews = new 
Component[MAX_NUM_UNDERLYING_VIEWS];
+        int i = 0;
+        final EntityModel emptyModel = dummyOf(model);
+        for (ComponentFactory componentFactory : componentFactories) {
+            final String underlyingId = underlyingIdPrefix + "-" + i;
+
+            Component underlyingView = 
componentFactory.createComponent(underlyingId,i==selected? model: emptyModel);
+            underlyingViews[i++] = underlyingView;
+            selectorPanel.addOrReplace(underlyingView);
+        }
+
+        // hide any unused placeholders
+        while(i<MAX_NUM_UNDERLYING_VIEWS) {
+            String underlyingId = underlyingIdPrefix + "-" + i;
+            permanentlyHide(underlyingId);
+            i++;
+        }
+
+        // selector
+        if (componentFactories.size() <= 1) {
+            permanentlyHide(ID_VIEWS);
+        } else {
+            final Model<ComponentFactory> componentFactoryModel = new 
Model<>();
+
+            selectorPanel.selectedComponentFactory = 
componentFactories.get(selected);
+            
componentFactoryModel.setObject(selectorPanel.selectedComponentFactory);
+
+            final WebMarkupContainer views = new WebMarkupContainer(ID_VIEWS);
+
+            final Label viewButtonTitle = new Label(ID_VIEW_BUTTON_TITLE, 
"Hidden");
+            views.addOrReplace(viewButtonTitle);
+
+            final Label viewButtonIcon = new Label(ID_VIEW_BUTTON_ICON, "");
+            views.addOrReplace(viewButtonIcon);
+
+            final WebMarkupContainer container = new 
WebMarkupContainer(ID_VIEW_LIST);
+
+            views.addOrReplace(container);
+            views.setOutputMarkupId(true);
+
+            this.setOutputMarkupId(true);
+
+            final ListView<ComponentFactory> listView = new 
ListView<ComponentFactory>(ID_VIEW_ITEM, componentFactories) {
+
+                private static final long serialVersionUID = 1L;
+
+                @Override
+                protected void populateItem(ListItem<ComponentFactory> item) {
+
+                    final int underlyingViewNum = item.getIndex();
+
+                    final ComponentFactory componentFactory = 
item.getModelObject();
+                    final AbstractLink link = new AjaxLink<Void>(ID_VIEW_LINK) 
{
+                        private static final long serialVersionUID = 1L;
+                        @Override
+                        public void onClick(AjaxRequestTarget target) {
+                            EntityLinksSelectorPanel linksSelectorPanel = 
EntityLinksSelectorPanel.this;
+                            
linksSelectorPanel.setViewHintAndBroadcast(underlyingViewNum, target);
+
+                            final EntityModel dummyModel = dummyOf(model);
+                            for(int i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
+                                final Component component = underlyingViews[i];
+                                if(component == null) {
+                                    continue;
+                                }
+                                final boolean isSelected = i == 
underlyingViewNum;
+                                applyCssVisibility(component, isSelected);
+                                component.setDefaultModel(isSelected? model: 
dummyModel);
+                            }
+
+                            selectorPanel.selectedComponentFactory = 
componentFactory;
+                            selectorPanel.selectedComponent = 
underlyingViews[underlyingViewNum];
+                            selectorPanel.onSelect(target);
+                            target.add(selectorPanel, views);
+                        }
+
+                        @Override
+                        protected void onComponentTag(ComponentTag tag) {
+                            super.onComponentTag(tag);
+                            Buttons.fixDisabledState(this, tag);
+                        }
+                    };
+
+                    IModel<String> title = nameFor(componentFactory);
+                    Label viewItemTitleLabel = new Label(ID_VIEW_ITEM_TITLE, 
title);
+                    link.add(viewItemTitleLabel);
+
+                    Label viewItemIcon = new Label(ID_VIEW_ITEM_ICON, "");
+                    link.add(viewItemIcon);
+
+                    boolean isEnabled = componentFactory != 
selectorPanel.selectedComponentFactory;
+                    if (!isEnabled) {
+                        viewButtonTitle.setDefaultModel(title);
+                        IModel<String> cssClass = 
cssClassFor(componentFactory, viewButtonIcon);
+                        viewButtonIcon.add(AttributeModifier.replace("class", 
"ViewLinkItem " + cssClass.getObject()));
+                        link.setVisible(false);
+                    } else {
+                        IModel<String> cssClass = 
cssClassFor(componentFactory, viewItemIcon);
+                        viewItemIcon.add(new CssClassAppender(cssClass));
+                    }
+
+                    item.add(link);
+                }
+
+                private IModel<String> cssClassFor(final ComponentFactory 
componentFactory, Label viewIcon) {
+                    IModel<String> cssClass = null;
+                    if (componentFactory instanceof 
CollectionContentsAsFactory) {
+                        CollectionContentsAsFactory 
collectionContentsAsFactory = (CollectionContentsAsFactory) componentFactory;
+                        cssClass = collectionContentsAsFactory.getCssClass();
+                        viewIcon.setDefaultModelObject("");
+                        viewIcon.setEscapeModelStrings(true);
+                    }
+                    if (cssClass == null) {
+                        String name = componentFactory.getName();
+                        cssClass = 
Model.of(StringExtensions.asLowerDashed(name));
+                        // Small hack: if there is no specific CSS class then 
we assume that background-image is used
+                        // the span.ViewItemLink should have some content to 
show it
+                        // FIX: find a way to do this with CSS (width and 
height don't seems to help)
+                        
viewIcon.setDefaultModelObject("&#160;&#160;&#160;&#160;&#160;");
+                        viewIcon.setEscapeModelStrings(false);
+                    }
+                    return cssClass;
+                }
+
+                private IModel<String> nameFor(final ComponentFactory 
componentFactory) {
+                    IModel<String> name = null;
+                    if (componentFactory instanceof 
CollectionContentsAsFactory) {
+                        CollectionContentsAsFactory 
collectionContentsAsFactory = (CollectionContentsAsFactory) componentFactory;
+                        name = collectionContentsAsFactory.getTitleLabel();
+                    }
+                    if (name == null) {
+                        name = Model.of(componentFactory.getName());
+                    }
+                    return name;
+                }
+            };
+            container.add(listView);
+            addOrReplace(views);
+        }
+
+        for(i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
+            Component component = underlyingViews[i];
+            if(component != null) {
+                if(i != selected) {
+                    component.add(new CssClassAppender(INVISIBLE_CLASS));
+                } else {
+                    selectedComponent = component;
+                }
+            }
+        }
+    }
+
+
+
+    protected void setViewHintAndBroadcast(int viewNum, AjaxRequestTarget 
target) {
+        final UiHintContainer uiHintContainer = getUiHintContainer();
+        if(uiHintContainer == null) {
+            return;
+        }
+        uiHintContainer.setHint(this, UIHINT_VIEW, ""+viewNum);
+        send(getPage(), Broadcast.EXACT, new IsisUiHintEvent(uiHintContainer, 
target));
+    }
+
+    /**
+     * Overrideable hook.
+     */
+    protected void onSelect(AjaxRequestTarget target) {
+    }
+
+    /**
+     * Ask for a dummy (empty) {@link Model} to pass into those components 
that are rendered but will be
+     * made invisible using CSS styling.
+     */
+    protected EntityModel dummyOf(EntityModel model) {
+        return model;
+    }
+
+    protected static void applyCssVisibility(final Component component, final 
boolean visible) {
+        if(component == null) {
+            return;
+        }
+        AttributeModifier modifier = visible ? new 
CssClassRemover(INVISIBLE_CLASS) : new CssClassAppender(INVISIBLE_CLASS);
+        component.add(modifier);
+    }
+
+    protected int honourViewHintElseDefault(final List<ComponentFactory> 
componentFactories, final IModel<?> model) {
+        // honour hints ...
+        final UiHintContainer hintContainer = getUiHintContainer();
+        if(hintContainer != null) {
+            String viewStr = hintContainer.getHint(this, UIHINT_VIEW);
+            if(viewStr != null) {
+                try {
+                    int view = Integer.parseInt(viewStr);
+                    if(view >= 0 && view < componentFactories.size()) {
+                        return view;
+                    }
+                } catch(NumberFormatException ex) {
+                    // ignore
+                }
+            }
+        }
+
+        // ... else default
+        int initialFactory = determineInitialFactory(componentFactories, 
model);
+        if(hintContainer != null) {
+            hintContainer.setHint(this, UIHINT_VIEW, ""+initialFactory);
+            // don't broadcast (no AjaxRequestTarget, still configuring 
initial setup)
+        }
+        return initialFactory;
+    }
+
+
+    private List<ComponentFactory> findOtherComponentFactories(final 
EntityModel model, final ComponentFactory ignoreFactory) {
+        final List<ComponentFactory> componentFactories = 
getComponentFactoryRegistry().findComponentFactories(componentType, model);
+        ArrayList<ComponentFactory> otherFactories = 
Lists.newArrayList(Collections2.filter(componentFactories, new 
Predicate<ComponentFactory>() {
+            @Override
+            public boolean apply(final ComponentFactory input) {
+                return input != ignoreFactory;
+            }
+        }));
+        return ordered(otherFactories);
+    }
+
+    protected List<ComponentFactory> ordered(List<ComponentFactory> 
otherFactories) {
+        return otherFactories;
+    }
+
+
+    @Override
+    public void renderHead(final IHeaderResponse response) {
+        super.renderHead(response);
+        PanelUtil.renderHead(response, LinksSelectorPanelAbstract.class);
+    }
+
+
+
 }

http://git-wip-us.apache.org/repos/asf/isis/blob/b1677ca8/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
----------------------------------------------------------------------
diff --git 
a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
 
b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
index e494ce8..2b6b96f 100644
--- 
a/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
+++ 
b/component/viewer/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/selector/links/LinksSelectorPanelAbstract.java
@@ -73,20 +73,20 @@ public abstract class LinksSelectorPanelAbstract<T extends 
IModel<?>> extends Pa
     private static final String ID_VIEW_ITEM = "viewItem";
     private static final String ID_VIEW_ITEM_TITLE = "viewItemTitle";
     private static final String ID_VIEW_ITEM_ICON = "viewItemIcon";
-    
+
     private static final String UIHINT_VIEW = "view";
     private static final String ID_VIEW_BUTTON_TITLE = "viewButtonTitle";
     private static final String ID_VIEW_BUTTON_ICON = "viewButtonIcon";
 
     private final ComponentType componentType;
     private final String underlyingIdPrefix;
-    
+
     private ComponentFactory selectedComponentFactory;
     protected Component selectedComponent;
 
     /**
      * May be <tt>null</tt>, depending upon the model implementation.
-     * 
+     *
      * @see #addAdditionalLinks(IModel)
      */
     protected WebMarkupContainer additionalLinks;
@@ -97,7 +97,7 @@ public abstract class LinksSelectorPanelAbstract<T extends 
IModel<?>> extends Pa
         this.underlyingIdPrefix = underlyingIdPrefix;
         this.componentType = factory.getComponentType();
     }
-    
+
     /**
      * Build UI only after added to parent.
      */
@@ -115,7 +115,7 @@ public abstract class LinksSelectorPanelAbstract<T extends 
IModel<?>> extends Pa
         }
         LinksProvider linksProvider = (LinksProvider) model;
         List<LinkAndLabel> links = linksProvider.getLinks();
-        
+
         addAdditionalLinks(this, links);
     }
 
@@ -125,25 +125,25 @@ public abstract class LinksSelectorPanelAbstract<T 
extends IModel<?>> extends Pa
             return;
         }
         links = Lists.newArrayList(links); // copy, to serialize any lazy 
evaluation
-        
+
         additionalLinks = new AdditionalLinksPanel(ID_ADDITIONAL_LINKS, links);
         markupContainer.addOrReplace(additionalLinks);
     }
-    
+
     private void addUnderlyingViews(final String underlyingIdPrefix, final T 
model, final ComponentFactory factory) {
         final List<ComponentFactory> componentFactories = 
findOtherComponentFactories(model, factory);
 
         final int selected = honourViewHintElseDefault(componentFactories, 
model);
 
         final LinksSelectorPanelAbstract<T> selectorPanel = 
LinksSelectorPanelAbstract.this;
-        
+
         // create all, hide the one not selected
         final Component[] underlyingViews = new 
Component[MAX_NUM_UNDERLYING_VIEWS];
         int i = 0;
         final T emptyModel = dummyOf(model);
         for (ComponentFactory componentFactory : componentFactories) {
             final String underlyingId = underlyingIdPrefix + "-" + i;
-            
+
             Component underlyingView = 
componentFactory.createComponent(underlyingId,i==selected? model: emptyModel);
             underlyingViews[i++] = underlyingView;
             selectorPanel.addOrReplace(underlyingView);
@@ -155,13 +155,13 @@ public abstract class LinksSelectorPanelAbstract<T 
extends IModel<?>> extends Pa
             permanentlyHide(underlyingId);
             i++;
         }
-        
+
         // selector
         if (componentFactories.size() <= 1) {
             permanentlyHide(ID_VIEWS);
         } else {
             final Model<ComponentFactory> componentFactoryModel = new 
Model<>();
-            
+
             selectorPanel.selectedComponentFactory = 
componentFactories.get(selected);
             
componentFactoryModel.setObject(selectorPanel.selectedComponentFactory);
 
@@ -174,21 +174,21 @@ public abstract class LinksSelectorPanelAbstract<T 
extends IModel<?>> extends Pa
             views.addOrReplace(viewButtonIcon);
 
             final WebMarkupContainer container = new 
WebMarkupContainer(ID_VIEW_LIST);
-            
+
             views.addOrReplace(container);
             views.setOutputMarkupId(true);
-            
+
             this.setOutputMarkupId(true);
-            
+
             final ListView<ComponentFactory> listView = new 
ListView<ComponentFactory>(ID_VIEW_ITEM, componentFactories) {
 
                 private static final long serialVersionUID = 1L;
 
                 @Override
                 protected void populateItem(ListItem<ComponentFactory> item) {
-                    
+
                     final int underlyingViewNum = item.getIndex();
-                    
+
                     final ComponentFactory componentFactory = 
item.getModelObject();
                     final AbstractLink link = new AjaxLink<Void>(ID_VIEW_LINK) 
{
                         private static final long serialVersionUID = 1L;
@@ -196,7 +196,7 @@ public abstract class LinksSelectorPanelAbstract<T extends 
IModel<?>> extends Pa
                         public void onClick(AjaxRequestTarget target) {
                             LinksSelectorPanelAbstract<T> linksSelectorPanel = 
LinksSelectorPanelAbstract.this;
                             
linksSelectorPanel.setViewHintAndBroadcast(underlyingViewNum, target);
-                            
+
                             final T dummyModel = dummyOf(model);
                             for(int i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
                                 final Component component = underlyingViews[i];
@@ -207,7 +207,7 @@ public abstract class LinksSelectorPanelAbstract<T extends 
IModel<?>> extends Pa
                                 applyCssVisibility(component, isSelected);
                                 component.setDefaultModel(isSelected? model: 
dummyModel);
                             }
-                            
+
                             selectorPanel.selectedComponentFactory = 
componentFactory;
                             selectorPanel.selectedComponent = 
underlyingViews[underlyingViewNum];
                             selectorPanel.onSelect(target);
@@ -277,7 +277,7 @@ public abstract class LinksSelectorPanelAbstract<T extends 
IModel<?>> extends Pa
             container.add(listView);
             addOrReplace(views);
         }
-        
+
         for(i=0; i<MAX_NUM_UNDERLYING_VIEWS; i++) {
             Component component = underlyingViews[i];
             if(component != null) {
@@ -365,7 +365,7 @@ public abstract class LinksSelectorPanelAbstract<T extends 
IModel<?>> extends Pa
         return otherFactories;
     }
 
-    
+
     @Override
     public void renderHead(final IHeaderResponse response) {
         super.renderHead(response);

Reply via email to