=== modified file 'rules/annotations/ocx_conversion.rules'
--- rules/annotations/ocx_conversion.rules	2020-05-20 21:37:20 +0000
+++ rules/annotations/ocx_conversion.rules	2020-05-27 22:23:56 +0000
@@ -354,7 +354,7 @@
          <action>mapRef.put("key", "NodeKey")</action>
          <action>mapRef.put("index", "TreeNodeIndex")</action>
          <action>mapRef.put("text", "NodeText")</action>
-         <action>mapRef.put("selectedimage", "ExpandNodeIcon")</action>
+         <action>mapRef.put("selectedimage", "SelectedNodeIcon")</action>
          <action>mapRef.put("image", "NodeIcon")</action>
          <action>aliasMap.put("COM.GOLDENCODE.P2J.UI.TREENODEFACE", mapRef)</action>
       </rule>

=== modified file 'rules/convert/builtin_functions.rules'
--- rules/convert/builtin_functions.rules	2020-05-27 16:27:27 +0000
+++ rules/convert/builtin_functions.rules	2020-05-28 13:40:00 +0000
@@ -624,7 +624,7 @@
          </rule>
          
          <rule>ftype == prog.kw_load_pic
-            <action>methodText = "com.goldencode.p2j.ui.ocx.ListImage.newInstance"</action>
+            <action>methodText = "com.goldencode.p2j.ui.ocx.PictureObject.loadPicture"</action>
             <action>methodType = java.static_method_call</action>
          </rule>
          

=== modified file 'src/com/goldencode/p2j/ui/TreeNodeCollection.java'
--- src/com/goldencode/p2j/ui/TreeNodeCollection.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/TreeNodeCollection.java	2020-05-28 15:31:31 +0000
@@ -79,18 +79,6 @@
         Iterable<T>,
         RemoveNode
 {
-   /** The index out of bounds error message reason */
-   String INDEX_OUT_OF_BOUNDS = "Index out of bounds";
-   
-   /** The invalid key error message reason */
-   String INVALID_KEY = "Invalid key";
-   
-   /** Key is not unique in collection error message reason */
-   String KEY_NOT_UNIQUE = "Key is not unique in collection";
-   
-   /** The type mismatch the error message reason */
-   String TYPE_MISMATCH = "Type mismatch.";
-   
    /**
     * Creates a new node and inserts it as the first item in the collection.
     *
@@ -218,8 +206,8 @@
     *           The label of the new node
     * @param    nodeIcon
     *           The node icon 1-based index within its tree images list
-    * @param    expandNodeIcon
-    *           The expand node icon 1-based index within its tree images list
+    * @param    selectedNodeIcon
+    *           The node icon 1-based index within its tree images list
     * 
     * @return   The handle to the new node.
     */
@@ -229,7 +217,7 @@
               character key,
               character text,
               NumberType nodeIcon,
-              NumberType expandNodeIcon);
+              NumberType selectedNodeIcon);
 
    /**
     * Adds a new tree node as a last node relative to the root tree node. This node has a default

=== modified file 'src/com/goldencode/p2j/ui/TreeNodeCollectionResource.java'
--- src/com/goldencode/p2j/ui/TreeNodeCollectionResource.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/TreeNodeCollectionResource.java	2020-05-28 16:54:19 +0000
@@ -75,6 +75,8 @@
 
 import com.goldencode.p2j.util.*;
 
+import static com.goldencode.p2j.util.ErrorManager.ErrorReasons.*;
+
 import java.util.*;
 
 /**
@@ -336,7 +338,8 @@
       if (nodeIndex == null || nodeIndex.isUnknown())
       {
          // throw Invalid key 4GL error
-         ErrorManager.throwErrorWhileProcessingComponentProperty("Item", INVALID_KEY);
+         ErrorManager.throwErrorWhileProcessingComponentProperty("Item",
+                                                                 INVALID_KEY.getReason());
          
          return new handle();
       }
@@ -348,7 +351,8 @@
       if (node == null)
       {
          // throw Index out of bounds 4GL error
-         ErrorManager.throwErrorWhileProcessingComponentProperty("Item", INDEX_OUT_OF_BOUNDS);
+         ErrorManager.throwErrorWhileProcessingComponentProperty("Item",
+                                                                 INDEX_OUT_OF_BOUNDS.getReason());
          
          return new handle();
       }
@@ -368,7 +372,8 @@
       if (nodeIndex == null || nodeIndex.isUnknown())
       {
          // throw Invalid key 4GL error
-         ErrorManager.throwErrorWhileProcessingComponentProperty("remove", INVALID_KEY);
+         ErrorManager.throwErrorWhileProcessingComponentProperty("remove",
+                                                                 INVALID_KEY.getReason());
          
          return;
       }
@@ -380,7 +385,8 @@
       if (node == null)
       {
          // throw Index out of bounds 4GL error
-         ErrorManager.throwErrorWhileProcessingComponentProperty("remove", INDEX_OUT_OF_BOUNDS);
+         ErrorManager.throwErrorWhileProcessingComponentProperty("remove",
+                                                                 INDEX_OUT_OF_BOUNDS.getReason());
          
          return;
       }
@@ -480,7 +486,8 @@
          if (relative == null)
          {
             // throw Index out of bound error.
-            ErrorManager.throwErrorWhileProcessingComponentProperty("ADD", INDEX_OUT_OF_BOUNDS);
+            ErrorManager.throwErrorWhileProcessingComponentProperty("ADD",
+                                                                    INDEX_OUT_OF_BOUNDS.getReason());
             
             return new handle();
          }
@@ -492,7 +499,7 @@
       
       if (key == null || key.isUnknown())
       {
-         ErrorManager.throwErrorWhileProcessingComponentProperty("ADD", INVALID_KEY);
+         ErrorManager.throwErrorWhileProcessingComponentProperty("ADD", INVALID_KEY.getReason());
          
          return new handle();
       }
@@ -501,7 +508,7 @@
       
       if (treeNode != null)
       {
-         ErrorManager.throwErrorWhileProcessingComponentProperty("ADD", KEY_NOT_UNIQUE);
+         ErrorManager.throwErrorWhileProcessingComponentProperty("ADD", KEY_NOT_UNIQUE.getReason());
          
          return new handle();
       }
@@ -529,7 +536,7 @@
     *           The label of the new node
     * @param    nodeIcon
     *           The node icon index within its tree images list, 1-based index
-    * @param    expandNodeIcon
+    * @param    selectedNodeIcon
     *           The expand node icon index within its tree images list, 1-based index
     * 
     * @return   The handle to the new node.
@@ -540,7 +547,7 @@
                      character key,
                      character text,
                      NumberType nodeIcon,
-                     NumberType expandNodeIcon)
+                     NumberType selectedNodeIcon)
    {
       handle hNode = add(anchorIndex, relationType, key, text);
       
@@ -548,17 +555,13 @@
       {
          TreeNodeFace node = hNode.unwrapTreeNodeFace();
          
-         integer nodeIconCopy = new integer(nodeIcon);
-         
-         nodeIconCopy.decrement();
-         
-         node.setNodeIcon(nodeIconCopy);
-         
-         integer expandNodeIconCopy = new integer(expandNodeIcon);
-         
-         expandNodeIconCopy.decrement();
-         
-         node.setExpandNodeIcon(expandNodeIconCopy);
+         node.setNodeIcon(nodeIcon);
+         
+         node.setExpandNodeIcon(nodeIcon);
+         
+         node.setSelectedNodeIcon(selectedNodeIcon);
+         
+         tree.pushNodes();
       }
       
       return hNode;

=== modified file 'src/com/goldencode/p2j/ui/TreeNodeEntry.java'
--- src/com/goldencode/p2j/ui/TreeNodeEntry.java	2019-11-27 09:16:30 +0000
+++ src/com/goldencode/p2j/ui/TreeNodeEntry.java	2020-05-26 20:10:27 +0000
@@ -92,6 +92,9 @@
 
    /** Icon image id */
    public int iconId = -1;
+   
+   /** The icon image id for the selected node */
+   public int selectedIconId = -1;
 
    /** Leaf or non-leaf node flag */
    public boolean hasChildren;

=== modified file 'src/com/goldencode/p2j/ui/TreeNodeFace.java'
--- src/com/goldencode/p2j/ui/TreeNodeFace.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/TreeNodeFace.java	2020-05-28 15:44:14 +0000
@@ -186,7 +186,8 @@
     * Setter for the NODE-ICON attribute. The attribute holds the id of the image displayed as the
     * leaf-node or collapsed icon, depending whether this is a leaf-node or not. The image must be first
     * created with the call to {@link TreeView#createImage(character)} or built-in image ids can
-    * be used. For more details see {@link TreeView#createSubNode}.
+    * be used. For more details see {@link TreeView#createSubNode}. If ImageList is used by
+    * this node tree, then imageId means 1-based index of this image within its image list.
     *
     * @param   imageId
     *          the attribute value.
@@ -207,6 +208,35 @@
    void setNodeIcon(BaseDataType imageId);
 
    /**
+    * Getter for the SelectedImage attribute. The attribute holds the id of the image displayed when
+    * this node is selected.
+    *
+    * @return   The attribute value.
+    */
+   @LegacyAttribute(name = "SelectedImage")
+   integer getSelectedNodeIcon();
+
+   /**
+    * Setter for the SelectedImage attribute. The attribute holds the id of the image displayed when
+    * this node is selected.
+    *
+    * @param    imageId
+    *           The attribute value.
+    */
+   @LegacyAttribute(name = "SelectedImage", setter = true)
+   void setSelectedNodeIcon(NumberType imageId);
+
+   /**
+    * Setter for the SelectedImage attribute. The attribute holds the id of the image displayed when
+    * this node is selected.
+    *
+    * @param    imageId
+    *           The attribute value.
+    */
+   @LegacyAttribute(name = "SelectedImage", setter = true)
+   void setSelectedNodeIcon(BaseDataType imageId);
+
+   /**
     * Getter for the EXPAND-NODE-ICON attribute. The attribute holds the id of the image displayed as the
     * expanded icon.
     *

=== modified file 'src/com/goldencode/p2j/ui/TreeNodeResource.java'
--- src/com/goldencode/p2j/ui/TreeNodeResource.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/TreeNodeResource.java	2020-05-28 21:03:40 +0000
@@ -77,6 +77,8 @@
 
 import com.goldencode.p2j.util.*;
 
+import static com.goldencode.p2j.util.ErrorManager.ErrorReasons.*;
+
 /**
  * Tree Node resource implementation base class.
  */
@@ -102,6 +104,9 @@
 
    /** Expand icon id */
    private int expandIconId = -1;
+   
+   /** The icon id for the selected node */
+   private int selectedIconId = -1;
 
    /** Flag for leaf or non-leaf node */
    private boolean hasChildren;
@@ -186,7 +191,7 @@
       if (newKey == null || newKey.isUnknown())
       {
          ErrorManager.throwErrorWhileProcessingComponentProperty("KEY",
-                                                                 TreeNodeCollection.TYPE_MISMATCH);
+                                                                 TYPE_MISMATCH.getReason());
          
          ErrorManager.throwErrorUnableToSetComHandleProperty();
          
@@ -205,7 +210,7 @@
       if (treeNode != null)
       {
          ErrorManager.throwErrorWhileProcessingComponentProperty("KEY",
-                                                                 TreeNodeCollection.KEY_NOT_UNIQUE);
+                                                                 KEY_NOT_UNIQUE.getReason());
       }
       
       String oldKeyValue = key;
@@ -388,10 +393,19 @@
    @Override
    public void setNodeIcon(NumberType imageId)
    {
+      imageId = tree.makeZeroBasedIndex(imageId);
+      
       int id = imageId != null && !imageId.isUnknown() ? tree.getImageId(imageId) : -1;
+      
       if (id != iconId)
       {
          _setNodeIcon(id);
+         
+         if (expandIconId == -1)
+         {
+            _setExpandNodeIcon(id);
+         }
+         
          tree.pushNodes();
       }
    }
@@ -416,6 +430,54 @@
    }
 
    /**
+    * Returns the global icon id for the selected tree node.
+    * 
+    * @return   The global icon id for the selected tree node
+    */
+   @Override
+   public integer getSelectedNodeIcon()
+   {
+      return selectedIconId < 0 ? new integer() : new integer(selectedIconId);
+   }
+
+   /**
+    * Sets the icon id for the selected tree node. If the images list is used, then the given imageId is
+    * 1-based index within its images list.
+    * 
+    * @param    imageId
+    *           The image id for the selected tree node
+    */
+   @Override
+   public void setSelectedNodeIcon(NumberType imageId)
+   {
+      imageId = tree.makeZeroBasedIndex(imageId);
+      
+      int id = imageId != null && !imageId.isUnknown() ? tree.getImageId(imageId) : -1;
+      if (id != selectedIconId)
+      {
+         _setSelectedNodeIcon(id);
+         tree.pushNodes();
+      }
+   }
+
+   /**
+    * Sets the icon id for the selected tree node. If the images list is used, then the given imageId is
+    * 1-based index within its images list.
+    * 
+    * @param    imageId
+    *           The image id for the selected tree node
+    */
+   @Override
+   public void setSelectedNodeIcon(BaseDataType imageId)
+   {
+      integer id = new integer();
+      
+      id.assign(imageId);
+      
+      setSelectedNodeIcon(id);
+   }
+
+   /**
     * Getter for the EXPAND-NODE-ICON attribute. The attribute holds the id of the image displayed as the
     * expanded icon.
     *
@@ -424,7 +486,7 @@
    @Override
    public integer getExpandNodeIcon()
    {
-      return iconId < 0 ? new integer() : new integer(expandIconId);
+      return expandIconId < 0 ? new integer() : new integer(expandIconId);
    }
 
    /**
@@ -439,7 +501,10 @@
    @Override
    public void setExpandNodeIcon(NumberType imageId)
    {
+      imageId = tree.makeZeroBasedIndex(imageId);
+      
       int id = imageId != null && !imageId.isUnknown() ? tree.getImageId(imageId) : -1;
+      
       if (id != expandIconId)
       {
          _setExpandNodeIcon(id);
@@ -774,6 +839,17 @@
    }
 
    /**
+    * Sets icon for the selected node.
+    *
+    * @param   imgId
+    *          Image id.
+    */
+   void _setSelectedNodeIcon(int imgId)
+   {
+      selectedIconId = imgId;
+   }
+
+   /**
     * Getter for the hadChildren flag.
     *
     * @return  value
@@ -866,5 +942,7 @@
       entry.expandIconId = expandIconId;
       entry.hasChildren = hasChildren;
       entry.tooltip = tooltip;
+      entry.selectedIconId = selectedIconId;
+      System.out.println(String.format("nodeId=%d key=%s iconId=%d expandIconId=%d selectedIconId=%d", nodeId, key, iconId, expandIconId, selectedIconId));
    }
 }

=== modified file 'src/com/goldencode/p2j/ui/TreeWidgetBase.java'
--- src/com/goldencode/p2j/ui/TreeWidgetBase.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/TreeWidgetBase.java	2020-05-28 21:12:54 +0000
@@ -1872,6 +1872,7 @@
       }
 
       ArrayList<TNodeEntry> result = new ArrayList<>();
+      
       TreeNodeCollectionResource coll = rootNode._getNodes(false);
       if (coll != null)
       {
@@ -2281,6 +2282,7 @@
 
          if (imageList != null && !imageList.isUnknown())
          {
+            
             ImageList list = (ImageList) imageList.getResource();
             integer[] imgIds = list.getImageIds();
             if (i >= 0 && i < imgIds.length)
@@ -2312,6 +2314,34 @@
    }
 
    /**
+    * Calculates 0-based index based on the given image id. 
+    *  
+    * @param    imgIndex
+    *           The image index that holds 1-based index if the images list is used. If the predefined
+    *           images are used, then imageId is 0-based.
+    * 
+    * @return   The 0-based index or unknown if null value is given.
+    */
+   integer makeZeroBasedIndex(NumberType imgIndex)
+   {
+      integer index = new integer();
+      
+      if (imgIndex == null)
+      {
+         return index;
+      }
+      
+      index.assign(imgIndex);
+      
+      if (config.imageListId != -1 && !index.isUnknown())
+      {
+         index.decrement();
+      }
+      
+      return index;
+   }
+   
+   /**
     * Push the specified widget attribute change to the client-side.
     *
     * @param   fname

=== modified file 'src/com/goldencode/p2j/ui/client/gui/theme/ClassicTheme.java'
--- src/com/goldencode/p2j/ui/client/gui/theme/ClassicTheme.java	2020-05-08 21:53:42 +0000
+++ src/com/goldencode/p2j/ui/client/gui/theme/ClassicTheme.java	2020-05-28 16:47:52 +0000
@@ -6481,31 +6481,53 @@
       int imgId = node.value.expanded && node.value.expandIconId != -1 ?
                   node.value.expandIconId :
                   node.value.iconId;
-
+      
+      if (nodeSelected)
+      {
+         if (node.value.selectedIconId != -1)
+         {
+            imgId = node.value.selectedIconId;
+         }
+         else
+         {
+            imgId = node.value.iconId;
+         }
+      }
+      
       if (imgId != -1)
       {
          ImageWrapper img = null;
-         TreeConfig.StockImage stockImage = TreeConfig.StockImage.fromId(imgId);
-         if (stockImage != null)
-         {
-            switch (stockImage)
-            {
-               case EXPANDED_NODE:
-                  img = getImage("treeview-node-expanded.png", true);
-                  break;
-               case COLLAPSED_NODE:
-                  img = getImage("treeview-node-collapsed.png", true);
-                  break;
-               case LEAF_NODE:
-                  img = getImage("treeview-node-leaf.png", true);
-                  break;
-            }
-         }
-         else
-         {
-            img = ImageHelper.getGlobalImage(imageListId != -1 ? imageListId : tree.getId().asInt(), imgId);
-         }
-
+         
+         if (imageListId != -1)
+         {
+            img = ImageHelper.getGlobalImage(imageListId, imgId);
+         }
+         
+         if (img == null)
+         {
+            TreeConfig.StockImage stockImage = TreeConfig.StockImage.fromId(imgId);
+            
+            if (stockImage != null)
+            {
+               switch (stockImage)
+               {
+                  case EXPANDED_NODE:
+                     img = getImage("treeview-node-expanded.png", true);
+                     break;
+                  case COLLAPSED_NODE:
+                     img = getImage("treeview-node-collapsed.png", true);
+                     break;
+                  case LEAF_NODE:
+                     img = getImage("treeview-node-leaf.png", true);
+                     break;
+               }
+            }
+            else
+            {
+               img = ImageHelper.getGlobalImage(tree.getId().asInt(), imgId);
+            }
+         }
+         
          if (img != null)
          {
             int w = img.getWidth();

=== modified file 'src/com/goldencode/p2j/ui/ocx/ComInterface.java'
--- src/com/goldencode/p2j/ui/ocx/ComInterface.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/ocx/ComInterface.java	2020-05-26 11:16:20 +0000
@@ -113,12 +113,7 @@
    @Override
    public boolean valid()
    {
-      if (parent != null)
-      {
-         return parent.valid();
-      }
-      
-      return false;
+      return parent != null && parent.valid();
    }
 
    /**

=== modified file 'src/com/goldencode/p2j/ui/ocx/IImage.java'
--- src/com/goldencode/p2j/ui/ocx/IImage.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/ocx/IImage.java	2020-05-26 12:49:06 +0000
@@ -152,17 +152,17 @@
    /**
     * Get the image Picture
     * 
-    * @return image bytes.
+    * @return   The picture comhandle
     */
-   public memptr getPicture();
+   public comhandle getPicture();
 
    /**
     * Set the image Picture
     * 
-    * @param   data
-    *          image bytes.
+    * @param    data
+    *           The picture comhandle
     */
-   public void setPicture(memptr data);
+   public void setPicture(comhandle data);
    
    /**
     * Get any extra data needed for your program.

=== modified file 'src/com/goldencode/p2j/ui/ocx/ListImage.java'
--- src/com/goldencode/p2j/ui/ocx/ListImage.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/ocx/ListImage.java	2020-05-27 04:58:42 +0000
@@ -80,11 +80,14 @@
 extends ComInterface
 implements IImage
 {
-   /** The image index within its parent ImageListWidget, 1-based index */
-   private integer index;
-   
-   /** The system file path to the binary image */
-   private character key;
+   /** The 4GL pictute object */
+   private comhandle picture;
+   
+   /** The extra data associated with this image */
+   private BaseDataType tag;
+   
+   /** Self instance reference */
+   private comhandle self;
    
    /**
     * Constructor.
@@ -94,20 +97,20 @@
     */
    private ListImage(GenericWidget parent)
    {
+      this(parent, null);
+   }
+
+   /**
+    * Constructor.
+    * 
+    * @param   parent
+    *          Parent widget.
+    */
+   private ListImage(GenericWidget parent, comhandle picture)
+   {
       super(IImage.class.getName(), parent);
-   }
-   
-   /**
-    * Create a detached instance of an image that can be attached to a widget later.
-    * 
-    * @param   pathToImage
-    *          The file system path to the image file.
-    */
-   private ListImage(character pathToImage)
-   {
-      super(IImage.class.getName(), null);
       
-      key = pathToImage;
+      setPicture(picture);
    }
 
    /**
@@ -118,38 +121,15 @@
     *          
     * @return a new instance wrapped in a comhandle.
     */
-   public static comhandle newInstance(GenericWidget parent)
-   {
-      return new comhandle(new ListImage(parent));
-   }
-
-   /**
-    * Creates a detached instance of an image
-    * 
-    * @param    pathToImage
-    *           The file system path to the image file.
-    * 
-    * @return   a new instance wrapped in a comhandle.
-    */
-   public static comhandle newInstance(String pathToImage)
-   {
-      return newInstance(new character(pathToImage));
-   }
-
-   /**
-    * Creates a detached instance of an image
-    * 
-    * @param    pathToImage
-    *           The file system path to the image file.
-    * 
-    * @return   a new instance wrapped in a comhandle.
-    */
-   public static comhandle newInstance(character pathToImage)
-   {
-      return new comhandle(new ListImage(pathToImage));
-   }
-
-   //public static comhandle newInstance
+   public static comhandle newInstance(GenericWidget parent, comhandle picture)
+   {
+      ListImage comObject = new ListImage(parent, picture);
+      comhandle selfReference = new comhandle(comObject);
+      comObject.self = selfReference;
+      
+      return selfReference;
+   }
+
    /**
     * {@inheritDoc}
     */
@@ -174,7 +154,16 @@
    @ComProperty(name = "INDEX")
    public integer getIndex()
    {
-      return this.index;
+      ListImages listImages = getListImages();
+      
+      int index = listImages.findIndex(self);
+      
+      if (index != -1)
+      {
+         return new integer(index + 1);
+      }
+      
+      return new integer();
    }
 
    /**
@@ -183,7 +172,6 @@
    @ComProperty(name = "INDEX")
    public void setIndex(integer index)
    {
-      this.index = index;
    }
 
    /**
@@ -192,7 +180,6 @@
    @ComProperty(name = "INDEX")
    public void setIndex(int index)
    {
-      this.index = new integer(index);
    }
 
    /**
@@ -201,12 +188,9 @@
    @ComProperty(name = "KEY")
    public character getKey()
    {
-      if (key == null)
-      {
-         return new character();
-      }
+      ListImages listImages = getListImages();
       
-      return key;
+      return new character(listImages.findKey(self));
    }
 
    /**
@@ -215,7 +199,6 @@
    @ComProperty(name = "KEY")
    public void setKey(character key)
    {
-      this.key = key;
    }
    
    /**
@@ -224,25 +207,24 @@
    @ComProperty(name = "KEY")
    public void setKey(String key)
    {
-      this.key = new character(key);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @ComProperty(name = "PICTURE")
-   public memptr getPicture()
-   {
-      return new memptr();
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @ComProperty(name = "PICTURE")
-   public void setPicture(memptr data)
-   {
-      // nop
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @ComProperty(name = "PICTURE")
+   public comhandle getPicture()
+   {
+      return picture;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @ComProperty(name = "PICTURE")
+   public void setPicture(comhandle picture)
+   {
+      this.picture = picture;
    }
 
    /**
@@ -251,7 +233,7 @@
    @ComProperty(name = "TAG")
    public BaseDataType getTag()
    {
-      return new character();
+      return tag;
    }
 
    /**
@@ -260,17 +242,21 @@
    @ComProperty(name = "TAG")
    public void setTag(BaseDataType tag)
    {
-      // nop
+      this.tag = tag;
    }
    
    /**
-    * Attach this iamge to the parent widget.
+    * Unwrap list images object.
     * 
-    * @param    parent
-    *           The parent widget
+    * @return   The ListImages instance, the container of list image objects.
     */
-   public void setParent(GenericWidget parent)
+   private ListImages getListImages()
    {
-      this.parent = parent;
+      if (parent instanceof ImageListWidget)
+      {
+         return ((ImageListWidget) parent).getListImages().unwrapListImages();
+      }
+      
+      return null;
    }
 }

=== modified file 'src/com/goldencode/p2j/ui/ocx/ListImages.java'
--- src/com/goldencode/p2j/ui/ocx/ListImages.java	2020-05-20 21:37:20 +0000
+++ src/com/goldencode/p2j/ui/ocx/ListImages.java	2020-05-27 04:56:12 +0000
@@ -64,10 +64,19 @@
 
 package com.goldencode.p2j.ui.ocx;
 
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
 import com.goldencode.p2j.comauto.*;
 import com.goldencode.p2j.ui.*;
 import com.goldencode.p2j.util.*;
 
+import static com.goldencode.p2j.util.ErrorManager.ErrorReasons.*;
+
 /**
  * A server side implementation of IImages COM interface 
  * 
@@ -79,6 +88,11 @@
 extends ComInterface
 implements IImages
 {
+   
+   private Map<String, comhandle> keysToImages = new LinkedHashMap<>();
+   
+   private List<comhandle> images = new LinkedList<>();
+   
    /**
     * Constructor.
     * 
@@ -93,10 +107,10 @@
    /**
     * Factory method.
     * 
-    * @param   parent
-    *          Parent widget.
+    * @param    parent
+    *           The image list parent widget.
     *          
-    * @return a new instance wrapped in a comhandle.
+    * @return   The new instance wrapped by comhandle.
     */
    public static handle newInstance(GenericWidget parent)
    {
@@ -109,39 +123,32 @@
    @ComMethod(name = "ADD")
    public comhandle add(integer index, character key, comhandle picture)
    {
-      character pathToImage;
-      
-      IImage unwrappedImage;
-      
-      if (picture != null && picture.getResource() instanceof IImage)
-      {
-         unwrappedImage = (IImage) picture.getResource();
-         pathToImage = unwrappedImage.getKey();
-      }
-      else
-      {
-         unwrappedImage = null;
-         pathToImage = key;
-      }
-      
-      ImageListWidget w = (ImageListWidget) parent;
-      
-      // returned 0-based index
-      integer indexOfImage = w.addImage(pathToImage);
-      
-      indexOfImage.increment();
-      
-      if (unwrappedImage != null)
-      {
-         unwrappedImage.setIndex(indexOfImage);
-         
-         if (unwrappedImage instanceof ListImage)
-         {
-            ((ListImage) unwrappedImage).setParent(parent);
-         }
-      }
-      
-      return picture;
+      if (key == null || key.isUnknown())
+      {
+         return new comhandle();
+      }
+      
+      if (keysToImages.containsKey(key.getValue()))
+      {
+         ErrorManager.throwErrorWhileProcessingComponentProperty("ADD", KEY_NOT_UNIQUE.getReason());
+         
+         return new comhandle();
+      }
+      
+      comhandle imageObj = ListImage.newInstance(parent, picture);
+      
+      images.add(imageObj);
+      
+      keysToImages.put(key.getValue(), imageObj);
+      
+      if (picture != null && picture.getResource() instanceof PictureObject)
+      {
+         PictureObject pictureObject = (PictureObject) picture.getResource();
+         
+         ((ImageListWidget) parent).addImage(new character(pictureObject.getImagePath()));
+      }
+      
+      return imageObj;
    }
 
    /**
@@ -173,7 +180,12 @@
    @ComProperty(name = "ITEM")
    public comhandle getItem(character key)
    {
-      return new comhandle();
+      if (key == null || key.isUnknown())
+      {
+         return new comhandle();
+      }
+      
+      return getItem(key.getValue());
    }
 
    /**
@@ -182,21 +194,12 @@
    @ComProperty(name = "ITEM")
    public comhandle getItem(int index)
    {
-      ImageListWidget w = (ImageListWidget) parent;
-      
-      integer[]  images = w.getImageIds();
-      
-      if (index <= 0 && index > images.length)
+      if (index <= 0 && index > images.size())
       {
          return new comhandle();
       }
       
-      comhandle image = ListImage.newInstance(parent);
-      
-      ((IImage) image.getResource()).setIndex(index);
-      
-      // TODO get image path
-      return image;
+      return images.get(index - 1);
    }
 
    /**
@@ -205,7 +208,12 @@
    @ComProperty(name = "ITEM")
    public comhandle getItem(String key)
    {
-      return new comhandle();
+      if (key == null)
+      {
+         return new comhandle();
+      }
+      
+      return keysToImages.get(key);
    }
 
    /**
@@ -223,9 +231,7 @@
    @ComProperty(name = "COUNT")
    public integer getCount()
    {
-      ImageListWidget w = (ImageListWidget) parent;
-      
-      return new integer(w.getImageIds().length);
+      return new integer(images.size());
    }
 
    /**
@@ -263,4 +269,34 @@
    {
       // nop      
    }
+   
+   public String findKey(comhandle imageReference)
+   {
+      Entry<String, comhandle> found = findKeyValueEntry(imageReference);
+      
+      if (found != null)
+      {
+         return found.getKey();
+      }
+      
+      return null;
+   }
+   
+   public int findIndex(comhandle imageReference)
+   {
+      return images.indexOf(imageReference);
+   }
+   
+   private Entry<String, comhandle> findKeyValueEntry(comhandle imageReference)
+   {
+      Optional<Entry<String, comhandle>> found = keysToImages.entrySet().stream().filter(
+               entry -> entry.getValue() == imageReference).findFirst();
+      
+      if (found.isPresent())
+      {
+         return found.get();
+      }
+      
+      return null;
+   }
 }

=== added file 'src/com/goldencode/p2j/ui/ocx/PictureObject.java'
--- src/com/goldencode/p2j/ui/ocx/PictureObject.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/ocx/PictureObject.java	2020-05-27 04:56:23 +0000
@@ -0,0 +1,161 @@
+/*
+** Module   : PictureObject.java
+** Abstract : Represents 4GL picture object.
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------Description----------------------------------
+** 001 SBI 20200525 Added 4GL picture object implementation.
+*/
+
+/*
+** This program is free software: you can redistribute it and/or modify
+** it under the terms of the GNU Affero General Public License as
+** published by the Free Software Foundation, either version 3 of the
+** License, or (at your option) any later version.
+**
+** This program is distributed in the hope that it will be useful,
+** but WITHOUT ANY WARRANTY; without even the implied warranty of
+** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+** GNU Affero General Public License for more details.
+**
+** You may find a copy of the GNU Affero GPL version 3 at the following
+** location: https://www.gnu.org/licenses/agpl-3.0.en.html
+** 
+** Additional terms under GNU Affero GPL version 3 section 7:
+** 
+**   Under Section 7 of the GNU Affero GPL version 3, the following additional
+**   terms apply to the works covered under the License.  These additional terms
+**   are non-permissive additional terms allowed under Section 7 of the GNU
+**   Affero GPL version 3 and may not be removed by you.
+** 
+**   0. Attribution Requirement.
+** 
+**     You must preserve all legal notices or author attributions in the covered
+**     work or Appropriate Legal Notices displayed by works containing the covered
+**     work.  You may not remove from the covered work any author or developer
+**     credit already included within the covered work.
+** 
+**   1. No License To Use Trademarks.
+** 
+**     This license does not grant any license or rights to use the trademarks
+**     Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
+**     of Golden Code Development Corporation. You are not authorized to use the
+**     name Golden Code, FWD, or the names of any author or contributor, for
+**     publicity purposes without written authorization.
+** 
+**   2. No Misrepresentation of Affiliation.
+** 
+**     You may not represent yourself as Golden Code Development Corporation or FWD.
+** 
+**     You may not represent yourself for publicity purposes as associated with
+**     Golden Code Development Corporation, FWD, or any author or contributor to
+**     the covered work, without written authorization.
+** 
+**   3. No Misrepresentation of Source or Origin.
+** 
+**     You may not represent the covered work as solely your work.  All modified
+**     versions of the covered work must be marked in a reasonable way to make it
+**     clear that the modified work is not originating from Golden Code Development
+**     Corporation or FWD.  All modified versions must contain the notices of
+**     attribution required in this license.
+*/
+
+package com.goldencode.p2j.ui.ocx;
+
+import com.goldencode.p2j.comauto.*;
+import com.goldencode.p2j.ui.*;
+import com.goldencode.p2j.util.character;
+import com.goldencode.p2j.util.comhandle;
+
+
+/**
+ * Represents 4GL picture object.
+ */
+public class PictureObject extends ComObject
+{
+   /** Stores the file system path to the picture */
+   private final String imagePath;
+
+   /** The global image identifier */
+   private final int imageId;
+
+   /**
+    * Creates immutable instance of this picture object
+    * 
+    * @param    path
+    *           The file system path to the image file.
+    */
+   public PictureObject(String path)
+   {
+      this.imagePath = path;
+      
+      id(WidgetId.nextID().asInt());
+      
+      this.imageId = LogicalTerminal.loadGlobalImage(id().intValue(), path);
+   }
+
+   /**
+    * Loads the picture object and returns the comhandle
+    * 
+    * @param    path
+    *           The file system path to the image file.
+    * 
+    * @return   The comhandle to the picture object.
+    */
+   public static comhandle loadPicture(String path)
+   {
+      return new comhandle(new PictureObject(path));
+   }
+
+   /**
+    * Returns this class simple name.
+    * 
+    * @return   "OlePictureObject"
+    */
+   @Override
+   public String getActivexName()
+   {
+      return getClass().getSimpleName();
+   }
+
+   /**
+    * Tests if there is an image given by this image path.
+    * 
+    * @return    True if there is an image given by this image path, otherwise false
+    */
+   @Override
+   public boolean valid()
+   {
+      return this.imageId != -1;
+   }
+
+   /**
+    * Tests if the image resource doesn't exist.
+    */
+   @Override
+   public boolean unknown()
+   {
+      return !valid();
+   }
+
+   /**
+    * Gets the system path to this image
+    * 
+    * @return   The image path
+    */
+   public String getImagePath()
+   {
+      return imagePath;
+   }
+
+   /**
+    * Gets the global image identifier
+    * 
+    * @return   The global image identifier
+    */
+   public int getImageId()
+   {
+      return imageId;
+   }
+}

=== modified file 'src/com/goldencode/p2j/util/ErrorManager.java'
--- src/com/goldencode/p2j/util/ErrorManager.java	2020-05-04 22:22:23 +0000
+++ src/com/goldencode/p2j/util/ErrorManager.java	2020-05-26 15:08:03 +0000
@@ -3176,7 +3176,49 @@
          }
       }
    }
-   
+
+   /**
+    * Enumerates known error reasons in object collections.
+    */
+   public static enum ErrorReasons
+   {
+      /** The index out of bounds error message reason */
+      INDEX_OUT_OF_BOUNDS("Index out of bounds"),
+      
+      /** The invalid key error message reason */
+      INVALID_KEY("Invalid key"),
+      
+      /** Key is not unique in collection error message reason */
+      KEY_NOT_UNIQUE("Key is not unique in collection"),
+      
+      /** The type mismatch the error message reason */
+      TYPE_MISMATCH("Type mismatch.");
+      
+      /** The error description */
+      private final String reason;
+      
+      /**
+       * Creates new enumeration constant from the given error description.
+       * 
+       * @param    reason
+       *           The error description
+       */
+      private ErrorReasons(String reason)
+      {
+         this.reason = reason;
+      }
+
+      /**
+       * Returns the error description.
+       * 
+       * @return   The error description.
+       */
+      public String getReason()
+      {
+         return reason;
+      }
+   }
+
    /**
     * Stores the data related to a single error.
     */

