As promised I've attached the patch against HEAD for the new attribute
view for those interested to review, here are the change higlights.

   1. New framework org.openjump.core.ui.builder for customizing the
      HTML display of attribute values, I'm going to extend this to
      allow customizing of the editing of values but want to get these
      changes committed first
   2. New enable check
      org.openjump.core.ui.plugin.enablecheck.BooleanPropertyEnableCheck
      that uses a boolean property on the object to see if the item
      should be enabled, we should reduce the number of anonymous
      classes that do simple things like this
   3. New Swing component factory under
      com.vividsolutions.jump.workbench.ui.swing, the goal here is we
      can define factories that create components in the workbench
      rather than hard coding creation of components. For example the
      InfoFrame is just a simple wrapper that displays the List and
      geometry panels using a tabbed UI. With the new framework you
      would register a list of factories that would create the
      components to be displayed in InfoFrame.
   4. Extended com.vividsolutions.jump.workbench.ui.InfoFrame to use the
      ComponentFactory framework for additional panels, I'd also propose
      to implement the current tabs also with this framework at some point.
   5. The new attribute view panel under org.openjump.core.ui.component
   6. Added all the new configuration for above to
      org.openjump.OpenJumpConfiguration

I'd like to propose checking in these changes, if people can have a
review and get any comments back to me that would be great.

Paul
Index: src/org/openjump/OpenJumpConfiguration.java
===================================================================
RCS file: 
/cvsroot/jump-pilot/openjump/src/org/openjump/OpenJumpConfiguration.java,v
retrieving revision 1.42
diff -u -r1.42 OpenJumpConfiguration.java
--- src/org/openjump/OpenJumpConfiguration.java 24 Mar 2007 12:27:45 -0000      
1.42
+++ src/org/openjump/OpenJumpConfiguration.java 11 Jun 2007 19:26:47 -0000
@@ -10,10 +10,16 @@
  */
 package org.openjump;
 
-import javax.swing.JMenu;
+import java.util.Date;
+
 import javax.swing.JPopupMenu;
 
 import org.openjump.core.ccordsys.srid.EnsureAllLayersHaveSRIDStylePlugIn;
+import org.openjump.core.ui.builder.DateTimeUiBuilder;
+import org.openjump.core.ui.builder.DateUIBuilder;
+import org.openjump.core.ui.builder.FeatureUiBuilder;
+import org.openjump.core.ui.builder.UiBuilderRegistry;
+import org.openjump.core.ui.component.InfoModelDetailPanel;
 import org.openjump.core.ui.plugin.customize.BeanToolsPlugIn;
 import org.openjump.core.ui.plugin.edit.ReplicateSelectedItemsPlugIn;
 import org.openjump.core.ui.plugin.edit.SelectAllLayerItemsPlugIn;
@@ -54,19 +60,19 @@
 import org.openjump.core.ui.plugin.view.ShowScalePlugIn;
 import org.openjump.core.ui.plugin.view.ZoomToScalePlugIn;
 import org.openjump.core.ui.plugin.wms.ZoomToWMSPlugIn;
+import org.openjump.core.ui.style.decoration.ArrowLineStringMiddlepointStyle;
 import org.openjump.sigle.plugin.geoprocessing.layers.SpatialJoinPlugIn;
 import 
org.openjump.sigle.plugin.geoprocessing.oneLayer.topology.PlanarGraphPlugIn;
 import org.openjump.sigle.plugin.joinTable.JoinTablePlugIn;
 import org.openjump.sigle.plugin.replace.ReplaceValuePlugIn;
 
-import org.openjump.core.ui.style.decoration.ArrowLineStringMiddlepointStyle;
-
+import com.vividsolutions.jump.feature.Feature;
 import com.vividsolutions.jump.workbench.WorkbenchContext;
 import com.vividsolutions.jump.workbench.plugin.PlugInContext;
+import com.vividsolutions.jump.workbench.ui.InfoFrame;
 import com.vividsolutions.jump.workbench.ui.LayerViewPanel;
-import com.vividsolutions.jump.workbench.ui.MenuNames;
-import com.vividsolutions.jump.workbench.ui.plugin.FeatureInstaller;
 import com.vividsolutions.jump.workbench.ui.plugin.BeanShellPlugIn;
+import com.vividsolutions.jump.workbench.ui.swing.ComponentFactoryRegistry;
 
 import 
de.fho.jump.pirol.plugins.EditAttributeByFormula.EditAttributeByFormulaPlugIn;
 import de.latlon.deejump.plugin.SaveLegendPlugIn;
@@ -360,5 +366,17 @@
                projectionPlugin.initialize(new PlugInContext(workbenchContext, 
null, null, null, null));
                */
 
+    /**********************
+     * UI Builders
+     **********************/
+    UiBuilderRegistry htmlrendererRepository = 
UiBuilderRegistry.getInstance(workbenchContext);
+    htmlrendererRepository.addRenderer(Feature.class, new FeatureUiBuilder());
+    htmlrendererRepository.addRenderer(Date.class, new DateTimeUiBuilder());
+    htmlrendererRepository.addRenderer(java.sql.Date.class, new 
DateUIBuilder());
+
+    /***********************
+     *  Info Frame Panels
+     **********************/
+    ComponentFactoryRegistry.addComponentFactory(workbenchContext, 
InfoFrame.CLASSIFICATION, InfoModelDetailPanel.class);
        }
 }
Index: src/com/vividsolutions/jump/workbench/ui/InfoFrame.java
===================================================================
RCS file: 
/cvsroot/jump-pilot/openjump/src/com/vividsolutions/jump/workbench/ui/InfoFrame.java,v
retrieving revision 1.3
diff -u -r1.3 InfoFrame.java
--- src/com/vividsolutions/jump/workbench/ui/InfoFrame.java     3 Jun 2007 
20:53:31 -0000       1.3
+++ src/com/vividsolutions/jump/workbench/ui/InfoFrame.java     11 Jun 2007 
19:26:46 -0000
@@ -30,159 +30,201 @@
  * www.vividsolutions.com
  */
 package com.vividsolutions.jump.workbench.ui;
+
 import java.awt.BorderLayout;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.swing.JComponent;
 import javax.swing.JInternalFrame;
 import javax.swing.JPanel;
 import javax.swing.JTabbedPane;
 import javax.swing.event.InternalFrameAdapter;
 import javax.swing.event.InternalFrameEvent;
+
 import com.vividsolutions.jts.util.Assert;
 import com.vividsolutions.jump.I18N;
 import com.vividsolutions.jump.workbench.WorkbenchContext;
 import com.vividsolutions.jump.workbench.model.LayerManager;
 import com.vividsolutions.jump.workbench.model.LayerManagerProxy;
 import com.vividsolutions.jump.workbench.model.Task;
-import com.vividsolutions.jump.workbench.ui.cursortool.editing.EditingPlugIn;
+import com.vividsolutions.jump.workbench.registry.Registry;
 import com.vividsolutions.jump.workbench.ui.images.IconLoader;
+import com.vividsolutions.jump.workbench.ui.swing.ComponentFactory;
+import com.vividsolutions.jump.workbench.ui.swing.ComponentFactoryRegistry;
+import com.vividsolutions.jump.workbench.ui.swing.IconComponent;
 
 /**
  * Provides proxied (non-spatial) views of a Layer.
  */
-public class InfoFrame
-    extends JInternalFrame
-    implements
-        LayerManagerProxy,
-        SelectionManagerProxy,
-        LayerNamePanelProxy,
-        TaskFrameProxy,
-        LayerViewPanelProxy {
-    public LayerManager getLayerManager() {
-        return layerManager;
-    }
-    public TaskFrame getTaskFrame() {
-        return attributeTab.getTaskFrame();
-    }
-    private LayerManager layerManager;
-    private BorderLayout borderLayout1 = new BorderLayout();
-    private AttributeTab attributeTab;
-    private InfoModel model = new InfoModel();
-    private GeometryInfoTab geometryInfoTab;
-    private JTabbedPane tabbedPane = new JTabbedPane();
-    private WorkbenchFrame workbenchFrame;
-    public InfoFrame(
-        WorkbenchContext workbenchContext,
-        LayerManagerProxy layerManagerProxy,
-        final TaskFrame taskFrame) {
-        geometryInfoTab = new GeometryInfoTab(model, workbenchContext);
-        //Keep my own copy of LayerManager, because it will be nulled in 
TaskFrame
-        //when TaskFrame closes (it may in fact already be closed, which is why
-        //a LayerManagerProxy must be passed in too). But I have to 
-        //remember to null it when I close. [Jon Aquino]
-        Assert.isTrue(layerManagerProxy.getLayerManager() != null);
-        layerManager = layerManagerProxy.getLayerManager();
-        // I cannot see any reason to add this listener [mmichaud 2007-06-03]
-        // See also WorkbenchFrame
-        /*addInternalFrameListener(new InternalFrameAdapter() {
-            public void internalFrameClosed(InternalFrameEvent e) {
-                layerManager = new LayerManager();
-            }
-        });*/
-        attributeTab = new AttributeTab(model, workbenchContext, taskFrame, 
this, false);
-        addInternalFrameListener(new InternalFrameAdapter() {
-            public void internalFrameOpened(InternalFrameEvent e) {
-                attributeTab.getToolBar().updateEnabledState();
-            }
-        });
-        workbenchFrame = workbenchContext.getWorkbench().getFrame();
-        this.setResizable(true);
-        this.setClosable(true);
-        this.setMaximizable(true);
-        this.setIconifiable(true);
-        //This size is chosen so that when the user hits the Info tool, the 
window
-        //fits between the lower edge of the TaskFrame and the lower edge of 
the
-        //WorkbenchFrame. See the call to #setSize in WorkbenchFrame. [Jon 
Aquino]
-        //Make sure there's a little space for a custom FeatureTextWriter 
-        //[Jon Aquino 12/31/2003]
-        this.setSize(550, 185);
-        try {
-            jbInit();
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        tabbedPane.addTab("", IconLoader.icon("Table.gif"), attributeTab, 
"Table View");
-        tabbedPane.addTab("", IconLoader.icon("Paper.gif"), geometryInfoTab, 
"HTML View");
+public class InfoFrame extends JInternalFrame implements LayerManagerProxy,
+  SelectionManagerProxy, LayerNamePanelProxy, TaskFrameProxy,
+  LayerViewPanelProxy {
+
+  public static final String CLASSIFICATION = InfoFrame.class.getName();
+
+  public LayerManager getLayerManager() {
+    return layerManager;
+  }
+
+  public TaskFrame getTaskFrame() {
+    return attributeTab.getTaskFrame();
+  }
+
+  private LayerManager layerManager;
+
+  private BorderLayout borderLayout1 = new BorderLayout();
+
+  private AttributeTab attributeTab;
+
+  private InfoModel model = new InfoModel();
+
+  private GeometryInfoTab geometryInfoTab;
+
+  private JTabbedPane tabbedPane = new JTabbedPane();
+
+  private WorkbenchFrame workbenchFrame;
+
+  public InfoFrame(WorkbenchContext workbenchContext,
+    LayerManagerProxy layerManagerProxy, final TaskFrame taskFrame) {
+    geometryInfoTab = new GeometryInfoTab(model, workbenchContext);
+    // Keep my own copy of LayerManager, because it will be nulled in TaskFrame
+    // when TaskFrame closes (it may in fact already be closed, which is why
+    // a LayerManagerProxy must be passed in too). But I have to
+    // remember to null it when I close. [Jon Aquino]
+    Assert.isTrue(layerManagerProxy.getLayerManager() != null);
+    layerManager = layerManagerProxy.getLayerManager();
+    // I cannot see any reason to add this listener [mmichaud 2007-06-03]
+    // See also WorkbenchFrame
+    /*
+     * addInternalFrameListener(new InternalFrameAdapter() { public void
+     * internalFrameClosed(InternalFrameEvent e) { layerManager = new
+     * LayerManager(); } });
+     */
+    attributeTab = new AttributeTab(model, workbenchContext, taskFrame, this,
+      false);
+    addInternalFrameListener(new InternalFrameAdapter() {
+      public void internalFrameOpened(InternalFrameEvent e) {
+        attributeTab.getToolBar().updateEnabledState();
+      }
+    });
+    workbenchFrame = workbenchContext.getWorkbench().getFrame();
+    this.setResizable(true);
+    this.setClosable(true);
+    this.setMaximizable(true);
+    this.setIconifiable(true);
+    // This size is chosen so that when the user hits the Info tool, the window
+    // fits between the lower edge of the TaskFrame and the lower edge of the
+    // WorkbenchFrame. See the call to #setSize in WorkbenchFrame. [Jon Aquino]
+    // Make sure there's a little space for a custom FeatureTextWriter
+    // [Jon Aquino 12/31/2003]
+    this.setSize(550, 185);
+    try {
+      jbInit();
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    tabbedPane.addTab("", IconLoader.icon("Table.gif"), attributeTab,
+      "Table View");
+    tabbedPane.addTab("", IconLoader.icon("Paper.gif"), geometryInfoTab,
+      "HTML View");
+
+    for (Iterator factories = 
ComponentFactoryRegistry.getComponentFactories(workbenchContext, 
CLASSIFICATION).iterator(); factories.hasNext();) {
+      ComponentFactory factory = (ComponentFactory)factories.next();
+      JComponent component = factory.createComponent();
+      if (component instanceof IconComponent) {
+        IconComponent iconComponent = (IconComponent)component;
+        tabbedPane.addTab(component.getName(), iconComponent.getIcon(), 
component,
+          component.getToolTipText());
+      } else {
+        tabbedPane.addTab(component.getName(), null, component,
+          component.getToolTipText());
+      }
+      if (component instanceof InfoModelAware) {
+        InfoModelAware tabAware = (InfoModelAware)component;
+        tabAware.setInfoModel(model);
+      }
+    }
+    updateTitle(taskFrame.getTask().getName());
+    taskFrame.getTask().add(new Task.NameListener() {
+      public void taskNameChanged(String name) {
         updateTitle(taskFrame.getTask().getName());
-        taskFrame.getTask().add(new Task.NameListener() {
-            public void taskNameChanged(String name) {
-                updateTitle(taskFrame.getTask().getName());
-            }
-        });
-        addInternalFrameListener(new InternalFrameAdapter() {
-            public void internalFrameClosed(InternalFrameEvent e) {
-                //Assume that there are no other views on the model
-                model.dispose();
-            }
-        });
-    }
-    public JPanel getAttributeTab() {
-        return attributeTab;
-    }
-    public JPanel getGeometryTab() {
-        return geometryInfoTab;
-    }
-    public void setSelectedTab(JPanel tab) {
-        tabbedPane.setSelectedComponent(tab);
-    }
-    public static String title(String taskName) {
-        return I18N.get("ui.InfoFrame.feature-info")+": " + taskName;
-    }
-    private void updateTitle(String taskName) {
-        setTitle(title(taskName));
-    }
-    public InfoModel getModel() {
-        return model;
-    }
-    private void jbInit() throws Exception {
-        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
-        // With the DefaultInternalFrameCloser of WorkbenchFrame,
-        // I think this code is no more necessary [mmichaud 2007-06-03]
-        /*addInternalFrameListener(new InternalFrameAdapter() {
-            public void internalFrameClosing(InternalFrameEvent e) {
-                //Regardless of the defaultCloseOperation, this InfoFrame 
should be
-                //removed from the WorkbenchFrame when the user hits X so it 
won't
-                //appear on the Window list. [Jon Aquino]
-                try {
-                    workbenchFrame.removeInternalFrame(InfoFrame.this);
-                } catch (Exception x) {
-                    workbenchFrame.handleThrowable(x);
-                }
-            }
-        });*/
-        this.setTitle(I18N.get("ui.InfoFrame.feature-info"));
-        this.getContentPane().setLayout(borderLayout1);
-        tabbedPane.setTabPlacement(JTabbedPane.LEFT);
-        this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
-    }
-    public void surface() {
-        JInternalFrame activeFrame = workbenchFrame.getActiveInternalFrame();
-        if (!workbenchFrame.hasInternalFrame(this)) {
-            workbenchFrame.addInternalFrame(this, false, true);
-        }
-        if (activeFrame != null) {
-            workbenchFrame.activateFrame(activeFrame);
-        }
-        moveToFront();
-        //Move this frame to the front, but don't activate it if the TaskFrame 
is
-        //active. Otherwise the user would need to re-activate the TaskFrame 
before
-        //making another Info gesture. [Jon Aquino]
-    }
-    public SelectionManager getSelectionManager() {
-        return attributeTab.getPanel().getSelectionManager();
-    }
-    public LayerNamePanel getLayerNamePanel() {
-        return attributeTab;
-    }
-    public LayerViewPanel getLayerViewPanel() {
-        return getTaskFrame().getLayerViewPanel();
-    }
+      }
+    });
+    addInternalFrameListener(new InternalFrameAdapter() {
+      public void internalFrameClosed(InternalFrameEvent e) {
+        // Assume that there are no other views on the model
+        model.dispose();
+      }
+    });
+  }
+
+  public JPanel getAttributeTab() {
+    return attributeTab;
+  }
+
+  public JPanel getGeometryTab() {
+    return geometryInfoTab;
+  }
+
+  public void setSelectedTab(JPanel tab) {
+    tabbedPane.setSelectedComponent(tab);
+  }
+
+  public static String title(String taskName) {
+    return I18N.get("ui.InfoFrame.feature-info") + ": " + taskName;
+  }
+
+  private void updateTitle(String taskName) {
+    setTitle(title(taskName));
+  }
+
+  public InfoModel getModel() {
+    return model;
+  }
+
+  private void jbInit() throws Exception {
+    setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+    // With the DefaultInternalFrameCloser of WorkbenchFrame,
+    // I think this code is no more necessary [mmichaud 2007-06-03]
+    /*
+     * addInternalFrameListener(new InternalFrameAdapter() { public void
+     * internalFrameClosing(InternalFrameEvent e) { //Regardless of the
+     * defaultCloseOperation, this InfoFrame should be //removed from the
+     * WorkbenchFrame when the user hits X so it won't //appear on the Window
+     * list. [Jon Aquino] try {
+     * workbenchFrame.removeInternalFrame(InfoFrame.this); } catch (Exception 
x) {
+     * workbenchFrame.handleThrowable(x); } } });
+     */
+    this.setTitle(I18N.get("ui.InfoFrame.feature-info"));
+    this.getContentPane().setLayout(borderLayout1);
+    tabbedPane.setTabPlacement(JTabbedPane.LEFT);
+    this.getContentPane().add(tabbedPane, BorderLayout.CENTER);
+  }
+
+  public void surface() {
+    JInternalFrame activeFrame = workbenchFrame.getActiveInternalFrame();
+    if (!workbenchFrame.hasInternalFrame(this)) {
+      workbenchFrame.addInternalFrame(this, false, true);
+    }
+    if (activeFrame != null) {
+      workbenchFrame.activateFrame(activeFrame);
+    }
+    moveToFront();
+    // Move this frame to the front, but don't activate it if the TaskFrame is
+    // active. Otherwise the user would need to re-activate the TaskFrame 
before
+    // making another Info gesture. [Jon Aquino]
+  }
+
+  public SelectionManager getSelectionManager() {
+    return attributeTab.getPanel().getSelectionManager();
+  }
+
+  public LayerNamePanel getLayerNamePanel() {
+    return attributeTab;
+  }
+
+  public LayerViewPanel getLayerViewPanel() {
+    return getTaskFrame().getLayerViewPanel();
+  }
 }
Index: src/com/vividsolutions/jump/workbench/ui/InfoModel.java
===================================================================
RCS file: 
/cvsroot/jump-pilot/openjump/src/com/vividsolutions/jump/workbench/ui/InfoModel.java,v
retrieving revision 1.1
diff -u -r1.1 InfoModel.java
--- src/com/vividsolutions/jump/workbench/ui/InfoModel.java     16 Jun 2005 
22:11:46 -0000      1.1
+++ src/com/vividsolutions/jump/workbench/ui/InfoModel.java     11 Jun 2007 
19:26:46 -0000
@@ -92,13 +92,13 @@
 
     public void remove(Layer layer) {
         LayerTableModel layerTableModel = getTableModel(layer);
-        ((LayerTableModel) layerToTableModelMap.get(layer)).dispose();
-        layerToTableModelMap.remove(layer);
 
         for (Iterator i = listeners.iterator(); i.hasNext();) {
             InfoModelListener listener = (InfoModelListener) i.next();
             listener.layerRemoved(layerTableModel);
         }
+        ((LayerTableModel) layerToTableModelMap.get(layer)).dispose();
+        layerToTableModelMap.remove(layer);
     }
 
     public void clear() {
Index: src/com/vividsolutions/jump/feature/FeatureSchema.java
===================================================================
RCS file: 
/cvsroot/jump-pilot/openjump/src/com/vividsolutions/jump/feature/FeatureSchema.java,v
retrieving revision 1.2
diff -u -r1.2 FeatureSchema.java
--- src/com/vividsolutions/jump/feature/FeatureSchema.java      27 Jul 2005 
21:20:09 -0000      1.2
+++ src/com/vividsolutions/jump/feature/FeatureSchema.java      11 Jun 2007 
19:26:45 -0000
@@ -51,6 +51,9 @@
     private int attributeCount = 0;
     private ArrayList attributeNames = new ArrayList();
     private ArrayList attributeTypes = new ArrayList();
+    
+    /** The name of the schema. */
+    private String name;
 
     //todo Deep-copy! [Jon Aquino]
     //deep copy done 25. Juli 2005 [sstein] 
@@ -195,4 +198,22 @@
                return coordinateSystem;
        }
 
+    /**
+     * Get the name of the schema.
+     * 
+     * @return The name of the schema.
+     */
+    public String getName() {
+      return name;
+    }
+
+    /**
+     * Set the name of the schema.
+     * 
+     * @param name The name of the schema.
+     */
+    public void setName(String name) {
+      this.name = name;
+    }
+
 }
Index: src/com/vividsolutions/jump/workbench/ui/InfoModelAware.java
===================================================================
RCS file: src/com/vividsolutions/jump/workbench/ui/InfoModelAware.java
diff -N src/com/vividsolutions/jump/workbench/ui/InfoModelAware.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/com/vividsolutions/jump/workbench/ui/InfoModelAware.java        1 Jan 
1970 00:00:00 -0000
@@ -0,0 +1,6 @@
+package com.vividsolutions.jump.workbench.ui;
+
+public interface InfoModelAware {
+  InfoModel getInfoModel();
+  void setInfoModel(InfoModel model);
+}
Index: src/org/openjump/core/ui/builder/StringUiBuilder.java
===================================================================
RCS file: src/org/openjump/core/ui/builder/StringUiBuilder.java
diff -N src/org/openjump/core/ui/builder/StringUiBuilder.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/builder/StringUiBuilder.java       1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,12 @@
+package org.openjump.core.ui.builder;
+
+import com.vividsolutions.jump.workbench.ui.GUIUtil;
+
+public class StringUiBuilder extends AbstractUiBuilder {
+
+  public void appendHtml(StringBuffer s, Object object) {
+    if (object != null) {
+      s.append(GUIUtil.escapeHTML(object.toString(), false, false));
+    }
+  }
+}
Index: src/org/openjump/core/ui/component/FeatureViewPanel.java
===================================================================
RCS file: src/org/openjump/core/ui/component/FeatureViewPanel.java
diff -N src/org/openjump/core/ui/component/FeatureViewPanel.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/component/FeatureViewPanel.java    1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,100 @@
+package org.openjump.core.ui.component;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import org.openjump.core.ui.builder.FeatureUiBuilder;
+import org.openjump.core.ui.builder.UiBuilderRegistry;
+
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.workbench.WorkbenchContext;
+import com.vividsolutions.jump.workbench.model.Layer;
+import com.vividsolutions.jump.workbench.ui.GUIUtil;
+
+public class FeatureViewPanel extends JPanel {
+  private JEditorPane editorPane = new JEditorPane();
+
+  private Feature feature;
+
+  private FeatureUiBuilder renderer;
+
+  private Layer layer;
+
+  public FeatureViewPanel(WorkbenchContext context) {
+    renderer = new FeatureUiBuilder();
+    renderer.setRegistry(UiBuilderRegistry.getInstance(context));
+    jbInit();
+  }
+
+  void jbInit() {
+    JScrollPane scrollPane = new JScrollPane();
+    editorPane.setEditable(false);
+    editorPane.setText("jEditorPane1");
+    editorPane.setContentType("text/html");
+    this.setLayout(new BorderLayout());
+    this.add(scrollPane, BorderLayout.CENTER);
+    scrollPane.getViewport().add(editorPane, null);
+  }
+
+  public Color sidebarColor(Layer layer) {
+    Color basicColor = layer.getBasicStyle().isRenderingFill() ? 
layer.getBasicStyle()
+      .getFillColor()
+      : layer.getBasicStyle().getLineColor();
+    int alpha = layer.getBasicStyle().getAlpha();
+    return GUIUtil.toSimulatedTransparency(GUIUtil.alphaColor(basicColor, 
alpha));
+  }
+
+  private String toHTML(Color color) {
+    String colorString = "#";
+    colorString += pad(Integer.toHexString(color.getRed()));
+    colorString += pad(Integer.toHexString(color.getGreen()));
+    colorString += pad(Integer.toHexString(color.getBlue()));
+    return colorString;
+  }
+
+  private String pad(String s) {
+    return (s.length() == 1) ? ("0" + s) : s;
+  }
+
+  public void updateText() {
+    if (feature != null) {
+      StringBuffer s = new StringBuffer();
+      s.append("<div style=\"padding-left: 5px; background-color: "
+        + toHTML(sidebarColor(layer)) + "\">");
+      s.append("<div style=\"font-size: 14pt;font-weight:bold;padding: 1px 
2px;margin-bottom: 3px; background-color:white\">");
+      s.append(layer.getName());
+      s.append("</div>");
+      renderer.appendHtml(s, feature, false);
+      editorPane.setText(s.toString());
+      s.append("</div>");
+    } else {
+      editorPane.setText("");
+    }
+    editorPane.setCaretPosition(0);
+  }
+
+  /**
+   * @return the feature
+   */
+  public Feature getFeature() {
+    return feature;
+  }
+
+  /**
+   * @param feature the feature to set
+   */
+  public void setFeature(Feature feature) {
+    this.feature = feature;
+    updateText();
+  }
+
+  public void setLayer(Layer layer) {
+    this.layer = layer;
+
+  }
+
+}
Index: 
src/com/vividsolutions/jump/workbench/ui/swing/ComponentFactoryRegistry.java
===================================================================
RCS file: 
src/com/vividsolutions/jump/workbench/ui/swing/ComponentFactoryRegistry.java
diff -N 
src/com/vividsolutions/jump/workbench/ui/swing/ComponentFactoryRegistry.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ 
src/com/vividsolutions/jump/workbench/ui/swing/ComponentFactoryRegistry.java    
    1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,60 @@
+package com.vividsolutions.jump.workbench.ui.swing;
+
+import java.util.List;
+
+import com.vividsolutions.jump.workbench.WorkbenchContext;
+import com.vividsolutions.jump.workbench.registry.Registry;
+
+/**
+ * Utility class to manage the list of [EMAIL PROTECTED] ComponentFactory} in 
the
+ * [EMAIL PROTECTED] Registry} for the classification.
+ * 
+ * @author Paul Austin
+ * @see ComponentFactory
+ */
+public class ComponentFactoryRegistry {
+  /**
+   * Add a new component factory for the component class to the registry for 
the
+   * classification.
+   * 
+   * @param context The workbench context that contains the registry.
+   * @param classification The classification to add the factory to.
+   * @param componentClass The component class.
+   * @see WorkbenchContextComponentFactory
+   */
+  public static void addComponentFactory(WorkbenchContext context,
+    String classification, Class componentClass) {
+    WorkbenchContextComponentFactory factory = new 
WorkbenchContextComponentFactory(
+      componentClass, context);
+    addComponentFactory(context, classification, factory);
+  }
+
+  /**
+   * Add the component factory to the registry for the classification.
+   * 
+   * @param context The workbench context that contains the registry.
+   * @param classification The classification to add the factory to.
+   * @param factory The factory that creates the components.
+   */
+  public static void addComponentFactory(WorkbenchContext context,
+    String classification, ComponentFactory factory) {
+    Registry registry = context.getRegistry();
+    registry.createEntry(classification, factory);
+  }
+
+  /**
+   * Get the component factories for the classification from the registry.
+   * 
+   * @param context The workbench context that contains the registry.
+   * @param classification The classification to add the factory to.
+   * @return The list of component factories
+   * @see ComponentFactory
+   */
+  public static List getComponentFactories(WorkbenchContext context,
+    String classification) {
+    Registry registry = context.getRegistry();
+    List factories = registry.getEntries(classification);
+    return factories;
+  }
+
+}
Index: src/com/vividsolutions/jump/workbench/ui/swing/ComponentFactory.java
===================================================================
RCS file: src/com/vividsolutions/jump/workbench/ui/swing/ComponentFactory.java
diff -N src/com/vividsolutions/jump/workbench/ui/swing/ComponentFactory.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/com/vividsolutions/jump/workbench/ui/swing/ComponentFactory.java        
1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,7 @@
+package com.vividsolutions.jump.workbench.ui.swing;
+
+import javax.swing.JComponent;
+
+public interface ComponentFactory {
+  JComponent createComponent();
+}
Index: src/org/openjump/core/ui/builder/AbstractUiBuilder.java
===================================================================
RCS file: src/org/openjump/core/ui/builder/AbstractUiBuilder.java
diff -N src/org/openjump/core/ui/builder/AbstractUiBuilder.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/builder/AbstractUiBuilder.java     1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,28 @@
+package org.openjump.core.ui.builder;
+
+
+public abstract class AbstractUiBuilder implements UiBuilder {
+  private UiBuilderRegistry registry;
+
+  public String toHtml(final Object object) {
+    StringBuffer s = new StringBuffer();
+    appendHtml(s, object);
+    return s.toString();
+  }
+
+  /**
+   * @return the registry
+   */
+  public UiBuilderRegistry getRegistry() {
+    return registry;
+  }
+
+  /**
+   * @param registry the registry to set
+   */
+  public void setRegistry(UiBuilderRegistry registry) {
+    this.registry = registry;
+  }
+  
+  
+}
Index: src/org/openjump/core/ui/component/InfoModelDetailPanel.java
===================================================================
RCS file: src/org/openjump/core/ui/component/InfoModelDetailPanel.java
diff -N src/org/openjump/core/ui/component/InfoModelDetailPanel.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/component/InfoModelDetailPanel.java        1 Jan 
1970 00:00:00 -0000
@@ -0,0 +1,244 @@
+package org.openjump.core.ui.component;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Iterator;
+
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.JTextPane;
+
+import org.openjump.core.ui.plugin.enablecheck.BooleanPropertyEnableCheck;
+
+import com.vividsolutions.jump.I18N;
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.workbench.WorkbenchContext;
+import com.vividsolutions.jump.workbench.model.Layer;
+import com.vividsolutions.jump.workbench.ui.EnableableToolBar;
+import com.vividsolutions.jump.workbench.ui.InfoModel;
+import com.vividsolutions.jump.workbench.ui.InfoModelAware;
+import com.vividsolutions.jump.workbench.ui.InfoModelListener;
+import com.vividsolutions.jump.workbench.ui.LayerTableModel;
+import com.vividsolutions.jump.workbench.ui.images.IconLoader;
+import com.vividsolutions.jump.workbench.ui.swing.IconComponent;
+
+public class InfoModelDetailPanel extends JPanel implements InfoModelAware,
+  InfoModelListener, IconComponent {
+  private Icon icon = IconLoader.icon("Attribute.gif");
+
+  private InfoModel infoModel;
+
+  private FeatureViewPanel featureDetailPanel;
+
+  private EnableableToolBar toolBar = new EnableableToolBar();
+
+  private int layerFeatureIndex = 0;
+
+  private int featureIndex = 0;
+
+  private int featureCount = 0;
+
+  private Feature currentFeature;
+
+  private JTextField featureIndexField;
+
+  private JTextPane featureCountPane;
+
+  public InfoModelDetailPanel(WorkbenchContext context) {
+    setToolTipText("Detail View");
+    featureDetailPanel = new FeatureViewPanel(context);
+
+    try {
+      jbInit();
+    } catch (Exception ex) {
+      ex.printStackTrace();
+    }
+    toolBar.add(new JButton(), I18N.get("ui.GeometryInfoTab.attributes"),
+      IconLoader.icon("Start.gif"), new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          firstFeature();
+        }
+      }, new BooleanPropertyEnableCheck(this, "isAtFirst", false));
+    toolBar.add(new JButton(), I18N.get("ui.GeometryInfoTab.attributes"),
+      IconLoader.icon("Prev.gif"), new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          previousFeature();
+        }
+      }, new BooleanPropertyEnableCheck(this, "isAtFirst", false));
+    toolBar.add(new JButton(), I18N.get("ui.GeometryInfoTab.attributes"),
+      IconLoader.icon("Next.gif"), new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          nextFeature();
+        }
+      }, new BooleanPropertyEnableCheck(this, "isAtLast", false));
+    toolBar.add(new JButton(), I18N.get("ui.GeometryInfoTab.attributes"),
+      IconLoader.icon("End.gif"), new ActionListener() {
+        public void actionPerformed(ActionEvent e) {
+          lastFeature();
+        }
+      }, new BooleanPropertyEnableCheck(this, "isAtLast", false));
+    featureIndexField = new JTextField();
+    toolBar.add(featureIndexField);
+    featureCountPane = new JTextPane();
+    toolBar.add(featureCountPane);
+
+  }
+
+  public boolean isAtFirst() {
+    return featureIndex == 0;
+  }
+
+  public boolean isAtLast() {
+    return featureIndex >= featureCount - 1;
+  }
+
+  protected void firstFeature() {
+    setSelectedFeature(0);
+  }
+
+  protected void previousFeature() {
+    setSelectedFeature(featureIndex - 1);
+  }
+
+  protected void nextFeature() {
+    setSelectedFeature(featureIndex + 1);
+  }
+
+  protected void lastFeature() {
+    setSelectedFeature(featureCount - 1);
+  }
+
+  private void updateText() {
+    featureDetailPanel.updateText();
+  }
+
+  void jbInit() throws Exception {
+    setLayout(new BorderLayout());
+    add(featureDetailPanel, BorderLayout.CENTER);
+    add(toolBar, BorderLayout.NORTH);
+  }
+
+  public InfoModel getInfoModel() {
+    return infoModel;
+  }
+
+  public void setInfoModel(InfoModel model) {
+    this.infoModel = model;
+    model.addListener(this);
+    for (Iterator layers = infoModel.getLayers().iterator(); 
layers.hasNext();) {
+      Layer layer = (Layer)layers.next();
+      LayerTableModel layerModel = infoModel.getTableModel(layer);
+      layerAdded(layerModel);
+    }
+    firstFeature();
+  }
+
+  public void layerAdded(LayerTableModel layerTableModel) {
+    int rowCount = layerTableModel.getRowCount();
+    featureCount += rowCount;
+    if (featureCount == rowCount) {
+      setSelectedFeature(0);
+    } else {
+      update();
+    }
+  }
+
+  public void layerRemoved(LayerTableModel layerTableModel) {
+    int rowCount = layerTableModel.getRowCount();
+    int layerIndex = 0;
+    boolean finish = false;
+    featureCount -= rowCount;
+    Iterator layers = infoModel.getLayers().iterator();
+    while (layers.hasNext() && !finish) {
+      Layer layer = (Layer)layers.next();
+      LayerTableModel layerModel = infoModel.getTableModel(layer);
+      if (featureIndex < layerIndex) {
+        finish = true;
+      } else if (layerModel == layerTableModel) {
+        featureIndex -= rowCount - 1;
+        if (featureIndex < layerIndex) {
+          featureIndex = layerIndex;
+        }
+        if (featureIndex >= featureCount) {
+          featureIndex = 0;
+        }
+        layerFeatureIndex = 0;
+        finish = true;
+      } else {
+        layerIndex += layerModel.getRowCount();
+      }
+    }
+    setSelectedFeature(featureIndex);
+  }
+
+  private void setSelectedFeature(final int i) {
+    boolean found = false;
+    if (featureCount != 0) {
+      if (i >= featureCount) {
+        if (featureCount == 0) {
+          featureIndex = 0;
+        } else {
+          featureIndex = featureCount - 1;
+        }
+      } else if (i <= 0) {
+        featureIndex = 0;
+      } else {
+        featureIndex = i;
+      }
+      int layerIndex = 0;
+      Iterator layers = infoModel.getLayers().iterator();
+      while (layers.hasNext() && !found) {
+        Layer layer = (Layer)layers.next();
+        LayerTableModel layerModel = infoModel.getTableModel(layer);
+
+        int layerRows = layerModel.getRowCount();
+        if (featureIndex < layerIndex + layerRows) {
+          layerFeatureIndex = featureIndex - layerIndex;
+          currentFeature = layerModel.getFeature(layerFeatureIndex);
+          featureDetailPanel.setLayer(layer);
+          featureDetailPanel.setFeature(currentFeature);
+          update();
+          found = true;
+        } else {
+          layerIndex += layerRows;
+        }
+      }
+    }
+    if (!found) {
+      currentFeature = null;
+      featureDetailPanel.setFeature(null);
+      update();
+    }
+  }
+
+  private void update() {
+    if (currentFeature != null) {
+      featureDetailPanel.setFeature(currentFeature);
+      featureIndexField.setText(String.valueOf(featureIndex + 1));
+      featureCountPane.setText(" of " + featureCount);
+    } else {
+      featureDetailPanel.setFeature(null);
+      featureIndexField.setText("0");
+      featureCountPane.setText(" of 0");
+    }
+    toolBar.updateEnabledState();
+  }
+
+  /**
+   * @return the icon
+   */
+  public Icon getIcon() {
+    return icon;
+  }
+
+  /**
+   * @param icon the icon to set
+   */
+  public void setIcon(Icon icon) {
+    this.icon = icon;
+  }
+
+}
Index: 
src/com/vividsolutions/jump/workbench/ui/swing/WorkbenchContextComponentFactory.java
===================================================================
RCS file: 
src/com/vividsolutions/jump/workbench/ui/swing/WorkbenchContextComponentFactory.java
diff -N 
src/com/vividsolutions/jump/workbench/ui/swing/WorkbenchContextComponentFactory.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ 
src/com/vividsolutions/jump/workbench/ui/swing/WorkbenchContextComponentFactory.java
        1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,44 @@
+package com.vividsolutions.jump.workbench.ui.swing;
+
+import java.lang.reflect.Constructor;
+
+import javax.swing.JComponent;
+
+import com.vividsolutions.jump.workbench.WorkbenchContext;
+
+public class WorkbenchContextComponentFactory implements ComponentFactory {
+  private Class componentClass;
+
+  private WorkbenchContext context;
+
+  private Constructor constructor;
+
+  public WorkbenchContextComponentFactory(Class componentClass,
+    WorkbenchContext context) {
+    this.componentClass = componentClass;
+    this.context = context;
+    try {
+      constructor = componentClass.getConstructor(new Class[] {
+        WorkbenchContext.class
+      });
+    } catch (Throwable e) {
+      throw new RuntimeException(componentClass
+        + " must have a constructor with argument " + WorkbenchContext.class, 
e);
+    }
+  }
+
+  public JComponent createComponent() {
+    try {
+      return (JComponent)constructor.newInstance(new Object[] {
+        context
+      });
+    } catch (RuntimeException e) {
+      throw e;
+    } catch (Error e) {
+      throw e;
+    } catch (Exception e) {
+      throw new RuntimeException("Unable to construct " + componentClass, e);
+    }
+  }
+
+}
Index: src/org/openjump/core/ui/builder/UiBuilder.java
===================================================================
RCS file: src/org/openjump/core/ui/builder/UiBuilder.java
diff -N src/org/openjump/core/ui/builder/UiBuilder.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/builder/UiBuilder.java     1 Jan 1970 00:00:00 
-0000
@@ -0,0 +1,10 @@
+package org.openjump.core.ui.builder;
+
+public interface UiBuilder {
+   void appendHtml(StringBuffer s, Object object);
+   String toHtml(Object object);
+  
+   UiBuilderRegistry getRegistry();
+   void setRegistry(UiBuilderRegistry registry);
+  
+}
Index: src/org/openjump/core/ui/builder/UiBuilderRegistry.java
===================================================================
RCS file: src/org/openjump/core/ui/builder/UiBuilderRegistry.java
diff -N src/org/openjump/core/ui/builder/UiBuilderRegistry.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/builder/UiBuilderRegistry.java     1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,71 @@
+package org.openjump.core.ui.builder;
+
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.vividsolutions.jump.util.Blackboard;
+import com.vividsolutions.jump.workbench.WorkbenchContext;
+
+public class UiBuilderRegistry {
+  private static final String BLACKBOARD_KEY = 
UiBuilderRegistry.class.getName();
+
+  private static final StringUiBuilder DEFAULT_RENDERER = new 
StringUiBuilder();
+
+  private Map renderers = new LinkedHashMap();
+
+  public static UiBuilderRegistry getInstance(WorkbenchContext context) {
+    Blackboard blackboard = context.getBlackboard();
+    UiBuilderRegistry repository = 
(UiBuilderRegistry)blackboard.get(BLACKBOARD_KEY);
+    if (repository == null) {
+      repository = new UiBuilderRegistry();
+      blackboard.put(BLACKBOARD_KEY, repository);
+    }
+    return repository;
+  }
+
+  public void addRenderer(Class clazz, UiBuilder builder) {
+    renderers.put(clazz, builder);
+    builder.setRegistry(this);
+  }
+
+  public UiBuilder getRenderer(Class clazz) {
+    UiBuilder renderer = getRendererPrivate(clazz);
+    if (renderer != null) {
+      return renderer;
+    } else {
+      return DEFAULT_RENDERER;
+    }
+  }
+
+  private UiBuilder getRendererPrivate(Class clazz) {
+    if (clazz != null) {
+      UiBuilder renderer = (UiBuilder)renderers.get(clazz);
+      if (renderer == null) {
+        Class superClass = clazz.getSuperclass();
+        if (superClass != Object.class) {
+          renderer = getRendererPrivate(superClass);
+        }
+        Class[] interfaces = clazz.getInterfaces();
+        for (int i = 0; i < interfaces.length && renderer == null; i++) {
+          Class interfaceClass = interfaces[i];
+          renderer = getRendererPrivate(interfaceClass);
+        }
+      }
+      return renderer;
+    } else {
+      return null;
+    }
+  }
+
+  public String toHtml(Object object) {
+    if (object != null) {
+      Class objectClass = object.getClass();
+      UiBuilder renderer = getRenderer(objectClass);
+      String html = renderer.toHtml(object);
+      return html;
+    } else {
+      return null;
+    }
+  }
+}
Index: 
src/org/openjump/core/ui/plugin/enablecheck/BooleanPropertyEnableCheck.java
===================================================================
RCS file: 
src/org/openjump/core/ui/plugin/enablecheck/BooleanPropertyEnableCheck.java
diff -N 
src/org/openjump/core/ui/plugin/enablecheck/BooleanPropertyEnableCheck.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/plugin/enablecheck/BooleanPropertyEnableCheck.java 
1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,61 @@
+package org.openjump.core.ui.plugin.enablecheck;
+
+import java.lang.reflect.Method;
+
+import javax.swing.JComponent;
+
+import com.vividsolutions.jump.workbench.plugin.EnableCheck;
+
+public class BooleanPropertyEnableCheck implements EnableCheck {
+  String disabledMessage = "";
+
+  /** The check method. */
+  private Method method;
+
+  /** The object to invoke the method on.*/
+  private Object object;
+
+  /** The expected value to be returned for the check to be enabled. */
+  private boolean expectedValue;
+  
+  /**
+   * 
+   * @param object The object to invoke the method on.
+   * @param checkMethodName The name of the check method which returns a 
boolean value.
+   */
+  public BooleanPropertyEnableCheck(final Object object, final String 
checkMethodName) {
+    this(object, checkMethodName, true);
+  }
+
+  /**
+   * 
+   * @param object The object to invoke the method on.
+   * @param checkMethodName The name of the check method which returns a 
boolean value.
+   * @param expectedValue The expected value to be returned for the check to 
be enabled.
+   */
+  public BooleanPropertyEnableCheck(final Object object, final String 
checkMethodName, final boolean expectedValue) {
+    Class clazz = object.getClass();
+    this.object = object;
+    this.expectedValue = expectedValue;
+    try {
+      method = clazz.getMethod(checkMethodName, new Class[] {});
+    } catch (Throwable e) {
+      throw new IllegalArgumentException("Unable to get check method "
+        + checkMethodName + " on " + clazz);
+    }
+  }
+
+  public String check(JComponent component) {
+    try {
+      Boolean result = (Boolean)method.invoke(object, new Object[0]);
+      if (result.booleanValue() == expectedValue) {
+        return null;
+      } else {
+        return disabledMessage;
+      }
+    } catch (Throwable e) {
+      e.printStackTrace();
+      return e.getMessage();
+    }
+  }
+}
Index: src/org/openjump/core/ui/builder/DateTimeUiBuilder.java
===================================================================
RCS file: src/org/openjump/core/ui/builder/DateTimeUiBuilder.java
diff -N src/org/openjump/core/ui/builder/DateTimeUiBuilder.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/builder/DateTimeUiBuilder.java     1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,23 @@
+package org.openjump.core.ui.builder;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.vividsolutions.jump.workbench.ui.GUIUtil;
+
+public class DateTimeUiBuilder extends AbstractUiBuilder {
+
+  public void appendHtml(StringBuffer s, Object object) {
+    if (object instanceof Date) {
+      Date date = (Date)object;
+      DateFormat format = getDateFormat();
+      s.append(GUIUtil.escapeHTML(format.format(date), false, false));
+    }
+      
+  }
+
+  protected DateFormat getDateFormat() {
+    return SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.MEDIUM, 
SimpleDateFormat.MEDIUM);
+  }
+}
Index: src/org/openjump/core/ui/builder/DateUIBuilder.java
===================================================================
RCS file: src/org/openjump/core/ui/builder/DateUIBuilder.java
diff -N src/org/openjump/core/ui/builder/DateUIBuilder.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/builder/DateUIBuilder.java 1 Jan 1970 00:00:00 
-0000
@@ -0,0 +1,14 @@
+package org.openjump.core.ui.builder;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import com.vividsolutions.jump.workbench.ui.GUIUtil;
+
+public class DateUIBuilder extends DateTimeUiBuilder {
+
+  protected DateFormat getDateFormat() {
+    return SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM);
+  }
+}
Index: src/org/openjump/core/ui/builder/FeatureUiBuilder.java
===================================================================
RCS file: src/org/openjump/core/ui/builder/FeatureUiBuilder.java
diff -N src/org/openjump/core/ui/builder/FeatureUiBuilder.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/org/openjump/core/ui/builder/FeatureUiBuilder.java      1 Jan 1970 
00:00:00 -0000
@@ -0,0 +1,114 @@
+package org.openjump.core.ui.builder;
+
+import com.vividsolutions.jts.geom.Geometry;
+import com.vividsolutions.jump.feature.Feature;
+import com.vividsolutions.jump.feature.FeatureSchema;
+import com.vividsolutions.jump.workbench.ui.GUIUtil;
+
+public class FeatureUiBuilder extends AbstractUiBuilder {
+  private static final String TABLE_STYLE = "width: 100%;font-family: Tahoma, 
Arial, sans-serif;font-size: 10pt;padding: 0px;border-collapse: collapse;";
+
+  private static final String TABLE_ATTRS = "style=\"" + TABLE_STYLE + "\" ";
+
+  private static final String TABLE_HEADING_STYLE = "vertical-align: 
top;text-align: left;padding: 1px 2px;width: auto;";
+
+  private static final String TABLE_HEADING_ODD_ATTRS = 
"style=\"background-color: #FFFFFF;"
+    + TABLE_HEADING_STYLE + "\"";
+
+  private static final String TABLE_HEADING_EVEN_ATTRS = 
"style=\"background-color: #E6E6E6;"
+    + TABLE_HEADING_STYLE + "\"";
+
+  private static final String TABLE_CELL_STYLE = "vertical-align: 
top;text-align: left;border-left: 1px solid black;padding: 0px;";
+
+  private static final String TABLE_CELL_ODD_ATTRS = 
"style=\"background-color: #FFFFFF;"
+    + TABLE_CELL_STYLE + "\"";
+
+  private static final String TABLE_CELL_EVEN_ATTRS = 
"style=\"background-color: #E6E6E6;"
+    + TABLE_CELL_STYLE + "\"";
+
+  private static final String SPAN_ATTRS = "style=\"padding: 1px 2px;\"";
+
+  private static final String TYPE_TITLE_ATTRS = "style=\"font-size: 
12pt;font-weight:bold;padding: 1px 2px;background-color:#FFFFCC\"";
+
+  public FeatureUiBuilder() {
+  }
+
+  public void appendHtml(StringBuffer s, Object object) {
+    appendHtml(s, object, true);
+  }
+
+  public void appendHtml(StringBuffer s, Object object, boolean nested) {
+    if (object instanceof Feature) {
+      Feature feature = (Feature)object;
+      FeatureSchema schema = feature.getSchema();
+      if (nested) {
+        String schemaName = schema.getName();
+        if (schemaName != null) {
+          appendValue(s, schemaName, TYPE_TITLE_ATTRS);
+        }
+      }
+      s.append("<table ").append(TABLE_ATTRS).append(">\n");
+      boolean odd = true;
+      if (!nested) {
+        appendRow(s, true, "FID", String.valueOf(feature.getID()));
+        odd = !odd;
+      }
+      for (int i = 0; i < schema.getAttributeCount(); i++) {
+        Object value = feature.getAttribute(i);
+        if (value instanceof Geometry) {
+          continue;
+        }
+        String name = schema.getAttributeName(i);
+        appendRow(s, odd, name, value);
+        odd = !odd;
+      }
+      s.append("</table>\n");
+    }
+  }
+
+  private void appendRow(StringBuffer s, boolean odd, String name, Object 
value) {
+    if (odd) {
+      s.append("<tr valign=\"top\" class=\"odd\"><th ").append(
+        TABLE_HEADING_ODD_ATTRS).append(">");
+    } else {
+      s.append("<tr valign=\"top\" class=\"even\"><th ").append(
+        TABLE_HEADING_EVEN_ATTRS).append(">");
+    }
+    appendEscaped(s, name);
+    s.append("</th>\n");
+    if (odd) {
+      s.append("<td ").append(TABLE_CELL_ODD_ATTRS).append(">");
+    } else {
+      s.append("<td ").append(TABLE_CELL_EVEN_ATTRS).append(">");
+    }
+    appendValue(s, value);
+    s.append("</td></tr>\n");
+  }
+
+  private void appendValue(StringBuffer s, Object value) {
+    String style = SPAN_ATTRS;
+    s.append("<div>");
+    if (value == null) {
+      s.append("-");
+    } else {
+      String string = getRegistry().toHtml(value);
+      s.append(string);
+    }
+    s.append("</div>");
+  }
+
+  private void appendValue(StringBuffer s, String value, String style) {
+    s.append("<div ").append(style).append(">");
+    if (value == null) {
+      s.append("-");
+    } else {
+      appendEscaped(s, value);
+    }
+    s.append("</div>");
+  }
+
+  private void appendEscaped(StringBuffer s, String value) {
+    s.append(GUIUtil.escapeHTML(value, false, false));
+  }
+
+}
Index: src/com/vividsolutions/jump/workbench/ui/swing/IconComponent.java
===================================================================
RCS file: src/com/vividsolutions/jump/workbench/ui/swing/IconComponent.java
diff -N src/com/vividsolutions/jump/workbench/ui/swing/IconComponent.java
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ src/com/vividsolutions/jump/workbench/ui/swing/IconComponent.java   1 Jan 
1970 00:00:00 -0000
@@ -0,0 +1,10 @@
+package com.vividsolutions.jump.workbench.ui.swing;
+
+import javax.swing.Icon;
+
+public interface IconComponent  {
+  public Icon getIcon();
+
+  public void setIcon(Icon icon);
+
+}
-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Jump-pilot-devel mailing list
Jump-pilot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel

Reply via email to