Added SubmitPanel, HelpPanel for PlanetLab and VINI
authorAndy Bavier <acb@localhost.localdomain>
Fri, 4 Jun 2010 17:44:55 +0000 (13:44 -0400)
committerAndy Bavier <acb@localhost.localdomain>
Fri, 4 Jun 2010 17:44:55 +0000 (13:44 -0400)
HelpPanel.py [new file with mode: 0644]
LinkPanel.py
NodePanel.py
PlanetLab.py
SfaData.py
SfaGUI.py
SliverPanel.py
SubmitPanel.py [new file with mode: 0644]
VINI.py
Welcome.py [moved from Info.py with 83% similarity]
public/SfaGUI.css

diff --git a/HelpPanel.py b/HelpPanel.py
new file mode 100644 (file)
index 0000000..2237aed
--- /dev/null
@@ -0,0 +1,22 @@
+from pyjamas.ui.VerticalPanel import VerticalPanel
+from pyjamas.ui.HTML import HTML
+from pyjamas.ui import HasAlignment
+
+class HelpPanel(VerticalPanel):
+    def __init__(self):
+        VerticalPanel.__init__(self)
+        
+    def refresh(self):
+        pass
+                    
+class PlanetLabHelpPanel(HelpPanel):
+    def __init__(self):
+        HelpPanel.__init__(self)
+        self.add(HTML("This is the PlanetLab help panel"))
+
+class VINIHelpPanel(HelpPanel):        
+    def __init__(self):
+        HelpPanel.__init__(self)
+        self.add(HTML("This is the VINI help panel"))
+
+        
index ea7164f..cf54567 100644 (file)
@@ -76,21 +76,22 @@ class VlinkPanel(DockPanel):
         self.setWidth("100%")
 
     def onClick(self, sender):
         self.setWidth("100%")
 
     def onClick(self, sender):
-        self.top.rspec.remove_vlink(self.handle)
+        rspec = self.top.sfadata.getRSpec()
+        rspec.remove_vlink(self.handle)
         self.top.refresh()
 
 
 class LinkPanel(VerticalPanel):
         self.top.refresh()
 
 
 class LinkPanel(VerticalPanel):
-    def __init__(self, sfadata, rspec):
+    def __init__(self, sfadata):
         VerticalPanel.__init__(self)
         VerticalPanel.__init__(self)
-        self.data = sfadata
-        self.rspec = rspec
+        self.sfadata = sfadata
         self.defaultbw = 1000
         self.refresh()
 
     def refresh(self):
         self.clear()
         self.defaultbw = 1000
         self.refresh()
 
     def refresh(self):
         self.clear()
-        vlinks = self.rspec.get_vlink_list()
+        rspec = self.sfadata.getRSpec()
+        vlinks = rspec.get_vlink_list()
 
         self.toppanel = TopPanel(self, self.defaultbw, (len(vlinks) > 0))
         self.add(self.toppanel)
 
         self.toppanel = TopPanel(self, self.defaultbw, (len(vlinks) > 0))
         self.add(self.toppanel)
@@ -104,18 +105,20 @@ class LinkPanel(VerticalPanel):
         self.add(cp)
 
     def clear_vlinks(self):
         self.add(cp)
 
     def clear_vlinks(self):
-        vlinks = self.rspec.get_vlink_list()
+        rspec = self.sfadata.getRSpec()
+        vlinks = rspec.get_vlink_list()
         for (handle, desc, bw) in vlinks:
         for (handle, desc, bw) in vlinks:
-            self.rspec.remove_vlink(handle)
+            rspec.remove_vlink(handle)
 
     def build_topology(self):
 
     def build_topology(self):
-        nodes = self.rspec.get_sliver_list()
-        links = self.rspec.get_link_list()
+        rspec = self.sfadata.getRSpec()
+        nodes = rspec.get_sliver_list()
+        links = rspec.get_link_list()
         self.defaultbw = self.toppanel.getDefaultBW()
 
         for (name, end1nodes, end2nodes) in links:
             for node1 in end1nodes:
                 for node2 in end2nodes:
                     if (node1 in nodes) and (node2 in nodes):
         self.defaultbw = self.toppanel.getDefaultBW()
 
         for (name, end1nodes, end2nodes) in links:
             for node1 in end1nodes:
                 for node2 in end2nodes:
                     if (node1 in nodes) and (node2 in nodes):
-                        self.rspec.add_vlink(node1, node2, self.defaultbw)
+                        rspec.add_vlink(node1, node2, self.defaultbw)
                 
                 
index a4c11e1..c4861b2 100644 (file)
@@ -8,6 +8,7 @@ from pyjamas.ui.ListBox import ListBox
 from pyjamas.ui.TextBox import TextBox
 from pyjamas.ui.HTML import HTML
 from pyjamas.ui import HasAlignment
 from pyjamas.ui.TextBox import TextBox
 from pyjamas.ui.HTML import HTML
 from pyjamas.ui import HasAlignment
+from SfaData import ViniData
 import re
 
 def faster_clear(self):
 import re
 
 def faster_clear(self):
@@ -18,15 +19,15 @@ def faster_clear(self):
 ListBox.clear = faster_clear            
 
 class NodePanel(DockPanel):
 ListBox.clear = faster_clear            
 
 class NodePanel(DockPanel):
-    def __init__(self, sfadata, rspec):
+    def __init__(self, sfadata):
         DockPanel.__init__(self)
         DockPanel.__init__(self)
-        self.data = sfadata
-        self.rspec = rspec
+        self.sfadata = sfadata
 
         self.setSize("100%", "100%")
         self.setHorizontalAlignment(HasAlignment.ALIGN_CENTER)
         self.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE)
 
         self.setSize("100%", "100%")
         self.setHorizontalAlignment(HasAlignment.ALIGN_CENTER)
         self.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE)
-        self.regex = None
+        self.availableRegex = None
+        self.selectedRegex = None
 
         leftpanel = VerticalPanel()
         leftcap = CaptionPanel("Available nodes")
 
         leftpanel = VerticalPanel()
         leftcap = CaptionPanel("Available nodes")
@@ -35,10 +36,10 @@ class NodePanel(DockPanel):
         leftpanel.add(leftcap)
 
         hp1 = HorizontalPanel()
         leftpanel.add(leftcap)
 
         hp1 = HorizontalPanel()
-        filterButton = Button("Filter", self.filter)
-        self.regExBox = TextBox()
+        filterButton = Button("Filter", self.filterLeft)
+        self.leftRegExBox = TextBox()
         hp1.add(filterButton)
         hp1.add(filterButton)
-        hp1.add(self.regExBox)
+        hp1.add(self.leftRegExBox)
         leftpanel.add(hp1)
 
         rightpanel = VerticalPanel()
         leftpanel.add(hp1)
 
         rightpanel = VerticalPanel()
@@ -48,10 +49,10 @@ class NodePanel(DockPanel):
         rightpanel.add(rightcap)
 
         hp2 = HorizontalPanel()
         rightpanel.add(rightcap)
 
         hp2 = HorizontalPanel()
-        applyButton = Button("Apply", self.apply)
-        resetButton = Button("Reset", self.reset)
-        hp2.add(applyButton)
-        hp2.add(resetButton)
+        filterButton = Button("Filter", self.filterRight)
+        self.rightRegExBox = TextBox()
+        hp2.add(filterButton)
+        hp2.add(self.rightRegExBox)
         rightpanel.add(hp2)
 
         addButton = Button(">>", self.addNodes)
         rightpanel.add(hp2)
 
         addButton = Button(">>", self.addNodes)
@@ -68,12 +69,20 @@ class NodePanel(DockPanel):
         self.refresh()
 
     def refresh(self):
         self.refresh()
 
     def refresh(self):
-        slivers = self.sortNodes(self.rspec.get_sliver_list())
-        all = self.rspec.get_node_list()
+        rspec = self.sfadata.getRSpec()
+
+        slivers = self.sortNodes(rspec.get_sliver_list())
+        all = rspec.get_node_list()
         available = self.sortNodes(filter(lambda x:x not in slivers,all))
         available = self.sortNodes(filter(lambda x:x not in slivers,all))
-        if self.regex:
-            pattern = re.compile(self.regex)
+
+        if self.availableRegex:
+            pattern = re.compile(self.availableRegex)
             available = filter(pattern.search, available)
             available = filter(pattern.search, available)
+
+        if self.selectedRegex:
+            pattern = re.compile(self.selectedRegex)
+            slivers = filter(pattern.search, slivers)
+
         self.leftlist.clear()
         for i in available:
             self.leftlist.addItem(i)
         self.leftlist.clear()
         for i in available:
             self.leftlist.addItem(i)
@@ -96,24 +105,23 @@ class NodePanel(DockPanel):
         return got
 
     def addNodes(self, sender):
         return got
 
     def addNodes(self, sender):
+        rspec = self.sfadata.getRSpec()
         nodes = self.getSelected(self.leftlist)
         for node in nodes:
         nodes = self.getSelected(self.leftlist)
         for node in nodes:
-            self.rspec.add_sliver(node)
+            rspec.add_sliver(node)
         self.refresh()
 
     def removeNodes(self, sender):
         self.refresh()
 
     def removeNodes(self, sender):
+        rspec = self.sfadata.getRSpec()
         nodes = self.getSelected(self.rightlist)
         for node in nodes:
         nodes = self.getSelected(self.rightlist)
         for node in nodes:
-            self.rspec.remove_sliver(node)
+            rspec.remove_sliver(node)
         self.refresh()
 
         self.refresh()
 
-    def apply(self, sender):
-        self.data.applyRSpec(self.rspec)
-
-    def reset(self, sender):
-        self.rspec = self.data.getRSpec()
+    def filterLeft(self, sender):
+        self.availableRegex = self.leftRegExBox.getText()
         self.refresh()
 
         self.refresh()
 
-    def filter(self, sender):
-        self.regex = self.regExBox.getText()
+    def filterRight(self, sender):
+        self.selectedRegex = self.rightRegExBox.getText()
         self.refresh()
         self.refresh()
index 6488da9..b26241e 100644 (file)
@@ -7,19 +7,26 @@ from SfaData import PlanetLabData
 from NodePanel import NodePanel
 from LinkPanel import LinkPanel
 from SliverPanel import SliverPanel
 from NodePanel import NodePanel
 from LinkPanel import LinkPanel
 from SliverPanel import SliverPanel
+from SubmitPanel import SubmitPanel
+from HelpPanel import PlanetLabHelpPanel
 
 class PlTabs(TabPanel):
     def __init__(self):
         TabPanel.__init__(self)
 
         self.data = PlanetLabData()
 
 class PlTabs(TabPanel):
     def __init__(self):
         TabPanel.__init__(self)
 
         self.data = PlanetLabData()
-        self.rspec = self.data.getRSpec()
+        self.data.refreshRSpec()
+
+        nodetab = NodePanel(self.data)
+        slivertab = SliverPanel(self.data)
+        submittab = SubmitPanel(self.data)
+        helptab = PlanetLabHelpPanel()
 
 
-        nodetab = NodePanel(self.data, self.rspec)
-        slivertab = SliverPanel(self.data, self.rspec)
-    
         self.add(nodetab, "Nodes")
         self.add(slivertab, "Slivers")
         self.add(nodetab, "Nodes")
         self.add(slivertab, "Slivers")
+        self.add(submittab, "Submit")
+        self.add(HTML("force tabs to right"), None)
+        self.add(helptab, "Help")
         self.selectTab(0)
 
     def onTabSelected(self, sender, index):
         self.selectTab(0)
 
     def onTabSelected(self, sender, index):
index 343ad0e..049b0d3 100644 (file)
@@ -59,13 +59,19 @@ class ViniData(SfaData):
         SfaData.__init__(self)
         self.registry = "http://www.planet-lab.org:12345"
         self.slicemgr = "http://www.vini-veritas.net:12346"
         SfaData.__init__(self)
         self.registry = "http://www.planet-lab.org:12345"
         self.slicemgr = "http://www.vini-veritas.net:12346"
+        self.rspec = None
 
 
-    def getRSpec(self):
+    def refreshRSpec(self):
         xml = SfaData.getRSpec(self)
         xml = SfaData.getRSpec(self)
-        return RSpec(xml)
+        self.rspec = RSpec(xml)
 
 
-    def applyRSpec(self, rspec):
-        xml = rspec.toxml()
+    def getRSpec(self):
+        if self.rspec is None:
+            self.refreshRSpec()
+        return self.rspec
+    
+    def applyRSpec(self):
+        xml = self.rspec.toxml()
         SfaData.applyRSpec(self, xml)
 
 class PlanetLabData(SfaData):
         SfaData.applyRSpec(self, xml)
 
 class PlanetLabData(SfaData):
@@ -73,13 +79,19 @@ class PlanetLabData(SfaData):
         SfaData.__init__(self)
         self.registry = "http://www.planet-lab.org:12345"
         self.slicemgr = "http://www.planet-lab.org:12346"
         SfaData.__init__(self)
         self.registry = "http://www.planet-lab.org:12345"
         self.slicemgr = "http://www.planet-lab.org:12346"
+        self.rspec = None
 
 
-    def getRSpec(self):
+    def refreshRSpec(self):
         xml = SfaData.getRSpec(self)
         xml = SfaData.getRSpec(self)
-        return RSpec(xml)
+        self.rspec = RSpec(xml)
 
 
-    def applyRSpec(self, rspec):
-        xml = rspec.toxml()
+    def getRSpec(self):
+        if self.rspec is None:
+            self.refreshRSpec()
+        return self.rspec
+    
+    def applyRSpec(self):
+        xml = self.rspec.toxml()
         SfaData.applyRSpec(self, xml)
 
 class OpenCirrusData(SfaData):
         SfaData.applyRSpec(self, xml)
 
 class OpenCirrusData(SfaData):
index 8cd7cc1..aa23ace 100644 (file)
--- a/SfaGUI.py
+++ b/SfaGUI.py
@@ -11,7 +11,7 @@ from pyjamas import Window
 from SinkList import SinkList
 from Configure import TopPanel
 from pyjamas import History
 from SinkList import SinkList
 from Configure import TopPanel
 from pyjamas import History
-import Info
+import Welcome
 import Slices
 import Identities
 import PlanetLab
 import Slices
 import Identities
 import PlanetLab
@@ -89,9 +89,9 @@ class SfaGUI:
         self.curSink.onShow()
         
     def loadSinks(self):
         self.curSink.onShow()
         
     def loadSinks(self):
-        self.sink_list.addSink(Info.init())
+        self.sink_list.addSink(Welcome.init())
         self.sink_list.addSink(Identities.init())
         self.sink_list.addSink(Identities.init())
-        self.sink_list.addSink(Slices.init())
+        self.sink_list.addSink(Slices.init())
         self.sink_list.addSink(PlanetLab.init())
         self.sink_list.addSink(VINI.init())
         self.sink_list.addSink(OpenCirrus.init())
         self.sink_list.addSink(PlanetLab.init())
         self.sink_list.addSink(VINI.init())
         self.sink_list.addSink(OpenCirrus.init())
@@ -99,7 +99,7 @@ class SfaGUI:
         self.sink_list.addSink(Configure.init())
 
     def showInfo(self):
         self.sink_list.addSink(Configure.init())
 
     def showInfo(self):
-        self.show(self.sink_list.find("Info"), False)
+        self.show(self.sink_list.find("Welcome"), False)
 
 
 
 
 
 
index bf91adb..a4b2602 100644 (file)
@@ -16,7 +16,8 @@ class AddPanel(CaptionPanel):
         hp = HorizontalPanel()
         self.nodes = ListBox()
         self.nodes.addItem("All nodes")
         hp = HorizontalPanel()
         self.nodes = ListBox()
         self.nodes.addItem("All nodes")
-        for sliver in self.top.rspec.get_sliver_list():
+        rspec = self.top.sfadata.getRSpec()
+        for sliver in rspec.get_sliver_list():
             self.nodes.addItem(sliver)
 
         self.attrs = ListBox()
             self.nodes.addItem(sliver)
 
         self.attrs = ListBox()
@@ -44,11 +45,12 @@ class AddPanel(CaptionPanel):
         name = self.attrs.getItemText(self.attrs.getSelectedIndex())
         value = self.value.getText()
         nodeindex = self.nodes.getSelectedIndex()
         name = self.attrs.getItemText(self.attrs.getSelectedIndex())
         value = self.value.getText()
         nodeindex = self.nodes.getSelectedIndex()
+        rspec = self.top.sfadata.getRSpec()
         if nodeindex == 0:
         if nodeindex == 0:
-            self.top.rspec.add_default_sliver_attribute(name, value)
+            rspec.add_default_sliver_attribute(name, value)
         else:
             node = self.nodes.getItemText(nodeindex)
         else:
             node = self.nodes.getItemText(nodeindex)
-            self.top.rspec.add_sliver_attribute(node, name, value)
+            rspec.add_sliver_attribute(node, name, value)
         self.top.refresh()
         
 
         self.top.refresh()
         
 
@@ -68,31 +70,30 @@ class AttributePanel(HorizontalPanel):
         self.add(HTML("%s: %s" % (self.name, self.value)))
 
     def onClick(self, sender):
         self.add(HTML("%s: %s" % (self.name, self.value)))
 
     def onClick(self, sender):
+        rspec = self.top.sfadata.getRSpec()
         if self.node:
         if self.node:
-            self.top.rspec.remove_sliver_attribute(self.node, self.name, 
-                                                   self.value)
+            rspec.remove_sliver_attribute(self.node, self.name, self.value)
         else:
         else:
-            self.top.rspec.remove_default_sliver_attribute(self.name, 
-                                                           self.value)
+            rspec.remove_default_sliver_attribute(self.name, self.value)
         self.top.refresh()
         
 
 class SliverPanel(VerticalPanel):
         self.top.refresh()
         
 
 class SliverPanel(VerticalPanel):
-    def __init__(self, sfadata, rspec):
+    def __init__(self, sfadata):
         VerticalPanel.__init__(self)
         VerticalPanel.__init__(self)
-        self.data = sfadata
-        self.rspec = rspec
+        self.sfadata = sfadata
         self.refresh()
 
     def refresh(self):
         self.clear()
         self.add(AddPanel(self))
 
         self.refresh()
 
     def refresh(self):
         self.clear()
         self.add(AddPanel(self))
 
-        allattrs = self.rspec.get_default_sliver_attributes()
+        rspec = self.sfadata.getRSpec()
+        allattrs = rspec.get_default_sliver_attributes()
         attrdict = {}
         attrdict = {}
-        slivers = self.rspec.get_sliver_list()
+        slivers = rspec.get_sliver_list()
         for sliver in slivers:
         for sliver in slivers:
-            attrs = self.rspec.get_sliver_attributes(sliver)
+            attrs = rspec.get_sliver_attributes(sliver)
             attrdict[sliver] = attrs
 
         if allattrs:
             attrdict[sliver] = attrs
 
         if allattrs:
diff --git a/SubmitPanel.py b/SubmitPanel.py
new file mode 100644 (file)
index 0000000..f3c10a9
--- /dev/null
@@ -0,0 +1,40 @@
+from pyjamas.ui.VerticalPanel import VerticalPanel
+from pyjamas.ui.HorizontalPanel import HorizontalPanel
+from pyjamas.ui.Button import Button
+from pyjamas.ui.HTML import HTML
+from pyjamas.ui import HasAlignment
+
+class SubmitPanel(VerticalPanel):
+    def __init__(self, sfadata):
+        VerticalPanel.__init__(self)
+        self.sfadata = sfadata
+        self.setSpacing(10)
+        
+        hp1 = HorizontalPanel()
+        hp1.setSpacing(10)
+        hp1.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE)
+        b1 = Button("Apply", self.apply)
+        hp1.add(b1)
+        hp1.add(HTML("Apply the configured changes to the slice"))
+                
+        hp2 = HorizontalPanel()
+        hp2.setSpacing(10)
+        hp2.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE)
+        b2 = Button("Reset", self.reset)
+        hp2.add(b2)
+        hp2.add(HTML("Reset the local slice configuration"))
+        
+        self.add(hp1)
+        self.add(hp2)
+        
+    def refresh(self):
+        pass
+                    
+    def apply(self, sender):
+        self.sfadata.applyRSpec()
+
+    def reset(self):
+        self.sfadata.refreshRSpec()
+        
+
+        
diff --git a/VINI.py b/VINI.py
index 15f5795..d38b87e 100644 (file)
--- a/VINI.py
+++ b/VINI.py
@@ -1,25 +1,34 @@
 from Sink import Sink, SinkInfo
 from pyjamas.ui.TabPanel import TabPanel
 from pyjamas.ui.VerticalPanel import VerticalPanel
 from Sink import Sink, SinkInfo
 from pyjamas.ui.TabPanel import TabPanel
 from pyjamas.ui.VerticalPanel import VerticalPanel
+from pyjamas.ui.HTML import HTML
 from SfaData import ViniData
 from NodePanel import NodePanel
 from LinkPanel import LinkPanel
 from SliverPanel import SliverPanel
 from SfaData import ViniData
 from NodePanel import NodePanel
 from LinkPanel import LinkPanel
 from SliverPanel import SliverPanel
+from SubmitPanel import SubmitPanel
+from HelpPanel import VINIHelpPanel
 
 class VINITabs(TabPanel):
     def __init__(self):
         TabPanel.__init__(self)
 
         self.data = ViniData()
 
 class VINITabs(TabPanel):
     def __init__(self):
         TabPanel.__init__(self)
 
         self.data = ViniData()
-        self.rspec = self.data.getRSpec()
+        self.data.refreshRSpec()
+
+        nodetab = NodePanel(self.data)
+        linktab = LinkPanel(self.data)
+        slivertab = SliverPanel(self.data)
+        submittab = SubmitPanel(self.data)
+        helptab = VINIHelpPanel()
 
 
-        nodetab = NodePanel(self.data, self.rspec)
-        linktab = LinkPanel(self.data, self.rspec)
-        slivertab = SliverPanel(self.data, self.rspec)
-    
         self.add(nodetab, "Nodes")
         self.add(linktab, "Links")
         self.add(slivertab, "Slivers")
         self.add(nodetab, "Nodes")
         self.add(linktab, "Links")
         self.add(slivertab, "Slivers")
+        self.add(submittab, "Submit")
+        self.add(HTML("force tabs to right"), None)
+        self.add(helptab, "Help")
+
         self.selectTab(0)
 
     def onTabSelected(self, sender, index):
         self.selectTab(0)
 
     def onTabSelected(self, sender, index):
@@ -39,4 +48,4 @@ class VINI(Sink):
         self.initWidget(self.tabs)
 
 def init():
         self.initWidget(self.tabs)
 
 def init():
-    return SinkInfo("VINI", "Specify VINI Resources", VINI)
+    return SinkInfo("VINI", "<b>Specify VINI Resources</b>", VINI)
similarity index 83%
rename from Info.py
rename to Welcome.py
index 4fcf72a..3c7d664 100644 (file)
--- a/Info.py
@@ -1,7 +1,7 @@
 from Sink import Sink, SinkInfo
 from pyjamas.ui.HTML import HTML
 
 from Sink import Sink, SinkInfo
 from pyjamas.ui.HTML import HTML
 
-class Info(Sink):
+class Welcome(Sink):
     def __init__(self):
 
         Sink.__init__(self)
     def __init__(self):
 
         Sink.__init__(self)
@@ -17,4 +17,4 @@ class Info(Sink):
 
 
 def init():
 
 
 def init():
-    return SinkInfo("Info", "Introduction to the SFA Federation GUI", Info)
+    return SinkInfo("Welcome", "Welcome to the SFA Federation GUI", Welcome)
index 5c3f69c..9449ca0 100644 (file)
@@ -110,7 +110,7 @@ a:visited {
 
 .gwt-TabBar {
   background-color: #C3D9FF;
 
 .gwt-TabBar {
   background-color: #C3D9FF;
-  font-size: smaller;
+/*  font-size: smaller; */
 }
 
 .gwt-TabBar .gwt-TabBarFirst {
 }
 
 .gwt-TabBar .gwt-TabBarFirst {
@@ -183,13 +183,13 @@ a:visited {
 .ks-Info {
   background-color: #C3D9FF;
   padding: 10px 10px 2px 10px;
 .ks-Info {
   background-color: #C3D9FF;
   padding: 10px 10px 2px 10px;
-  font-size: smaller;
+  font-weight: bold
 }
 
 .ks-List {
   margin-top: 8px;
   margin-bottom: 8px;
 }
 
 .ks-List {
   margin-top: 8px;
   margin-bottom: 8px;
-  font-size: smaller;
+  font-size: smaller; 
 }
 
 .ks-List .ks-SinkItem {
 }
 
 .ks-List .ks-SinkItem {