=== modified file 'rules/annotations/ocx_conversion.rules'
--- rules/annotations/ocx_conversion.rules	2020-01-29 14:38:53 +0000
+++ rules/annotations/ocx_conversion.rules	2020-02-25 17:11:49 +0000
@@ -4,7 +4,7 @@
 ** Module   : ocx_conversion.rules
 ** Abstract : Converts supported OCX objects to 4GL widgets.
 **
-** Copyright (c) 2018-2019, Golden Code Development Corporation.
+** Copyright (c) 2018-2020, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ _________________________________Description__________________________________
 ** 001 HC  20181127 Initial version.
@@ -28,6 +28,7 @@
 **                  Added rules to ignore (eliminate) RELEASE OBJECT statement for known com-handle
 **                  Make noop-com annotation to work with the new rules.
 **                  Fixed indexed properties conversion rules.
+** 008 SBI 20200225 Added Calendar widget support.
 */
  -->
 <!--
@@ -117,9 +118,11 @@
    <variable name="map3"           type="java.util.Map" />
    <variable name="map4"           type="java.util.Map" />
    <variable name="map5"           type="java.util.Map" />
+   <variable name="map6"           type="java.util.Map" />
 
    <variable name="ref"            type="com.goldencode.ast.AnnotatedAst" />
    <variable name="ref1"           type="com.goldencode.ast.Aast" />
+   <variable name="ref3"           type="com.goldencode.ast.Aast" />
    <variable name="refl"           type="java.util.List" />
    <variable name="it"             type="java.util.Iterator" />
    <variable name="parRef"         type="com.goldencode.ast.Aast" />
@@ -135,6 +138,7 @@
    <variable name="name"           type="java.lang.String" />
    <variable name="name1"          type="java.lang.String" />
    <variable name="name2"          type="java.lang.String" />
+   <variable name="name3"          type="java.lang.String" />
 
    <variable name="callbacks"      type="java.util.List" />
    <variable name="widgetClass"    type="java.lang.Class"/>
@@ -226,6 +230,7 @@
          <action>widgetKwords.put("HTML-BROWSER", prog.kw_html_bws)</action>
          <action>widgetKwords.put("IMAGELIST", prog.kw_il_img)</action>
          <action>widgetKwords.put("PROGRESS-BAR", prog.kw_prog_bar)</action>
+         <action>widgetKwords.put("CALENDAR", prog.kw_calendar)</action>
 
          <action>widgetClasses = create("java.util.HashMap")</action>
          <action>widgetClasses.put("TREELIST", classForName("com.goldencode.p2j.ui.TreeList"))</action>
@@ -234,6 +239,7 @@
          <action>widgetClasses.put("HTML-BROWSER", classForName("com.goldencode.p2j.ui.HtmlBrowser"))</action>
          <action>widgetClasses.put("IMAGELIST", classForName("com.goldencode.p2j.ui.ImageList"))</action>
          <action>widgetClasses.put("PROGRESS-BAR", classForName("com.goldencode.p2j.ui.ProgressBar"))</action>
+         <action>widgetClasses.put("CALENDAR", classForName("com.goldencode.p2j.ui.Calendar"))</action>
 
          <!-- fill the method aliases, where the key is the original com method/property name (in lower case),
               the rules will prepend is/get/set accordingly to the aliased values so for properties,
@@ -282,10 +288,18 @@
          
          <!-- PROGRESS-BAR -->
          <action>map5 = create("java.util.HashMap")</action>
-         <action>map5.put("mousepointer"          , "ProgBarMousePointer")</action>
-         <action>map5.put("refresh"               , "progBarRefresh")</action>
+         <action>map5.put("mousepointer"          , "MousePointerExt")</action>
+         <action>map5.put("refresh"               , "refreshUI")</action>
          <action>aliasMap.put("PROGRESS-BAR"      , map5)</action>
-    
+         
+         <!-- Calendar -->
+         <action>map6 = create("java.util.HashMap")</action>
+         <action>map6.put("value"            , "DateTimeValue")</action>
+         <action>map6.put("CustomFormat"     , "CustomFormat")</action>
+         <action>map6.put("font:name"        , "FontName")</action>
+         <action>map6.put("font:size"        , "FontSize")</action>
+         <action>map6.put("format"           , "FormatStyle")</action>
+         <action>aliasMap.put("CALENDAR"   , map6)</action>
       </rule>
    </init-rules>
 
@@ -406,7 +420,7 @@
                </rule>
 
                <rule>ocxHandle == null and isConversion
-                  <action>ocxHandleKey = copy.text)</action>
+                  <action>ocxHandleKey = copy.text</action>
                   <action>ocxHandleKey = ocxHandleKey.toUpperCase()</action>
                   <action>ocxHandle = ch2th.get(ocxHandleKey)</action> 
                   <action>cache = null</action>
@@ -428,10 +442,9 @@
                <rule>ocxHandle != null
                   <rule>ocxHandle != null and copy.parent.type == prog.com_invocation
                      <action>printfln("INFO: FWD OCX Extension: convert %s to %s.", ocxHandleKey, ocxHandle)</action>
-       
                      <!-- get legacy node -->
                      <rule>not isConversion
-                     	<action>ref2 = copy.parent.nextSibling</action>
+                        <action>ref2 = copy.parent.nextSibling</action>
                      </rule>
 
                      <!-- no legacy node, only handle -->
@@ -447,151 +460,10 @@
                      </rule>
 
                      <!-- convert legacy node -->
+                     <action>ref2 = execLib("processLegacyNode",copy, ref2, ocxHandle, ocxHandleKey, cache, isConversion)</action>
                      <rule>ref2 != null
-                        <action>name = ref2.text</action>
-
-                        <!-- get the method/prop alias -->
-                        <action>ocxType = ch2wtype.get(ocxHandleKey)</action>
-
-                        <action>map = aliasMap.get(ocxType)</action>
-                        <rule>map != null
-                           <action>name1 = name.toLowerCase()</action>
-                           <action>name2 = map.get(name1)</action>
-                           <rule>name2 != null
-	                          <action>name = name2</action>
-                           </rule>
-                           <rule>name2 == null
-                             <!-- check for composed properties like font:name -->
-                             <action>ref1 = copy.parent.parent.nextSibling</action>
-                             <rule>ref1 != null and ref1.type == prog.com_property
-                                <action>name2 = ref1.text.toLowerCase()</action>
-                             	<action>name1 = sprintf("%s:%s", name1, name2)</action>
-	                            <action>name2 = map.get(name1)</action>
-	                            <rule>name2 != null
-	                               <action>ref1 = copy.parent.parent</action>
-	                               <action>ref = copy.parent.parent.parent</action>
-	                               <action>ref1.move(ref.parent, 0)</action>
-	                               <action>ref.remove()</action>
-	                               <action>name = name2</action>
-	                            </rule>
-                             </rule>
-                           </rule>
-                        </rule>
-
-                        <action>widgetClass = widgetClasses.get(ocxType)</action>
-
-                        <!-- find the root com_invocation -->
-                        <action>ref = copy.parent</action>
-                        <action>rootComInvocCpy = ref</action>
-                        <while>ref.type == prog.com_invocation
-                           <action>rootComInvocCpy = ref</action>
-                           <action>ref = ref.parent</action>
-                        </while>
-
-                        <!-- find the java counterpart method -->
-                        <action>name1 = getLegacyName(widgetClass, name)</action>
-
-                        <rule>name1 == null and
-                              rootComInvocCpy.parent.type == prog.assign and
-                              rootComInvocCpy.indexPos == 0
-                           <!-- for prop assignment -->
-                           <action>name1 = sprintf("set%s", name)</action>
-                           <action>name1 = getLegacyName(widgetClass, name1)</action>
-                        </rule>
-
-                        <!-- for prop getter or method invocation -->
-                        <rule>name1 == null
-                           <action>name1 = sprintf("get%s", name)</action>
-                           <action>name1 = getLegacyName(widgetClass, name1)</action>
-                        </rule>
-
-                        <!-- for prop getter or method invocation -->
-                        <rule>name1 == null
-                           <action>name1 = sprintf("is%s", name)</action>
-                           <action>name1 = getLegacyName(widgetClass, name1)</action>
-                        </rule>         
-
-                        <rule>name1 == null
-                           <action>printfln("WARN: FWD OCX Extension: Failed to resolve legacy name for %s. Is %s defined in %s?", name, name, widgetClass)</action>
-                           <action>copy.putAnnotation("noop-com", true)</action>
-                        </rule>
-
-                        <rule>name1 != null
-                           <action>reftype1 = -1</action>
-                           <action>reftype = prog.lookupKeywordToken(name1)</action>
-                           <rule>reftype == -1
-                              <action>printfln("WARN: FWD OCX Extension: Failed to resolve keyword for %s.", name1)</action>
-                              <action>copy.putAnnotation("noop-com", true)</action>
-                              <action on="false">reftype1 = lookupAttributeOrMethod(reftype)</action>
-                              <rule on="false">reftype1 == -1
-                                 <action>printfln("WARN: FWD OCX Extension: Failed to resolve attribute or method type for %s.", name1)</action>
-	                         <action>copy.putAnnotation("noop-com", true)</action>
-                              </rule>
-                           </rule>
-
-                           <rule>reftype1 != -1
-                              <!-- is this an indexed COM property being assigned? -->
-                              <action>methPropCpy = rootComInvocCpy.getImmediateChild(createSet(prog.com_method, prog.com_property), null)</action>
-                              <rule>rootComInvocCpy.parent.type == prog.assign and
-                                    rootComInvocCpy.indexPos == 0 and
-                                    methPropCpy.getImmediateChild(prog.com_parameter, null) != null
-
-                                 <!-- get the assigning expression and make it a parameter -->
-                                 <action>ref1 = rootComInvocCpy.nextSibling</action>
-
-                                 <!-- the com parameter will be converted below -->
-                                 <action>ref1.type = prog.com_parameter</action>
-                                 <action>ref1.text = ""</action>
-                                 <action>ref1.move(methPropCpy, -1)</action>
-                              </rule>
-
-                              <!-- set legacy node properties -->
-                              <action>ref2.type = reftype1</action>
-                              <action>ref2.text = name1</action>
-                              <action>ref2.putAnnotation("oldtype", #(long)reftype)</action>
-
-                              <!-- lookup for com parameters and replace with parameters -->
-                              <action>ref1 = ref2.firstChild</action>
-                              <while>ref1 != null
-                                 <action>ref2 = null</action>
-                                 <rule>ref1.type == prog.com_parameter
-                                    <action>ref1.type = prog.parameter</action>
-                                    <action>ref2 = ref1</action>
-                                 </rule>
-                                 <action>ref1 = ref1.nextSibling</action>
-
-                                 <!-- check for empty parameter and remove it -->
-                                 <rule>ref2 != null and ref2.numChildren == 0
-                                    <action>ref2.remove()</action>
-                                 </rule>
-                              </while>
-
-                              <!-- convert var_com_handle into var_handle -->
-                              <action>copy.type = prog.var_handle</action>
-                              <action>copy.text = ocxHandle</action>
-
-                              <!-- remove second token since is no longer needed -->
-                              <rule>cache != null
-                                 <action>cache.remove()</action>
-                              </rule>
-
-                              <!-- rearrange nodes and remove unnecessary node -->
-                              <rule>isConversion == false and copy.parent.parent != null and copy.parent.parent.type == prog.com_invocation
-                                 <action>ref1 = copy.parent</action>
-                                 <action>invoc = copy.parent.parent</action>
-                                 <action>invoc.type = prog.colon</action>
-                                 <action>copy.move(invoc, 0)</action>
-                                 <action>ref1.remove()</action>
-                              </rule>
-
-                              <!-- when is conversion  -->
-                              <rule>isConversion == true and copy.parent != null and copy.parent.type == prog.com_invocation
-                                 <action>invoc = copy.parent</action>
-                                 <action>invoc.type = prog.colon</action>
-                              </rule>
-                           </rule>
-                        </rule>
-                     </rule>
+                        <action>execLib("processLegacyNode",copy, ref2, ocxHandle, ocxHandleKey, cache, isConversion)</action>
+                     </rule>   
                   </rule>
                </rule>
             </rule>
@@ -898,7 +770,178 @@
    </post-rules>
 
    <func-library access="private">
-
+      <function name="processLegacyNode">
+         <parameter name="copy" type="com.goldencode.ast.Aast" />
+         <parameter name="ref2" type="com.goldencode.ast.Aast" />
+         <parameter name="ocxHandle"       type="java.lang.String"/>
+         <parameter name="ocxHandleKey"    type="java.lang.String"/>
+         <parameter name="cache"           type="com.goldencode.ast.Aast"/>
+         <parameter name="isConversion"    type="java.lang.Boolean"/>
+
+         <return    name="ref3"            type="com.goldencode.ast.Aast"/>
+
+         <variable name="ref1"     type="com.goldencode.ast.Aast" />
+         <variable name="name"     type="java.lang.String"/>
+         <variable name="name1"    type="java.lang.String"/>
+         <variable name="name2"    type="java.lang.String"/>
+         <variable name="name3"    type="java.lang.String"/>
+         <variable name="ocxType"  type="java.lang.String" />
+         <variable name="reftype"  type="java.lang.Integer"/>
+         <variable name="reftype1" type="java.lang.Integer"/>
+         <variable name="invoc"    type="com.goldencode.ast.Aast"/>
+         <variable name="ref"      type="com.goldencode.ast.AnnotatedAst" />
+         <variable name="rootComInvocCpy" type="com.goldencode.ast.Aast"/>
+         <variable name="methPropCpy"     type="com.goldencode.ast.Aast"/>
+                     <!-- convert legacy node -->
+                     <rule>ref2 != null
+                        <action>name = ref2.text</action>
+                        <!-- get the method/prop alias -->
+                        <action>ocxType = ch2wtype.get(ocxHandleKey)</action>
+                        <action>ref3 = ref2.parent.nextSibling</action>
+                        <action>map = aliasMap.get(ocxType)</action>
+                        <rule>map != null
+                           <action>name1 = name.toLowerCase()</action>
+                           <action>name2 = map.get(name1)</action>
+                           <rule>name2 != null
+                             <action>name = name2</action>
+                           </rule>
+                           <rule>name2 == null
+                             <!-- check for composed properties like font:name -->
+                             <action>ref1 = copy.parent.parent.nextSibling</action>
+                             <rule>ref1 != null and ref1.type == prog.com_property
+                               <action>name2 = ref1.text.toLowerCase()</action>
+                               <action>name1 = sprintf("%s:%s", name1, name2)</action>
+                               <action>name2 = map.get(name1)</action>
+                               <rule>name2 != null
+                                  <action>ref1 = copy.parent.parent</action>
+                                  <action>ref = copy.parent.parent.parent</action>
+                                  <action>ref1.move(ref.parent, 0)</action>
+                                  <action>ref.remove()</action>
+                                  <action>name = name2</action>
+                               </rule>
+                             </rule>
+                           </rule>
+                        </rule>
+
+                        <action>widgetClass = widgetClasses.get(ocxType)</action>
+
+                        <!-- find the root com_invocation -->
+                        <action>ref = copy.parent</action>
+                        <action>rootComInvocCpy = ref</action>
+                        <while>ref.type == prog.com_invocation
+                           <action>rootComInvocCpy = ref</action>
+                           <action>ref = ref.parent</action>
+                        </while>
+
+                        <!-- find the java counterpart method -->
+                        <action>name1 = getLegacyName(widgetClass, name)</action>
+                        <rule>name1 == null and
+                              rootComInvocCpy.parent.type == prog.assign and
+                              rootComInvocCpy.indexPos == 0
+                           <!-- for prop assignment -->
+                           <action>name1 = sprintf("set%s", name)</action>
+                           <action>name1 = getLegacyName(widgetClass, name1)</action>
+                        </rule>
+
+                        <!-- for prop getter or method invocation -->
+                        <rule>name1 == null
+                           <action>name1 = sprintf("get%s", name)</action>
+                           <action>name1 = getLegacyName(widgetClass, name1)</action>
+                        </rule>
+
+                        <!-- for prop getter or method invocation -->
+                        <rule>name1 == null
+                           <action>name1 = sprintf("is%s", name)</action>
+                           <action>name1 = getLegacyName(widgetClass, name1)</action>
+                        </rule>         
+
+                        <rule>name1 == null
+                           <action>printfln("WARN: FWD OCX Extension: Failed to resolve legacy name for %s. Is %s defined in %s?", name, name, widgetClass)</action>
+                           <action>copy.putAnnotation("noop-com", true)</action>
+                        </rule>
+
+                        <rule>name1 != null
+                           <action>reftype1 = -1</action>
+                           <action>reftype = prog.lookupKeywordToken(name1)</action>
+                           <rule>reftype == -1
+                              <action>printfln("WARN: FWD OCX Extension: Failed to resolve keyword for %s.", name1)</action>
+                              <action>copy.putAnnotation("noop-com", true)</action>
+                              <action on="false">reftype1 = lookupAttributeOrMethod(reftype)</action>
+                              <rule on="false">reftype1 == -1
+                                 <action>printfln("WARN: FWD OCX Extension: Failed to resolve attribute or method type for %s.", name1)</action>
+                                 <action>copy.putAnnotation("noop-com", true)</action>
+                              </rule>
+                           </rule>
+
+                           <rule>reftype1 != -1
+                              <!-- is this an indexed COM property being assigned? -->
+                              <action>methPropCpy = rootComInvocCpy.getImmediateChild(createSet(prog.com_method, prog.com_property), null)</action>
+                              <rule>rootComInvocCpy.parent.type == prog.assign and
+                                    rootComInvocCpy.indexPos == 0 and
+                                    methPropCpy.getImmediateChild(prog.com_parameter, null) != null
+
+                                 <!-- get the assigning expression and make it a parameter -->
+                                 <action>ref1 = rootComInvocCpy.nextSibling</action>
+
+                                 <!-- the com parameter will be converted below -->
+                                 <action>ref1.type = prog.com_parameter</action>
+                                 <action>ref1.text = ""</action>
+                                 <action>ref1.move(methPropCpy, -1)</action>
+                              </rule>
+                             <rule>map != null and ref3 != null and ref3.compareWith(methPropCpy, true)
+                                 <action>printfln("hit: %s", ref3.text)</action>
+                                 <action>name3=map.get(ref3.text.toLowerCase())</action>
+                                 <action>printfln("name3= %s", name3)</action>
+                                 <action>methPropCpy.text = name3</action>
+                              </rule>
+                              <!-- set legacy node properties -->
+                              <action>ref2.type = reftype1</action>
+                              <action>ref2.text = name1</action>
+                              <action>ref2.putAnnotation("oldtype", #(long)reftype)</action>
+
+                              <!-- lookup for com parameters and replace with parameters -->
+                              <action>ref1 = ref2.firstChild</action>
+                              <while>ref1 != null
+                                 <action>ref2 = null</action>
+                                 <rule>ref1.type == prog.com_parameter
+                                    <action>ref1.type = prog.parameter</action>
+                                    <action>ref2 = ref1</action>
+                                 </rule>
+                                 <action>ref1 = ref1.nextSibling</action>
+
+                                 <!-- check for empty parameter and remove it -->
+                                 <rule>ref2 != null and ref2.numChildren == 0
+                                    <action>ref2.remove()</action>
+                                 </rule>
+                              </while>
+
+                              <!-- convert var_com_handle into var_handle -->
+                              <action>copy.type = prog.var_handle</action>
+                              <action>copy.text = ocxHandle</action>
+
+                              <!-- remove second token since is no longer needed -->
+                              <rule>cache != null
+                                 <action>cache.remove()</action>
+                              </rule>
+
+                              <!-- rearrange nodes and remove unnecessary node -->
+                              <rule>isConversion == false and copy.parent.parent != null and copy.parent.parent.type == prog.com_invocation
+                                 <action>ref1 = copy.parent</action>
+                                 <action>invoc = copy.parent.parent</action>
+                                 <action>invoc.type = prog.colon</action>
+                                 <action>copy.move(invoc, 0)</action>
+                                 <action>ref1.remove()</action>
+                              </rule>
+
+                              <!-- when is conversion  -->
+                              <rule>isConversion == true and copy.parent != null and copy.parent.type == prog.com_invocation
+                                 <action>invoc = copy.parent</action>
+                                 <action>invoc.type = prog.colon</action>
+                              </rule>
+                           </rule>
+                        </rule>
+                     </rule>
+      </function>
       <!-- Assumes that we are under browse DISPLAY node. Finds the field reference (which defines
            the column) with the given schemaname under the DISPLAY node.-->
       <function name="doTrigger">

=== modified file 'rules/convert/expressions.rules'
--- rules/convert/expressions.rules	2019-10-30 17:07:39 +0000
+++ rules/convert/expressions.rules	2020-02-25 17:15:52 +0000
@@ -4,7 +4,7 @@
 ** Module   : expressions.rules
 ** Abstract : converts Progress expression root and wrapping/unwrapping
 **
-** Copyright (c) 2005-2019, Golden Code Development Corporation.
+** Copyright (c) 2005-2020, Golden Code Development Corporation.
 **
 ** _#_ _I_ __Date__ __JPRM__ ____________________________Description_____________________________
 ** 001 GES 20050726   @21855 Good working version which converts Progress
@@ -212,6 +212,7 @@
 **                           parameters to COPY-LOB statement. Data types for such arguments must
 **                           be verified at runtime.
 ** 085 CA  20190927          Added support for direct Java access from 4GL code.
+** 086 SBI 20200225          Added Calendar widget support.
 */
 -->
 

=== modified file 'rules/convert/methods_attributes.rules'
--- rules/convert/methods_attributes.rules	2020-01-21 14:39:51 +0000
+++ rules/convert/methods_attributes.rules	2020-02-25 17:13:50 +0000
@@ -462,6 +462,7 @@
 ** 215 MAG 20191127          Added TabSet TabIndex property.
 **                           Added TabSet MultiLine property.
 **                           Added TabSet show() method.
+** 216 SBI 20200225          Added Calendar widget support.
 */
  -->
  
@@ -943,7 +944,10 @@
          <rule>list.put(prog.kw_att_file, execLib("cr_descr", "EmailSender"                , "attachFile"             , ""                       , true       ))</rule>
          <rule>list.put(prog.kw_att_url , execLib("cr_descr", "EmailSender"                , "attachURL"              , ""                       , true       ))</rule>
          <rule>list.put(prog.kw_build_tr, execLib("cr_descr", "TreeFace"                   , "isBuildingTree"         , "setBuildingTree"        , true       ))</rule>
+         <rule>list.put(prog.kw_calcufmt, execLib("cr_descr", "Calendar"                   , "getCustomFormat"        , "setCustomFormat"        , true       ))</rule>
+         <rule>list.put(prog.kw_calfmtst, execLib("cr_descr", "Calendar"                   , "getFormatStyle"         , "setFormatStyle"         , true       ))</rule>
          <rule>list.put(prog.kw_callback, execLib("cr_descr", "FWDTimer"                   , "getCallback"            , "setCallback"            , true       ))</rule>
+         <rule>list.put(prog.kw_calvalue, execLib("cr_descr", "Calendar"                   , "getDateTimeValue"       , "setDateTimeValue"       , true       ))</rule>
          <rule>list.put(prog.kw_cease   , execLib("cr_descr", "FWDTimer"                   , "cease"                  , null                     , true       ))</rule>
          <rule>list.put(prog.kw_clr_all , execLib("cr_descr", "TreeFace"                   , "clearAll"               , null                     , true       ))</rule>
          <rule>list.put(prog.kw_clr_attl, execLib("cr_descr", "EmailSender"                , "clearAttachmentsList"   , ""                       , true       ))</rule>
@@ -1026,6 +1030,8 @@
          <rule>list.put(prog.kw_is_c_vis, execLib("cr_descr", "TreeList"                   , "isColumnVisible"        , null                     , true       ))</rule>
          <rule>list.put(prog.kw_is_msel , execLib("cr_descr", "TreeFace"                   , "isMultiSelect"          , null                     , true       ))</rule>
          <rule>list.put(prog.kw_is_n_exp, execLib("cr_descr", "TreeFace"                   , "isNodeExpanded"         , null                     , true       ))</rule>
+         <rule>list.put(prog.kw_msicon,   execLib("cr_descr", "WidgetExtension"            , "getMouseIcon"           , "setMouseIcon"        , true       ))</rule>
+         <rule>list.put(prog.kw_mspntnum, execLib("cr_descr", "WidgetExtension"            , "getMousePointerNum"     , "setMousePointerNum"     , true       ))</rule>
          <rule>list.put(prog.kw_m_d_i_p,  execLib("cr_descr", "TreeList"                   , "moveDownInParent"       , null                     , true       ))</rule>
          <rule>list.put(prog.kw_m_u_i_p,  execLib("cr_descr", "TreeList"                   , "moveUpInParent"         , null                     , true       ))</rule>
          <rule>list.put(prog.kw_n_height, execLib("cr_descr", "TreeFace"                   , "getNodeHeight"          , "setNodeHeight"          , true       ))</rule>
@@ -1058,10 +1064,7 @@
          <rule>list.put(prog.kw_pb_enabl, execLib("cr_descr", "ProgressBar"                , "isEnabled"              , "setEnabled"             , true       ))</rule>
          <rule>list.put(prog.kw_pb_max,   execLib("cr_descr", "ProgressBar"                , "getMax"                 , "setMax"                 , true       ))</rule>
          <rule>list.put(prog.kw_pb_min,   execLib("cr_descr", "ProgressBar"                , "getMin"                 , "setMin"                 , true       ))</rule>
-         <rule>list.put(prog.kw_pb_msico, execLib("cr_descr", "ProgressBar"                , "getMouseIcon"           , "setMouseIcon"           , true       ))</rule>
-         <rule>list.put(prog.kw_pb_mspnt, execLib("cr_descr", "ProgressBar"                , "getProgBarMousePointer" , "setProgBarMousePointer" , true       ))</rule>
          <rule>list.put(prog.kw_pb_orien, execLib("cr_descr", "ProgressBar"                , "getOrientation"         , "setOrientation"         , true       ))</rule>
-         <rule>list.put(prog.kw_pb_rfrsh, execLib("cr_descr", "ProgressBar"                , "progBarRefresh"         , null                     , true       ))</rule>
          <rule>list.put(prog.kw_pb_scrol, execLib("cr_descr", "ProgressBar"                , "getScrolling"           , "setScrolling"           , true       ))</rule>
          <rule>list.put(prog.kw_pb_value, execLib("cr_descr", "ProgressBar"                , "getValue"               , "setValue"               , true       ))</rule>
          <rule>list.put(prog.kw_rem_noco, execLib("cr_descr", "TreeFace"                   , "getRemoveNodeOnCollapse", "setRemoveNodeOnCollapse", true       ))</rule>
@@ -1069,6 +1072,7 @@
          <rule>list.put(prog.kw_report,   execLib("cr_descr", "FwdReport"                  , "getReport"              , "setReport"              , true       ))</rule>
          <rule>list.put(prog.kw_res_base, execLib("cr_descr", "HtmlBrowser"                , "getResourceBase"        , "setResourceBase"        , true       ))</rule>
          <rule>list.put(prog.kw_resort,   execLib("cr_descr", "TreeList"                   , "resort"                 , null                     , true       ))</rule>
+         <rule>list.put(prog.kw_rfrsh_ui, execLib("cr_descr", "WidgetExtension"            , "refreshUI"              , null                     , true       ))</rule>
          <rule>list.put(prog.kw_rpt_csv,  execLib("cr_descr", "FwdReport"                  , "exportReportCsv"        , null                     , true       ))</rule>
          <rule>list.put(prog.kw_rpt_dsgn, execLib("cr_descr", "FwdReport"                  , "getReportDesign"        , "setReportDesign"        , true       ))</rule>
          <rule>list.put(prog.kw_rpt_html, execLib("cr_descr", "FwdReport"                  , "exportReportHtml"       , null                     , true       ))</rule>

=== modified file 'rules/convert/ui_statements.rules'
--- rules/convert/ui_statements.rules	2020-01-29 14:38:53 +0000
+++ rules/convert/ui_statements.rules	2020-02-24 18:12:00 +0000
@@ -968,6 +968,10 @@
             <action>explicitw = "DialogBox"</action>
          </rule>
          <rule>explicitw == null and 
+               (downPath("KW_CALENDAR") or evalLib("match_widget_type", "calendar"))
+            <action>explicitw = "Calendar"</action>
+         </rule>
+         <rule>explicitw == null and 
                (downPath("KW_EDITOR") or evalLib("match_widget_type", "editor"))
             <action>explicitw = "Editor"</action>
          </rule>

=== modified file 'rules/gaps/expressions.rules'
--- rules/gaps/expressions.rules	2020-01-21 14:39:51 +0000
+++ rules/gaps/expressions.rules	2020-02-24 19:29:54 +0000
@@ -1739,6 +1739,7 @@
          
          <rule>widAndQual.put(prog.kw_as       , rw.cvt_lvl_full       | rw.rt_lvl_full)</rule>
          <rule>widAndQual.put(prog.kw_browse   , rw.cvt_lvl_full       | rw.rt_lvl_full)</rule>
+         <rule>widAndQual.put(prog.kw_calendar , rw.cvt_lvl_partial    | rw.rt_lvl_partial)</rule>
          <rule>widAndQual.put(prog.kw_tabset   , rw.cvt_lvl_partial    | rw.rt_lvl_partial)</rule>
          <rule>widAndQual.put(prog.kw_img_list , rw.cvt_lvl_partial    | rw.rt_lvl_partial)</rule>
          <rule>widAndQual.put(prog.kw_treelist , rw.cvt_lvl_full       | rw.rt_lvl_partial)</rule>
@@ -2032,12 +2033,12 @@
          <rule>map.put(prog.kw_pb_appea, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
          <rule>map.put(prog.kw_pb_brsty, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
          <rule>map.put(prog.kw_pb_enabl, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
-         <rule>map.put(prog.kw_pb_msico, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
-         <rule>map.put(prog.kw_pb_mspnt, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
+         <rule>map.put(prog.kw_msicon,   rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
+         <rule>map.put(prog.kw_mspntnum, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
          <rule>map.put(prog.kw_pb_max,   rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
          <rule>map.put(prog.kw_pb_min,   rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
          <rule>map.put(prog.kw_pb_orien, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
-         <rule>map.put(prog.kw_pb_rfrsh, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
+         <rule>map.put(prog.kw_rfrsh_ui, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
          <rule>map.put(prog.kw_pb_scrol, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
          <rule>map.put(prog.kw_pb_value, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>
          <rule>map.put(prog.kw_ole_drag, rw.cvt_lvl_full    | rw.rt_lvl_full)</rule>

=== modified file 'src/com/goldencode/p2j/uast/progress.g'
--- src/com/goldencode/p2j/uast/progress.g	2020-01-21 14:39:51 +0000
+++ src/com/goldencode/p2j/uast/progress.g	2020-02-25 17:19:38 +0000
@@ -2086,7 +2086,8 @@
 ** 382 MAG 20191127          Added TabSet TabIndex property.
 **                           Added TabSet MULTILINE property.
 **                           Added TabSet show() method.
-*/                             
+** 384 SBI 20200225          Added Calendar widget support.
+*/
 
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -4320,6 +4321,8 @@
       KW_MOV_2_B;
       KW_MOV_2_T;
       KW_MOV_2EOF;
+      KW_MSICON;    // FWD extension, not real 4GL! WidgetExtension:MOUSE-ICON
+      KW_MSPNTNUM;    // FWD extension, not real 4GL! WidgetExtension:MOUSE-POINTER-NUM
       KW_MTIME;
       KW_MULT_CMP;
       KW_MULTIPLE;
@@ -4599,6 +4602,7 @@
       KW_RET_2SD;
       KW_RETURNS;
       KW_REV_FROM;
+      KW_RFRSH_UI;    // FWD extension, not real 4GL! WidgetExtension:REFRESH-UI
       KW_RGB_VAL;
       KW_RIGHT;
       KW_RIGHT_AL;
@@ -5183,7 +5187,7 @@
          KW_IL_OVER;
          KW_IL_UMASK;
          KW_ADD_IMG;    // end IMAGELIST
-         KW_MS_CLICK;   // begin PROGESS-BAR
+         KW_MS_CLICK;   // begin PROGRESS-BAR
          KW_MS_DOWN;
          KW_MS_MOVE;
          KW_MS_UP;
@@ -5214,11 +5218,17 @@
          KW_PB_MIN;
          KW_PB_MAX;
          KW_PB_VALUE;
-         KW_PB_RFRSH;
          KW_PB_SCROL;
-         KW_PB_MSPNT;
-         KW_PB_MSICO;
-         KW_COM_DATA;    // end PROGESS-BAR
+         KW_COM_DATA;    // end PROGRESS-BAR
+         KW_CALBGCLR;    // CALENDAR:CalendarBackColor
+         KW_CALFGCLR;    // CALENDAR:CalendarForeColor
+         KW_CALTITBG;    // CALENDAR:TitleBackColor
+         KW_CALTITFG;    // CALENDAR:TitleForeColor
+         KW_CALTRLFG;    // CALENDAR:TrailingForeColor
+         KW_CALENDAR;    // CALENDAR
+         KW_CALCUFMT;    // CALENDAR:CustomFormat
+         KW_CALFMTST;    // CALENDAR:FormatStyle
+         KW_CALVALUE;    // CALENDAR:DateTimeValue
       END_FWD_EXTENSION;
    END_UNRESERVED;
    BEGIN_CALLGRAPH;
@@ -8069,9 +8079,17 @@
       attrsAndMethods.put( KW_BYTES_R , ATTR_INT     );
       attrsAndMethods.put( KW_BYTES_W , ATTR_INT     );
       attrsAndMethods.put( KW_CACHE,    ATTR_INT     );
+      attrsAndMethods.put( KW_CALBGCLR, ATTR_INT     ); // FWD CALENDAR extension, not real 4GL
+      attrsAndMethods.put( KW_CALCUFMT, ATTR_CHAR    ); // FWD CALENDAR:CustomFormat extension, not real 4GL
+      attrsAndMethods.put( KW_CALFGCLR, ATTR_INT     ); // FWD CALENDAR extension, not real 4GL
+      attrsAndMethods.put( KW_CALFMTST, ATTR_INT     ); // FWD CALENDAR:FormatStyle extension, not real 4GL
+      attrsAndMethods.put( KW_CALVALUE, ATTR_CHAR    ); // FWD CALENDAR:DateTimeValue extension, not real 4GL
       attrsAndMethods.put( KW_CALL_NAM, ATTR_CHAR    );
       attrsAndMethods.put( KW_CALL_TYP, ATTR_INT     );
       attrsAndMethods.put( KW_CALLBACK, ATTR_CHAR    ); // FWD TIMER extension
+      attrsAndMethods.put( KW_CALTITBG, ATTR_INT     ); // FWD CALENDAR extension, not real 4GL
+      attrsAndMethods.put( KW_CALTITFG, ATTR_INT     ); // FWD CALENDAR extension, not real 4GL
+      attrsAndMethods.put( KW_CALTRLFG, ATTR_INT     ); // FWD CALENDAR extension, not real 4GL
       attrsAndMethods.put( KW_CANC_BRK, METH_LOGICAL );
       attrsAndMethods.put( KW_CANCEL_B, ATTR_HANDLE  );
       attrsAndMethods.put( KW_CANCEL_R, METH_LOGICAL );
@@ -8662,8 +8680,10 @@
       attrsAndMethods.put( KW_MIN_VAL , ATTR_INT     );
       attrsAndMethods.put( KW_MIN_W_C , ATTR_DEC     );
       attrsAndMethods.put( KW_MIN_W_P , ATTR_INT     );
+      attrsAndMethods.put( KW_MSICON,   ATTR_CHAR    ); // FWD extension, not real 4GL
       attrsAndMethods.put( KW_MSG_AREA, ATTR_LOGICAL );
       attrsAndMethods.put( KW_MSG_AFNT, ATTR_INT     );
+      attrsAndMethods.put( KW_MSPNTNUM, ATTR_INT     ); // FWD extension, not real 4GL
       attrsAndMethods.put( KW_MNEMON,   ATTR_INT     ); // FWD extension, not real 4GL!
       attrsAndMethods.put( KW_MODIFIED, ATTR_LOGICAL );
       attrsAndMethods.put( KW_MOU_PTR , ATTR_CHAR    );
@@ -8786,10 +8806,7 @@
       attrsAndMethods.put( KW_PB_ENABL, ATTR_LOGICAL ); // FWD PROGRESS-BAR extension, not real 4GL
       attrsAndMethods.put( KW_PB_MAX,   ATTR_DEC     ); // FWD PROGRESS-BAR extension, not real 4GL
       attrsAndMethods.put( KW_PB_MIN,   ATTR_DEC     ); // FWD PROGRESS-BAR extension, not real 4GL
-      attrsAndMethods.put( KW_PB_MSICO, ATTR_CHAR    ); // FWD PROGRESS-BAR extension, not real 4GL
-      attrsAndMethods.put( KW_PB_MSPNT, ATTR_INT     ); // FWD PROGRESS-BAR extension, not real 4GL
       attrsAndMethods.put( KW_PB_ORIEN, ATTR_INT     ); // FWD PROGRESS-BAR extension, not real 4GL
-      attrsAndMethods.put( KW_PB_RFRSH, METH_VOID    ); // FWD PROGRESS-BAR extension, not real 4GL
       attrsAndMethods.put( KW_PB_SCROL, ATTR_INT     ); // FWD PROGRESS-BAR extension, not real 4GL
       attrsAndMethods.put( KW_PB_VALUE, ATTR_DEC     ); // FWD PROGRESS-BAR extension, not real 4GL
       attrsAndMethods.put( KW_PREF_DS , ATTR_LOGICAL );
@@ -8871,6 +8888,7 @@
       attrsAndMethods.put( KW_REPOS_2R, METH_LOGICAL );
       attrsAndMethods.put( KW_REPOS_2I, METH_LOGICAL );
       attrsAndMethods.put( KW_REQ_INFO, ATTR_CLASS   );
+      attrsAndMethods.put( KW_RFRSH_UI, METH_VOID    ); // FWD extension, not real 4GL
       attrsAndMethods.put( KW_RSP_INFO, ATTR_CLASS   );
       attrsAndMethods.put( KW_RES_BASE, ATTR_CHAR    ); // FWD extension, not real 4GL
       attrsAndMethods.put( KW_RESET   , METH_LOGICAL );
@@ -24472,6 +24490,7 @@
          | KW_COMBO_BX   { is_widget = true; }
          | KW_CNTRL_FR   { is_widget = true; }
          | KW_DIALOG     { is_widget = true; }
+         | KW_CALENDAR   { is_widget = true; }
          | KW_EDITOR     { is_widget = true; }
          | KW_FILL_IN    { is_widget = true; }
          | KW_FRAME      { is_widget = true; }
@@ -33137,6 +33156,9 @@
          new Keyword("bytes-written"                  ,  0, KW_BYTES_W , false),
          new Keyword("cache"                          ,  0, KW_CACHE   , false),
          new Keyword("cache-size"                     ,  0, KW_CACHE_SZ, false),
+         new Keyword("calendar"                       ,  0, KW_CALENDAR, false), // FWD CALENDAR extension, not a real 4GL keyword
+         new Keyword("CalendarBackColor"              ,  0, KW_CALBGCLR, false), // FWD CALENDAR extension
+         new Keyword("CalendarForeColor"              ,  0, KW_CALFGCLR, false), // FWD CALENDAR extension
          new Keyword("call"                           ,  0, KW_CALL    , true ),
          new Keyword("call-name"                      ,  0, KW_CALL_NAM, false),
          new Keyword("call-type"                      ,  0, KW_CALL_TYP, false),
@@ -33323,6 +33345,7 @@
          new Keyword("cursor-offset"                  ,  0, KW_CUR_OFF , false),
          new Keyword("cursor-right"                   ,  0, KW_CUR_RGHT, false),  // missing in keyword index, found elsewhere in lang ref
          new Keyword("cursor-up"                      ,  0, KW_CUR_UP  , false),  // missing in keyword index, found elsewhere in lang ref
+         new Keyword("CustomFormat"                   ,  0, KW_CALCUFMT, false),  // CALENDAR:CustomFormat extension
          new Keyword("cut"                            ,  0, KW_CUT     , false),  // missing in keyword index, found in prog handbook as a key function
          new Keyword("data-bind"                      ,  0, KW_DATA_BND, false),
          new Keyword("data-entry-return"              , 14, KW_DATA_E_R, false),
@@ -33338,6 +33361,7 @@
          new Keyword("dataset-handle"                 ,  0, KW_DSET_HND, true ),
          new Keyword("date"                           ,  0, KW_DATE    , false),
          new Keyword("datetime"                       ,  0, KW_DATETIME, false),  // missing from keyword index and UNTESTED at this time
+         new Keyword("DateTimeValue"                  ,  0, KW_CALVALUE, false),  // CALENDAR:VALUE extension
          new Keyword("datetime-tz"                    ,  0, KW_DATE_TZ , false),  // missing from keyword index and UNTESTED at this time
          new Keyword("date-format"                    ,  6, KW_DATE_FMT, false),
          new Keyword("date-separator"                 ,  0, KW_DATE_SEP, false),  // FWD extension, not a real 4GL keyword
@@ -33645,6 +33669,7 @@
          new Keyword("form-long-input"                ,  0, KW_FORM_LIN, false),
          new Keyword("format"                         ,  4, KW_FORMAT  , true ),  // handles "separate keyword" form too
          new Keyword("format-datetime"                ,  0, KW_FMT_DT  , false),  // not a real keyword, but is normally defined in the PSC webspeed 4GL code
+         new Keyword("FormatStyle"                    ,  0, KW_CALFMTST, false),  // FWD extension for CALENDAR:FormatStyle, not a real 4GL keyword
          new Keyword("formatted"                      ,  8, KW_FORMATTE, false),
          new Keyword("forward-only"                   ,  0, KW_FWD_ONLY, false),  // missing from keyword index and UNTESTED at this time
          new Keyword("forwards"                       ,  7, KW_FORWARD , false),  // handles "separate keyword" forward too
@@ -34384,6 +34409,8 @@
          new Keyword("object"                         ,  0, KW_OBJECT  , false),
          new Keyword("octet-length"                   ,  0, KW_OCT_LEN , false),
          new Keyword("ocx-mouse-button"               ,  0, KW_OCX_MBTN, false),  // FWD extension, not a real 4GL keyword
+         new Keyword("mouse-icon"                     ,  0, KW_MSICON,   false),  // FWD WidgetExtension extension, not a real 4GL keyword
+         new Keyword("mouse-pointer-num"              ,  0, KW_MSPNTNUM, false),  // FWD WidgetExtension extension, not a real 4GL keyword
          new Keyword("ocx-mouse-shift"                ,  0, KW_OCX_MSHT, false),  // FWD extension, not a real 4GL keyword
          new Keyword("ocx-mouse-x"                    ,  0, KW_OCX_MSX , false),  // FWD extension, not a real 4GL keyword
          new Keyword("ocx-mouse-y"                    ,  0, KW_OCX_MSY , false),  // FWD extension, not a real 4GL keyword
@@ -34547,12 +34574,9 @@
          new Keyword("pb-appearance"                  ,  0, KW_PB_APPEA, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
          new Keyword("pb-border-style"                ,  0, KW_PB_BRSTY, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
          new Keyword("pb-enabled"                     ,  0, KW_PB_ENABL, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
-         new Keyword("pb-mouse-icon"                  ,  0, KW_PB_MSICO, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
-         new Keyword("pb-mouse-pointer"               ,  0, KW_PB_MSPNT, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
          new Keyword("pb-max"                         ,  0, KW_PB_MAX  , false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
          new Keyword("pb-min"                         ,  0, KW_PB_MIN  , false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
          new Keyword("pb-orientation"                 ,  0, KW_PB_ORIEN, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
-         new Keyword("pb-refresh"                     ,  0, KW_PB_RFRSH, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
          new Keyword("pb-scrolling"                   ,  0, KW_PB_SCROL, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
          new Keyword("pb-value"                       ,  0, KW_PB_VALUE, false),  // FWD PROGRESS-BAR extension, not a real 4GL keyword
          new Keyword("progress-source"                , 10, KW_PROG_SRC, false),
@@ -34622,6 +34646,7 @@
          new Keyword("refresh"                        ,  0, KW_REFRESH , false),
          new Keyword("refresh-audit-policy"           ,  0, KW_REFR_A_P, false),
          new Keyword("refreshable"                    ,  0, KW_REFRABLE, false),
+         new Keyword("refresh-ui"                     ,  0, KW_RFRSH_UI, false),  // FWD WidgetExtension extension, not a real 4GL keyword
          new Keyword("register-domain"                ,  0, KW_REG_DMN , false),
          new Keyword("reject-changes"                 ,  0, KW_REJ_CHGS, false),  // missing from keyword index and UNTESTED at this time
          new Keyword("reject-row-changes"             ,  0, KW_REJ_RCHG, false),  // missing from keyword index and UNTESTED at this time
@@ -35083,6 +35108,9 @@
          new Keyword("ttcodepage"                     ,  0, KW_TTCP    , false),  // missing from keyword index and UNTESTED at this time
          new Keyword("type"                           ,  0, KW_TYPE    , false),
          new Keyword("type-of"                        ,  0, KW_TYPE_OF , false),
+         new Keyword("TitleBackColor"                 ,  0, KW_CALTITBG, false),  // CALENDAR:TitleBackColor
+         new Keyword("TitleForeColor"                 ,  0, KW_CALTITFG, false),  // CALENDAR:TitleForeColor
+         new Keyword("TrailingForeColor"              ,  0, KW_CALTRLFG, false),  // CALENDAR:TrailingForeColor
          new Keyword("unbox"                          ,  6, KW_UNBOX   , false),
          new Keyword("unbuffered"                     ,  6, KW_UNBUF   , false),
          new Keyword("undo"                           ,  0, KW_UNDO    , true ),

=== added file 'src/com/goldencode/p2j/ui/Calendar.java'
--- src/com/goldencode/p2j/ui/Calendar.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/Calendar.java	2020-02-25 16:18:50 +0000
@@ -0,0 +1,437 @@
+/*
+** Module   : Calendar.java
+** Abstract : Calendar widget interface.
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description-----------------------------------
+** 001 SBI 20200114 Created initial version.
+*/
+
+/*
+** 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;
+
+import com.goldencode.p2j.util.*;
+
+
+/**
+ * Defines exported methods and constants for the calendar widget.
+ */
+@LegacyResource(resource = LegacyResource.CALENDAR)
+public interface Calendar
+extends WrappedResource,
+        ComData,
+        OcxMouse,
+        OcxDragDrop,
+        CommonWidget,
+        CustomFont,
+        Enabled,
+        WidgetExtension
+{
+   /**
+    * Returns the current date time value as a character value.
+    * 
+    * @return   The current date time value as a character value
+    */
+   @LegacyAttribute(name = "DateTimeValue")
+   public character getDateTimeValue();
+   
+   /**
+    * Sets the current date value.
+    * 
+    * @param    value
+    *           The new current date value
+    */
+   @LegacyAttribute(name = "DateTimeValue", setter = true)
+   public void setDateTimeValue(date value);
+
+   /**
+    * Sets the current date and time value.
+    * 
+    * @param    value
+    *           The new current date and time value
+    */
+   @LegacyAttribute(name = "DateTimeValue", setter = true)
+   public void setDateTimeValue(datetime value);
+
+   /**
+    * Sets the current date and time value as a character value.
+    * 
+    * @param    value
+    *           The new current date and time value as a character value
+    */
+   @LegacyAttribute(name = "DateTimeValue", setter = true)
+   public void setDateTimeValue(character value);
+
+   /**
+    * Sets the current date and time value representing by a string.
+    * 
+    * @param    value
+    *           The new current date and time value representing by a string
+    */
+   @LegacyAttribute(name = "DateTimeValue", setter = true)
+   public void setDateTimeValue(String value);
+
+   /**
+    * Determines whether dates and times are displayed using standard or custom formatting.
+    * 
+    * @return   The date and time display format style constant.
+    */
+   @LegacyAttribute(name = "FormatStyle")
+   integer getFormatStyle();
+   
+   /**
+    * Determines whether dates and times are displayed using standard or custom formatting.
+    * 
+    * @param    formatStyle
+    *           The date and time display format style constant.
+    */
+   @LegacyAttribute(name = "FormatStyle", setter = true)
+   void setFormatStyle(integer formatStyle);
+   
+   /**
+    * Returns the background color used to display the month portion of the dropdown calendar.
+    * 
+    * @return   The background color for the dropdown calendar given by the BGR encoding
+    */
+   @LegacyAttribute(name = "CalendarBackColor")
+   integer getCalendarBackColor();
+   
+   /**
+    * Sets the background color used to display the month portion of the dropdown calendar.
+    * 
+    * @param    calendarBackColor
+    *           The background color for the dropdown calendar given by the BGR encoding
+    */
+   @LegacyAttribute(name = "CalendarBackColor", setter = true)
+   void setCalendarBackColor(integer calendarBackColor);
+   
+   /**
+    * Returns the foreground color used to display text in the month portion of the dropdown calendar.
+    * 
+    * @return   The foreground color for the dropdown calendar given by the BGR encoding
+    */
+   @LegacyAttribute(name = "CalendarForeColor")
+   integer getCalendarForeColor();
+   
+   /**
+    * Sets the foreground color used to display text in the month portion of the dropdown calendar.
+    * 
+    * @param    calendarForeColor
+    *           The foreground color for the dropdown calendar given by the BGR encoding
+    */
+   @LegacyAttribute(name = "CalendarForeColor", setter = true)
+   void setCalendarForeColor(integer calendarForeColor);
+   
+   /**
+    * Returns the background color used to display the title portion of the dropdown calendar.
+    * 
+    * @return   The background color for the title portion of the dropdown calendar given by
+    *           the BGR encoding.
+    */
+   @LegacyAttribute(name = "TitleBackColor")
+   integer getCalendarTitleBackColor();
+   
+   /**
+    * Sets the background color used to display the title portion of the dropdown calendar.
+    * 
+    * @param    titleBackColor
+    *           The background color for the title portion of the dropdown calendar given by
+    *           the BGR encoding.
+    */
+   @LegacyAttribute(name = "TitleBackColor", setter = true)
+   void setCalendarTitleBackColor(integer titleBackColor);
+   
+   /**
+    * Returns the foreground color used to display the title portion of the dropdown calendar.
+    * 
+    * @return   The foreground color for the title portion of the dropdown calendar given by
+    *           the BGR encoding.
+    */
+   @LegacyAttribute(name = "TitleForeColor")
+   integer getCalendarTitleForeColor();
+   
+   /**
+    * Sets the foreground color used to display the title portion of the dropdown calendar.
+    * 
+    * @param    titleForeColor
+    *           The foreground color for the title portion of the dropdown calendar given by
+    *           the BGR encoding.
+    */
+   @LegacyAttribute(name = "TitleForeColor", setter = true)
+   void setCalendarTitleForeColor(integer titleForeColor);
+   
+   /**
+    * Returns the foreground color used to display the days at the beginning and end of
+    * the dropdown calendar that are from previous and following months.
+    * 
+    * @return   The foreground color for the previous and following months of
+    *           the dropdown calendar given by the BGR encoding.
+    */
+   @LegacyAttribute(name = "TrailingForeColor")
+   integer getCalendarTrailingForeColor();
+   
+   /**
+    * Sets the foreground color used to display the days at the beginning and end of the dropdown
+    * calendar that are from previous and following months.
+    * 
+    * @param    trailingForeColor
+    *           The foreground color for the previous and following months of
+    *           the dropdown calendar given by the BGR encoding.
+    */
+   @LegacyAttribute(name = "TrailingForeColor", setter = true)
+   void setCalendarTrailingForeColor(integer trailingForeColor);
+   
+   /**
+    * Returns whether the control displays a checkbox to the left of the date. When unchecked,
+    * no date is selected.
+    * 
+    * @return   True if it displays a checkbox to the left of the date, otherwise false.
+    */
+   @LegacyAttribute(name = "CheckBox")
+   logical isCheckBox();
+   
+   /**
+    * Sets whether the control displays a checkbox to the left of the date. When unchecked,
+    * no date is selected.
+    * 
+    * @param    enabledCheckBox
+    *           The logical value indicating a checkbox is displayed or not.
+    */
+   @LegacyAttribute(name = "CheckBox", setter = true)
+   void setCheckBox(logical enabledCheckBox);
+   
+   /**
+    * Returns the custom format string used to format the date and/or time displayed
+    * in the control.
+    * 
+    * @return   The custom format string
+    */
+   @LegacyAttribute(name = "CustomFormat")
+   character getCustomFormat();
+   
+   /**
+    * Sets the custom format string used to format the date and/or time displayed in the control.
+    * 
+    * @param    customFormat
+    *           The custom format string
+    */
+   @LegacyAttribute(name = "CustomFormat", setter = true)
+   void setCustomFormat(character customFormat);
+   
+   /**
+    * Sets the custom format string used to format the date and/or time displayed in the control.
+    * 
+    * @param    customFormat
+    *           The custom format string
+    */
+   @LegacyAttribute(name = "CustomFormat", setter = true)
+   void setCustomFormat(String customFormat);
+   
+   /**
+    * Returns the day number [1-31] for the current date.
+    * 
+    * @return   The day number for the current date
+    */
+   integer getDay();
+   
+   /**
+    * Sets the day number [1-31] for the current date.
+    * 
+    * @param    day
+    *           The day number for the current date
+    */
+   void setDay(integer day);
+   
+   /**
+    * Returns the day of the week [0-6] for the current date.
+    * 
+    * @return   The day of the week for the current date
+    */
+   integer getDayOfWeek();
+   
+   /**
+    * Sets the day of the week [0-6] for the current date.
+    * 
+    * @param    dayOfWeek
+    *           The day of the week for the current date
+    */
+   void setDayOfWeek(integer dayOfWeek);
+   
+   /**
+    * Returns the hour number [0-23] for the current time.
+    * 
+    * @return   The hour number for the current time
+    */
+   integer getHour();
+   
+   /**
+    * Sets the hour number [0-23] for the current time.
+    * 
+    * @param    hour
+    *           The hour number for the current time
+    */
+   void setHour(integer hour);
+   
+   /**
+    * Returns the latest date that can be displayed or accepted by the control.
+    * 
+    * @return   The latest date that can be displayed or accepted by this control
+    */ 
+   date getMaxDate();
+   
+   /**
+    * Sets the latest date that can be displayed or accepted by the control.
+    * 
+    * @param    dateMaxDate
+    *           The new latest date
+    */
+   void setMaxDate(date dateMaxDate);
+   
+   /**
+    * Returns the earliest date that can be displayed or accepted by the control.
+    * 
+    * @return   The earliest date
+    */
+   date getMinDate();
+   
+   /**
+    * Sets the earliest date that can be displayed or accepted by the control.
+    * 
+    * @param    dateMinDate
+    *           The new earliest date
+    */
+   void setMinDate(date dateMinDate);
+   
+   /**
+    * Returns the minute number [0-59] for the current time.
+    * 
+    * @return   The minute number for the current time
+    */
+   integer getMinute();
+   
+   /**
+    * Sets the minute number [0-59] for the current time.
+    * 
+    * @param    minute
+    *           The minute number for the current time
+    */
+   void setMinute(integer minute);
+   
+   /**
+    * Returns the month number [1-12] for the current date.
+    * 
+    * @return   The month number for the current date
+    */
+   integer getMonth();
+   
+   /**
+    * Sets the month number [1-12] for the current date.
+    * 
+    * @param    month
+    *           The month number for the current date
+    */
+   void setMonth(integer month);
+   
+   /**
+    * Returns the second number [0-59] for the current time.
+    * 
+    * @return   The second number for the current time
+    */
+   integer getSecond();
+   
+   /**
+    * Sets the second number [0-59] for the current time.
+    * 
+    * @param    second
+    *           The second number for the current time
+    */
+   void setSecond(integer second);
+   
+   /**
+    * Returns a value that determines whether an updown (spin) button is used to modify dates
+    * instead of a dropdown calendar.
+    * 
+    * @return   True value if the updown spin button is used to modify dates instead of a dropdown
+    *           calendar, otherwise false value.
+    */
+   @LegacyAttribute(name = "UpDown")
+   logical isUpDown();
+
+   /**
+    * Sets a value that determines whether an updown (spin) buttons are used to modify dates
+    * instead of a dropdown calendar.
+    * 
+    * @param    upDown
+    *           The logical value indicating if the up and down buttons are used to modify dates
+    */
+   @LegacyAttribute(name = "UpDown", setter = true)
+   void setUpDown(logical upDown);
+   
+   /**
+    * Returns the year for the current date.
+    * 
+    * @return   The year for the current date
+    */
+   integer getYear();
+   
+   /**
+    * Sets the year for the current date.
+    * 
+    * @param    year
+    *           The year for the current date
+    */
+   void setYear(integer year);
+}

=== added file 'src/com/goldencode/p2j/ui/CalendarConfig.java'
--- src/com/goldencode/p2j/ui/CalendarConfig.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/CalendarConfig.java	2020-02-25 12:56:55 +0000
@@ -0,0 +1,574 @@
+/*
+** Module   : CalendarConfig.java
+** Abstract : The calendar widget configuration settings.
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description-----------------------------------
+** 001 SBI 20200114 Created initial version.
+*/
+
+/*
+** 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;
+
+import java.io.*;
+import java.text.*;
+import java.time.format.*;
+import java.time.temporal.*;
+import java.util.*;
+
+import com.goldencode.p2j.util.*;
+
+
+
+/**
+ * Represents the calendar settings.
+ */
+public class CalendarConfig
+extends WidgetConfigExt
+{
+   /**
+    * Displays the date in long format. The default format string for this style is defined by
+    * the current locale settings, which produces output like "Saturday, February 1, 2020". 
+    */
+   public static final int DTS_LONGDATEFORMAT  = 0;
+   
+   /**
+    * Displays the date in short format. The default format string for this style is defined by
+    * the current locale settings, which produces output like "02/01/20". 
+    */
+   public static final int DTS_SHORTDATEFORMAT = 1;
+   
+   /**
+    * Displays the time. The default format string for this style is defined by the current locale
+    * settings, which produces output like "10:29:00 AM". 
+    */
+   public static final int DTS_TIMEFORMAT      = 2;
+   
+   /**
+    * Displays date and time using the format string specified in the 'CustomFormat' property.
+    */
+   public static final int DTS_CUSTOMFORMAT    = 3;
+   
+   /**
+    * The format style constant, it can be one of DTS_LONGDATEFORMAT, DTS_SHORTDATEFORMAT,
+    * DTS_TIMEFORMAT, DTS_CUSTOMFORMAT.
+    */
+   public int formatStyle;
+   
+   /** Holds the current date time format.*/
+   public String customFormat;
+   
+   /** Holds the current date or time value as a string value in the ISO-8601 calendar system. */
+   public String dateTimeValue;
+   
+   /** Holds the day number from 1 to 31. */
+   public int day;
+   
+   /** Holds the month number from 1 to 12. */
+   public int month;
+   
+   /** Holds the year number. */
+   public int year;
+   
+   /** Holds the day of week number from 0 to 6 */
+   public int dayOfWeek;
+   
+   /** Holds the hour number from 0 to 23. */
+   public int hour;
+   
+   /** Holds the minute number from 0 to 59. */
+   public int minute;
+   
+   /** Holds the second number from 0 to 59. */
+   public int second;
+   
+   /** Holds the latest possible date */
+   public date latestDate;
+   
+   /** Holds the earliest possible date */
+   public date earliestDate;
+   
+   /** The background color for the dropdown calendar given by the BGR encoding */
+   public int bodyBackgroundColor;
+   
+   /** The foreground color for the dropdown calendar given by the BGR encoding */
+   public int bodyForegroundColor;
+   
+   /**
+    * The background color for the title portion of the dropdown calendar
+    * given by the BGR encoding.
+    */
+   public int titleBackgroundColor;
+   
+   /**
+    * The foreground color for the title portion of the dropdown calendar
+    * given by the BGR encoding.
+    */
+   public int titleForegroundColor;
+   
+   /**
+    * The foreground color for the previous and following months of
+    * the dropdown calendar given by the BGR encoding
+    */
+   public int trailingForegroundColor;
+   
+   /** True if a checkbox to the left of the date must be displayed, otherwise false */
+   public boolean checkBox;
+   
+   /** Its value indicates if the up and down buttons are used to modify dates or not*/
+   public boolean upDown;
+   
+   /** Represents an index of the selected field of the date/time content */
+   public int selectedFieldIndex;
+   
+   /**
+    * Converts the control format style to the pair of date and time legacy format style used
+    * by java time api. If for the date format style element or for the time format style element
+    * there doesn't exist a such correspondent legacy format style used by java time api,
+    * then this element holds a null value.
+    * 
+    * @param    formatStyle
+    *           The format style constant provided for the calendar widget
+    * 
+    * @return   The array of the date and time legacy format styles used by java time api.
+    */
+   public static final FormatStyle[] toDateTimeStyles(int formatStyle)
+   {
+      FormatStyle[] legacyStyles = new FormatStyle[2];
+      
+      switch(formatStyle)
+      {
+         case DTS_LONGDATEFORMAT:
+            legacyStyles[0] = FormatStyle.FULL;
+            break;
+         case DTS_SHORTDATEFORMAT:
+            legacyStyles[0] = FormatStyle.SHORT;
+            break;
+         case DTS_TIMEFORMAT:
+            legacyStyles[1] = FormatStyle.SHORT;
+            break;
+         default:
+      }
+      
+      return legacyStyles;
+   }
+   
+   /**
+    * Tests if the given format style is one of the predefined localized system date and time
+    * formats to display dates as full or short form styles or to display times in a short form.
+    *  
+    * @param    formatStyle
+    *           The given format style
+    * 
+    * @return   true if the given format style is one of the predefined formats, otherwise false.
+    */
+   public static final boolean isLocalizedFormat(int formatStyle)
+   {
+      return formatStyle != DTS_CUSTOMFORMAT;
+   }
+   
+   /**
+    * The default constructor.
+    */
+   public CalendarConfig()
+   {
+      selectedFieldIndex = 0;
+   }
+   
+   /**
+    * Creates this instance using the given identifier.
+    * 
+    * @param    id
+    *           The widget identifier
+    */
+   public CalendarConfig(int id)
+   {
+      super(id);
+      selectedFieldIndex = 0;
+   }
+   
+   /**
+    * Creates this instance using the given identifier.
+    * 
+    * @param    id
+    *           The widget identifier
+    */
+   public CalendarConfig(WidgetId id)
+   {
+      super(id);
+      selectedFieldIndex = 0;
+   }
+   
+   /**
+    * Set new values from the provided configuration instance.
+    * 
+    * @param    config
+    *           The instance from which to take values.
+    */
+   @Override
+   public void applyConfig(WidgetConfig config)
+   {
+      super.applyConfig(config);
+      
+      CalendarConfig cfg = (CalendarConfig) config;
+      
+      formatStyle   = cfg.formatStyle; 
+      customFormat  = cfg.customFormat;
+      dateTimeValue = cfg.dateTimeValue;
+      
+      day = cfg.day;
+      month = cfg.month;
+      year = cfg.year;
+      
+      dayOfWeek = cfg.dayOfWeek;
+      
+      hour = cfg.hour;
+      minute = cfg.minute;
+      second = cfg.second;
+      
+      latestDate   = cfg.latestDate;
+      earliestDate = cfg.earliestDate;
+      
+      bodyBackgroundColor = cfg.bodyBackgroundColor;
+      bodyForegroundColor = cfg.bodyForegroundColor;
+      titleBackgroundColor = cfg.titleBackgroundColor;
+      titleForegroundColor = cfg.titleForegroundColor;
+      trailingForegroundColor = cfg.trailingForegroundColor;
+      
+      checkBox = cfg.checkBox;
+      upDown   = cfg.upDown;
+      
+      selectedFieldIndex = cfg.selectedFieldIndex;
+   }
+   
+   /**
+    * Replacement for the default object reading method. The latest
+    * state is read from the input source.  The parent's state is
+    * read first.
+    * 
+    * @param    in
+    *           The input source from which fields will be restored.
+    *
+    * @throws   IOException
+    *           In case of I/O errors.
+    * @throws   ClassNotFoundException
+    *           If payload can't be instantiated.
+    */
+   @Override
+   public void readExternal(ObjectInput in)
+   throws IOException,
+          ClassNotFoundException
+   {
+      super.readExternal(in);
+      
+      formatStyle   = in.readInt();
+      customFormat  = (String) in.readObject();
+      dateTimeValue = (String) in.readObject();
+      
+      day = in.readInt();
+      month = in.readInt();
+      year = in.readInt();
+      
+      dayOfWeek = in.readInt();
+      
+      hour = in.readInt();
+      minute = in.readInt();
+      second = in.readInt();
+      
+      latestDate   = (date) in.readObject();
+      earliestDate = (date) in.readObject();
+      
+      bodyBackgroundColor = in.readInt();
+      bodyForegroundColor = in.readInt();
+      titleBackgroundColor = in.readInt();
+      titleForegroundColor = in.readInt();
+      trailingForegroundColor = in.readInt();
+      
+      checkBox = in.readBoolean();
+      upDown = in.readBoolean();
+      
+      selectedFieldIndex = in.readInt();
+   }
+
+   /**
+    * Replacement for the default object writing method. The latest
+    * state is written to the output destination.  The parent's state is
+    * written first.
+    * 
+    * @param    out
+    *           The output destination to which fields will be saved.
+    *
+    * @throws   IOException
+    *           In case of I/O errors.
+    */
+   @Override
+   public void writeExternal(ObjectOutput out)
+   throws IOException
+   {
+      super.writeExternal(out);
+      
+      out.writeInt(formatStyle);
+      out.writeObject(customFormat);
+      out.writeObject(dateTimeValue);
+      
+      out.writeInt(day);
+      out.writeInt(month);
+      out.writeInt(year);
+      
+      out.writeInt(dayOfWeek);
+      
+      out.writeInt(hour);
+      out.writeInt(minute);
+      out.writeInt(second);
+      
+      out.writeObject(latestDate);
+      out.writeObject(earliestDate);
+      
+      out.writeInt(bodyBackgroundColor);
+      out.writeInt(bodyForegroundColor);
+      out.writeInt(titleBackgroundColor);
+      out.writeInt(titleForegroundColor);
+      out.writeInt(trailingForegroundColor);
+      
+      out.writeBoolean(checkBox);
+      out.writeBoolean(upDown);
+      
+      out.writeInt(selectedFieldIndex);
+   }
+   
+   /**
+    * Adapts DateFormat to ChronoField and resolves positions of date and time fields within
+    * date and time patterns.
+    */
+   public static class DateFormatFieldsAdapter
+   {
+      /** Maps DateFormat fields to ChronoField enums. */
+      private static final Map<Integer, ChronoField> DATA_CHRONO_FIELDS;
+      
+      static
+      {
+         HashMap<Integer, ChronoField> temp = new HashMap<>(DateTimeField.values().length);
+         
+         for (DateTimeField dateTimeField : DateTimeField.values())
+         {
+            temp.put(dateTimeField.getDateFormat(), dateTimeField.getChronoField());
+         }
+         
+         DATA_CHRONO_FIELDS = Collections.unmodifiableMap(temp);
+      }
+      
+      /**
+       * Returns positions of date and time fields used by the given date time pattern string.
+       * 
+       * @param    dateTimePattern
+       *           The given date time pattern string
+       * @param    date
+       *           The given date to display
+       * 
+       * @return   The ordered array of positions of date and time fields
+       */
+      public static FieldPosition[] getDateTimeFieldPositions(String dateTimePattern, Date date)
+      {
+         SimpleDateFormat positionParser = new SimpleDateFormat(dateTimePattern);
+         
+         String displayedDate = positionParser.format(date);
+         
+         DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
+         builder.appendPattern(dateTimePattern);
+         DateTimeFormatter formatter = builder.toFormatter();
+         TemporalAccessor accessor = formatter.parse(displayedDate);
+
+         ArrayList<FieldPosition> positions = new ArrayList<FieldPosition>(DATA_CHRONO_FIELDS.size());
+         
+         StringBuffer buffer = new StringBuffer();
+         
+         for (DateTimeField dateTimeField : DateTimeField.values())
+         {
+            if (accessor.isSupported(dateTimeField.getChronoField()))
+            {
+               FieldPosition fieldPosition = new FieldPosition(dateTimeField.getDateFormat());
+               
+               positionParser.format(date, buffer, fieldPosition);
+               
+               if (fieldPosition.getBeginIndex() != fieldPosition.getEndIndex())
+               {
+                  positions.add(fieldPosition);
+               }
+               
+               buffer.setLength(0);
+            }
+         }
+         
+         positions.sort(Comparator.<FieldPosition>comparingInt(FieldPosition::getBeginIndex));
+         
+         return positions.toArray(new FieldPosition[positions.size()]);
+      }
+      
+      /**
+       * Maps the DataFormat field to ChronoField.
+       * 
+       * @param    dataField
+       *           The given DataFormat field
+       * 
+       * @return   The target ChronoField
+       */
+      public static ChronoField getChronoField(int dataField)
+      {
+         return DATA_CHRONO_FIELDS.get(Integer.valueOf(dataField));
+      }
+      
+      /**
+       * Defines date time fields that maps DateFormat date/time constants to ChronoField members.
+       */
+      public enum DateTimeField
+      {
+         /**
+          * Represents the day of month field.
+          */
+         DATE_FIELD(DateFormat.DATE_FIELD, ChronoField.DAY_OF_MONTH),
+         
+         /**
+          * Represents the month field.
+          */
+         MONTH_FIELD(DateFormat.MONTH_FIELD, ChronoField.MONTH_OF_YEAR),
+         
+         /**
+          * Represents the year field.
+          */
+         YEAR_FIELD(DateFormat.YEAR_FIELD, ChronoField.YEAR),
+         
+         /**
+          * Represents the day of week field.
+          */
+         DAY_OF_WEEK_FIELD(DateFormat.DAY_OF_WEEK_FIELD, ChronoField.DAY_OF_WEEK),
+         
+         /**
+          * Represents the hour field of the zero-based 24-hour clock.
+          */
+         HOUR_OF_DAY0_FIELD(DateFormat.HOUR_OF_DAY0_FIELD, ChronoField.HOUR_OF_DAY),
+         
+         /**
+          * Represents the hour field of the one-based 24-hour clock.
+          */
+         HOUR_OF_DAY1_FIELD(DateFormat.HOUR_OF_DAY1_FIELD, ChronoField.CLOCK_HOUR_OF_DAY),
+         
+         /**
+          * Represents the hour field of the zero-based 12-hour clock.
+          */
+         HOUR0_FIELD(DateFormat.HOUR0_FIELD, ChronoField.HOUR_OF_AMPM),
+         
+         /**
+          * Represents the hour field of the one-based 12-hour clock.
+          */
+         HOUR1_FIELD(DateFormat.HOUR1_FIELD, ChronoField.CLOCK_HOUR_OF_AMPM),
+         
+         /**
+          * Represents the AM/PM field of a 12-hour clock.
+          */
+         AM_PM_FIELD(DateFormat.AM_PM_FIELD, ChronoField.AMPM_OF_DAY),
+         
+         /**
+          * Represents the minute field.
+          */
+         MINUTE_FIELD(DateFormat.MINUTE_FIELD, ChronoField.MINUTE_OF_HOUR),
+         
+         /**
+          * Represents the second field.
+          */
+         SECOND_FIELD(DateFormat.SECOND_FIELD, ChronoField.SECOND_OF_MINUTE);
+         
+         /** DateFormat fields used to display date and time */
+         private final int dateFormat;
+         
+         /** ChronoField used to display date and time */
+         private final ChronoField chronoField;
+         
+         /**
+          * Constructs this enum member with the given pair of DateFormat and ChronoField fields.
+          * 
+          * @param    dateFormat
+          *           The given DateFormat field
+          * @param    chronoField
+          *           The given ChronoField field
+          */
+         private DateTimeField(int dateFormat, ChronoField chronoField)
+         {
+            this.dateFormat = dateFormat;
+            this.chronoField = chronoField;
+         }
+         
+         /**
+          * Returns the DateFormat constant corresponding to this enum member.
+          * 
+          * @return   The corresponding DateFormat constant
+          */
+         public int getDateFormat()
+         {
+            return dateFormat;
+         }
+         
+         /**
+          * Returns the ChronoField member corresponding to this enum member.
+          * 
+          * @return   The corresponding ChronoField member.
+          */
+         public ChronoField getChronoField()
+         {
+            return chronoField;
+         }
+      }
+   }
+}

=== added file 'src/com/goldencode/p2j/ui/CalendarWidget.java'
--- src/com/goldencode/p2j/ui/CalendarWidget.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/CalendarWidget.java	2020-02-25 14:33:57 +0000
@@ -0,0 +1,768 @@
+/*
+** Module   : CalendarWidget.java
+** Abstract : The calendar widget on the server side.
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description-----------------------------------
+** 001 SBI 20200114 Created initial version.
+*/
+
+/*
+** 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;
+
+import com.goldencode.p2j.util.*;
+
+
+/**
+ * Implements the calendar widget on the server side.
+ */
+public class CalendarWidget
+extends ControlEntityExt<CalendarConfig>
+implements Calendar
+{
+   /**
+    * Creates the instance of the calendar widget.
+    */
+   public CalendarWidget()
+   {
+      this(false);
+   }
+   
+   /**
+    * Creates the instance of the calendar widget.
+    * 
+    * @param   dynamic
+    *          The flag indicating if this is a static or dynamic resource.
+    */
+   public CalendarWidget(boolean dynamic)
+   {
+      this(dynamic, new CalendarConfig());
+   }
+   
+   /**
+    * Creates the instance of the calendar widget.
+    * 
+    * @param    dynamic
+    *           The flag indicating if this is a static or dynamic resource.
+    * @param    config
+    *           The widget configuration settings
+    */
+   public CalendarWidget(boolean dynamic, CalendarConfig config)
+   {
+      super(dynamic, config);
+   }
+   
+   /**
+    * Return a value that determines whether a form or control can respond to user-generated events.
+    * 
+    * @return   The current state whether a form or control can respond to user-generated events
+    */
+   @Override
+   public logical isEnabled()
+   {
+      return this.isSensitive();
+   }
+
+   /**
+    * Sets a value that determines whether a form or control can respond to user-generated events.
+    * 
+    * @param    enabled
+    *           The new state whether a form or control can respond to user-generated events
+    */
+   @Override
+   public void setEnabled(logical enabled)
+   {
+      this.setSensitive(enabled);
+   }
+
+   /**
+    * Sets a value that determines whether a form or control can respond to user-generated events.
+    * 
+    * @param    enabled
+    *           The new state whether a form or control can respond to user-generated events
+    */
+   @Override
+   public void setEnabled(boolean enabled)
+   {
+      this.setSensitive(enabled);
+   }
+
+   /**
+    * Returns the current date time value as a character value.
+    * 
+    * @return   The current date time value as a character value
+    */
+   @Override
+   public character getDateTimeValue()
+   {
+      return new character(config.dateTimeValue);
+   }
+
+   /**
+    * Sets the current date and time value.
+    * 
+    * @param    value
+    *           The new current date and time value
+    */
+   @Override
+   public void setDateTimeValue(datetime value)
+   {
+      config.dateTimeValue = value.getIsoDate();
+      
+      pushWidgetAttr(new String[] {"dateTimeValue", "formatStyle"},
+                     new Object[] {config.dateTimeValue, config.formatStyle});
+   }
+
+   /**
+    * Sets the current date value.
+    * 
+    * @param    value
+    *           The new current date value
+    */
+   @Override
+   public void setDateTimeValue(date value)
+   {
+      setDateTimeValue(new datetime(value));
+   }
+
+   /**
+    * Sets the current date and time value as a character value.
+    * 
+    * @param    value
+    *           The new current date and time value as a character value
+    */
+   @Override
+   public void setDateTimeValue(character value)
+   {
+      setDateTimeValue(new datetime(value));
+   }
+   
+   /**
+    * Sets the current date and time value representing by a string.
+    * 
+    * @param    value
+    *           The new current date and time value representing by a string
+    */
+   @Override
+   public void setDateTimeValue(String value)
+   {
+      setDateTimeValue(new datetime(value));
+   }
+   
+   /**
+    * Determines whether dates and times are displayed using standard or custom formatting.
+    * 
+    * @return   The date and time display format style constant.
+    */
+   @Override
+   public integer getFormatStyle()
+   {
+      return new integer(config.formatStyle);
+   }
+   
+   /**
+    * Determines whether dates and times are displayed using standard or custom formatting.
+    * 
+    * @param    formatStyle
+    *           The date and time display format style constant.
+    */
+   @Override
+   public void setFormatStyle(integer formatStyle)
+   {
+      if (formatStyle == null || formatStyle.isUnknown())
+      {
+         return;
+      }
+      
+      config.formatStyle = formatStyle.intValue();
+
+      pushWidgetAttr(new String[] {"formatStyle"}, new Object[] {config.formatStyle});
+   }
+   
+   /**
+    * Returns the background color used to display the month portion of the dropdown calendar.
+    * 
+    * @return   The background color for the dropdown calendar
+    */
+   @Override
+   public integer getCalendarBackColor()
+   {
+      return new integer(config.bodyBackgroundColor);
+   }
+   
+   /**
+    * Sets the background color used to display the month portion of the dropdown calendar.
+    * 
+    * @param    calendarBackColor
+    *           The background color for the dropdown calendar
+    */
+   @Override
+   public void setCalendarBackColor(integer calendarBackColor)
+   {
+      if (calendarBackColor == null || calendarBackColor.isUnknown())
+      {
+         return;
+      }
+      
+      config.bodyBackgroundColor = calendarBackColor.intValue();
+      
+      pushWidgetAttr(new String[] {"bodyBackgroundColor"},
+                     new Object[] {config.bodyBackgroundColor});
+   }
+   
+   /**
+    * Returns the foreground color used to display text in the month portion of the dropdown calendar.
+    * 
+    * @return   The foreground color for the dropdown calendar
+    */
+   @Override
+   public integer getCalendarForeColor()
+   {
+      return new integer(config.bodyForegroundColor);
+   }
+   
+   /**
+    * Sets the foreground color used to display text in the month portion of the dropdown calendar.
+    * 
+    * @param    calendarForeColor
+    *           The foreground color for the dropdown calendar
+    */
+   @Override
+   public void setCalendarForeColor(integer calendarForeColor)
+   {
+      if (calendarForeColor == null || calendarForeColor.isUnknown())
+      {
+         return;
+      }
+      
+      config.bodyForegroundColor = calendarForeColor.intValue();
+      
+      pushWidgetAttr(new String[] {"bodyForegroundColor"},
+                     new Object[] {config.bodyForegroundColor});
+   }
+   
+   /**
+    * Returns the background color used to display the title portion of the dropdown calendar.
+    * 
+    * @return   The background color for the title portion of the dropdown calendar.
+    */
+   @Override
+   public integer getCalendarTitleBackColor()
+   {
+      return new integer(config.titleBackgroundColor);
+   }
+   
+   /**
+    * Sets the background color used to display the title portion of the dropdown calendar.
+    * 
+    * @param    titleBackColor
+    *           The background color for the title portion of the dropdown calendar.
+    */
+   @Override
+   public void setCalendarTitleBackColor(integer titleBackColor)
+   {
+      if (titleBackColor == null || titleBackColor.isUnknown())
+      {
+         return;
+      }
+      
+      config.titleBackgroundColor = titleBackColor.intValue();
+      
+      pushWidgetAttr(new String[] {"titleBackgroundColor"},
+                     new Object[] {config.titleBackgroundColor});
+   }
+   
+   /**
+    * Returns the foreground color used to display the title portion of the dropdown calendar.
+    * 
+    * @return   The foreground color for the title portion of the dropdown calendar.
+    */
+   @Override
+   public integer getCalendarTitleForeColor()
+   {
+      return new integer(config.titleForegroundColor);
+   }
+   
+   /**
+    * Sets the foreground color used to display the title portion of the dropdown calendar.
+    * 
+    * @param    titleForeColor
+    *           The foreground color for the title portion of the dropdown calendar.
+    */
+   @Override
+   public void setCalendarTitleForeColor(integer titleForeColor)
+   {
+      if (titleForeColor == null || titleForeColor.isUnknown())
+      {
+         return;
+      }
+      
+      config.titleForegroundColor = titleForeColor.intValue();
+      
+      pushWidgetAttr(new String[] {"titleForegroundColor"},
+                     new Object[] {config.titleForegroundColor});
+   }
+   
+   /**
+    * Returns the foreground color used to display the days at the beginning and end of
+    * the dropdown calendar that are from previous and following months.
+    * 
+    * @return   The foreground color for the previous and following months of
+    *           the dropdown calendar.
+    */
+   @Override
+   public integer getCalendarTrailingForeColor()
+   {
+      return new integer(config.trailingForegroundColor);
+   }
+   
+   /**
+    * Sets the foreground color used to display the days at the beginning and end of the dropdown
+    * calendar that are from previous and following months.
+    * 
+    * @param    trailingForeColor
+    *           The foreground color for the previous and following months of
+    *           the dropdown calendar.
+    */
+   @Override
+   public void setCalendarTrailingForeColor(integer trailingForeColor)
+   {
+      if (trailingForeColor == null || trailingForeColor.isUnknown())
+      {
+         return;
+      }
+      
+      config.trailingForegroundColor = trailingForeColor.intValue();
+      
+      pushWidgetAttr(new String[] {"trailingForegroundColor"},
+                     new Object[] {config.trailingForegroundColor});
+   }
+   
+   /**
+    * Returns whether the control displays a checkbox to the left of the date. When unchecked,
+    * no date is selected.
+    * 
+    * @return   True if it displays a checkbox to the left of the date, otherwise false.
+    */
+   @Override
+   public logical isCheckBox()
+   {
+      return new logical(config.checkBox);
+   }
+   
+   /**
+    * Sets whether the control displays a checkbox to the left of the date. When unchecked,
+    * no date is selected.
+    * 
+    * @param    enabledCheckBox
+    *           The logical value indicating a checkbox is displayed or not.
+    */
+   @Override
+   public void setCheckBox(logical enabledCheckBox)
+   {
+      if (enabledCheckBox == null || enabledCheckBox.isUnknown())
+      {
+         return;
+      }
+      
+      config.checkBox = enabledCheckBox.booleanValue();
+      
+      pushWidgetAttr(new String[] {"checkBox"}, new Object[] {config.checkBox});
+   }
+   
+   /**
+    * Returns the custom format string used to format the date and/or time displayed
+    * in the control.
+    * 
+    * @return   The custom format string
+    */
+   @Override
+   public character getCustomFormat()
+   {
+      return new character(config.customFormat);
+   }
+   
+   /**
+    * Sets the custom format string used to format the date and/or time displayed in the control.
+    * 
+    * @param    customFormat
+    *           The custom format string
+    */
+   @Override
+   public void setCustomFormat(character customFormat)
+   {
+      setCustomFormat(customFormat.getValue());
+   }
+   
+   /**
+    * Sets the custom format string used to format the date and/or time displayed in the control.
+    * 
+    * @param    customFormat
+    *           The custom format string
+    */
+   @Override
+   public void setCustomFormat(String customFormat)
+   {
+      config.customFormat = customFormat;
+      
+      pushWidgetAttr(new String[] {"customFormat", "formatStyle"},
+                     new Object[] {config.customFormat, config.formatStyle});
+   }
+   
+   /**
+    * Returns the day number [1-31] for the current date.
+    * 
+    * @return   The day number for the current date
+    */
+   @Override
+   public integer getDay()
+   {
+      return new integer(config.day);
+   }
+   
+   /**
+    * Sets the day number [1-31] for the current date.
+    * 
+    * @param    day
+    *           The day number for the current date
+    */
+   @Override
+   public void setDay(integer day)
+   {
+      if (day == null || day.isUnknown())
+      {
+         return;
+      }
+      
+      config.day = day.intValue();
+      
+      pushWidgetAttr(new String[] {"day"}, new Object[] {config.day});
+   }
+   
+   /**
+    * Returns the day of the week [0-6] for the current date.
+    * 
+    * @return   The day of the week for the current date
+    */
+   @Override
+   public integer getDayOfWeek()
+   {
+      return new integer(config.dayOfWeek);
+   }
+   
+   /**
+    * Sets the day of the week [0-6] for the current date.
+    * 
+    * @param    dayOfWeek
+    *           The day of the week for the current date
+    */
+   @Override
+   public void setDayOfWeek(integer dayOfWeek)
+   {
+      if (dayOfWeek == null || dayOfWeek.isUnknown())
+      {
+         return;
+      }
+      
+      config.dayOfWeek = dayOfWeek.intValue();
+      
+      pushWidgetAttr(new String[] {"dayOfWeek"}, new Object[] {config.dayOfWeek});
+   }
+   
+   /**
+    * Returns the hour number [0-23] for the current time.
+    * 
+    * @return   The hour number for the current time
+    */
+   @Override
+   public integer getHour()
+   {
+      return new integer(config.hour);
+   }
+   
+   /**
+    * Sets the hour number [0-23] for the current time.
+    * 
+    * @param    hour
+    *           The hour number for the current time
+    */
+   @Override
+   public void setHour(integer hour)
+   {
+      if (hour == null || hour.isUnknown())
+      {
+         return;
+      }
+      
+      config.hour = hour.intValue();
+      
+      pushWidgetAttr(new String[] {"hour"}, new Object[] {config.hour});
+   }
+   
+   
+   /**
+    * Returns the latest date that can be displayed or accepted by the control.
+    * 
+    * @return   The latest date that can be displayed or accepted by this control
+    */ 
+   @Override
+   public date getMaxDate()
+   {
+      return config.latestDate;
+   }
+   
+   /**
+    * Sets the latest date that can be displayed or accepted by the control.
+    * 
+    * @param    dateMaxDate
+    *           The new latest date
+    */
+   @Override
+   public void setMaxDate(date dateMaxDate)
+   {
+      if (dateMaxDate == null || dateMaxDate.isUnknown())
+      {
+         return;
+      }
+      
+      config.latestDate = dateMaxDate;
+      
+      pushWidgetAttr(new String[] {"latestDate"}, new Object[] {config.latestDate});
+   }
+   
+   /**
+    * Returns the earliest date that can be displayed or accepted by the control.
+    * 
+    * @return   The earliest date
+    */
+   @Override
+   public date getMinDate()
+   {
+      return config.earliestDate;
+   }
+   
+   /**
+    * Sets the earliest date that can be displayed or accepted by the control.
+    * 
+    * @param    dateMinDate
+    *           The new earliest date
+    */
+   @Override
+   public void setMinDate(date dateMinDate)
+   {
+      if (dateMinDate == null || dateMinDate.isUnknown())
+      {
+         return;
+      }
+      
+      config.earliestDate = dateMinDate;
+      
+      pushWidgetAttr(new String[] {"earliestDate"}, new Object[] {config.earliestDate});
+   }
+   
+   /**
+    * Returns the minute number [0-59] for the current time.
+    * 
+    * @return   The minute number for the current time
+    */
+   @Override
+   public integer getMinute()
+   {
+      return new integer(config.minute);
+   }
+   
+   /**
+    * Sets the minute number [0-59] for the current time.
+    * 
+    * @param    minute
+    *           The minute number for the current time
+    */
+   @Override
+   public void setMinute(integer minute)
+   {
+      if (minute == null || minute.isUnknown())
+      {
+         return;
+      }
+
+      config.minute = minute.intValue();
+      
+      pushWidgetAttr(new String[] {"minute"}, new Object[] {config.minute});
+   }
+   
+   /**
+    * Returns the month number [1-12] for the current date.
+    * 
+    * @return   The month number for the current date
+    */
+   @Override
+   public integer getMonth()
+   {
+      return new integer(config.month);
+   }
+   
+   /**
+    * Sets the month number [1-12] for the current date.
+    * 
+    * @param    month
+    *           The month number for the current date
+    */
+   @Override
+   public void setMonth(integer month)
+   {
+      if (month == null || month.isUnknown())
+      {
+         return;
+      }
+      
+      config.month = month.intValue();
+      
+      pushWidgetAttr(new String[] {"month"}, new Object[] {config.month});
+   }
+   
+   /**
+    * Returns the second number [0-59] for the current time.
+    * 
+    * @return   The second number for the current time
+    */
+   @Override
+   public integer getSecond()
+   {
+      return new integer(config.second);
+   }
+   
+   /**
+    * Sets the second number [0-59] for the current time.
+    * 
+    * @param    second
+    *           The second number for the current time
+    */
+   @Override
+   public void setSecond(integer second)
+   {
+      if (second == null || second.isUnknown())
+      {
+         return;
+      }
+      
+      config.second = second.intValue();
+      
+      pushWidgetAttr(new String[] {"second"}, new Object[] {config.second});
+   }
+   
+   /**
+    * Returns a value that determines whether an updown (spin) button is used to modify dates
+    * instead of a dropdown calendar.
+    * 
+    * @return   True value if the updown spin button is used to modify dates instead of a dropdown
+    *           calendar, otherwise false value.
+    */
+   @Override
+   public logical isUpDown()
+   {
+      return new logical(config.upDown);
+   }
+   
+   /**
+    * Sets a value that determines whether an updown (spin) button is used to modify dates instead
+    * of a dropdown calendar.
+    * 
+    * @param    upDown
+    *           The logical value indicating if the updown (spin) button is used to modify dates
+    */
+   @Override
+   public void setUpDown(logical upDown)
+   {
+      if (upDown == null || upDown.isUnknown())
+      {
+         return;
+      }
+      
+      config.upDown = upDown.booleanValue();
+      
+      pushWidgetAttr(new String[] {"upDown"}, new Object[] {config.upDown});
+   }
+   
+   /**
+    * Returns the year for the current date.
+    * 
+    * @return   The year for the current date
+    */
+   @Override
+   public integer getYear()
+   {
+      return new integer(config.year);
+   }
+   
+   /**
+    * Sets the year for the current date.
+    * 
+    * @param    year
+    *           The year for the current date
+    */
+   @Override
+   public void setYear(integer year)
+   {
+      if (year == null || year.isUnknown())
+      {
+         return;
+      }
+      
+      config.year = year.intValue();
+      
+      pushWidgetAttr(new String[] {"year"}, new Object[] {config.year});
+   }
+}

=== modified file 'src/com/goldencode/p2j/ui/ConfigManager.java'
--- src/com/goldencode/p2j/ui/ConfigManager.java	2020-01-30 19:23:31 +0000
+++ src/com/goldencode/p2j/ui/ConfigManager.java	2020-02-25 17:07:06 +0000
@@ -27,6 +27,7 @@
 ** 017 HC  20200128 Removed client-server widget config definitions synchronization. Client and
 **                  server config managers can now be dynamically extended with new widget
 **                  configurations.
+** 018 SBI 20200225 Added Calendar widget support.
 */ 
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -164,6 +165,7 @@
       configs.add(ControlFrameConfig.class);
       configs.add(ControlSetConfig.class);
       configs.add(ControlTextConfig.class);
+      configs.add(CalendarConfig.class);
       configs.add(EditorConfig.class);
       configs.add(FieldGroupConfig.class);
       configs.add(FillInConfig.class);

=== added file 'src/com/goldencode/p2j/ui/ControlEntityExt.java'
--- src/com/goldencode/p2j/ui/ControlEntityExt.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/ControlEntityExt.java	2020-02-25 16:18:50 +0000
@@ -0,0 +1,783 @@
+/*
+** Module   : ControlEntityExt.java
+** Abstract : Implements WidgetExtension
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------Description---------------------------------
+** 001 SBI 20200223 Created initial version.
+*/
+
+/*
+** 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;
+
+
+import com.goldencode.p2j.ui.ocx.DataObject;
+import com.goldencode.p2j.ui.ocx.IVBDataObject;
+import com.goldencode.p2j.util.*;
+
+import static com.goldencode.p2j.ui.WidgetConfigExt.*;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.CF_BITMAP;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.CF_DIB;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.CF_EMETAFILE;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.CF_FILES;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.CF_METAFILE;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.CF_PALETTE;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.CF_RTF;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.CF_TEXT;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.COPY;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.DROP_MANUAL;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.DROP_NONE;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.MOVE;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.NONE;
+import static com.goldencode.p2j.ui.ocx.IVBDataObject.SCROLL;
+import static com.goldencode.p2j.util.Utils.isValidValue;
+
+/**
+ * Implements WidgetExtension.
+ * 
+ * @param    <T>
+ *           Data type that extends WidgetConfigExt
+ */
+public class ControlEntityExt<T extends WidgetConfigExt>
+extends ControlEntity<T>
+implements WidgetExtension,
+           ComData,
+           OcxMouse,
+           OcxDragDrop
+{
+   /** Com data */
+   protected comhandle comData;
+   
+   /**
+    * Creates this instance
+    * 
+    * @param    dynamic
+    *           Indicates that widget is not static
+    * @param    ctrl
+    *           The given widget configuration
+    */
+   public ControlEntityExt(boolean dynamic, T ctrl)
+   {
+      super(dynamic, ctrl);
+      
+      comData = DataObject.newInstance(this);
+   }
+
+   /**
+    * Gets the type of mouse pointer displayed when over part of an object.
+    * 
+    * @return mouse pointer
+    */
+   @Override
+   public integer getMousePointerNum()
+   {
+      return new integer(config.mousePointer);
+   }
+
+   /**
+    * Sets the type of mouse pointer displayed when over part of an object.
+    * 
+    * @param   value
+    *          new mouse pointer
+    */
+   @Override
+   public void setMousePointerNum(integer value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute MOUSE-POINTER on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setMousePointerNum(value.intValue());
+      }
+   }
+
+   /**
+    * Sets the type of mouse pointer displayed when over part of an object.
+    * 
+    * @param   value
+    *          new mouse pointer
+    */
+   @Override
+   public void setMousePointerNum(int value)
+   {
+      if (config.mousePointer == value)
+      {
+         return;
+      }
+      
+      if (Utils.isValidValue(value, CC_DEFAULT, CC_ARROW, CC_CROSS, CC_IBEAM, CC_ICON, CC_SIZE,
+               CC_SIZE_NESW, CC_SIZE_NS, CC_SIZE_NWSE, CC_SIZE_EW, CC_UP_ARROW,  CC_HOURGLASS,
+               CC_NO_DROP, CC_ARROW_HOURGLASS, CC_ARROW_QUESTION, CC_SIZE_ALL, CC_CUSTOM))
+      {
+         config.mousePointer = value;
+         pushWidgetAttr("mousePointer", config.mousePointer);
+      }
+      else
+      {
+         ErrorManager.recordOrShowError(5990, String.format(
+                  "Error occured while accessing component property/method: MousePointer\n"
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
+      }            
+   }
+
+   /**
+    * Gets a mouse icon for this control.
+    * 
+    * @return   The mouse icon for this control
+    */
+   @Override
+   public character getMouseIcon()
+   {
+      return new character(config.mouseIcon);
+   }
+
+   /**
+    * Sets a custom mouse icon for this control.
+    * 
+    * @param   value
+    *          The user defined mouse icon
+    */
+   @Override
+   public void setMouseIcon(character value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute MOUSE-ICON on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setMouseIcon(value.getValue());
+      }
+   }
+
+   /**
+    * Sets a custom mouse icon for this control.
+    * 
+    * @param   value
+    *          The user defined mouse icon
+    */
+   @Override
+   public void setMouseIcon(String value)
+   {
+      if (!config.realized)
+      {
+         if (config.mouseIcon == value)
+         {
+            return;
+         }
+         
+         config.mouseIcon = value;
+         pushWidgetAttr("mouseIcon", config.mouseIcon);
+      }
+      else
+      {
+         ErrorManager.recordOrShowError(4053, String.format(
+               "Unable to set MOUSE-ICON because the %s %s has been realised", type(),
+                  widgetName()), false);
+      }
+   }
+
+   /**
+    * Forces a complete repaint of a form or control.
+    */
+   @Override
+   public void refreshUI()
+   {
+      LogicalTerminal.refreshWidget(config.id.asInt());
+   }
+
+   /**
+    * Return whether this control can act as an OLE drop target.
+    * OLEDropConstants - ccOLEDropManual=1 ccOLEDropNone=0
+    * 
+    * @return   The OLE drop mode
+    */
+   @Override
+   public integer getOleDropMode()
+   {
+      return new integer(config.oleDropMode);
+   }
+
+   /**
+    * Set whether this control can act as an OLE drop target.
+    * OLEDropConstants - ccOLEDropManual=1 ccOLEDropNone=0
+    * 
+    * @param    value
+    *           The OLE drop mode
+    */
+   @Override
+   public void setOleDropMode(integer value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute OLE-DROP-MODE on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setOleDropMode(value.intValue());
+      }
+   }
+
+   /**
+    * Set whether this control can act as an OLE drop target.
+    * OLEDropConstants - ccOLEDropManual=1 ccOLEDropNone=0
+    * 
+    * @param    value 
+    *           The OLE drop mode
+    */
+   @Override
+   public void setOleDropMode(int value)
+   {
+      super.setDropTarget(value == IVBDataObject.DROP_MANUAL);
+      
+      if (config.oleDropMode == value)
+      {
+         return;
+      }
+      
+      if (isValidValue(value, DROP_MANUAL, DROP_NONE))
+      {         
+         config.oleDropMode = value;
+         pushWidgetAttr("oleDropMode", config.oleDropMode);
+      }
+      else
+      {
+         ErrorManager.recordOrShowError(5990, String.format(
+                  "Error occured while accessing component property/method: OLEDropMode\n"
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
+      }
+   }
+
+   /**
+    * Start an OLE drag/drop event with the given control as the source.
+    */
+   @Override
+   public void oleDrag()
+   {
+      LogicalTerminal.oleDrag(config.id.asInt());
+   }
+
+   /**
+    * Get the drag and drop mouse effect.
+    * 
+    * @return   The drag and drop mouse effect
+    */
+   @Override
+   public integer getEffect()
+   {
+      return new integer(config.oleEffect);
+   }
+
+   /**
+    * Set the drag and drop mouse effect.
+    * 
+    * @param    value
+    *           The drag and drop mouse effect
+    */
+   @Override
+   public void setEffect(integer value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute OLE-EFFECT on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setEffect(value.intValue());
+      }
+   }
+
+   /**
+    * Set the drag and drop mouse effect.
+    * 
+    * @param    value
+    *           The drag and drop mouse effect
+    */
+     @Override
+   public void setEffect(int value)
+   {
+      if (config.oleEffect == value)
+      {
+         return;
+      }
+      
+      if (isValidValue(value, NONE, COPY, MOVE, SCROLL))
+      {
+         config.oleEffect = value;
+         pushWidgetAttr("oleEffect", config.oleEffect);
+      }
+      else
+      {
+         ErrorManager.recordOrShowError(5990, String.format(
+                  "Error occured while accessing component property/method: Effect\n"
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
+      }
+   }
+
+   /**
+    * Get the OLE X coordinate in character units.
+    * 
+    * @return   The OLE X coordinate
+    */
+   @Override
+   public decimal getOleX()
+   {
+      return new decimal(config.oleX);
+   }
+
+   /**
+    * Set the OLE X coordinate in character units.
+    * 
+    * @param   value
+    *          The OLE X coordinate
+    */
+   @Override
+   public void setOleX(decimal value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute OLE-X on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setOleX(value.doubleValue());
+      }
+   }
+
+   /**
+    * Set the OLE X coordinate in character units.
+    * 
+    * @param   value
+    *          The OLE X coordinate
+    */
+   @Override
+   public void setOleX(double value)
+   {
+      if (!config.realized)
+      {
+         if (config.oleX == value)
+         {
+            return;
+         }
+         
+         config.oleX = value;
+         pushWidgetAttr("oleX", config.oleX);
+      }
+      else
+      {
+         ErrorManager.recordOrShowError(4053, String.format(
+               "Unable to set OLE-X because the %s %s has been realised", type(),
+                  widgetName()), false);
+      }
+   }
+
+   /**
+    * Get the OLE Y coordinate in character units.
+    * 
+    * @return   The OLE Y coordinate
+    */
+   @Override
+   public decimal getOleY()
+   {
+      return new decimal(config.oleY);
+   }
+
+   /**
+    * Set the OLE Y coordinate in character units.
+    * 
+    * @param    value
+    *           The OLE Y coordinate
+    */
+   @Override
+   public void setOleY(decimal value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute OLE-Y on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setOleY(value.doubleValue());
+      }
+   }
+
+   /**
+    * Set the OLE Y coordinate in character units.
+    * 
+    * @param    value
+    *           The OLE Y coordinate
+    */
+   @Override
+   public void setOleY(double value)
+   {
+      if (!config.realized)
+      {
+         if (config.oleY == value)
+         {
+            return;
+         }
+         
+         config.oleY = value;
+         pushWidgetAttr("oleY", config.oleY);
+      }
+      else
+      {
+         ErrorManager.recordOrShowError(4053, String.format(
+               "Unable to set OLE-Y because the %s %s has been realised", type(),
+                  widgetName()), false);
+      }
+   }
+
+   /**
+    * Get the OLE state.
+    * 
+    * @return   The OLE state
+    */
+   @Override
+   public integer getState()
+   {
+      return new integer(config.state);
+   }
+
+   /**
+    * Set the OLE value state.
+    * 
+    * @param    value
+    *           The OLE state
+    */
+   @Override
+   public void setState(integer value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute OLE-STATE on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setState(value.intValue());
+      }
+   }
+
+   /**
+    * Set the OLE value state.
+    * 
+    * @param    value
+    *           The OLE state
+    */
+   @Override
+   public void setState(int value)
+   {
+      if (config.oleState == value)
+      {
+         return;
+      }
+      
+      config.oleState = value;
+      pushWidgetAttr("oleState", config.oleState);
+   }
+
+   /**
+    * Tests if the default cursors are used.
+    * 
+    * @return   The default cursors flag
+    */
+   @Override
+   public logical isDefaultCursors()
+   {
+      return new logical(config.oleDefaultCursors);
+   }
+
+   /**
+    * Set the default cursors flag.
+    * 
+    * @param    value 
+    *           The default cursors flag
+    */
+   @Override
+   public void setDefaultCursors(logical value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute DEFAULT-CURSORS on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setDefaultCursors(value.booleanValue());
+      }
+   }
+
+   /**
+    * Set the default cursors flag.
+    * 
+    * @param    value 
+    *           The default cursors flag
+    */
+   @Override
+   public void setDefaultCursors(boolean value)
+   {
+      if (config.oleDefaultCursors == value)
+      {
+         return;
+      }
+      
+      config.oleDefaultCursors = value;
+      pushWidgetAttr("oleDefaultCursors", config.oleDefaultCursors);
+   }
+
+   /**
+    * Get the data format.
+    * 
+    * @return   The data format
+    */
+   @Override
+   public integer getDataFormat()
+   {
+      return new integer(config.oleDataFormat);
+   }
+
+   /**
+    * Set the data format.
+    * 
+    * @param    value
+    *           The data format.
+    */
+   @Override
+   public void setDataFormat(integer value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute DATA-FORMAT on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setDataFormat(value.intValue());
+      }
+   }
+
+   /**
+    * Set the data format.
+    * 
+    * @param    value
+    *           The data format.
+    */
+   @Override
+   public void setDataFormat(int value)
+   {
+      if (config.oleDataFormat == value)
+      {
+         return;
+      }
+            
+      if (isValidValue(value, CF_TEXT, CF_BITMAP, CF_METAFILE, CF_DIB, 
+               CF_PALETTE, CF_EMETAFILE, CF_FILES, CF_RTF))
+      {         
+         config.oleDataFormat = value;
+         pushWidgetAttr("oleDataFormat", config.oleDataFormat);
+      }
+      else
+      {
+         ErrorManager.recordOrShowError(5990, String.format(
+                  "Error occured while accessing component property/method: DataFormat\n"
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
+      }
+   }
+
+   /**
+    * Get the allowed effects.
+    * 
+    * @return   The allowed effects
+    */
+   @Override
+   public integer getAllowedEffects()
+   {
+      return new integer(config.oleAllowedEffects);
+   }
+
+   /**
+    * Set the allowed effects.
+    * 
+    * @param    value
+    *           The allowed effects
+    */
+   @Override
+   public void setAllowedEffects(integer value)
+   {
+      if (value.isUnknown())
+      {
+         ErrorManager.recordOrShowError(4083, String.format(
+               "Unable to assign UNKNOWN value to attribute ALLOWED-EFFECTS on %s %s", type(),
+                  widgetName()), false);
+      }
+      else
+      {
+         setAllowedEffects(value.intValue());
+      }
+   }
+
+   /**
+    * Set the allowed effects.
+    * 
+    * @param    value
+    *           The allowed effects
+    */
+   @Override
+   public void setAllowedEffects(int value)
+   {
+      if (config.oleAllowedEffects == value)
+      {
+         return;
+      }
+      
+      if (isValidValue(value, NONE, COPY, MOVE, SCROLL))
+      {
+         config.oleAllowedEffects = value;
+         pushWidgetAttr("oleAllowedEffects", config.oleAllowedEffects);
+      }
+      else
+      {
+         ErrorManager.recordOrShowError(5990, String.format(
+                  "Error occured while accessing component property/method: AllowedEffects\n"
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
+      }
+   }
+
+   /**
+    * Get the mouse button number.
+    * 
+    * @return   The mouse button number
+    */
+   @Override
+   public integer getMouseButton()
+   {
+      return new integer(config.mouseButton);
+   }
+
+   /**
+    * Get the mouse shift.
+    * 
+    * @return   The mouse shift
+    */
+   @Override
+   public integer getMouseShift()
+   {
+      return new integer(config.mouseShift);
+   }
+
+   /**
+    * Get the mouse X position.
+    * 
+    * @return   The mouse X position
+    */
+   @Override
+   public integer getMouseX()
+   {
+      return new integer(config.mouseX);
+   }
+
+   /**
+    * Get the mouse Y position.
+    * 
+    * @return   The mouse Y position
+    */
+   @Override
+   public integer getMouseY()
+   {
+      return new integer(config.mouseY);
+   }
+
+   /**
+    * Set the COM data.
+    * 
+    * @param    value
+    *           The COM data
+    */
+   @Override
+   public void setComData(comhandle value)
+   {
+   }
+
+   /**
+    * Get COM data.
+    * 
+    * @return   The COM data
+    */
+   @Override
+   public comhandle getComData()
+   {
+      return comData;
+   }
+}

=== modified file 'src/com/goldencode/p2j/ui/DynamicWidgetFactory.java'
--- src/com/goldencode/p2j/ui/DynamicWidgetFactory.java	2020-01-30 19:23:31 +0000
+++ src/com/goldencode/p2j/ui/DynamicWidgetFactory.java	2020-02-25 17:05:33 +0000
@@ -38,6 +38,7 @@
 ** 022 HC  20190614 Initial implementation of IMAGELIST widget.
 ** 023 MAG 20190920 Added PROGRESS-BAR widget.
 ** 024 HC  20200119 Added extension points to allow to externalize widget implementation.
+** 025 SBI 20200225 Added Calendar widget support.
 */
 
 /*
@@ -206,6 +207,7 @@
       widgetFactory.put(LegacyResource.BUTTON,         ButtonWidget.class);
       widgetFactory.put(LegacyResource.COMBO_BOX,      ComboBoxWidget.class);
       widgetFactory.put(LegacyResource.CONTROL_FRAME,  ControlFrameWidget.class);
+      widgetFactory.put(LegacyResource.CALENDAR, CalendarWidget.class);
       widgetFactory.put(LegacyResource.EDITOR,         EditorWidget.class);
       widgetFactory.put(LegacyResource.FILL_IN,        FillInWidget.class);
       widgetFactory.put(LegacyResource.FRAME,          FrameWidget.class);
@@ -568,6 +570,47 @@
    }
 
    /**
+    * Create a CALENDAR widget and save it in the given handle and in the unnamed pool, if 
+    * available.
+    * 
+    * @param    h
+    *           A handle where to save the create widget. Must be not-null.
+    */
+   public static void createCalendar(handle h)
+   {
+      createCalendar(h, (character) null);
+   }
+
+   /**
+    * Create a CALENDAR widget and save it in the given handle and in the specified pool 
+    * (which can be the unnamed pool if <code>widgetPool</code> is null).
+    * 
+    * @param    h
+    *           A handle where to save the create widget. Must be not-null.
+    * @param    widgetPool
+    *           The named widget pool where to save the widget. If <code>null</code>, the unnamed
+    *           widget pool will be used.
+    */
+   public static void createCalendar(handle h, character widgetPool)
+   {
+      create(LegacyResource.CALENDAR, h, widgetPool);
+   }
+
+   /**
+    * Create a CALENDAR widget and save it in the given handle and in the specified pool 
+    * (which can be the unnamed pool if <code>widgetPool</code> is null).
+    * 
+    * @param    h
+    *           A handle where to save the create widget. Must be not-null.
+    * @param    widgetPool
+    *           The named widget pool where to save the widget. If <code>null</code>, the unnamed
+    *           widget pool will be used.
+    */
+   public static void createCalendar(handle h, String widgetPool)
+   {
+      create(LegacyResource.CALENDAR, h, widgetPool);
+   }
+   /**
     * Create a DIALOG-BOX widget and save it in the given handle and in the unnamed pool, if 
     * available.
     * 

=== modified file 'src/com/goldencode/p2j/ui/OcxDragDrop.java'
--- src/com/goldencode/p2j/ui/OcxDragDrop.java	2019-12-12 09:45:54 +0000
+++ src/com/goldencode/p2j/ui/OcxDragDrop.java	2020-02-25 17:04:24 +0000
@@ -2,7 +2,7 @@
 ** Module   : OcxDragDrop.java
 ** Abstract : interface implemented by widgets which support OLE Drag and Drop.
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description-----------------------------------
 ** 001 MAG 20191023 Created initial version.
@@ -71,7 +71,7 @@
     * Return whether this control can act as an OLE drop target.
     * OLEDropConstants - ccOLEDropManual=1 ccOLEDropNone=0
     * 
-    * @return OLE drop mode
+    * @return   The OLE drop mode
     */
    @LegacyMethod(name = "OLE-DROP-MODE")
    public integer getOleDropMode();
@@ -80,8 +80,8 @@
     * Set whether this control can act as an OLE drop target.
     * OLEDropConstants - ccOLEDropManual=1 ccOLEDropNone=0
     * 
-    * @param   value
-    *          OLE drop mode
+    * @param    value
+    *           The OLE drop mode
     */
    @LegacyMethod(name = "OLE-DROP-MODE")
    public void setOleDropMode(integer value);
@@ -90,8 +90,8 @@
     * Set whether this control can act as an OLE drop target.
     * OLEDropConstants - ccOLEDropManual=1 ccOLEDropNone=0
     * 
-    * @param   value 
-    *          OLE drop mode
+    * @param    value 
+    *           The OLE drop mode
     */
    @LegacyMethod(name = "OLE-DROP-MODE")
    public void setOleDropMode(int value);
@@ -103,183 +103,183 @@
    public void oleDrag();
    
    /**
-    * Get mouse effect.
+    * Get the mouse effect.
     * 
-    * @return mouse effect
+    * @return   The mouse effect
     */
    @LegacyMethod(name = "OLE-EFFECT")
    public integer getEffect();
    
    /**
-    * Set mouse effect.
+    * Set the mouse effect.
     * 
-    * @param   value
-    *          mouse effect
+    * @param    value
+    *           The mouse effect
     */
    @LegacyMethod(name = "OLE-EFFECT")
    public void setEffect(integer value);
 
    /**
-    * Set mouse effect.
+    * Set the mouse effect.
     * 
-    * @param   value
-    *          mouse effect
+    * @param    value
+    *           The mouse effect
     */
    @LegacyMethod(name = "OLE-EFFECT")
    public void setEffect(int value);
    
    /**
-    * Get OLE X coordinate in character units.
+    * Get the OLE X coordinate in character units.
     * 
-    * @return OLE X coordinate
+    * @return   The OLE X coordinate
     */
    @LegacyMethod(name = "OLE-X")
    public decimal getOleX();
    
    /**
-    * Set OLE X coordinate in character units.
+    * Set the OLE X coordinate in character units.
     * 
-    * @param   value
-    *          OLE X coordinate
+    * @param    value
+    *           The OLE X coordinate
     */
    @LegacyMethod(name = "OLE-X")
    public void setOleX(decimal value);
 
    /**
-    * Set OLE X coordinate in character units.
+    * Set the OLE X coordinate in character units.
     * 
-    * @param   value
-    *          OLE X coordinate
+    * @param    value
+    *           The OLE X coordinate
     */
    @LegacyMethod(name = "OLE-X")
    public void setOleX(double value);
 
    /**
-    * Get OLE X coordinate in character units.
+    * Get the OLE Y coordinate in character units.
     * 
-    * @return OLE Y coordinate
+    * @return   The OLE Y coordinate
     */
    @LegacyMethod(name = "OLE-Y")
    public decimal getOleY();
 
    /**
-    * Set OLE Y coordinate in character units.
+    * Set the OLE Y coordinate in character units.
     * 
-    * @param   value
-    *          OLE Y coordinate
+    * @param    value
+    *           The OLE Y coordinate
     */
    @LegacyMethod(name = "OLE-Y")
    public void setOleY(decimal value);
    
    /**
-    * Set OLE Y coordinate in character units.
+    * Set the OLE Y coordinate in character units.
     * 
-    * @param   value
-    *          OLE Y coordinate
+    * @param    value
+    *           The OLE Y coordinate
     */
    @LegacyMethod(name = "OLE-Y")
    public void setOleY(double value);
 
    /**
-    * Get OLE state.
+    * Get the OLE state.
     * 
-    * @return OLE state
+    * @return   The OLE state
     */
    @LegacyMethod(name = "OLE-STATE")
    public integer getState();
    
    /**
-    * Set OLE value state.
+    * Set the OLE value state.
     * 
-    * @param   value
-    *          OLE state
+    * @param    value
+    *           The OLE state
     */
    @LegacyMethod(name = "OLE-STATE")
    public void setState(integer value);
   
    /**
-    * Set OLE state.
+    * Set the OLE state.
     * 
-    * @param   value
-    *          OLE state
+    * @param    value
+    *           The OLE state
     */
    @LegacyMethod(name = "OLE-STATE")
    public void setState(int value);
 
    /**
-    * Get default cursors.
+    * Tests if the default cursors are used.
     * 
-    * @return default cursors
+    * @return   The default cursors flag
     */
    @LegacyMethod(name = "OLE-DEFAULT-CURSORS")
    public logical isDefaultCursors();
    
    /**
-    * Set default cursors.
+    * Set the default cursors flag.
     * 
-    * @param   value 
-    *          default cursors
+    * @param    value 
+    *           The default cursors flag
     */
    @LegacyMethod(name = "OLE-DEFAULT-CURSORS")
    public void setDefaultCursors(logical value);
 
    /**
-    * Set default cursors.
+    * Set the default cursors flag.
     * 
-    * @param   value
-    *          default cursors
+    * @param    value 
+    *           The default cursors flag
     */
    @LegacyMethod(name = "OLE-DEFAULT-CURSORS")
    public void setDefaultCursors(boolean value);
 
    /**
-    * Get data format.
+    * Get the data format.
     * 
-    * @return data format
+    * @return   The data format
     */
    @LegacyMethod(name = "OLE-DATA-FORMAT")
    public integer getDataFormat();
    
    /**
-    * Set data format.
+    * Set the data format.
     * 
-    * @param   value
-    *          data format.
+    * @param    value
+    *           The data format.
     */
    @LegacyMethod(name = "OLE-DATA_FORMAT")
    public void setDataFormat(integer value);
 
    /**
-    * Set data format.
+    * Set the data format.
     * 
-    * @param   value
-    *          data format.
+    * @param    value
+    *           The data format.
     */
    @LegacyMethod(name = "OLE-DATA_FORMAT")
    public void setDataFormat(int value);
 
    /**
-    * Get allowed effects.
+    * Get the allowed effects.
     * 
-    * @return allowed effects
+    * @return   The allowed effects
     */
    @LegacyMethod(name = "OLE-ALLOWED-EFFECTS")
    public integer getAllowedEffects();
    
    /**
-    * Set allowed effects.
+    * Set the allowed effects.
     * 
-    * @param   value
-    *          allowed effects
+    * @param    value
+    *           The allowed effects
     */
    @LegacyMethod(name = "OLE-ALLOWED-EFFECTS")
-
    public void setAllowedEffects(integer value);
+   
    /**
-    * Set allowed effects.
+    * Set the allowed effects.
     * 
-    * @param   value
-    *          allowed effects
+    * @param    value
+    *           The allowed effects
     */
    @LegacyMethod(name = "OLE-ALLOWED-EFFECTS")
    public void setAllowedEffects(int value);

=== modified file 'src/com/goldencode/p2j/ui/ProgressBar.java'
--- src/com/goldencode/p2j/ui/ProgressBar.java	2019-10-24 14:58:49 +0000
+++ src/com/goldencode/p2j/ui/ProgressBar.java	2020-02-25 17:03:00 +0000
@@ -2,10 +2,11 @@
 ** Module   : ProgressBar.java
 ** Abstract : ProgressBar widget interface.
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description-----------------------------------
 ** 001 MAG 20190919 Created initial version.
+** 002 SBI 20200225 Reused the common code.
 */
 
 /*
@@ -262,12 +263,6 @@
     */
    @LegacyMethod(name = "PB-VALUE")
    public void setValue(double value);
-
-   /**
-    * Refresh control.
-    */
-   @LegacyMethod(name = "PB-REFRESH")
-   public void progBarRefresh();
    
    /**
     * Get a value that determines whether the control displays progress with a standard segmented bar or a smooth bar.
@@ -297,56 +292,4 @@
     */
    @LegacyMethod(name = "PB-SCROLLING")
    public void setScrolling(int value);
-   
-   /**
-    * Get mouse pointer.
-    * 
-    * @return mouse pointer
-    */
-   @LegacyMethod(name = "PB-MOUSE-POINTER")
-   public integer getProgBarMousePointer();
-   
-   /**
-    * Set mouse pointer.
-    * 
-    * @param   value
-    *          new mouse pointer.
-    */
-   @LegacyMethod(name = "PB-MOUSE-POINTER")
-   public void setProgBarMousePointer(integer value);
-
-   /**
-    * Set mouse pointer.
-    * 
-    * @param   value 
-    *          new mouse pointer.
-    */
-   @LegacyMethod(name = "PB-MOUSE-POINTER")
-   public void setProgBarMousePointer(int value);
-   
-   /**
-    * Get mouse icon.
-    * 
-    * @return mouse icon
-    */
-   @LegacyMethod(name = "PB-MOUSE-ICON")
-   public character getMouseIcon();
-   
-   /**
-    * Sets a custom mouse icon.
-    * 
-    * @param   value
-    *          user defined mouse icon
-    */
-   @LegacyMethod(name = "PB-MOUSE-ICON")
-   public void setMouseIcon(character value);
-
-   /**
-    * Sets a custom mouse icon.
-    * 
-    * @param   value
-    *          user defined mouse icon
-    */
-   @LegacyMethod(name = "PB-MOUSE-ICON")
-   public void setMouseIcon(String value);   
 }

=== modified file 'src/com/goldencode/p2j/ui/ProgressBarConfig.java'
--- src/com/goldencode/p2j/ui/ProgressBarConfig.java	2019-12-13 08:58:45 +0000
+++ src/com/goldencode/p2j/ui/ProgressBarConfig.java	2020-02-24 05:15:18 +0000
@@ -71,7 +71,7 @@
  * Contains data for PROGRESS-BAR widget.
  */
 public class ProgressBarConfig 
-extends OcxDragDropConfig
+extends WidgetConfigExt
 {
    /** The Progress Bar PB-APPEARANCE attribute. */
    public int appearance = CC_3D;

=== modified file 'src/com/goldencode/p2j/ui/ProgressBarWidget.java'
--- src/com/goldencode/p2j/ui/ProgressBarWidget.java	2019-12-13 08:58:45 +0000
+++ src/com/goldencode/p2j/ui/ProgressBarWidget.java	2020-02-25 17:02:05 +0000
@@ -2,10 +2,11 @@
 ** Module   : ProgressBarWidget.java
 ** Abstract : Implementation of server side PROGRESS-BAR widget.
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description-----------------------------------
 ** 001 MAG 20190923 Created initial version.
+** 002 SBI 20200225 Reused the common code.
 */
 
 /*
@@ -63,22 +64,18 @@
 
 package com.goldencode.p2j.ui;
 
-import com.goldencode.p2j.ui.ocx.*;
 import com.goldencode.p2j.util.*;
 
 import static com.goldencode.p2j.ui.client.gui.ProgressBarGui.*;
-import static com.goldencode.p2j.ui.ocx.IVBDataObject.*;
+import static com.goldencode.p2j.util.Utils.*;
 
 /**
  * Server side PROGRESS-BAR widget implementation.
  */
 public class ProgressBarWidget
-extends ControlEntity<ProgressBarConfig>
+extends ControlEntityExt<ProgressBarConfig>
 implements ProgressBar
 {
-   /* PB-COM-DATA */
-   private comhandle comData = DataObject.newInstance(this);
-
    /**
     * Default constructor.
     */
@@ -191,7 +188,7 @@
       {
          ErrorManager.recordOrShowError(5990, String.format(
                   "Error occured while accessing component property/method: Appearance\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
       }            
    }
 
@@ -242,7 +239,7 @@
       {
          ErrorManager.recordOrShowError(5990, String.format(
                   "Error occured while accessing component property/method: BorderStyle\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
       }
    }
    
@@ -287,13 +284,13 @@
       if (isValidValue(value, CC_ORIENTATION_HORIZONTAL, CC_ORIENTATION_VERTICAL))
       {
          config.orientation = value;
-         pushWidgetAttr("orientation", config.orientation);         
+         pushWidgetAttr("orientation", config.orientation);
       }
       else
       {
          ErrorManager.recordOrShowError(5990, String.format(
                   "Error occured while accessing component property/method: Orientation\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
       }
    }
 
@@ -339,7 +336,7 @@
       {
          ErrorManager.recordOrShowError(5990, String.format(
                   "Error occured while accessing component property/method: Min\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);            
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
       }
       else
       {
@@ -399,7 +396,7 @@
       {
          ErrorManager.recordOrShowError(5990, String.format(
                   "Error occured while accessing component property/method: Max\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);            
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
       }
       else
       {
@@ -459,11 +456,11 @@
       {
          ErrorManager.recordOrShowError(5990, String.format(
                   "Error occured while accessing component property/method: Value\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);            
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
       }
       else
       {         
-         config.value = value;            
+         config.value = value;
          pushWidgetAttr("value", config.value);
       }      
    }
@@ -490,68 +487,6 @@
     * {@inheritDoc}
     */
    @Override
-   public void progBarRefresh()
-   {
-      LogicalTerminal.refreshWidget(config.id.asInt());
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getProgBarMousePointer()
-   {
-      return new integer(config.mousePointer);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setProgBarMousePointer(int value)
-   {
-      if (config.mousePointer == value)
-      {
-         return;
-      }
-      
-      if (isValidValue(value, CC_DEFAULT, CC_ARROW, CC_CROSS, CC_IBEAM, CC_ICON, CC_SIZE, CC_SIZE_NESW, 
-               CC_SIZE_NS, CC_SIZE_NWSE, CC_SIZE_EW, CC_UP_ARROW,  CC_HOURGLASS, CC_NO_DROP, 
-               CC_ARROW_HOURGLASS, CC_ARROW_QUESTION, CC_SIZE_ALL, CC_CUSTOM))
-      {
-         config.mousePointer = value;
-         pushWidgetAttr("mousePointer", config.mousePointer);
-      }
-      else
-      {
-         ErrorManager.recordOrShowError(5990, String.format(
-                  "Error occured while accessing component property/method: MousePointer\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
-      }            
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setProgBarMousePointer(integer value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute MOUSE-POINTER on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setProgBarMousePointer(value.intValue());
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
    public integer getScrolling()
    {
       return new integer(config.scrolling);
@@ -577,7 +512,7 @@
       {
          ErrorManager.recordOrShowError(5990, String.format(
                   "Error occured while accessing component property/method: Scrolling\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
+                  + "Invalid property value\n%s %s", type(), widgetName()), true);
       }      
    }
 
@@ -598,534 +533,4 @@
          setScrolling(value.intValue());
       }
    }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getOleDropMode()
-   {
-      return new integer(config.oleDropMode);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setOleDropMode(int value)
-   {
-      super.setDropTarget(value == IVBDataObject.DROP_MANUAL);
-      
-      if (config.oleDropMode == value)
-      {
-         return;
-      }
-      
-      if (isValidValue(value, DROP_MANUAL, DROP_NONE))
-      {         
-         config.oleDropMode = value;
-         pushWidgetAttr("oleDropMode", config.oleDropMode);
-      }
-      else
-      {
-         ErrorManager.recordOrShowError(5990, String.format(
-                  "Error occured while accessing component property/method: OLEDropMode\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setOleDropMode(integer value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute OLE-DROP-MODE on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setOleDropMode(value.intValue());
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void oleDrag()
-   {
-      LogicalTerminal.oleDrag(config.id.asInt());
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public character getMouseIcon()
-   {
-      return new character(config.mouseIcon);
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setMouseIcon(String value)
-   {
-      if (!config.realized)
-      {
-         if (config.mouseIcon == value)
-         {
-            return;
-         }
-         
-         config.mouseIcon = value;
-         pushWidgetAttr("mouseIcon", config.mouseIcon);
-      }
-      else
-      {
-         ErrorManager.recordOrShowError(4053, String.format(
-               "Unable to set MOUSE-ICON because the %s %s has been realised", type(),
-                  widgetName()), false);
-      }                              
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setMouseIcon(character value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute MOUSE-ICON on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setMouseIcon(value.getValue());
-      }
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getMouseButton()
-   {
-      return new integer(config.mouseButton);
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getMouseShift()
-   {
-      return new integer(config.mouseShift);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getMouseX()
-   {
-      return new integer(config.mouseX);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getMouseY()
-   {
-      return new integer(config.mouseY);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getEffect()
-   {
-      return new integer(config.oleEffect);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setEffect(int value)
-   {
-      if (config.oleEffect == value)
-      {
-         return;
-      }
-      
-      if (isValidValue(value, NONE, COPY, MOVE, SCROLL))
-      {
-         config.oleEffect = value;
-         pushWidgetAttr("oleEffect", config.oleEffect);
-      }
-      else
-      {
-         ErrorManager.recordOrShowError(5990, String.format(
-                  "Error occured while accessing component property/method: Effect\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setEffect(integer value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute OLE-EFFECT on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setEffect(value.intValue());
-      }
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public comhandle getComData()
-   {
-      return comData;
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setComData(comhandle value)
-   {
-      // no-op
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public decimal getOleX()
-   {
-      return new decimal(config.oleX);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setOleX(double value)
-   {
-      if (!config.realized)
-      {
-         if (config.oleX == value)
-         {
-            return;
-         }
-         
-         config.oleX = value;
-         pushWidgetAttr("oleX", config.oleX);
-      }
-      else
-      {
-         ErrorManager.recordOrShowError(4053, String.format(
-               "Unable to set OLE-X because the %s %s has been realised", type(),
-                  widgetName()), false);
-      }                                   
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setOleX(decimal value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute OLE-X on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setOleX(value.doubleValue());
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public decimal getOleY()
-   {
-      return new decimal(config.oleY);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setOleY(double value)
-   {
-      if (!config.realized)
-      {
-         if (config.oleY == value)
-         {
-            return;
-         }
-         
-         config.oleY = value;
-         pushWidgetAttr("oleY", config.oleY);
-      }
-      else
-      {
-         ErrorManager.recordOrShowError(4053, String.format(
-               "Unable to set OLE-Y because the %s %s has been realised", type(),
-                  widgetName()), false);
-      }                                   
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setOleY(decimal value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute OLE-Y on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setOleY(value.doubleValue());
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getState()
-   {
-      return new integer(config.state);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setState(int value)
-   {
-      if (config.oleState == value)
-      {
-         return;
-      }
-      
-      config.oleState = value;
-      pushWidgetAttr("oleState", config.oleState);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setState(integer value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute OLE-STATE on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setState(value.intValue());
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public logical isDefaultCursors()
-   {
-      return new logical(config.oleDefaultCursors);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setDefaultCursors(boolean value)
-   {
-      if (config.oleDefaultCursors == value)
-      {
-         return;
-      }
-      
-      config.oleDefaultCursors = value;
-      pushWidgetAttr("oleDefaultCursors", config.oleDefaultCursors);
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setDefaultCursors(logical value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute DEFAULT-CURSORS on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setDefaultCursors(value.booleanValue());
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getDataFormat()
-   {
-      return new integer(config.oleDataFormat);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setDataFormat(int value)
-   {
-      if (config.oleDataFormat == value)
-      {
-         return;
-      }
-            
-      if (isValidValue(value, CF_TEXT, CF_BITMAP, CF_METAFILE, CF_DIB, 
-               CF_PALETTE, CF_EMETAFILE, CF_FILES, CF_RTF))
-      {         
-         config.oleDataFormat = value;
-         pushWidgetAttr("oleDataFormat", config.oleDataFormat);
-      }
-      else
-      {
-         ErrorManager.recordOrShowError(5990, String.format(
-                  "Error occured while accessing component property/method: DataFormat\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setDataFormat(integer value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute DATA-FORMAT on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setDataFormat(value.intValue());
-      }
-   }
-   
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public integer getAllowedEffects()
-   {
-      return new integer(config.oleAllowedEffects);
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setAllowedEffects(int value)
-   {
-      if (config.oleAllowedEffects == value)
-      {
-         return;
-      }
-      
-      if (isValidValue(value, NONE, COPY, MOVE, SCROLL))
-      {
-         config.oleAllowedEffects = value;
-         pushWidgetAttr("oleAllowedEffects", config.oleAllowedEffects);
-      }
-      else
-      {
-         ErrorManager.recordOrShowError(5990, String.format(
-                  "Error occured while accessing component property/method: AllowedEffects\n"
-                  + "Invalid property value\n%s %s", type(), widgetName()), true);                     
-      }
-   }
-
-   /**
-    * {@inheritDoc}
-    */
-   @Override
-   public void setAllowedEffects(integer value)
-   {
-      if (value.isUnknown())
-      {
-         ErrorManager.recordOrShowError(4083, String.format(
-               "Unable to assign UNKNOWN value to attribute ALLOWED-EFFECTS on %s %s", type(),
-                  widgetName()), false);
-      }
-      else
-      {
-         setAllowedEffects(value.intValue());
-      }
-   }
-   
-   /**
-    * Check if a given value is a valid value.
-    * 
-    * @param   value
-    *          Value to check.
-    * @param   validValues
-    *          List of valid values.
-    *          
-    * @return  <code>true</code> if value is in the list of valid values <code>false</code> otherwise.
-    */
-   private boolean isValidValue(int value, int ... validValues)
-   {
-      for (int v : validValues)
-      {
-         if (value == v)
-         {
-            return true;
-         }
-      }
-      
-      return false;
-   }
 }

=== modified file 'src/com/goldencode/p2j/ui/WidgetConfigDef.java'
--- src/com/goldencode/p2j/ui/WidgetConfigDef.java	2020-01-30 15:38:58 +0000
+++ src/com/goldencode/p2j/ui/WidgetConfigDef.java	2020-02-25 17:00:48 +0000
@@ -22,6 +22,7 @@
 ** 012 HC  20200128 Removed client-server widget config definitions synchronization. Client and
 **                  server config managers can now be dynamically extended with new widget
 **                  configurations.
+** 013 SBI 20200225 Added Calendar widget support.
 */
 
 /*
@@ -223,6 +224,10 @@
       {
          return (T) new ControlSetConfig();
       }
+      else if (cfgCls == CalendarConfig.class)
+      {
+         return (T) new CalendarConfig();
+      }
       else if (cfgCls == EditorConfig.class)
       {
          return (T) new EditorConfig();

=== added file 'src/com/goldencode/p2j/ui/WidgetConfigExt.java'
--- src/com/goldencode/p2j/ui/WidgetConfigExt.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/WidgetConfigExt.java	2020-02-25 16:17:41 +0000
@@ -0,0 +1,193 @@
+/*
+** Module   : WidgetConfigExt.java
+** Abstract : Contains data specific for WidgetExtension
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------Description---------------------------------
+** 001 SBI 20200223 Created initial version.
+*/
+
+/*
+** 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;
+
+import java.io.*;
+
+
+/**
+ * Contains data for widgets that implements WidgetExtension.
+ */
+public class WidgetConfigExt 
+extends OcxDragDropConfig
+{
+   /** Mouse Pointer constants */
+   public static final int CC_DEFAULT          = 0;
+   public static final int CC_ARROW            = 1;
+   public static final int CC_CROSS            = 2;
+   public static final int CC_IBEAM            = 3;
+   public static final int CC_ICON             = 4;
+   public static final int CC_SIZE             = 5;
+   public static final int CC_SIZE_NESW        = 6;
+   public static final int CC_SIZE_NS          = 7;
+   public static final int CC_SIZE_NWSE        = 8;
+   public static final int CC_SIZE_EW          = 9;
+   public static final int CC_UP_ARROW        = 10;
+   public static final int CC_HOURGLASS       = 11;
+   public static final int CC_NO_DROP         = 12;
+   public static final int CC_ARROW_HOURGLASS = 13;
+   public static final int CC_ARROW_QUESTION  = 14;
+   public static final int CC_SIZE_ALL        = 15;
+   public static final int CC_CUSTOM          = 99;
+   
+   /** The MOUSE-ICON attribute. */
+   public String mouseIcon;
+
+   /** The MOUSE-POINTER-NUM attribute. */
+   public int mousePointer;
+
+   /**
+    * Default constructor (only used in de-serialization).
+    */
+   public WidgetConfigExt()
+   {
+   }
+
+   /**
+    * Create a new config and associate it with the given widget.
+    * 
+    * @param    id
+    *           The ID of the widget to which this configuration belongs.
+    */
+   protected WidgetConfigExt(int id)
+   {
+      super(id);
+   }
+
+   /**
+    * Create a new config and associate it with the given widget.
+    * 
+    * @param    id
+    *           The ID of the widget to which this configuration belongs.
+    */
+   protected WidgetConfigExt(WidgetId id)
+   {
+      super(id);
+   }
+
+   /**
+    * Set new values from the provided configuration instance.
+    * 
+    * @param    config
+    *           The instance from which to take values.
+    */
+   @Override
+   public void applyConfig(WidgetConfig config)
+   {
+      super.applyConfig(config);
+
+      if (!(config instanceof WidgetConfigExt))
+      {
+         return;
+      }
+
+      WidgetConfigExt cfg = (WidgetConfigExt) config;
+
+      mouseIcon = cfg.mouseIcon;
+      mousePointer = cfg.mousePointer;
+   }
+
+   /**
+    * Replacement for the default object reading method. The latest
+    * state is read from the input source.  The parent's state is
+    * read first.
+    * 
+    * @param    in
+    *           The input source from which fields will be restored.
+    *
+    * @throws   IOException
+    *           In case of I/O errors.
+    * @throws   ClassNotFoundException
+    *           If payload can't be instantiated.
+    */
+   @Override
+   public void readExternal(ObjectInput in) 
+   throws IOException, 
+          ClassNotFoundException
+   {
+      super.readExternal(in);
+      mouseIcon = (String) in.readObject();
+      mousePointer = in.readInt();
+   }
+
+   /**
+    * Replacement for the default object writing method. The latest
+    * state is written to the output destination.  The parent's state is
+    * written first.
+    * 
+    * @param    out
+    *           The output destination to which fields will be saved.
+    *
+    * @throws   IOException
+    *           In case of I/O errors.
+    */
+   @Override
+   public void writeExternal(ObjectOutput out) 
+   throws IOException
+   {
+      super.writeExternal(out);
+      out.writeObject(mouseIcon);
+      out.writeInt(mousePointer);
+   }
+}

=== added file 'src/com/goldencode/p2j/ui/WidgetExtension.java'
--- src/com/goldencode/p2j/ui/WidgetExtension.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/WidgetExtension.java	2020-02-24 19:18:25 +0000
@@ -0,0 +1,131 @@
+/*
+** Module   : WidgetExtension.java
+** Abstract : WidgetExtension interface.
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description-----------------------------------
+** 001 SBI 20200114 Created initial version.
+*/
+
+/*
+** 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;
+
+import com.goldencode.p2j.util.*;
+
+
+/**
+ * Defines additional common methods of a UI component that extends legacy widget methods.
+ */
+public interface WidgetExtension
+{
+   /**
+    * Gets the type of mouse pointer displayed when over part of an object.
+    * 
+    * @return mouse pointer
+    */
+   @LegacyAttribute(name = "MOUSE-POINTER-NUM")
+   public integer getMousePointerNum();
+
+   /**
+    * Sets the type of mouse pointer displayed when over part of an object.
+    * 
+    * @param   value
+    *          new mouse pointer
+    */
+   @LegacyAttribute(name = "MOUSE-POINTER-NUM", setter = true)
+   public void setMousePointerNum(integer value);
+
+   /**
+    * Sets the type of mouse pointer displayed when over part of an object.
+    * 
+    * @param   value
+    *          new mouse pointer
+    */
+   @LegacyAttribute(name = "MOUSE-POINTER-NUM", setter = true)
+   public void setMousePointerNum(int value);
+
+   /**
+    * Gets a mouse icon for this control.
+    * 
+    * @return   The mouse icon for this control
+    */
+   @LegacyAttribute(name = "MOUSE-ICON")
+   public character getMouseIcon();
+
+   /**
+    * Sets a custom mouse icon for this control.
+    * 
+    * @param   value
+    *          The user defined mouse icon
+    */
+   @LegacyAttribute(name = "MOUSE-ICON", setter = true)
+   public void setMouseIcon(character value);
+   
+   /**
+    * Sets a custom mouse icon.
+    * 
+    * @param   value
+    *          user defined mouse icon
+    */
+   @LegacyAttribute(name = "MOUSE-ICON", setter = true)
+   public void setMouseIcon(String value);
+   
+   /**
+    * Forces a complete repaint of a form or control.
+    */
+   @LegacyMethod(name = "REFRESH-UI")
+   public void refreshUI();
+}

=== modified file 'src/com/goldencode/p2j/ui/client/UiUtils.java'
--- src/com/goldencode/p2j/ui/client/UiUtils.java	2020-01-21 14:39:51 +0000
+++ src/com/goldencode/p2j/ui/client/UiUtils.java	2020-02-25 16:59:06 +0000
@@ -2,7 +2,7 @@
 ** Module   : UiUtils.java
 ** Abstract : UI-modality-independent helper methods
 **
-** Copyright (c) 2010-2019, Golden Code Development Corporation.
+** Copyright (c) 2010-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 SIY 20100821 Created initial version
@@ -80,6 +80,7 @@
 **     HC  20191231 Fixed getCurrentFocus(boolean) to return legacy widget when legacy requested.
 ** 092 MAG 20191106 Initial implementation of IMAGELIST widget.
 ** 093 MAG 20191003 Implemented PROGRESS-BAR widget and related changes.
+** 094 SBI 20200225 Added getDropDownPhysicalLocation.
 */
 
 /*
@@ -147,6 +148,8 @@
 import com.goldencode.p2j.ui.*;
 import com.goldencode.p2j.ui.chui.ThinClient;
 import com.goldencode.p2j.ui.client.gui.*;
+import com.goldencode.p2j.ui.client.gui.driver.*;
+import com.goldencode.p2j.ui.client.gui.theme.*;
 import com.goldencode.p2j.ui.client.widget.*;
 import com.goldencode.p2j.util.*;
 
@@ -752,6 +755,68 @@
    }
 
    /**
+    * Calculates the drop down physical location based on the owner physical location taking
+    * into account that the dropdown belongs to the different parent frame.
+    * 
+    * @param    owner
+    *           The dropdown's owner: combobox, calendar widget
+    * @param    dropdown
+    *           The dropdown widget
+    * @param    gd
+    *           The GUI driver
+    * 
+    * @return   The drop down physical location to set.
+    */
+   public static NativePoint getDropDownPhysicalLocation(AbstractWidget<GuiOutputManager> owner,
+                                                         AbstractWidget<GuiOutputManager> dropdown,
+                                                         GuiDriver gd)
+   {
+      // start GUI specifics
+      // get the absolute coord of the combo-box this drop-down belongs to
+      NativePoint cbbPhysLoc = owner.screenPhysicalLocation();
+      
+      // set the coordinates relative to parent combo-box widget
+      //
+      // The combo-box and drop-down both belong to different frames. The parent frame
+      // for drop-down is border-less and header-less invisible frame located inside Window
+      // workspaces. So when we get the physical location of the combo-box we have location
+      // related to frame the combo-box belongs to. If we use this coords to place drop-down we
+      // have XY shift according to combo-box's frame placement and decoration. That's why we can
+      // not just use combo-box location plus combo box height here.
+      //
+      // The approach is to get absolute coordinates of the combo-box, get the coordinate of the
+      // workspace, subtract second from first and add the combo-box height.
+      
+      // the combo-box height plus some extra pixels to match 4GL look and feel
+      int cbbHeightPlus = dropdown.screen().coordinates().heightToNative(owner.height()) + 
+                          ThemeManager.getCurrentTheme().getComboBoxDropDownOffset();
+      NativePoint ddPhysLoc = new NativePoint(cbbPhysLoc.x, cbbPhysLoc.y);
+      
+      // check if the drop-down will fit the current display dimension
+      // get combo-box window ID
+      int mainWndId = owner.topLevelWindow().getId().asInt();
+      // get combo-box absolute coordinates
+      NativePoint cbbDispLoc = owner.displayPhysicalLocation();
+      // total screen and drop-down dimension
+      NativeRectangle nr = gd.getDisplayBounds(mainWndId);
+      NativeDimension nDim = dropdown.physicalDimension();
+      
+      // calculate final drop-down y coordinate (to display the full list above or below the
+      // parent) based on space available, but the preference will be below if possible
+      if (cbbDispLoc.y + cbbHeightPlus + nDim.height > nr.bottom() &&
+          cbbDispLoc.y - nDim.height > nr.top())
+      {
+         ddPhysLoc.y -= nDim.height;
+      }
+      else
+      {
+         ddPhysLoc.y += cbbHeightPlus;
+      }
+      
+      return ddPhysLoc;
+   }
+   
+   /**
     * Return the alert box which contains the specified widget.
     *
     * @param    widget
@@ -1008,6 +1073,10 @@
       {
          clazz = ProgressBarGuiImpl.class;
       }
+      else if (cfgCls == CalendarConfig.class)
+      {
+         clazz = CalendarGuiImpl.class;
+      }
       
       return OutputManager.instance().getFactory().create(clazz, id, cfg);
    }

=== modified file 'src/com/goldencode/p2j/ui/client/gui/ButtonGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/ButtonGuiImpl.java	2019-12-02 20:57:20 +0000
+++ src/com/goldencode/p2j/ui/client/gui/ButtonGuiImpl.java	2020-02-25 16:57:54 +0000
@@ -2,7 +2,7 @@
 ** Module   : ButtonGuiImpl.java
 ** Abstract : push button implementation for GUI
 **
-** Copyright (c) 2014-2019, Golden Code Development Corporation.
+** Copyright (c) 2014-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 EVL 20141120 Initial version.
@@ -91,6 +91,7 @@
 **         20190802 Added canProduceAction to handleBasicEvents, so that suitable key inputs
 **                  generate ActionEvents.
 ** 047 SBI 20191202 Fixed isMnemonicSupported() to return true.
+** 048 SBI 20200225 Reduced the code by using the common code. Fixed button with image usages.
 */
 
 /*
@@ -151,10 +152,10 @@
 // this usage of AWT events is intentional, they are being used separately from the
 // AWT/Swing event model itself; no other AWT/Swing usage is occurring in this class
 import java.awt.event.MouseEvent;
-import java.util.*;
+
+import java.util.function.*;
 
 import com.goldencode.p2j.ui.*;
-import com.goldencode.p2j.ui.chui.*;
 import com.goldencode.p2j.ui.client.*;
 import com.goldencode.p2j.ui.client.event.*;
 import com.goldencode.p2j.ui.client.gui.driver.*;
@@ -204,6 +205,9 @@
    /** Image to draw on button in disabled state. */
    private ImageGuiImpl imgDisabled = null;
    
+   /** Indicates that this event is processed by super class implementation logic */
+   private boolean processedBySuperClass;
+   
    /**
     * This constructor creates a button with a given text label.
     * 
@@ -259,7 +263,20 @@
 
       // handle pending images to preload
       loadPendingImages();
-
+      
+      if (imgDisabled != null)
+      {
+         imgDisabled.initialize(imgDisabled.getId(), imgDisabled.config());
+      }
+      if (imgDown != null)
+      {
+         imgDown.initialize(imgDown.getId(), imgDown.config());
+      }
+      if (imgUp != null)
+      {
+         imgUp.initialize(imgUp.getId(), imgUp.config());
+      }
+      
       // adjust image coordinates and width
       recalcImagesCoords();
 
@@ -311,79 +328,15 @@
    {
       boolean result = false;
       
-      switch (type)
+      Images ib = Images.valueOf(type);
+      
+      if (ib == null)
       {
-         case ButtonConfig.IMAGE_UP:
-         {
-            // get the image name
-            String imgName = config().imgUpDef.name;
-            
-            if (imgUp == null)
-            {
-               boolean isIcon = imgName != null && imgName.toLowerCase().contains(".ico");
-               imgUp = new ImageGuiImpl(WidgetId.nextID(), isIcon);
-            }
-            result = imgUp.setImage(image, false);
-            
-            // fixup button coordinates
-            if (result)
-            {
-               fixupImageCoord(imgUp, config().imgUpDef, type);
-               // prepackaged images has transparent background
-               if (ImageHelper.isPrepackaged(imgName))
-               {
-                  imgUp.config().transparent = true;
-               }
-               // if image is used on the top of the button it must have convert-3d-colors on
-               imgUp.config().convert3D = !imgUp.isIcon();
-            }
-            break;
-         }
-         case ButtonConfig.IMAGE_DOWN:
-         {
-            if (imgDown == null)
-            {
-               boolean isIcon = config().imgDownDef.name != null &&
-                                config().imgDownDef.name.toLowerCase().contains(".ico");
-               imgDown = new ImageGuiImpl(WidgetId.nextID(), isIcon);
-            }
-            result = imgDown.setImage(image, false);
-
-            // fixup button coordinates
-            if (result)
-            {
-               fixupImageCoord(imgDown, config().imgDownDef, type);
-               // if image is used on the top of the button it must have convert-3d-colors on
-               imgDown.config().convert3D = !imgDown.isIcon();
-            }
-            break;
-         }
-         case ButtonConfig.IMAGE_INS:
-         {
-            if (imgDisabled == null)
-            {
-               boolean isIcon = config().imgInsDef.name != null &&
-                                config().imgInsDef.name.toLowerCase().contains(".ico");
-               imgDisabled = new ImageGuiImpl(WidgetId.nextID(), isIcon);
-            }
-            result = imgDisabled.setImage(image, false);
-
-            // fixup button coordinates
-            if (result)
-            {
-               fixupImageCoord(imgDisabled, config().imgInsDef, type);
-               // prepackaged images has transparent background
-               if (ImageHelper.isPrepackaged(config().imgInsDef.name))
-               {
-                  imgDisabled.config().transparent = true;
-               }
-               // if image is used on the top of the button it must have convert-3d-colors on
-               imgDisabled.config().convert3D = !imgDisabled.isIcon();
-            }
-            break;
-         }
+         return result;
       }
       
+      result = ib.setImage(this, image);
+      
       // if the image was loaded after button initialization, we need to recalculate new
       // position inside the button
       if (result)
@@ -395,6 +348,36 @@
    }
    
    /**
+    * Sets the new image for given button widget on the client side.
+    * 
+    * @param    type
+    *           Image type to load.  Up, Down or Insensitive for buttons.
+    * @param    image
+    *           The image wrapper.
+    *
+    * @return   <code>TRUE</code> if operation successful <code>FALSE</code> otherwise.
+    */
+   public boolean setImage(int type, ImageWrapper image)
+   {
+      boolean result = false;
+      
+      Images ib = Images.valueOf(type);
+      
+      if (ib == null)
+      {
+         return result;
+      }
+      
+      ib.setImage(this, image);
+      
+      // if the image was loaded after button initialization, we need to recalculate new
+      // position inside the button
+      recalcImagesCoords();
+      
+      return true;
+   }
+   
+   /**
     * Loads the new image for given button widget from the client side filesystem.
     * 
     * @param    type
@@ -408,72 +391,15 @@
    {
       boolean result = false;
       
-      switch (type)
+      Images ib = Images.valueOf(type);
+      
+      if (ib == null)
       {
-         case ButtonConfig.IMAGE_UP:
-         {
-            if (imgUp == null)
-            {
-               imgUp = new ImageGuiImpl(WidgetId.nextID());
-            }
-
-            result = imgUp.loadImage(fullName, false);
-            
-            // fixup button coordinates
-            if (result)
-            {
-               fixupImageCoord(imgUp, config().imgUpDef, type);
-               // prepackaged images has transparent background
-               if (ImageHelper.isPrepackaged(fullName))
-               {
-                  imgUp.config().transparent = true;
-               }
-               // if image is used on the top of the button it must have convert-3d-colors on
-               imgUp.config().convert3D = !imgUp.isIcon();
-            }
-            break;
-         }
-         case ButtonConfig.IMAGE_DOWN:
-         {
-            if (imgDown == null)
-            {
-               imgDown = new ImageGuiImpl(WidgetId.nextID());
-            }
-            result = imgDown.loadImage(fullName, false); 
-
-            // fixup button coordinates
-            if (result)
-            {
-               fixupImageCoord(imgDown, config().imgDownDef, type);
-               // if image is used on the top of the button it must have convert-3d-colors on
-               imgDown.config().convert3D = !imgDown.isIcon();
-            }
-            break;
-         }
-         case ButtonConfig.IMAGE_INS:
-         {
-            if (imgDisabled == null)
-            {
-               imgDisabled = new ImageGuiImpl(WidgetId.nextID());
-            }
-            result = imgDisabled.loadImage(fullName, false); 
-
-            // fixup button coordinates
-            if (result)
-            {
-               fixupImageCoord(imgDisabled, config().imgInsDef, type);
-               // prepackaged images has transparent background
-               if (ImageHelper.isPrepackaged(fullName))
-               {
-                  imgDisabled.config().transparent = true;
-               }
-               // if image is used on the top of the button it must have convert-3d-colors on
-               imgDisabled.config().convert3D = !imgDisabled.isIcon();
-            }
-            break;
-         }
+         return result;
       }
-    
+      
+      result = ib.loadImage(this, fullName);
+      
       // if the image was loaded after button initialization, we need to recalculate new
       // position inside the button
       if (result)
@@ -975,7 +901,7 @@
          repaint();
       }
 
-      if (isEnabled() && event.canProduceAction())
+      if (!processedBySuperClass && isEnabled() && event.canProduceAction())
       {
          EventManager.postEvent(new ActionEvent(this, getCommand()));
       }
@@ -997,6 +923,20 @@
    }
 
    /**
+    * It wraps around its super class implementation.
+    * 
+    * @param    event
+    *           The processed event
+    */
+   @Override
+   public void processEvent(Event event)
+   {
+      processedBySuperClass = true;
+      super.processEvent(event);
+      processedBySuperClass = false;
+   }
+
+   /**
     * Handle cursor movement keys.
     * 
     * @param   key
@@ -1240,4 +1180,155 @@
    {
       return "ButtonGuiImpl{" + String.valueOf(getId()) + ", \"" + textLabel() + "\"}";
    }
+   
+   /**
+    * Defines common methods for up, down and insensitive images for buttons.
+    */
+   public enum Images
+   {
+      IMAGE_UP (ButtonConfig.IMAGE_UP,
+                (b) -> b.config().imgUpDef,
+                (b) -> (img) -> b.imgUp = img,
+                (b) -> b.imgUp),
+      IMAGE_DOWN (ButtonConfig.IMAGE_DOWN,
+                  (b) -> b.config().imgDownDef,
+                  (b) -> (img) -> b.imgDown = img,
+                  (b) -> b.imgDown),
+      IMAGE_INS (ButtonConfig.IMAGE_INS,
+                 (b) -> b.config().imgInsDef,
+                 (b) -> (img) -> b.imgDisabled = img,
+                 (b) -> b.imgDisabled);
+      
+      private final Function<ButtonGuiImpl, ButtonImageDefinition> imageDefinition;
+      
+      private final Function<ButtonGuiImpl, Consumer<ImageGuiImpl>> setter;
+      
+      private final Function<ButtonGuiImpl, ImageGuiImpl> getter;
+      
+      private final int type;
+      
+      private Images(int type,
+                     Function<ButtonGuiImpl, ButtonImageDefinition> imageDefinition,
+                     Function<ButtonGuiImpl, Consumer<ImageGuiImpl>> setter,
+                     Function<ButtonGuiImpl, ImageGuiImpl> getter)
+      {
+         this.type = type;
+         this.imageDefinition = imageDefinition;
+         this.setter = setter;
+         this.getter = getter;
+      }
+      
+      public boolean setImage(ButtonGuiImpl bt, byte[] image)
+      {
+         ButtonImageDefinition btImgDef = imageDefinition.apply(bt);
+         
+         String imgName = btImgDef.name;
+         
+         ImageGuiImpl img = getOrCreate(bt, imgName);
+         
+         boolean result = img.setImage(image, false);
+         
+         if (result)
+         {
+            fixButtonCoordinates(bt, btImgDef, img);
+         }
+
+         return result;
+      }
+      
+      public void setImage(ButtonGuiImpl bt, ImageWrapper image)
+      {
+         ButtonImageDefinition btImgDef = imageDefinition.apply(bt);
+         
+         String imgName = btImgDef.name;
+         
+         ImageGuiImpl img = getOrCreate(bt, imgName);
+         
+         img.setImage(image);
+         
+         fixButtonCoordinates(bt, btImgDef, img);
+      }
+      
+      public boolean loadImage(ButtonGuiImpl bt, String fullName)
+      {
+         ImageGuiImpl img = getOrCreate(bt);
+         
+         boolean result = img.loadImage(fullName, false);
+         
+         // fixup button coordinates
+         if (result)
+         {
+            ButtonImageDefinition btImgDef = imageDefinition.apply(bt);
+            bt.fixupImageCoord(img, btImgDef, type);
+            // prepackaged images has transparent background
+            if (ImageHelper.isPrepackaged(fullName))
+            {
+               img.config().transparent = true;
+            }
+            // if image is used on the top of the button it must have convert-3d-colors on
+            img.config().convert3D = !img.isIcon();
+         }
+         
+         return result;
+      }
+      
+      private ImageGuiImpl getOrCreate(ButtonGuiImpl bt, String imgName)
+      {
+         ImageGuiImpl img = getter.apply(bt);
+         
+         if (img == null)
+         {
+            boolean isIcon = imgName != null && imgName.toLowerCase().contains(".ico");
+            
+            img = new ImageGuiImpl(WidgetId.nextID(), isIcon);
+            
+            setter.apply(bt).accept(img);
+         }
+         
+         return img;
+      }
+      
+      private ImageGuiImpl getOrCreate(ButtonGuiImpl bt)
+      {
+         ImageGuiImpl img = getter.apply(bt);
+         
+         if (img == null)
+         {
+            img = new ImageGuiImpl(WidgetId.nextID());
+            
+            setter.apply(bt).accept(img);
+         }
+         
+         return img;
+      }
+      
+      private void fixButtonCoordinates(ButtonGuiImpl bt,
+                                        ButtonImageDefinition btImgDef,
+                                        ImageGuiImpl img)
+      {
+         String imgName = btImgDef.name;
+         // fixup button coordinates
+         bt.fixupImageCoord(img, btImgDef, type);
+         // prepackaged images has transparent background
+         if (ImageHelper.isPrepackaged(imgName))
+         {
+            img.config().transparent = true;
+         }
+         // if image is used on the top of the button it must have convert-3d-colors on
+         img.config().convert3D = !img.isIcon();
+      }
+      
+      public static Images valueOf(int type)
+      {
+         for(Images ib : Images.values())
+         {
+            if (ib.type == type)
+            {
+               return ib;
+            }
+         }
+         
+         return null;
+      }
+   }
 }

=== added file 'src/com/goldencode/p2j/ui/client/gui/CalendarGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/CalendarGuiImpl.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/client/gui/CalendarGuiImpl.java	2020-02-25 12:55:07 +0000
@@ -0,0 +1,2800 @@
+/*
+** Module   : CalendarGuiImpl.java
+** Abstract : CalendarGuiImpl implements the calendar widget.
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- --------------------------------Description-----------------------------------
+** 001 SBI 20200114 Created initial version.
+*/
+
+/*
+** 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.client.gui;
+
+import java.awt.event.MouseEvent;
+import java.text.*;
+import java.time.*;
+import java.time.chrono.*;
+import java.time.format.*;
+import java.time.temporal.ChronoField;
+import java.util.*;
+import java.util.function.*;
+
+import com.goldencode.p2j.ui.*;
+import com.goldencode.p2j.ui.CalendarConfig.*;
+import com.goldencode.p2j.ui.chui.ThinClient;
+import com.goldencode.p2j.ui.client.*;
+import com.goldencode.p2j.ui.client.event.*;
+import com.goldencode.p2j.ui.client.event.listener.*;
+import com.goldencode.p2j.ui.client.gui.GuiFontResolver.*;
+import com.goldencode.p2j.ui.client.gui.driver.*;
+import com.goldencode.p2j.ui.client.gui.driver.LightweightTextWidget.*;
+import com.goldencode.p2j.ui.client.gui.theme.*;
+import com.goldencode.p2j.ui.client.gui.theme.Theme.TextLookAndFeel.*;
+import com.goldencode.p2j.ui.client.gui.theme.Theme.*;
+import com.goldencode.p2j.ui.client.layout.*;
+import com.goldencode.p2j.ui.client.layout.StackLayout.*;
+import com.goldencode.p2j.ui.client.widget.*;
+import com.goldencode.p2j.util.*;
+import com.goldencode.util.*;
+
+/**
+ * Implements the calendar widget.
+ */
+public class CalendarGuiImpl
+extends BorderedPanelGuiImpl
+implements WidgetWithConfig<CalendarConfig>,
+           FocusListener,
+           DataContainer
+{
+   /** The standard button's size */ 
+   private static final int BUTTON_SIZE = 28;
+   
+   /** The standard margins around a given button */
+   private static final int BUTTON_MARGIN = 2;
+   
+   /** The standard margin between controls in a row */
+   private static final int HORIZONTAL_CELL_MARGIN = 4;
+   
+   /** The standard margin between rows */
+   private static final int VERTICAL_ROW_MARGIN = 2;
+   
+   /** The displayed rows */
+   private static final int ROWS_NUMBER = 9;
+   
+   /** The displayed columns */
+   private static final int COLUMNS_NUMBER = 7;
+   
+   /** Holds its settings */
+   private CalendarConfig config;
+   
+   /** Screen driver */
+   private GuiDriver gd;
+   
+   /** Output manager */
+   private GuiOutputManager screen;
+   
+   /** GUI widgets factory */
+   private GuiWidgetFactory widgetsFactory;
+   
+   /** Coordinate converter */
+   private CoordinatesConversion cc;
+
+   /** Body color resolver */
+   private GuiColorResolver bodyColorResolver;
+   
+   /** Title color resolver for calendar header */
+   private GuiColorResolver titleColorResolver;
+   
+   /** Trailing days and selected day color resolver */
+   private GuiColorResolver trailingColorResolver;
+   
+   /** Font resolver */
+   private GuiFontResolver fontResolver;
+   
+   /** The system locale */
+   private Locale locale;
+   
+   /** The system calendar */
+   private Chronology chrono;
+   
+   /** The date time formatter */
+   private DateTimeFormatter dateTimeFormatter;
+   
+   /** The current date and time pattern */
+   private String dateTimePattern;
+   
+   /** The array of date and time fields displayed by the current pattern */
+   private FieldPosition[] fieldPositions;
+   
+   /** The displayed date and time value without a time-zone in the ISO-8601 calendar system */
+   private LocalDateTime displayedDateTime;
+   
+   /** The date/time content is implemented as a scrollable widget. */
+   private DateTimeContent dateTimeContent;
+   
+   /** The date/time content view */
+   private Viewport<GuiOutputManager> viewport;
+   
+   /** The button's panel */
+   private BorderedPanelGuiImpl buttonsPanel;
+   
+   /** The popup button */
+   private ButtonGuiImpl popupButton;
+   
+   /** The up button */
+   private ButtonGuiImpl upButton;
+   
+   /** The down button */
+   private ButtonGuiImpl downButton;
+   
+   /** The toggle button */
+   private ToggleBoxGuiImpl toggleButton;
+   
+   /** This flag has been set once, required for this widget initialization */
+   private boolean init;
+   
+   /** The flag indicating that the calendar popup is active or not */
+   private boolean popupIsActive;
+   
+   
+   /**
+    * Creates the calendar widget.
+    * 
+    * @param    id
+    *           The given widget's id.
+    * @param    cfg
+    *           The widget's configuration
+    */
+   public CalendarGuiImpl(WidgetId id, CalendarConfig cfg)
+   {
+      super(new StackLayout<GuiOutputManager>(Direction.HORIZONTAL), new LineBorderGuiImpl());
+      setId(id);
+      config = cfg;
+   }
+   
+   /**
+    * Defines a factory method to create a button.
+    * 
+    * @param    name
+    *           The given name
+    * @param    width
+    *           The given widget's width
+    * @param    height
+    *           The given widget's height
+    * @param    imageSetter
+    *           Sets the target images for a new created button.
+    * 
+    * @return   The target button implementation
+    */
+   public ButtonGuiImpl createButton(String name,
+                                     int width,
+                                     int height,
+                                     Consumer<ButtonGuiImpl> imageSetter)
+   {
+      WidgetId wid = WidgetId.nextID();
+      
+      ButtonGuiImpl button = widgetsFactory.createButton(wid, null, name + wid.asInt());
+      
+      button.config().widthPixels  = width;
+      button.config().heightPixels = height;
+      button.config().widthChars  = cc.widthFromNative(width);
+      button.config().heightChars = cc.heightFromNative(height);
+      button.config().enabled = true;
+      button.config().visible = true;
+      button.config().fixedWidth = true;
+      button.config().fixedHeight = true;
+      button.config().x = 0;
+      button.config().y = 0;
+      
+      imageSetter.accept(button);
+      
+      button.initialize(wid, null);
+      
+      return button;
+   }
+   
+   /**
+    * Calculates the size of this widget in character units.
+    * 
+    * @return   The size of this widget in character units
+    */
+   @Override
+   public Dimension dimension()
+   {
+      double width = width();
+      double height = height();
+      
+      return new Dimension(width, height);
+   }
+
+   /**
+    * Returns the height of this widget.
+    * 
+    * @return   The widget's height in character units.
+    */
+   @Override
+   public double height()
+   {
+      return config.heightChars;
+   }
+
+   /**
+    * Returns the minimum size of this widget.
+    * 
+    * @return   The minimum size of this widget
+    */
+   @Override
+   public Dimension minimumSize()
+   {
+      return dimension();
+   }
+
+   /**
+    * Returns the width of this widget.
+    * 
+    * @return   The widget's width in character units.
+    */
+   @Override
+   public double width()
+   {
+      return config.widthChars;
+   }
+
+   /**
+    * Returns the widget's configuration.
+    * 
+    * @return   The calendar configuration
+    */
+   @Override
+   public CalendarConfig config()
+   {
+      return config;
+   }
+   
+   /**
+    * Notifies this widget that its settings are changed.
+    * 
+    * @param    beforeUpdate
+    *           The old configuration
+    */
+   @Override
+   public void afterConfigUpdate(CalendarConfig beforeUpdate)
+   {
+      super.afterConfigUpdateBase(beforeUpdate);
+      
+      boolean repaint = false;
+      boolean updateSize = false;
+      
+      if (beforeUpdate.formatStyle != config.formatStyle || 
+          !StringHelper.equal(config.customFormat, beforeUpdate.customFormat) ||
+          !StringHelper.equal(config.dateTimeValue, beforeUpdate.dateTimeValue))
+      {
+         LocalDateTime modifiedDateTime;
+         
+         if (config.dateTimeValue != null)
+         {
+            modifiedDateTime = LocalDateTime.parse(config.dateTimeValue);
+         }
+         else
+         {
+            modifiedDateTime = displayedDateTime;
+         }
+
+         updateDateTimePattern(config.formatStyle, config.customFormat, modifiedDateTime);
+         updateSize = true;
+      }
+      
+      if (init || beforeUpdate.font != config.font ||
+          !Utils.equals(beforeUpdate.fontName, config.fontName) ||
+           beforeUpdate.fontSize != config.fontSize)
+      {
+         FontCache fntCache = fontResolver.font();
+         
+         fontResolver.updateFontCache(config.font, fntCache.font);
+         
+         fntCache = fontResolver.font();
+         
+         int fontNum = fontResolver.resolveFontNum();
+         
+         config.font = fontNum;
+         
+         config.fontSize = fntCache.font.pointSize;
+         
+         config.fontName = fntCache.font.fontName;
+         
+         updateSize = true;
+      }
+      
+      if (init || updateSize || 
+          beforeUpdate.x != config.x ||
+          beforeUpdate.y != config.y || 
+          beforeUpdate.widthChars != config.widthChars ||
+          beforeUpdate.heightChars != config.heightChars ||
+          beforeUpdate.widthPixels != config.widthPixels
+          )
+      {
+         updateSize();
+         setSize(width(), height());
+         
+         setPhysicalLocation(config.x, config.y);
+         
+         dateTimeContent.updateSize(window(), fontResolver.resolveFontNum());
+         
+         if (config.upDown)
+         {
+            upButton.setEnabled(true);
+            upButton.setVisible(true);
+            downButton.setEnabled(true);
+            downButton.setVisible(true);
+         }
+         else
+         {
+            popupButton.setEnabled(true);
+            popupButton.setVisible(true);
+         }
+         
+         if (config.checkBox)
+         {
+            toggleButton.setEnabled(true);
+            toggleButton.setVisible(true);
+         }
+         
+         viewport.setSize(cc.dimensionFromNative(dateTimeContent.getVisibleDimension()));
+         
+         double h = viewport.height();
+         
+         if (config.checkBox)
+         {
+            toggleButton.config().heightChars = h;
+         }
+         
+         if (config.upDown)
+         {
+            upButton.config().clientHeightChars = h/2;
+            upButton.config().heightChars = h/2;
+            downButton.config().clientHeightChars = h/2;
+            downButton.config().heightChars = h/2;
+            buttonsPanel.setSize(upButton.width(), h);
+            Point center = upButton.getImgUp().location();
+            double imgHeight = upButton.getImgUp().height();
+            center.y = (h / 2 - imgHeight) /2;
+            upButton.getImgDown().setLocation(center);
+            upButton.getImgUp().setLocation(center);
+            upButton.getImgDisabled().setLocation(center);
+            center = downButton.getImgUp().location();
+            imgHeight = downButton.getImgUp().height();
+            center.y = (h / 2 - imgHeight) /2;
+            downButton.getImgDown().setLocation(center);
+            downButton.getImgUp().setLocation(center);
+            downButton.getImgDisabled().setLocation(center);
+         }
+         else
+         {
+            popupButton.config().clientHeightChars = h;
+            popupButton.config().heightChars = h;
+            buttonsPanel.setSize(popupButton.width(), h);
+            Point center = popupButton.getImgUp().location();
+            double imgHeight = popupButton.getImgUp().height();
+            center.y = (h - imgHeight) /2;
+            popupButton.getImgDown().setLocation(center);
+            popupButton.getImgUp().setLocation(center);
+            popupButton.getImgDisabled().setLocation(center);
+         }
+         
+         buttonsPanel.doLayout();
+         
+         doLayout();
+         
+         repaint = true;
+      }
+      
+      if (beforeUpdate.bodyBackgroundColor != config.bodyBackgroundColor ||
+          beforeUpdate.bodyForegroundColor != config.bodyForegroundColor ||
+          beforeUpdate.titleBackgroundColor != config.titleBackgroundColor ||
+          beforeUpdate.titleForegroundColor != config.titleForegroundColor ||
+          beforeUpdate.trailingForegroundColor != config.trailingForegroundColor)
+      {
+         if (config.bodyBackgroundColor != -1)
+         {
+            ColorRgb newColorBGR = new ColorRgb();
+            newColorBGR.setRgb(config.bodyBackgroundColor);
+            bodyColorResolver.sysBgColor = newColorBGR;
+         }
+         
+         if (config.bodyForegroundColor != -1)
+         {
+            ColorRgb newColorBGR = new ColorRgb();
+            newColorBGR.setRgb(config.bodyForegroundColor);
+            bodyColorResolver.sysFgColor = newColorBGR;
+         }
+         
+         if (config.titleBackgroundColor != -1)
+         {
+            ColorRgb newColorBGR = new ColorRgb();
+            newColorBGR.setRgb(config.titleBackgroundColor);
+            titleColorResolver.sysBgColor = newColorBGR;
+         }
+         
+         if (config.titleForegroundColor != -1)
+         {
+            ColorRgb newColorBGR = new ColorRgb();
+            newColorBGR.setRgb(config.titleForegroundColor);
+            titleColorResolver.sysFgColor = newColorBGR;
+         }
+         
+         if (config.trailingForegroundColor != -1)
+         {
+            ColorRgb newColorBGR = new ColorRgb();
+            newColorBGR.setRgb(config.trailingForegroundColor);
+            trailingColorResolver.sysFgColor = newColorBGR;
+         }
+         
+         repaint = true;
+      }
+      
+      if (init)
+      {
+         init = false;
+      }
+      if (repaint)
+      {
+         repaint();
+      }
+   }
+
+   /**
+    * Initializes the date and time widget.
+    * 
+    * @param    id
+    *           This widget id
+    * @param    cfg
+    *           This widget configuration
+    */
+   @Override
+   public void initialize(WidgetId id, CalendarConfig cfg)
+   {
+      config = ConfigManager.getInstance().resolveWidgetConfig(id, cfg);
+      ConfigManager.getInstance().replaceConfig(config, cfg);
+      setId(id);
+      locale = Locale.getDefault();
+      chrono = Chronology.ofLocale(locale);
+      
+      LocalDateTime initDateTime;
+      
+      if (config.dateTimeValue != null)
+      {
+         initDateTime = LocalDateTime.parse(config.dateTimeValue);
+      }
+      else
+      {
+         initDateTime = LocalDateTime.now();
+      }
+      
+      updateDateTimePattern(config.formatStyle, config.customFormat, initDateTime);
+      
+      // screen driver
+      if (gd == null)
+      {
+         gd = (GuiDriver) OutputManager.getDriver();
+      }
+      
+      // output manager
+      screen = screen();
+      cc = screen.coordinates();
+      
+      // font resolver
+      if (fontResolver == null)
+      {
+         fontResolver = new GuiFontResolver(WindowManager.resolveWindow(this), gd, config(), false);
+      }
+      
+      Theme theme = ThemeManager.getCurrentTheme();
+      
+      bodyColorResolver = new GuiColorResolver(gd,
+                                               config,
+                                               theme.getCalendarBodyBackgroundColor(),
+                                               theme.getCalendarBodyForegroundColor());
+      
+      titleColorResolver = new GuiColorResolver(gd,
+                                                config,
+                                                theme.getCalendarTitleBackgroundColor(),
+                                                theme.getCalendarTitleForegroundColor());
+      
+      trailingColorResolver = new GuiColorResolver(gd,
+                                                   config,
+                                                   theme.getCalendarTrailingBackgroundColor(),
+                                                   theme.getCalendarTrailingForegroundColor());
+      
+      bodyColorResolver.boxColor = gd.getSysColor(theme.getCalendarBodyForegroundColor());
+      
+      trailingColorResolver.selectionBgColor = gd.getSysColor(
+               theme.getSelectedDayBackgroundColor());
+      
+      trailingColorResolver.selectionFgColor = gd.getSysColor(
+               theme.getSelectedDayForegroundColor());
+      
+      widgetsFactory = screen.getFactory();
+      
+      if (config.checkBox)
+      {
+         WidgetId wid0 = WidgetId.nextID();
+         ToggleBoxConfig tgc = new ToggleBoxConfig();
+         tgc.id = wid0;
+         
+         toggleButton = new ToggleBoxGuiImpl(wid0, tgc);
+         toggleButton.initialize(wid0, tgc);
+         toggleButton.config().name = "toggleButton" + wid0.asInt();
+         toggleButton.config().enabled = true;
+         toggleButton.config().visible = true;
+         toggleButton.config().fixedWidth = true;
+         toggleButton.config().fixedHeight = true;
+         toggleButton.config().x = 0;
+         toggleButton.config().y = 0;
+         toggleButton.config().checked = true;
+         toggleButton.setColorResolver(bodyColorResolver);
+         
+         add(toggleButton);
+         
+         toggleButton.addKeyListener(new KeyListener()
+         {
+            
+            @Override
+            public void onKeyTyped(KeyInput event)
+            {
+               int key = event.actionCode();
+               
+               if (key == Keyboard.SE_VALUE_CHANGED)
+               {
+                  dateTimeContent.setEnabled(toggleButton.config().checked);
+               }
+            }
+            
+            @Override
+            public void onKeyPressed(KeyInput event)
+            {
+            }
+         });
+      }
+      
+      dateTimeContent = new DateTimeContent();
+      viewport = new Viewport<GuiOutputManager>(dateTimeContent);
+      add(viewport);
+      
+      buttonsPanel = new BorderedPanelGuiImpl();
+      
+      buttonsPanel.setId(WidgetId.nextID());
+      
+      Consumer<ButtonGuiImpl> Down = b -> ThemeManager.getCurrentTheme().setDownArrowImages(b);
+      
+      Consumer<ButtonGuiImpl> Up = b -> ThemeManager.getCurrentTheme().setUpArrowImages(b);
+
+      if (config.upDown)
+      {
+         upButton = createButton("upButton", BUTTON_SIZE, BUTTON_SIZE / 2, Up);
+         downButton = createButton("downButton", BUTTON_SIZE, BUTTON_SIZE / 2, Down);
+         buttonsPanel.setLayout(new StackLayout(Direction.VERTICAL));
+         buttonsPanel.add(upButton);
+         buttonsPanel.add(downButton);
+      }
+      else
+      {
+         popupButton = createButton("popupButton", BUTTON_SIZE, BUTTON_SIZE, Down);
+         buttonsPanel.setLayout(new BorderLayout<GuiOutputManager>());
+         buttonsPanel.add(popupButton);
+      }
+      
+      buttonsPanel.setColorResolver(bodyColorResolver);
+      
+      add(buttonsPanel);
+      
+      if (config.checkBox)
+      {
+         screen.getRegistry().addWidget(toggleButton);
+      }
+      
+      if (config.upDown)
+      {
+         screen.getRegistry().addWidget(upButton);
+         screen.getRegistry().addWidget(downButton);
+      }
+      else
+      {
+         screen.getRegistry().addWidget(popupButton);
+      }
+      
+      screen.getRegistry().addWidget(buttonsPanel);
+      
+      init = true;
+      
+      doLayout();
+      
+      buttonsPanel.doLayout();
+      
+      if (config.upDown)
+      {
+         upButton.addListener(new ActionListener()
+         {
+            @Override
+            public void onAction(ActionEvent event)
+            {
+               KeyInput keyInput = new KeyInput(CalendarGuiImpl.this,
+                                                EventType.KEY_PRESSED,
+                                                Keyboard.KA_CURSOR_UP);
+               processKeyEvent(keyInput);
+            }
+         });
+         downButton.addListener(new ActionListener()
+         {
+            
+            @Override
+            public void onAction(ActionEvent event)
+            {
+               KeyInput keyInput = new KeyInput(CalendarGuiImpl.this,
+                                                EventType.KEY_PRESSED,
+                                                Keyboard.KA_CURSOR_DOWN);
+                        processKeyEvent(keyInput);
+            }
+         });
+      }
+      else
+      {
+         popupButton.addListener(new ActionListener()
+         {
+            @Override
+            public void onAction(ActionEvent event)
+            {
+                  event.consume();
+                  popupIsActive = true;
+                  
+                  popupButton.setEnabled(false);
+                  
+                  if (config().checkBox)
+                  {
+                     // enable checkbox if it is disabled
+                     if (!toggleButton.config().checked)
+                     {
+                        toggleButton.config().checked = true;
+                        dateTimeContent.setEnabled(true);
+                     }
+                  }
+                  
+                  repaint();
+                  
+                  // force the driver to be in control, as this affects UI
+                  gd.setControl(true);
+
+                  CalendarPopup cal = new CalendarPopup();
+                  cal.setVisible(true);
+            }
+         });
+      }
+   }
+   
+   /**
+    * Handles the focus gained event.
+    * 
+    * @param    event
+    *           The focus event
+    */
+   @Override
+   public void onFocusGained(FocusEvent event)
+   {
+      repaint();
+   }
+   
+   /**
+    * Handles the focus lost event.
+    * 
+    * @param    event
+    *           The focus event
+    */
+   @Override
+   public void onFocusLost(FocusEvent event)
+   {
+      repaint();
+   }
+   
+   /**
+    * Sets the current focus to this widget itself.
+    */
+   @Override
+   public void firstFocus()
+   {
+      setFocusInt(this);
+   }
+
+   /**
+    * Returns true.
+    * 
+    * @return   True
+    */
+   @Override
+   public boolean focusTraversable()
+   {
+      return true;
+   }
+
+   /**
+    * Returns this widget itself.
+    * 
+    * @return   The widget itself
+    */
+   @Override
+   public Widget<GuiOutputManager> currentFocus()
+   {
+      return this;
+   }
+
+   /**
+    * Returns this widget itself.
+    * 
+    * @return   The widget itself
+    */
+   @Override
+   public Widget<GuiOutputManager> defaultFocus()
+   {
+      return this;
+   }
+
+   /**
+    * Tests if there is a focusable widget in this container except the given widget.
+    * 
+    * @param    exclude
+    *           The given widget
+    * 
+    * @return   True
+    */
+   @Override
+   public boolean hasFocusable(Widget<GuiOutputManager> exclude)
+   {
+      return true;
+   }
+
+   /**
+    * Update widget's size.
+    */
+   public void updateSize()
+   {
+      if (gd == null)
+      {
+         return;
+      }
+      
+      // The current widget window
+      Window<?> window = window();
+      
+      if (window == null)
+      {
+         // the widget is not attached, just return
+         return;
+      }
+      
+      CoordinatesConversion cc = screen().coordinates();
+      
+      // get font resolver
+      if (fontResolver == null)
+      {
+         fontResolver = new GuiFontResolver(window, gd, config(), false);
+      }
+      
+      int font = fontResolver.resolveFontNum();
+      
+      double inputHeight = config.heightChars > 0.0 && config.heightChars < 1.0
+                           ? config.heightChars : 1.0;
+      
+      config.clientHeightPixels = cc.heightToNative(inputHeight, window.getId(), font);
+      
+      if (config.widthChars != 0)
+      {
+         // the explicitly assigned width is the exact width of the widget,
+         // ignore format in this case.
+         // the width in this case is converted to physical size using
+         // the default font.
+         config.clientWidthChars = config.widthChars;
+         return;
+      }
+      
+      double charWidth = dateTimePattern.length();
+      
+      int pixelWidth = cc.widthToNative(charWidth, window.getId(), font) + BUTTON_SIZE;
+      
+      config.clientWidthPixels = pixelWidth;
+   }
+   
+   /**
+    * Update the date time pattern and date and time model.
+    * 
+    * @param    formatStyle
+    *           The calendar look and feel style
+    * @param    customPattern
+    *           The date and time pattern for date and time presentation
+    * @param    dateTime
+    *           The date and time to display
+    */
+   public void updateDateTimePattern(int formatStyle, String customPattern, LocalDateTime dateTime)
+   {
+      FormatStyle[] legacyStyles = CalendarConfig.toDateTimeStyles(formatStyle);
+      
+      if (CalendarConfig.isLocalizedFormat(formatStyle))
+      {
+         
+         dateTimePattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(
+                  legacyStyles[0], legacyStyles[1], chrono, locale);
+         // do consistent changes
+         config.customFormat = dateTimePattern;
+      }
+      else
+      {
+         //TODO: convert MS Date Time Picker format string to legacy date and time format string
+         dateTimePattern = customPattern;
+      }
+      
+      DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
+      
+      builder.appendPattern(dateTimePattern);
+      
+      dateTimeFormatter = builder.toFormatter();
+      
+      java.util.Calendar c = java.util.Calendar.getInstance(locale);
+      
+      c.set(dateTime.getYear(), dateTime.getMonthValue() - 1, dateTime.getDayOfMonth(),
+               dateTime.getHour(), dateTime.getMinute(), dateTime.getSecond());
+      
+      fieldPositions = DateFormatFieldsAdapter.getDateTimeFieldPositions(dateTimePattern, c.getTime());
+      
+      displayedDateTime = dateTime;
+      
+      config.dateTimeValue = displayedDateTime.format(dateTimeFormatter);
+      
+      repaint();
+   }
+   
+   /**
+    * Gets the currently selected data and time field's index.
+    * 
+    * @return   The currently selected data and time field's index
+    */
+   public int getSelectedFieldIndex()
+   {
+      return Math.max(config.selectedFieldIndex, 0) % fieldPositions.length;
+   }
+   
+   /**
+    * Process key events that have been generated by this component.
+    * 
+    * @param    keyEvent
+    *           The key event to be handled.
+    */
+   @Override
+   public void processKeyEvent(KeyInput keyEvent)
+   {
+      if (!isEnabled())
+      {
+         return;
+      }
+      
+      if (hasFocus())
+      {
+         int key = keyEvent.actionCode();
+         
+         boolean respond = key == Keyboard.KA_CURSOR_LEFT || key == Keyboard.KA_CURSOR_RIGHT ||
+                           key == Keyboard.KA_CURSOR_UP || key == Keyboard.KA_CURSOR_DOWN ||
+                           key == Keyboard.KA_RETURN;
+         
+         if (key == Keyboard.KA_TAB || key == Keyboard.KA_BACK_TAB)
+         {
+            config.dateTimeValue = displayedDateTime.format(dateTimeFormatter);
+            if (key == Keyboard.KA_TAB)
+            {
+               ThinClient.nextTabStop(this);
+            }
+            else
+            {
+               ThinClient.previousTabStop(this);
+            }
+         }
+         
+         if (!respond)
+         {
+            return;
+         }
+         
+         int currentIndex = getSelectedFieldIndex();
+         
+         int upperLimit = fieldPositions.length - 1;
+         
+         if (key == Keyboard.KA_CURSOR_LEFT)
+         {
+            if (currentIndex == 0)
+            {
+               currentIndex = upperLimit;
+            }
+            else
+            {
+               currentIndex--;
+            }
+            
+            config.selectedFieldIndex = currentIndex;
+            
+            scrollLeftToTheCurrentField(currentIndex, upperLimit);
+         }
+         else if (key == Keyboard.KA_CURSOR_RIGHT)
+         {
+            if (currentIndex == upperLimit)
+            {
+               currentIndex = 0;
+            }
+            else
+            {
+               currentIndex++;
+            }
+            
+            config.selectedFieldIndex = currentIndex;
+            
+            scrollRightToTheCurrentField(currentIndex);
+         }
+         else if (key == Keyboard.KA_CURSOR_UP || key == Keyboard.KA_CURSOR_DOWN)
+         {
+            int dataField = fieldPositions[currentIndex].getField();
+            ChronoField modified = DateFormatFieldsAdapter.getChronoField(dataField);
+            
+            if (key == Keyboard.KA_CURSOR_UP)
+            {
+               displayedDateTime = displayedDateTime.plus(modified.getBaseUnit().getDuration());
+            }
+            else
+            {
+               displayedDateTime = displayedDateTime.minus(modified.getBaseUnit().getDuration());
+            }
+            
+            updateDateTimePattern(config.formatStyle, config.customFormat, displayedDateTime);
+            
+            config.dateTimeValue = displayedDateTime.format(dateTimeFormatter);
+         }
+         else if (key == Keyboard.KA_RETURN)
+         {
+            config.dateTimeValue = displayedDateTime.format(dateTimeFormatter);
+         }
+         
+         repaint();
+      }
+   }
+   
+   
+   
+   /**
+    * Selects a field under the mouse click pointer.
+    * 
+    * @param    event
+    *           The mouse event
+    */
+   @Override
+   public void mousePressed(MouseEvent event)
+   {
+      int targetX = event.getX();
+      
+      NativePoint widgetLoc = screenPhysicalLocation();
+      
+      targetX -= widgetLoc.x;
+      
+      if (config.checkBox)
+      {
+         targetX -= toggleButton.physicalDimension().width;
+      }
+      
+      String text = displayedDateTime.format(dateTimeFormatter);
+      NativePoint position = dateTimeContent.physicalLocation();
+      
+      for (int i = 0; i < fieldPositions.length; i++)
+      {
+         FieldPosition field = fieldPositions[i];
+         int visibleWidth = position.x + dateTimeContent.xOffset +
+                  getTextWidthNative(text.substring(0, field.getEndIndex()),
+                                     fontResolver.font().font);
+         if (visibleWidth > targetX)
+         {
+            config.selectedFieldIndex = i;
+            repaint();
+            break;
+         }
+      }
+      super.mouseClicked(event);
+   }
+   
+   /**
+    * Scrolls the date and time content right to the current highlighted field.
+    * 
+    * @param    currentIndex
+    *           The current index of the target field
+    */
+   private void scrollRightToTheCurrentField(int currentIndex)
+   {
+      FieldPosition field = fieldPositions[currentIndex];
+      String text = displayedDateTime.format(dateTimeFormatter);
+      
+      if (currentIndex == 0)
+      {
+         dateTimeContent.setPhysicalLocation(0, 0);
+      }
+      else
+      {
+         NativePoint position = dateTimeContent.physicalLocation();
+         int visibleWidth = position.x + dateTimeContent.xOffset +
+                  getTextWidthNative(text.substring(0, field.getEndIndex()),
+                                     fontResolver.font().font);
+         NativeDimension visiblePart = dateTimeContent.getVisibleDimension();
+         if (visibleWidth > visiblePart.width)
+         {
+            int scrollAmount = visibleWidth - visiblePart.width;
+            dateTimeContent.scroll(-scrollAmount, 0);
+         }
+      }
+   }
+   
+   /**
+    * Scrolls the date and time content left to the current highlighted field.
+    * 
+    * @param    currentIndex
+    *           The current index of the target field
+    * @param    upperLimit
+    *           The number of all date and time fields displayed by the date and time widget
+    */
+   private void scrollLeftToTheCurrentField(int currentIndex, int upperLimit)
+   {
+      String text = displayedDateTime.format(dateTimeFormatter);
+      
+      if (currentIndex == 0)
+      {
+         dateTimeContent.setPhysicalLocation(0, 0);
+      }
+      else
+      {
+         if (currentIndex != upperLimit)
+         {
+            NativePoint position = dateTimeContent.physicalLocation();
+            FieldPosition currField = fieldPositions[currentIndex];
+            int leftVisiblePartWidth = position.x + dateTimeContent.xOffset +
+                     getTextWidthNative(text.substring(0, currField.getBeginIndex()),
+                                        fontResolver.font().font);
+            if (leftVisiblePartWidth < 0)
+            {
+               dateTimeContent.scroll(-leftVisiblePartWidth, 0);
+            }
+         }
+         else
+         {
+            int scrollAmount = 0;
+            NativePoint position = dateTimeContent.physicalLocation();
+            NativeDimension visiblePart = dateTimeContent.getVisibleDimension();
+            for (int i = 1; i <= upperLimit; i++)
+            {
+               FieldPosition field = fieldPositions[i];
+               int visibleWidth = position.x + dateTimeContent.xOffset +
+                        getTextWidthNative(text.substring(0, field.getEndIndex()),
+                                           fontResolver.font().font);
+               if (visibleWidth > visiblePart.width)
+               {
+                  scrollAmount += getTextWidthNative(
+                           text.substring(field.getBeginIndex(), field.getEndIndex()),
+                           fontResolver.font().font);
+               }
+            }
+            dateTimeContent.scroll(-scrollAmount, 0);
+         }
+      }
+   }
+   
+   /**
+    * Determine the mouse actions processed by this widget.
+    * 
+    * @return   The mouse actions processed by this widget
+    */
+   @Override
+   protected int[] mouseActions()
+   {
+      return new int[]
+      {
+         MouseEvent.MOUSE_CLICKED, 
+         MouseEvent.MOUSE_PRESSED, 
+         MouseEvent.MOUSE_DRAGGED,
+         MouseEvent.MOUSE_ENTERED,
+         MouseEvent.MOUSE_EXITED,
+         MouseEvt.MOUSE_HOVERABLE
+      };
+   }
+   
+   /**
+    * Represents the calendar popup that helps to select a date via a calendar sheet using
+    * mouse or keyboard keys.
+    */
+   public class CalendarPopup
+   extends BorderedPanelGuiImpl
+   {
+      /** Calendar header panel with buttons and month and year title */
+      private BorderedPanelGuiImpl calendarHeaderPanel;
+      
+      /** The button to select the previous month that is the month before the selected one*/
+      private ButtonGuiImpl previousMonthButton;
+      
+      /** Calendar title */
+      private TextCell calendarTitle;
+      
+      /** Title panel with calendar title */
+      private BorderedPanelGuiImpl titlePanel;
+      
+      /** The button to select the next month that is the month after the selected one*/
+      private ButtonGuiImpl nextMonthButton;
+      
+      /** The days of week header panel */
+      private BorderedPanelGuiImpl weekHeadersPanel;
+      
+      /** The calendar model */
+      private CalendarMonthModel model;
+      
+      /** The calendar days panel */
+      private BorderedPanelGuiImpl daysInMonthPanel;
+      
+      /** The calendar current date panel */
+      private BorderedPanelGuiImpl currentDatePanel;
+      
+      /** The calendar owner window */
+      private OverlayWindow ownerWindow;
+      
+      /**
+       * Calculates the size of this widget in character units.
+       * 
+       * @return   The size of this widget in character units
+       */
+      @Override
+      public Dimension dimension()
+      {
+         return new Dimension(width(), height());
+      }
+      
+      /**
+       * Calculates the available area.
+       * 
+       * @param    size
+       *           The given size
+       * 
+       * @return   The size of the available area.
+       */
+      private Dimension getAvailableSize(Dimension size)
+      {
+         double width = size.width;
+         double height = size.height;
+         
+         Insets padding = insets();
+         
+         if (padding != null)
+         {
+            width -= padding.widthDelta();
+            height -= padding.heightDelta();
+         }
+         
+         if (border != null)
+         {
+            padding = border.insets();
+            
+            if (padding != null)
+            {
+               width -= padding.widthDelta();
+               height -= padding.heightDelta();
+            }
+         }
+         
+         return new Dimension(width, height);
+      }
+      
+      /**
+       * Returns the height of this widget.
+       * 
+       * @return   The widget's height in character units.
+       */
+      @Override
+      public double height()
+      {
+         FontCache fc = fontResolver.font();
+         
+         String titleText = model.getMonthAndYear();
+         
+         int titleHeight = getTextHeightNative(titleText, fc.font);
+         
+         int pixHeight= ROWS_NUMBER * titleHeight + ROWS_NUMBER * 2 * VERTICAL_ROW_MARGIN;
+         
+         return cc.heightFromNative(pixHeight);
+      }
+      
+      /**
+       * Returns the width of this widget.
+       * 
+       * @return   The widget's width in character units.
+       */
+      @Override
+      public double width()
+      {
+         FontCache fc = fontResolver.font();
+         
+         String titleText = model.getMonthAndYear();
+         
+         int titleWidth = getTextWidthNative(titleText, fc.font);
+         
+         int pixWidth = titleWidth + 2 * BUTTON_SIZE + 2 * 2 * BUTTON_MARGIN;
+         
+         List<String> days = model.getDaysOfWeek();
+         
+         int daysWidth = COLUMNS_NUMBER * 2 * HORIZONTAL_CELL_MARGIN;
+         
+         for(String day : days)
+         {
+            daysWidth += getTextWidthNative(day, fc.font);
+         };
+         
+         if (daysWidth > pixWidth)
+         {
+            pixWidth = daysWidth;
+         }
+         
+         return cc.widthFromNative(pixWidth);
+      }
+      
+      /**
+       * Show widget.
+       */
+      @Override
+      public void show()
+      {
+         previousMonthButton.setEnabled(true);
+         nextMonthButton.setEnabled(true);
+         
+         _setVisible(true);
+         topLevelWindow().show();
+      }
+      
+      /**
+       * Hide this widget and remove it.
+       */
+      @Override
+      public void hide()
+      {
+         if (!isVisible())
+         {
+            return;
+         }
+         topLevelWindow().close();
+         super.hide();
+         popupIsActive = false;
+         popupButton.setEnabled(true);
+         
+         ThinClient.getInstance().independentEventDrawingBracket(
+                  CalendarGuiImpl.this,
+                  () -> CalendarGuiImpl.this.repaint());
+      }
+
+      /**
+       * Creates the calendar panel with calendar header control panel with two buttons, the days
+       * of week static panel, the days of month control panel to select the date and the current
+       * date panel at the bottom to display the today date.
+       */
+      public CalendarPopup()
+      {
+         super(new StackLayout<GuiOutputManager>(Direction.VERTICAL), new LineBorderGuiImpl());
+         setId(WidgetId.nextID());
+         setParent(CalendarGuiImpl.this);
+         
+         model = new CalendarMonthModel(displayedDateTime.toLocalDate());
+         
+         Dimension size = dimension();
+         
+         setSize(size);
+         
+         Dimension reducedSize = getAvailableSize(size);
+         
+         double contentWidth = reducedSize.width;
+         double contentHeight = reducedSize.height;
+
+         calendarHeaderPanel = new BorderedPanelGuiImpl();
+         calendarHeaderPanel.setLayout(new StackLayout<GuiOutputManager>(Direction.HORIZONTAL));
+         calendarHeaderPanel.setId(WidgetId.nextID());
+         calendarHeaderPanel.setColorResolver(titleColorResolver);
+         Consumer<ButtonGuiImpl> Left = b -> ThemeManager.getCurrentTheme().setLeftArrowImages(b);
+         
+         previousMonthButton = createButton("prevMonthButton", BUTTON_SIZE, BUTTON_SIZE, Left);
+         
+         BorderedPanelGuiImpl leftPanel = new BorderedPanelGuiImpl();
+         leftPanel.setLayout(new BorderLayout<GuiOutputManager>());
+         leftPanel.setColorResolver(titleColorResolver);
+         leftPanel.add(previousMonthButton);
+         calendarHeaderPanel.add(leftPanel);
+
+         
+         Consumer<ButtonGuiImpl> Right = b -> ThemeManager.getCurrentTheme().setRightArrowImages(b);
+         
+         nextMonthButton = createButton("nextMonthButton", BUTTON_SIZE, BUTTON_SIZE, Right);
+         
+         double titleWidth = contentWidth - previousMonthButton.width() - nextMonthButton.width();
+         double rowHeight = contentHeight / 9;
+         double cellWidth = contentWidth / 7;
+         double titleHeight = contentHeight / 6;
+         double cellHeight = 11 * contentHeight / 108;
+         
+         titlePanel = new BorderedPanelGuiImpl();
+         titlePanel.setLayout(new BorderLayout());
+         titlePanel.setId(WidgetId.nextID());
+         titlePanel.setColorResolver(titleColorResolver);
+         calendarTitle = new TextCell(titleWidth, rowHeight, model.getMonthAndYear(),
+                  VerticalAlignment.CENTER);
+         calendarTitle.preferredFgColor = titleColorResolver.fgColor;
+         calendarTitle.preferredBgColor = titleColorResolver.bgColor;
+         titlePanel.add(calendarTitle);
+         calendarHeaderPanel.add(titlePanel);
+
+         BorderedPanelGuiImpl rightPanel = new BorderedPanelGuiImpl();
+         rightPanel.setLayout(new BorderLayout<GuiOutputManager>());
+         rightPanel.setColorResolver(titleColorResolver);
+         rightPanel.add(nextMonthButton);
+         calendarHeaderPanel.add(rightPanel);
+
+         add(calendarHeaderPanel);
+         
+         weekHeadersPanel = new BorderedPanelGuiImpl();
+         weekHeadersPanel.setLayout(new GridLayout(7, 1));
+         weekHeadersPanel.setId(WidgetId.nextID());
+         weekHeadersPanel.setColorResolver(bodyColorResolver);
+         
+         weekHeadersPanel.setSize(contentWidth, rowHeight);
+         
+         List<String> days = model.getDaysOfWeek();
+         
+         days.forEach((day)->
+         {
+            TextCell dayTitle = new TextCell(cellWidth, rowHeight, day, VerticalAlignment.CENTER);
+            weekHeadersPanel.add(dayTitle);
+         });
+         
+         add(weekHeadersPanel);
+         
+         daysInMonthPanel = new BorderedPanelGuiImpl();
+         daysInMonthPanel.setLayout(new GridLayout(7, 6));
+         daysInMonthPanel.setId(WidgetId.nextID());
+         
+         LocalDate sftd = model.startFirstTrailingDay;
+         LocalDate eftd = model.endFirstTrailingDay;
+         
+         if (sftd != null && eftd != null)
+         {
+            setContinuosPeriodOfDays(sftd, eftd, cellWidth, cellHeight,
+                     trailingColorResolver.bgColor, trailingColorResolver.fgColor);
+         }
+         
+         LocalDate smd = model.startMonthDay;
+         LocalDate emd = model.endMonthDay;
+         
+         if (smd != null && emd != null)
+         {
+            setContinuosPeriodOfDays(smd, emd, cellWidth, cellHeight, null, null);
+         }
+         
+         LocalDate sltd = model.startLastTrailingDay;
+         LocalDate eltd = model.endLastTrailingDay;
+         
+         if (sltd != null && eltd != null)
+         {
+            setContinuosPeriodOfDays(sltd, eltd, cellWidth, cellHeight,
+                     trailingColorResolver.bgColor, trailingColorResolver.fgColor);
+         }
+
+         add(daysInMonthPanel);
+         
+         screen.getRegistry().addWidget(this);
+         screen.getRegistry().addWidget(daysInMonthPanel);
+         currentDatePanel = new BorderedPanelGuiImpl();
+         currentDatePanel.setLayout(new GridLayout(1, 1));
+         currentDatePanel.setId(WidgetId.nextID());
+         
+         TextCell today = new TextCell(contentWidth, rowHeight, model.getToday());
+         currentDatePanel.add(today);
+         
+         add(currentDatePanel);
+         
+         nextMonthButton.addListener(new ActionListener()
+         {
+            
+            @Override
+            public void onAction(ActionEvent event)
+            {
+               event.consume();
+               model.selectNextMonth();
+               updateCalendarView();
+            }
+         });
+         
+         previousMonthButton.addListener(new ActionListener()
+         {
+            
+            @Override
+            public void onAction(ActionEvent event)
+            {
+               event.consume();
+               model.selectPreviousMonth();
+               updateCalendarView();
+            }
+         });
+         
+         leftPanel.setSize(previousMonthButton.width(), titleHeight);
+         leftPanel.doLayout();
+         
+         titlePanel.setSize(contentWidth - previousMonthButton.width() - nextMonthButton.width(),
+                            titleHeight);
+         titlePanel.doLayout();
+         
+         rightPanel.setSize(nextMonthButton.width(), titleHeight);
+         rightPanel.doLayout();
+         
+         calendarHeaderPanel.setSize(contentWidth, titleHeight);
+         calendarHeaderPanel.doLayout();
+         weekHeadersPanel.setSize(contentWidth, rowHeight);
+         weekHeadersPanel.doLayout();
+         daysInMonthPanel.setSize(contentWidth, 11 * contentHeight / 18);
+         daysInMonthPanel.doLayout();
+         currentDatePanel.setSize(contentWidth, rowHeight);
+         currentDatePanel.doLayout();
+         doLayout();
+
+         ownerWindow = new OverlayWindow(this, CalendarGuiImpl.this.topLevelWindow())
+         {
+            /**
+             * This overlay window returns its calendar popup as its unique focused widget.
+             * 
+             * @return   This calendar popup
+             */
+            @Override
+            public Widget<GuiOutputManager> currentFocus()
+            {
+               return CalendarPopup.this;
+            }
+
+            /**
+             * This overlay window returns its calendar popup as its unique focused widget.
+             * 
+             * @param    recurse
+             *           The boolean flag
+             * 
+             * @return   This calendar popup
+             */
+            @Override
+            public Widget<GuiOutputManager> currentFocus(boolean recurse)
+            {
+               return CalendarPopup.this;
+            }
+         };
+         
+         ownerWindow.setDestroyingListener(()->
+         {
+            CalendarPopup.this.hide();
+         });
+         
+         NativePoint np = UiUtils.getDropDownPhysicalLocation(CalendarGuiImpl.this, this, gd);
+         
+         setPhysicalLocation(np.x, np.y);
+         
+         ownerWindow.setLocation(screenLocation());
+         
+         topLevelWindow().setSize(width(), height());
+         
+         parent().setFocus(this);
+      }
+      
+      /**
+       * Sets up the continuous calendar area given by its days period, its cell's size and 
+       * the preferred foreground and background colors.
+       * 
+       * @param    start
+       *           The first day of the given period
+       * @param    end
+       *           The last day of the given period
+       * @param    cellWidth
+       *           The cell's width in character units
+       * @param    cellHeight
+       *           The cell's height in character units
+       * @param    prefBgColor
+       *           The preferred background color
+       * @param    prefFgColor
+       *           The preferred foreground color
+       */
+      private void setContinuosPeriodOfDays(LocalDate start,
+                                            LocalDate end,
+                                            double cellWidth,
+                                            double cellHeight,
+                                            ColorRgb prefBgColor,
+                                            ColorRgb prefFgColor)
+      {
+         int firstTrailing = start.get(ChronoField.DAY_OF_MONTH);
+         int endTrailing = end.get(ChronoField.DAY_OF_MONTH);
+         
+         int selected;
+         
+         if (model.selected.isAfter(start) && model.selected.isBefore(end) ||
+             (model.selected.equals(start) || model.selected.equals(end)))
+         {
+            selected = model.selected.get(ChronoField.DAY_OF_MONTH);
+         }
+         else
+         {
+            selected = -1;
+         }
+         
+         for (int i = firstTrailing; i <= endTrailing; i++)
+         {
+            TextCell dayInMonth = new TextCell(cellWidth, cellHeight, String.valueOf(i));
+            
+            if (i == selected)
+            {
+               dayInMonth.selected = true;
+            }
+            
+            if (prefBgColor != null)
+            {
+               dayInMonth.preferredBgColor = prefBgColor;
+            }
+            
+            if (prefFgColor != null)
+            {
+               dayInMonth.preferredFgColor = prefFgColor;
+            }
+            
+            daysInMonthPanel.add(dayInMonth);
+         }
+      }
+      
+      /**
+       * Updates the calendar content according to the calendar model.
+       */
+      private void updateDaysInMonth()
+      {
+         int widgetsNumber = daysInMonthPanel.widgets().length; // 42
+         
+         LocalDate firstDay = model.getFirstVisibleDate();
+         
+         for(int i = 0; i < widgetsNumber; i++)
+         {
+            Widget<?> widget = daysInMonthPanel.get(i);
+            if (widget instanceof TextCell)
+            {
+               LocalDate currDay = firstDay.plusDays(i);
+               int day = currDay.get(ChronoField.DAY_OF_MONTH);
+               ((TextCell) widget).text = String.valueOf(day);
+               if (currDay.equals(model.selected))
+               {
+                  ((TextCell) widget).selected = true;
+               }
+               else
+               {
+                  ((TextCell) widget).selected = false;
+               }
+               
+               if ((model.startFirstTrailingDay != null &&
+                        (model.startFirstTrailingDay.equals(currDay) ||
+                         (model.startFirstTrailingDay.isBefore(currDay) &&
+                          model.endFirstTrailingDay.isAfter(currDay)) ||
+                          model.endFirstTrailingDay.equals(currDay)))      ||
+                   (model.startLastTrailingDay != null &&
+                          (model.startLastTrailingDay.equals(currDay) ||
+                           (model.startLastTrailingDay.isBefore(currDay) &&
+                            model.endLastTrailingDay.isAfter(currDay)) ||
+                            model.endLastTrailingDay.equals(currDay))))
+               {
+                  ((TextCell) widget).preferredBgColor = trailingColorResolver.bgColor;
+                  ((TextCell) widget).preferredFgColor = trailingColorResolver.fgColor;
+               }
+               else
+               {
+                  ((TextCell) widget).preferredBgColor = null;
+                  ((TextCell) widget).preferredFgColor = null;
+               }
+            }
+         };
+      }
+      
+      /**
+       * Calculates the location of the calendar popup relative to the parent window.
+       * 
+       * @return   The location of the calendar popup relative to the parent window
+       */
+      @Override
+      public Point screenLocation()
+      {
+         if (parent() == null) {
+            throw new IllegalStateException("widget is not in the container");
+         }
+         
+         return Coordinate.scale(parent().screenLocation().translate(location()));
+      }
+      
+      /**
+       * Determine the mouse actions processed by this widget.
+       * 
+       * @return   The mouse actions processed by this widget
+       */
+      @Override
+      protected int[] mouseActions()
+      {
+         return new int[]
+         {
+                  MouseEvent.MOUSE_CLICKED, 
+                  MouseEvent.MOUSE_PRESSED, 
+                  MouseEvent.MOUSE_DRAGGED,
+                  MouseEvent.MOUSE_ENTERED,
+                  MouseEvent.MOUSE_EXITED,
+                  MouseEvt.MOUSE_HOVERABLE
+         };
+      }
+
+      /**
+       * Handles the date selection.
+       * 
+       * @param    event
+       *           The mouse event
+       */
+      @Override
+      public void mouseClicked(MouseEvent event)
+      {
+         NativePoint mousePoint = new NativePoint(event.getX(), event.getY());
+         
+         NativeRectangle area = daysInMonthPanel.physicalBounds();
+         
+         if (area.contains(mousePoint))
+         {
+            int col = Math.round((event.getX() - area.left()) * 7 / area.width());
+            int row = Math.round((event.getY() - area.top()) * 6 / area.height());
+            
+            // The number of days passed from the day in the first row and the first column of
+            // this calendar
+            int days = col + row * 7; 
+            
+            LocalDate firstDayInCalendar = model.getFirstVisibleDate();
+            
+            model.set(firstDayInCalendar.plusDays(days));
+            
+            updateDisplayedDateTimeValue();
+         }
+         
+      }
+
+      /**
+       * Overwrites the standard findMouseSource in order to delegate mouse clicks to this
+       * parent widget. 
+       * 
+       * @param    point
+       *           The physical point within this container
+       * 
+       * @return   The widget that handles this event
+       */
+      @Override
+      public Widget<GuiOutputManager> findMouseSource(NativePoint point)
+      {
+         Widget<GuiOutputManager> source = super.findMouseSource(point);
+         
+         if (source == previousMonthButton || source == nextMonthButton)
+         {
+            return source;
+         }
+         
+         return this;
+      }
+      
+      /**
+       * Updates the displayed date/time value according to the calendar model changes.
+       */
+      private void updateDisplayedDateTimeValue()
+      {
+         displayedDateTime = LocalDateTime.of(model.selected, LocalTime.of(0, 0, 0, 0));
+         
+         updateDateTimePattern(config.formatStyle, config.customFormat, displayedDateTime);
+         
+         config.dateTimeValue = displayedDateTime.format(dateTimeFormatter);
+         
+         hide();
+         
+         repaint();
+      }
+      
+      /**
+       * Updates the calendar view according to the changed calendar model.
+       */
+      private void updateCalendarView()
+      {
+         calendarTitle.text = model.getMonthAndYear();
+         updateDaysInMonth();
+         repaint();
+      }
+      
+      /**
+       * Handles UP, DOWN, LEFT and RIGHT keys.
+       * 
+       * @param    event
+       *           The key input event
+       */
+      private void handleKeyEvent(KeyInput event)
+      {
+         int key = event.actionCode();
+         
+         if (key == Keyboard.KA_RETURN)
+         {
+            updateDisplayedDateTimeValue();
+         }
+         
+         boolean respond = key == Keyboard.KA_CURSOR_LEFT || key == Keyboard.KA_CURSOR_RIGHT ||
+                           key == Keyboard.KA_CURSOR_UP || key == Keyboard.KA_CURSOR_DOWN;
+         
+         if (!respond)
+         {
+            return;
+         }
+         
+         if (key == Keyboard.KA_CURSOR_LEFT)
+         {
+            model.selectPreviousDay();
+         }
+         else if (key == Keyboard.KA_CURSOR_RIGHT)
+         {
+            model.selectNextDay();
+         }
+         else if (key == Keyboard.KA_CURSOR_DOWN)
+         {
+            model.selectNextWeekDay();
+         }
+         else if (key == Keyboard.KA_CURSOR_UP)
+         {
+            model.selectPreviousWeekDay();
+         }
+         
+         updateCalendarView();
+      }
+      
+      /**
+       * Returns the widget that has the current focus.
+       * 
+       * @return   The calendar popup widget
+       */
+      @Override
+      public Widget<GuiOutputManager> currentFocus()
+      {
+         return this;
+      }
+
+      /**
+       * Returns the widget that has the current focus by default.
+       * 
+       * @return   The calendar popup widget
+       */
+      @Override
+      public Widget<GuiOutputManager> defaultFocus()
+      {
+         return this;
+      }
+
+      /**
+       * Handles UP, DOWN, LEFT and RIGHT keys.
+       * 
+       * @param    event
+       *           The key input event
+       */
+      @Override
+      public void processKeyEvent(KeyInput event)
+      {
+         handleKeyEvent(event);
+      }
+   }
+   
+   /**
+    * Represents the model for the calendar popup.
+    */
+   public class CalendarMonthModel
+   {
+      /**
+       * The beginning day of the trailing days before the current calendar month
+       */
+      private LocalDate startFirstTrailingDay;
+      
+      /**
+       * The end day of the trailing days before the current calendar month
+       */
+      private LocalDate endFirstTrailingDay;
+      
+      /**
+       * The current calendar month represented by [1..12]
+       */
+      private int month;
+      
+      /**
+       * The current calendar year
+       */
+      private int year;
+      
+      /**
+       * The beginning day of the current calendar month
+       */
+      private LocalDate startMonthDay;
+      
+      /**
+       * The last day of the current calendar month
+       */
+      private LocalDate endMonthDay;
+      
+      /**
+       * The beginning day of the trailing days after the current calendar month
+       */
+      private LocalDate startLastTrailingDay;
+      
+      /**
+       * The end day of the trailing days after the current calendar month
+       */
+      private LocalDate endLastTrailingDay;
+      
+      /**
+       * The selected day of the current calendar month
+       */
+      private LocalDate selected;
+      
+      /**
+       * The special formatter for the calendar title
+       */
+      private final DateTimeFormatter monthYearFormatter;
+      
+      /**
+       * The special formatter for the calendar footer
+       */
+      private final DateTimeFormatter todayFormatter;
+      
+      /**
+       * The list of days of a week
+       */
+      private final ArrayList<String> daysOfWeek;
+      
+      /**
+       * Represents the month model for the calendar popup.
+       * 
+       * @param    selected
+       *           The given date
+       */
+      public CalendarMonthModel(LocalDate selected)
+      {
+         DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
+         
+         builder.appendPattern("MMMM  YYYY");
+         
+         monthYearFormatter = builder.toFormatter();
+         
+         DateTimeFormatterBuilder builder2 = new DateTimeFormatterBuilder();
+         
+         builder2.appendPattern(DateTimeFormatterBuilder.getLocalizedDateTimePattern(
+                  FormatStyle.MEDIUM, null, chrono, locale));
+         
+         todayFormatter = builder2.toFormatter();
+         
+         set(selected);
+         
+         daysOfWeek = new ArrayList<String>();
+         
+         daysOfWeek.add(DayOfWeek.of(7).getDisplayName(TextStyle.SHORT, locale));
+
+         for (int d = 1; d <= 6; d++)
+         {
+            daysOfWeek.add(DayOfWeek.of(d).getDisplayName(TextStyle.SHORT, locale));
+         }
+      }
+      
+      /**
+       * Updates the current month model for the calendar popup with the given date.
+       * 
+       * @param    selected
+       *           The given date
+       */
+      private void set(LocalDate selected)
+      {
+         this.selected = selected;
+
+         this.month = selected.get(ChronoField.MONTH_OF_YEAR);
+         this.year = selected.get(ChronoField.YEAR);
+         
+         YearMonth currentMonth = YearMonth.of(year, month);
+         startMonthDay = currentMonth.atDay(1);
+         endMonthDay   = currentMonth.atEndOfMonth();
+         
+         int firstTrailingDays = startMonthDay.get(ChronoField.DAY_OF_WEEK) % 7;
+         
+         if (firstTrailingDays > 0)
+         {
+            startFirstTrailingDay = startMonthDay.minusDays(firstTrailingDays);
+            endFirstTrailingDay = startMonthDay.minusDays(1);
+         }
+         else
+         {
+            startFirstTrailingDay = null;
+            endFirstTrailingDay = null;
+         }
+         
+         LocalDate endDayInView;
+         
+         if (startFirstTrailingDay != null)
+         {
+            endDayInView = startFirstTrailingDay.plusDays(41);
+         }
+         else
+         {
+            endDayInView = startMonthDay.plusDays(41);
+         }
+         
+         if (endMonthDay.isBefore(endDayInView))
+         {
+            
+            startLastTrailingDay = endMonthDay.plusDays(1);
+            endLastTrailingDay = endDayInView;
+         }
+         else
+         {
+            startLastTrailingDay = null;
+            endLastTrailingDay = null;
+         }
+      }
+      
+      /**
+       * Sets the following day after the selected date to be a new selected one and updates
+       * this model.
+       * 
+       * @return   This model instance
+       */
+      public CalendarMonthModel selectNextDay()
+      {
+         set(selected.plusDays(1));
+         return this;
+      }
+      
+      /**
+       * Sets the previous day before the selected date to be a new selected one and updates
+       * this model.
+       * 
+       * @return   This model instance
+       */
+      public CalendarMonthModel selectPreviousDay()
+      {
+         set(selected.minusDays(1));
+         return this;
+      }
+      
+      /**
+       * Sets the day of the following week after the selected date to be a new selected one and
+       * updates this model.
+       * 
+       * @return   This model instance
+       */
+      public CalendarMonthModel selectNextWeekDay()
+      {
+         set(selected.plusWeeks(1));
+         return this;
+      }
+      
+      /**
+       * Sets the day of the previous week before the selected date to be a new selected one and
+       * updates this model.
+       * 
+       * @return   This model instance
+       */
+      public CalendarMonthModel selectPreviousWeekDay()
+      {
+         set(selected.minusWeeks(1));
+         return this;
+      }
+      
+      /**
+       * Sets the day of the following month after the selected date to be a new selected one and
+       * updates this model.
+       * 
+       * @return   This model instance
+       */
+      public CalendarMonthModel selectNextMonth()
+      {
+         set(selected.plusMonths(1));
+         return this;
+      }
+      
+      /**
+       * Sets the day of the previous month before the selected date to be a new selected one and
+       * updates this model.
+       * 
+       * @return   This model instance
+       */
+      public CalendarMonthModel selectPreviousMonth()
+      {
+         set(selected.minusMonths(1));
+         return this;
+      }
+      
+      /**
+       * Gets the current calendar month and year as a title for this calendar.
+       * 
+       * @return   The current calendar month and year as a title for this calendar
+       */
+      public String getMonthAndYear()
+      {
+         return selected.format(monthYearFormatter);
+      }
+      
+      /**
+       * Gets the list of days of a week.
+       * 
+       * @return   The list of days of a week
+       */
+      public List<String> getDaysOfWeek()
+      {
+         return daysOfWeek;
+      }
+      
+      /**
+       * Gets the today date formatted according to the today formatter style.
+       * 
+       * @return   The today date
+       */
+      public String getToday()
+      {
+         return LocalDate.now().format(todayFormatter);
+      }
+      
+      /**
+       * Returns the date of the day beginning this calendar.
+       * 
+       * @return   The date of the day beginning this calendar
+       */
+      public LocalDate getFirstVisibleDate()
+      {
+         if (startFirstTrailingDay != null)
+         {
+            return startFirstTrailingDay;
+         }
+         
+         return startMonthDay;
+      }
+   }
+   
+   /**
+    * Defines vertical alignment enumeration type.
+    */
+   public enum VerticalAlignment
+   {
+      /** The top alignment */
+      TOP,
+      /** The center alignment */
+      CENTER,
+      /** The bottom alignment */
+      BOTTOM
+   }
+   
+   /**
+    * Represents a simple text cell. 
+    */
+   public class TextCell
+   extends AbstractWidget<GuiOutputManager>
+   {
+      /** Represents the cell's width given in character units */
+      private double widthInChars;
+      
+      /** Represents the cell's height given in character units */
+      private double heightInChars;
+      
+      /**
+       * The given displayed text
+       */
+      private String text;
+      
+      /**
+       * The given vertical alignment of the displayed text within this cell. The default text
+       * position is at the cell bottom.
+       */
+      private VerticalAlignment verticalAlignment;
+      
+      /**
+       * Indicates that this text cell is selected or not.
+       */
+      private boolean selected;
+      
+      /**
+       * The preferred background color
+       */
+      private ColorRgb preferredBgColor;
+      
+      /**
+       * The preferred foreground color
+       */
+      private ColorRgb preferredFgColor;
+      
+      /**
+       * Creates this text cell using the given size in character units and displaying the given
+       * text at the bottom.
+       * 
+       * @param    widthInChars
+       *           The given width in character units
+       * @param    heightInChars
+       *           The given height in character units
+       * @param    text
+       *           The given text
+       */
+      public TextCell(double widthInChars, double heightInChars, String text)
+      {
+         this(widthInChars, heightInChars, text, VerticalAlignment.BOTTOM);
+      }
+      
+      /**
+       * Creates this text cell using the given size in character units and displaying the given
+       * text according its vertical alignment.
+       * 
+       * @param    widthInChars
+       *           The given width in character units
+       * @param    heightInChars
+       *           The given height in character units
+       * @param    text
+       *           The given text
+       * @param    verticalAlignment
+       *           The given vertical alignment
+       */
+      public TextCell(double widthInChars,
+                      double heightInChars,
+                      String text,
+                      VerticalAlignment verticalAlignment)
+      {
+         this.widthInChars = widthInChars;
+         this.heightInChars = heightInChars;
+         this.text = text;
+         this.verticalAlignment = verticalAlignment;
+      }
+      
+      /**
+       * Calculates the size of this widget in character units.
+       * 
+       * @return   The size of this widget in character units
+       */
+      @Override
+      public Dimension dimension()
+      {
+         return new Dimension(width(), height());
+      }
+      
+      /**
+       * Implements the main method for drawing this widget.
+       */
+      @Override
+      public void draw()
+      {
+         NativePoint p = physicalLocation();
+         NativeDimension d = this.physicalDimension();
+         
+         // resolve font
+         final FontCache fntCache = fontResolver.font();
+         
+         int fontNum = fontResolver.resolveFontNum();
+         
+         int fontHeight = FontManager.getFontHeight(window(), fntCache.font, fontNum, gd);
+         
+         // set up clipping rectangle
+         NativeRectangle clip = clipRectangle(p, d);
+         
+         gd.draw(getId(), p, clip, () ->
+         {
+            gd.setGuiFont(fntCache.font);
+            
+            ColorRgb bgColor;
+            ColorRgb fgColor;
+            
+            if (selected)
+            {
+               bgColor = trailingColorResolver.selectionBgColor;
+               fgColor = trailingColorResolver.selectionFgColor;
+            }
+            else
+            {
+               if (preferredBgColor != null)
+               {
+                  bgColor = preferredBgColor;
+               }
+               else
+               {
+                  bgColor = bodyColorResolver.bgColor;
+               }
+               
+               if (preferredFgColor != null)
+               {
+                  fgColor = preferredFgColor;
+               }
+               else
+               {
+                  fgColor = bodyColorResolver.fgColor;
+               }
+            }
+            // this offset is used with drawStringWithinBox
+            // to make the target text be placed at the center,
+            // or the top, or bottom of this cell
+            int yOffset = calculateYOffset(d.height, fontHeight);
+            
+            // clear the area
+            gd.setColor(bgColor);
+            gd.fillRect(0, 0, d.width, d.height);
+            gd.setColor(fgColor);
+            gd.drawStringWithinBox(0, yOffset, d.width, d.height, text, null, true);
+         });
+      }
+      
+      /**
+       * Calculates the vertical offset of this text cell. The calculated offset is used with
+       * GuiDriver.drawStringWithinBox to make the target text be placed at the center,
+       * or the top, or bottom of this cell.
+       * 
+       * @param    cellHeight
+       *           The cell height
+       * @param    fontHeight
+       *           The font height
+       * 
+       * @return   The vertical offset of this text cell
+       */
+      private int calculateYOffset(int cellHeight, int fontHeight)
+      {
+         int yOffset = cellHeight;
+         
+         switch(verticalAlignment)
+         {
+            case TOP:
+               yOffset -= (3 * fontHeight / 4);
+               break;
+            case CENTER:
+               yOffset -= (5 * fontHeight / 8);
+               break;
+            default:
+               // alignment to the bottom side of this text cell
+               yOffset -= (fontHeight / 2);
+         }
+         
+         return yOffset;
+      }
+      
+      /**
+       * Returns the height of this widget.
+       * 
+       * @return   The widget's height in character units.
+       */
+      @Override
+      public double height()
+      {
+         return heightInChars;
+      }
+
+      /**
+       * Returns the minimum size of this cell text.
+       * 
+       * @return   The minimum size of this cell text
+       */
+      @Override
+      public Dimension minimumSize()
+      {
+         return dimension();
+      }
+      
+      /**
+       * Returns the width of this widget.
+       * 
+       * @return   The widget's width in character units.
+       */
+      @Override
+      public double width()
+      {
+         return widthInChars;
+      }
+      
+      /**
+       * Sets the new size of this text cell.
+       * 
+       * @param    width
+       *           The new width
+       * @param    height
+       *           The new height
+       */
+      public void setSize(double width, double height)
+      {
+         this.widthInChars = width;
+         this.heightInChars = height;
+         
+         repaint();
+      }
+   }
+   /**
+    * Represents a scrollable widget and control displaying and modifying the date/time content
+    * with help of keyboard keys. Buttons LEFT and RIGHT are used to navigate to the target
+    * component (field) of the displayed date/time and buttons UP and DOWN are used
+    * to modify the currently selected date/time component(field).
+    */
+   public class DateTimeContent
+   extends AbstractWidget<GuiOutputManager>
+   implements ScrollableWidget<GuiOutputManager>
+   {
+      /** The width of this widget given in pixel units */
+      private int widthInPixels;
+      
+      /** The height of this widget given in pixel units */
+      private int heightInPixels;
+      
+      /** The horizontal offset of the date/time content from the left side */
+      private int xOffset;
+      
+      /**
+       * Updates the size of this widget according to the given font.
+       * 
+       * @param    window
+       *           The owner of this widget
+       * @param    font
+       *           The given font
+       */
+      public void updateSize(Window window, int font)
+      {
+         WidgetId parentId = window.getId();
+         
+         double len = dateTimePattern.length();
+         String text = displayedDateTime.format(dateTimeFormatter);
+         int patternWidthInPixels = getTextWidthNative(text, fontResolver.font().font);
+         
+         widthInPixels = Math.max(cc.widthToNative(len, parentId, font), patternWidthInPixels) +
+                         BUTTON_SIZE;
+         
+         double inputHeight = config.heightChars > 0.0 && config.heightChars < 1.0
+                  ? config.heightChars : 1.0;
+
+         heightInPixels = cc.heightToNative(inputHeight, parentId, font);
+      }
+      
+      /**
+       * Calculates the scroll size of this widget given in pixel units.
+       * 
+       * @return   The scroll size of this widget given in pixel units 
+       */
+      @Override
+      public NativeDimension getScrollDimension()
+      {
+         NativeDimension vd = getVisibleDimension();
+         if (vd.width >= widthInPixels)
+         {
+            widthInPixels = vd.width;
+         }
+         if (vd.height >= heightInPixels)
+         {
+            heightInPixels = vd.height;
+         }
+         return new NativeDimension(widthInPixels, heightInPixels);
+      }
+      
+      /**
+       * Returns the visible size of this widget in pixel units.
+       * 
+       * @return   The visible size of this widget given in pixel units
+       */
+      @Override
+      public NativeDimension getVisibleDimension()
+      {
+         return cc.dimensionToNative(dimension());
+      }
+      
+      /**
+       * Returns the minimum scroll step.
+       * 
+       * @return   The minimum scroll step given in pixel units
+       */
+      @Override
+      public NativeDimension getScrollStep()
+      {
+         return new NativeDimension(1, 1);
+      }
+      
+      /**
+       * Scrolls the widget's content according to the given physical translation.
+       * 
+       * @param    newXPosition
+       *           The given translation along a horizontal in pixel units
+       * @param    newYPosition
+       *           The given translation along a vertical in pixel units
+       */
+      @Override
+      public void scroll(Integer newXPosition, Integer newYPosition)
+      {
+         NativePoint p = physicalLocation();
+         p.translate(newXPosition, newYPosition);
+         setPhysicalLocation(p.x, p.y);
+         repaint();
+      }
+      
+      /**
+       * Tests if this widget supports a scroll popup. This implementation always returns false.
+       * 
+       * @return   false
+       */
+      @Override
+      public boolean isScrollPopupSupported()
+      {
+         return false;
+      }
+      
+      /**
+       * Calculates the size of this widget in character units.
+       * 
+       * @return   The size of this widget in character units
+       */
+      @Override
+      public Dimension dimension()
+      {
+         Dimension dim = CalendarGuiImpl.this.dimension();
+         
+         Insets insets = CalendarGuiImpl.this.insets();
+         
+         if (insets != null)
+         {
+            dim = dim.reducedBy(insets);
+         }
+         
+         if (CalendarGuiImpl.this.border != null)
+         {
+            CalendarGuiImpl.this.border.insets();
+            
+            if (insets != null)
+            {
+               dim = dim.reducedBy(insets);
+            }
+         }
+         
+         insets = buttonsPanel.insets(); // doesn't have a border
+         
+         if (insets != null)
+         {
+            dim = dim.reducedBy(insets);
+         }
+         
+         double leftPadding;
+         
+         if (config.checkBox)
+         {
+            leftPadding = toggleButton.width();
+         }
+         else
+         {
+            leftPadding = 0;
+         }
+         
+         return dim.reducedBy(new Insets(0, leftPadding, 0, buttonsPanel.width()));
+      }
+      
+      /**
+       * Implements the main method for drawing this widget.
+       */
+      @Override
+      public void draw()
+      {
+         // get location and size
+         Viewport parent = parent(Viewport.class);
+         NativePoint p = parent.physicalLocation();
+         NativePoint delta = physicalLocation();
+         NativePoint p1 = p.offset(delta.x, 0);
+         NativeDimension d = this.physicalDimension();
+         Theme theme = ThemeManager.getCurrentTheme();
+         
+         // refresh widget settings
+         
+         // determine colors to use
+         bodyColorResolver.colors();
+         trailingColorResolver.colors();
+         titleColorResolver.colors();
+         
+         // resolve font
+         final FontCache fntCache = fontResolver.font();
+         
+         int fontNum = fontResolver.resolveFontNum();
+         
+         int fontHeight = FontManager.getFontHeight(window(), fntCache.font, fontNum, gd);
+         
+         xOffset = is3DBorder() ? 1 : 0;
+         
+         // set up clipping rectangle
+         NativeRectangle clip = clipRectangle(p, d);
+         
+         gd.draw(getId(), p, clip, () ->
+         {
+            // clear the area
+            gd.setColor(bodyColorResolver.bgColor);
+            gd.fillRect(0, 0, d.width, d.height);
+         });
+         
+         // clippable drawing start 
+         NativeRectangle clip1 = clipRectangle(p1, getScrollDimension());
+         gd.draw(getId(), p1, clip1, () ->
+         {
+            // clear the area
+            gd.setColor(bodyColorResolver.bgColor);
+            gd.fillRect(0, 0, d.width, d.height);
+            
+            gd.setGuiFont(fntCache.font);
+            
+            int y = d.height;
+            
+            String text = displayedDateTime.format(dateTimeFormatter);
+            
+            TextLookAndFeel textLookAndFeel;
+            
+            if (isEnabled())
+            {
+               //enabled look and feel
+               textLookAndFeel = theme.getTextLookAndFeel(bodyColorResolver,
+                                                          trailingColorResolver);
+            }
+            else
+            {
+               //disabled look and feel
+               textLookAndFeel = theme.getTextLookAndFeel(trailingColorResolver,
+                                                          trailingColorResolver);
+            }
+            
+            boolean select = CalendarGuiImpl.this.hasFocus() &&
+                             !CalendarGuiImpl.this.popupIsActive &&
+                             isEnabled();
+            
+            TextLineSelection selection = getFieldSelection(text, select);
+            
+            // calculates the y offset to use with drawTextSelection such
+            // that the target text is displayed vertically centered
+            if (textLookAndFeel.getTextBaseline() == TextBaseline.MIDDLE)
+            {
+               y -= 7 * fontHeight / 8;
+            }
+            else
+            {
+               y -= 11 * fontHeight / 8;
+            }
+            
+            gd.drawTextSelection(selection, textLookAndFeel, xOffset, y, fontHeight, 0);
+         });
+      }
+      
+      /**
+       * Returns the height of this widget.
+       * 
+       * @return   The widget's height in character units.
+       */
+      @Override
+      public double height()
+      {
+         return dimension().height;
+      }
+      
+      /**
+       * Returns the minimum size of this widget.
+       * 
+       * @return   The minimum size of this widget
+       */
+      @Override
+      public Dimension minimumSize()
+      {
+         return dimension();
+      }
+
+      /**
+       * Returns the width of this widget.
+       * 
+       * @return   The widget's width in character units.
+       */
+      @Override
+      public double width()
+      {
+         return dimension().width;
+      }
+
+      /**
+       * Gets the field selection for the given displayed date and time value.
+       * 
+       * @param    txt
+       *           The displayed date and time value
+       * @param    select
+       *           The flag indicating that the current field must be selected or not.
+       * 
+       * @return   The field selection
+       */
+      public TextLineSelection getFieldSelection(String txt, boolean select)
+      {
+         int txtLen = txt.length();
+         
+         int start;
+         
+         int end;
+         
+         if (select)
+         {
+            int selectedFieldIndex = getSelectedFieldIndex(); // selected field
+            
+            start = fieldPositions[selectedFieldIndex].getBeginIndex();
+            
+            end = fieldPositions[selectedFieldIndex].getEndIndex();
+         }
+         else
+         {
+            start = txtLen;
+            
+            end = txtLen;
+         }
+         
+         String[] texts = new String[3];
+         
+         // prefix not-selected text
+         texts[0] = (start == 0 ? "" : txt.substring(0, start));
+         // selected text
+         texts[1] = txt.substring(start, end);
+         // suffix not-selected text
+         texts[2] = (end == txtLen ? "" : txt.substring(end));
+
+         int[] hintWidths = new int[3];
+         
+         for(int i = 0; i < 3; i++)
+         {
+            if (texts[i].isEmpty())
+            {
+               hintWidths[i] = 0;
+            }
+            else
+            {
+               hintWidths[i] = getTextWidthNative(texts[i], fontResolver.font().font);
+            }
+         }
+         
+         /**
+          * Defines the current text selection object.
+          */
+         return new TextLineSelection()
+         {
+            /**
+             * Returns the 3-tuple of string widths used as hint widths for this line split into outer
+             * and selected text.
+             * 
+             * @return   The 3-tuple of string hint widths in pixels
+             */
+            @Override
+            public int[] getTextWidthsHint()
+            {
+               return hintWidths;
+            }
+            
+            /**
+             * Returns the column and row, where the current text selection gets the its origin.
+             * 
+             * @return   The beginning of the current text selection
+             */
+            @Override
+            public NativePoint getSelectionStart()
+            {
+               return new NativePoint(start, 0);
+            }
+
+            /**
+             * Returns the most closest point, column and row, that breaks the continuous selection text
+             * part.
+             * 
+             * @return   The break point, column and row, of the current text selection
+             */
+            @Override
+            public NativePoint getSelectionEnd()
+            {
+               return new NativePoint(end, 0);
+            }
+            
+            /**
+             * Returns the 3-tuple of text parts into which this line has been split by text selection.
+             * 
+             * @return   The 3-tuple of text parts
+             */
+            @Override
+            public String[] getLineParts()
+            {
+               return texts;
+            }
+         };
+      }
+   }
+   
+   /**
+    * Gets the current screen value formatted according its format style.
+    * 
+    * @return   The current screen value as a character
+    */
+   @Override
+   public BaseDataType getValue()
+   {
+      return new character(config.dateTimeValue);
+   }
+   
+   /**
+    * Sets the new selected date value.
+    * 
+    * @param    value
+    *           The new selected date value
+    */
+   @Override
+   public void setValue(BaseDataType value)
+   {
+      //TODO
+   }
+   
+   /**
+    * Gets the current screen value formatted according its format style.
+    * 
+    * @return   The current screen value as a character
+    */
+   @Override
+   public String getText()
+   {
+      return config.dateTimeValue;
+   }
+   
+   /**
+    * Tests if this widget has new settings.
+    * 
+    * @return   True if this widget has new settings, otherwise false.
+    */
+   @Override
+   public boolean isModified()
+   {
+      return config.modified;
+   }
+}

=== modified file 'src/com/goldencode/p2j/ui/client/gui/DropDownGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/DropDownGuiImpl.java	2019-04-04 17:03:54 +0000
+++ src/com/goldencode/p2j/ui/client/gui/DropDownGuiImpl.java	2020-02-25 16:54:43 +0000
@@ -2,7 +2,7 @@
 ** Module   : DropDownGuiImpl.java
 ** Abstract : Drop down box for combo-box GUI specific implementation.
 **
-** Copyright (c) 2015-2019, Golden Code Development Corporation.
+** Copyright (c) 2015-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description----------------------------------
 ** 001 EVL 20150428 Created initial version.
@@ -34,6 +34,7 @@
 ** 020 OM  20170320 Added support for Themes.
 ** 021 CA  20180502 Fixed COLOR option, COLOR statement and other color-related issues.
 ** 022 EVL 20190403 Adding implementation support for AUTO-COMPLETION attribute.
+** 023 SBI 20200225 Reused the common code.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -95,7 +96,6 @@
 import com.goldencode.p2j.ui.client.*;
 import com.goldencode.p2j.ui.client.event.*;
 import com.goldencode.p2j.ui.client.gui.driver.*;
-import com.goldencode.p2j.ui.client.gui.theme.*;
 import com.goldencode.p2j.ui.client.model.*;
 import com.goldencode.p2j.ui.client.ScrollBar.Bars;
 
@@ -197,48 +197,8 @@
       
       // set list width, account for the border
       list.setColumns(width() - scrollPaneGui.getBorder().insets().widthDelta());
-
-      // start GUI specifics
-      // get the absolute coord of the combo-box this drop-down belongs to
-      NativePoint cbbPhysLoc = parent.screenPhysicalLocation();
-      
-      // set the coordinates relative to parent combo-box widget
-      //
-      // The combo-box and drop-down both belong to different frames. The parent frame
-      // for drop-down is border-less and header-less invisible frame located inside Window
-      // workspaces. So when we get the physical location of the combo-box we have location
-      // related to frame the combo-box belongs to. If we use this coords to place drop-down we
-      // have XY shift according to combo-box's frame placement and decoration. That's why we can
-      // not just use combo-box location plus combo box height here.
-      //
-      // The approach is to get absolute coordinates of the combo-box, get the coordinate of the
-      // workspace, subtract second from first and add the combo-box height.
-      
-      // the combo-box height plus some extra pixels to match 4GL look and feel
-      int cbbHeightPlus = screen().coordinates().heightToNative(parent.height()) + 
-                          ThemeManager.getCurrentTheme().getComboBoxDropDownOffset();
-      NativePoint ddPhysLoc = new NativePoint(cbbPhysLoc.x, cbbPhysLoc.y);
-      
-      // check if the drop-down will fit the current display dimension
-      // get combo-box window ID
-      int mainWndId = getComboBox().topLevelWindow().getId().asInt();
-      // get combo-box absolute coordinates
-      NativePoint cbbDispLoc = parent.displayPhysicalLocation();
-      // total screen and drop-down dimension
-      NativeRectangle nr = gd.getDisplayBounds(mainWndId);
-      NativeDimension nDim = physicalDimension();
-      
-      // calculate final drop-down y coordinate (to display the full list above or below the
-      // parent) based on space available, but the preference will be below if possible
-      if (cbbDispLoc.y + cbbHeightPlus + nDim.height > nr.bottom() &&
-          cbbDispLoc.y - nDim.height > nr.top())
-      {
-         ddPhysLoc.y -= nDim.height;
-      }
-      else
-      {
-         ddPhysLoc.y += cbbHeightPlus;
-      }
+      
+      NativePoint ddPhysLoc = UiUtils.getDropDownPhysicalLocation(parent, this, gd);
       
       // set up drop-down location below or above the combo-box depending on above calculations
       setPhysicalLocation(ddPhysLoc.x, ddPhysLoc.y);

=== modified file 'src/com/goldencode/p2j/ui/client/gui/GuiColorResolver.java'
--- src/com/goldencode/p2j/ui/client/gui/GuiColorResolver.java	2018-05-03 15:25:10 +0000
+++ src/com/goldencode/p2j/ui/client/gui/GuiColorResolver.java	2020-02-25 16:53:34 +0000
@@ -2,7 +2,7 @@
 ** Module   : GuiColorResolver.java
 ** Abstract : GUI widget color resolver
 **
-** Copyright (c) 2014-2018, Golden Code Development Corporation.
+** Copyright (c) 2014-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 MAG 20141022 Created initial version.
@@ -14,6 +14,7 @@
 ** 006 OM  20170707 Rectangles do not inherit parent frame color.
 ** 007 CA  20171111 Added WIDGET:BGCOLOR-RGB and WIDGET:FGCOLOR-RGB FWD extension attributes
 ** 008 CA  20180502 Fixed COLOR option, COLOR statement and other color-related issues.
+** 009 SBI 20200225 Added Calendar widget support.
 */
 
 /*
@@ -182,6 +183,7 @@
                     !(config instanceof BrowseConfig && config.enabled  ||
                       config instanceof ComboBoxConfig && config.enabled||
                       config instanceof FillInConfig && config.enabled  ||
+                      config instanceof CalendarConfig ||
                       config instanceof RectangleConfig)))
                {
                   // disabled browses and rectangles do not inherit implicit frame color
@@ -225,6 +227,7 @@
                    (frame.config().fgcolor != -1 ||
                     !(config instanceof BrowseConfig && config.enabled   ||
                       config instanceof ComboBoxConfig && config.enabled ||
+                      config instanceof CalendarConfig ||
                       config instanceof RectangleConfig)))
                {
                   // disabled browses and rectangles do not inherit implicit frame color

=== modified file 'src/com/goldencode/p2j/ui/client/gui/GuiWidgetFactory.java'
--- src/com/goldencode/p2j/ui/client/gui/GuiWidgetFactory.java	2019-11-19 14:11:39 +0000
+++ src/com/goldencode/p2j/ui/client/gui/GuiWidgetFactory.java	2020-02-25 16:52:42 +0000
@@ -2,7 +2,7 @@
 ** Module   : GuiWidgetFactory.java
 ** Abstract : a factory for GUI widgets
 **
-** Copyright (c) 2014-2019, Golden Code Development Corporation.
+** Copyright (c) 2014-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 MAG 20140521 Created initial version.
@@ -53,6 +53,7 @@
 **                  it the base for the TREELIST widget.
 ** 034 MAG 20191106 Initial implementation of IMAGE-LIST widget.
 ** 035 MAG 20191003 Adding PROGRESS-BAR widget related changes.
+** 036 SBI 20200225 Added Calendar widget support.
 */
 
 /*
@@ -175,6 +176,7 @@
       classMap.put(HtmlBrowserGuiImpl.class, HtmlBrowserGuiImpl.class);
       classMap.put(ImageListGuiImpl.class, ImageListGuiImpl.class);
       classMap.put(ProgressBarGuiImpl.class, ProgressBarGuiImpl.class);
+      classMap.put(CalendarGuiImpl.class, CalendarGuiImpl.class);
    }
    
    /**

=== modified file 'src/com/goldencode/p2j/ui/client/gui/ProgressBarGui.java'
--- src/com/goldencode/p2j/ui/client/gui/ProgressBarGui.java	2019-12-13 08:58:45 +0000
+++ src/com/goldencode/p2j/ui/client/gui/ProgressBarGui.java	2020-02-25 16:51:37 +0000
@@ -2,10 +2,11 @@
 ** Module   : ProgressBarGui.java
 ** Abstract : PROGRESS-BAR widget interface.
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 MAG 20191011 Created initial version.
+** 002 SBI 20200225 Reused the common code.
 */
 
 /*
@@ -86,25 +87,6 @@
    public static final int CC_SCROLLING_STANDARD = 0;
    public static final int CC_SCROLLING_SMOOTH   = 1;
    
-   /* Mouse Pointer */
-   public static final int CC_DEFAULT          = 0;
-   public static final int CC_ARROW            = 1;
-   public static final int CC_CROSS            = 2;
-   public static final int CC_IBEAM            = 3;
-   public static final int CC_ICON             = 4;
-   public static final int CC_SIZE             = 5;
-   public static final int CC_SIZE_NESW        = 6;
-   public static final int CC_SIZE_NS          = 7;
-   public static final int CC_SIZE_NWSE        = 8;
-   public static final int CC_SIZE_EW          = 9;
-   public static final int CC_UP_ARROW        = 10;
-   public static final int CC_HOURGLASS       = 11;
-   public static final int CC_NO_DROP         = 12;
-   public static final int CC_ARROW_HOURGLASS = 13;
-   public static final int CC_ARROW_QUESTION  = 14;
-   public static final int CC_SIZE_ALL        = 15;
-   public static final int CC_CUSTOM          = 99;
-
    /**
     * If the method returns <code>true</code> the widget should be represented with 3D visuals.
     */

=== modified file 'src/com/goldencode/p2j/ui/client/gui/ProgressBarGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/ProgressBarGuiImpl.java	2019-12-09 12:20:19 +0000
+++ src/com/goldencode/p2j/ui/client/gui/ProgressBarGuiImpl.java	2020-02-25 16:50:52 +0000
@@ -2,10 +2,11 @@
 ** Module   : ProgressBarGuiImpl.java
 ** Abstract : GUI implementation of the PROGRESS-BAR widget.
 **
-** Copyright (c) 2019, Golden Code Development Corporation.
+** Copyright (c) 2019-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 MAG 20191003 Created initial version.
+** 002 SBI 20200225 Reused the common code.
 */
 
 /*
@@ -65,6 +66,7 @@
 
 import static com.goldencode.p2j.ui.client.gui.OcxMouseHelper.*;
 import static com.goldencode.p2j.ui.client.gui.OleDragDropHelper.*;
+import static com.goldencode.p2j.ui.WidgetConfigExt.*;
 
 import java.awt.event.MouseEvent;
 

=== modified file 'src/com/goldencode/p2j/ui/client/gui/ToggleBoxGuiImpl.java'
--- src/com/goldencode/p2j/ui/client/gui/ToggleBoxGuiImpl.java	2019-05-02 11:36:36 +0000
+++ src/com/goldencode/p2j/ui/client/gui/ToggleBoxGuiImpl.java	2020-02-25 16:49:16 +0000
@@ -2,7 +2,7 @@
 ** Module   : ToggleBoxGuiImpl.java
 ** Abstract : GUI toggle-box implementation
 **
-** Copyright (c) 2014-2019, Golden Code Development Corporation.
+** Copyright (c) 2014-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 VIG 20150210 Initial version.
@@ -40,6 +40,7 @@
 ** 024 EVL 20190408 Fixed issue with label first letter underlining when it should not be.
 ** 025 HC  20190418 Reverted "The widget should emit one time VALUE-CHANGED when initial value is set.".
 ** 026 SVL 20190502 Support for RETURN key for browsed toggle-boxes.
+** 027 SBI 20200225 Added setColorResolver.
 */
 /*
 ** This program is free software: you can redistribute it and/or modify
@@ -265,8 +266,12 @@
    @Override
    public void draw()
    {
-      int bgColor = isThreeD() ? SysColor.COLOR_3DFACE : SysColor.COLOR_FILLIN_BG;
-      gc = new GuiColorResolver(gd, config, bgColor, SysColor.COLOR_BTNTEXT);
+      if (gc == null)
+      {
+         int bgColor = isThreeD() ? SysColor.COLOR_3DFACE : SysColor.COLOR_FILLIN_BG;
+         
+         setColorResolver(new GuiColorResolver(gd, config, bgColor, SysColor.COLOR_BTNTEXT));
+      }
       
       // determine colors to use
       gc.colors();
@@ -282,6 +287,17 @@
             ThemeManager.getCurrentTheme().drawToggleBox(
                   this, gd, gc, gf, 0, 0, d.width, d.height, pressed, false));
    }
+   
+   /**
+    * Sets the color resolver.
+    * 
+    * @param    resolver
+    *           The color revolver
+    */
+   public void setColorResolver(GuiColorResolver resolver)
+   {
+      gc = resolver;
+   }
 
    /**
     * Recalculates WIDTH attribute using actual label.

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingEmulatedWindow.java'
--- src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingEmulatedWindow.java	2019-06-05 04:50:19 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingEmulatedWindow.java	2020-02-25 16:47:35 +0000
@@ -2,7 +2,7 @@
 ** Module   : SwingEmulatedWindow.java
 ** Abstract : core GUI renderer for AWT/Swing client
 **
-** Copyright (c) 2014-2019, Golden Code Development Corporation.
+** Copyright (c) 2014-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 MAG 20140508 First version.
@@ -143,6 +143,7 @@
 **     CA  20190226 Fixed layoutParagraphWorker when empty lines are involved.
 ** 066 AVK 20190527 Fixed an NPE in drawStringCenteredWithinBox, which was called with a null
 **                  text, for a button without an explicit label set (bug #4078).
+** 067 SBI 20200225 Fixed DRAW_STRING_WITHIN_BOX drawing operation.
 */
 
 /*
@@ -1768,7 +1769,7 @@
       
       int x = ps.x + (ps.width - nativeWidth) /2;
       
-      for (int k = 0; k < numberIndices / 2; k++)
+      for (int k = 0; k < numberIndices; k+=2)
       {
          int delta;
          

=== modified file 'src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js'
--- src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js	2019-12-18 22:26:05 +0000
+++ src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js	2020-02-25 16:46:36 +0000
@@ -2,7 +2,7 @@
 ** Module   : p2j.screen.js
 ** Abstract : GUI-specific screen management
 **
-** Copyright (c) 2014-2019, Golden Code Development Corporation.
+** Copyright (c) 2014-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ------------------------------Description----------------------------------
 ** 001 GES 20150325 Created GUI version from ChUI version. Right now it is not functional.
@@ -102,6 +102,7 @@
 ** 043 HC  20191120 Extended SYSTEM-DIALOG GET-FILE with AT-WEB-BROWSER option.
 **     HC  20191126 Implemented RESIZE attribute for DIALOG-BOX. This is an extension to 4GL.
 **     HC  20191218 Fixed wrong coordinates in the mouse move MsgBlaster event.
+** 044 SBI 20200225 Fixed DRAW_STRING_WITHIN_BOX drawing operation.
 */
 
 /*
@@ -2272,7 +2273,7 @@
                
                x = xPosition + (width - nativeWidth)/2;
                y = yPosition;
-               for (var k = 0; k < numberIndices / 2; k++)
+               for (var k = 0; k < numberIndices; k += 2)
                {
                   var delta;
                   

=== modified file 'src/com/goldencode/p2j/ui/client/gui/theme/ClassicTheme.java'
--- src/com/goldencode/p2j/ui/client/gui/theme/ClassicTheme.java	2020-01-29 14:38:53 +0000
+++ src/com/goldencode/p2j/ui/client/gui/theme/ClassicTheme.java	2020-02-25 16:44:22 +0000
@@ -2,7 +2,7 @@
 ** Module   : ClassicTheme.java
 ** Abstract : Windows Classic theme handler. 
 **
-** Copyright (c) 2017-2019, Golden Code Development Corporation.
+** Copyright (c) 2017-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 OM  20170224 First version.
@@ -69,6 +69,7 @@
 ** 038 HC  20190614 Implemented IMAGELIST attribute for TREEVIEW and TREELIST widgets.
 ** 039 MAG 20191011 Added ProgresBar widget draw methods. 
 ** 040 MAG 20191129 Draw TabSet widget.
+** 041 SBI 20200225 Implemented look and feel helpers for the calendar widget.
 */
 
 /*
@@ -139,6 +140,7 @@
 import com.goldencode.p2j.ui.client.gui.theme.Theme.TextLookAndFeel.*;
 import com.goldencode.p2j.ui.client.widget.*;
 import com.goldencode.p2j.util.*;
+
 import org.apache.commons.io.*;
 
 import java.awt.*;
@@ -7616,4 +7618,225 @@
          }
       }
    }
+   
+   /**
+    * Sets DOWN arrow images for the given button.
+    * 
+    * @param    button
+    *           The given button
+    */
+   @Override
+   public void setDownArrowImages(ButtonGuiImpl button)
+   {
+      setButtonImages(button, "sb-arr-en-down", "sb-arr-dis-down", "classic",
+               COMBO_BOX_BUTTON_SIZE, COMBO_BOX_BUTTON_SIZE);
+   }
+
+   /**
+    * Sets LEFT arrow images for the given button.
+    * 
+    * @param    button
+    *           The given button
+    */
+   @Override
+   public void setLeftArrowImages(ButtonGuiImpl button)
+   {
+      setButtonImages(button, "sb-arr-en-left", "sb-arr-dis-left", "classic",
+               COMBO_BOX_BUTTON_SIZE, COMBO_BOX_BUTTON_SIZE);
+   }
+
+   /**
+    * Sets RIGHT arrow images for the given button.
+    * 
+    * @param    button
+    *           The given button
+    */
+   @Override
+   public void setRightArrowImages(ButtonGuiImpl button)
+   {
+      setButtonImages(button, "sb-arr-en-right", "sb-arr-dis-right", "classic",
+               COMBO_BOX_BUTTON_SIZE, COMBO_BOX_BUTTON_SIZE);
+   }
+
+   /**
+    * Sets UP arrow images for the given button.
+    * 
+    * @param    button
+    *           The given button
+    */
+   @Override
+   public void setUpArrowImages(ButtonGuiImpl button)
+   {
+      setButtonImages(button, "sb-arr-en-up", "sb-arr-dis-up", "classic",
+               COMBO_BOX_BUTTON_SIZE, COMBO_BOX_BUTTON_SIZE);
+   }
+   
+   /**
+    * Gets the background color index for the selected day of the calendar popup.
+    * 
+    * @return   The background color index for the selected day
+    */
+   @Override
+   public int getSelectedDayBackgroundColor()
+   {
+      return SysColor.COLOR_HOTLIGHT;
+   }
+   
+   /**
+    * Gets the foreground color index for the selected day of the calendar popup.
+    * 
+    * @return   The foreground color index for the selected day
+    */
+   @Override
+   public int getSelectedDayForegroundColor()
+   {
+      return SysColor.COLOR_HIGHLIGHTTEXT;
+   }
+   
+   /**
+    * Gets the background color index for the title of the calendar popup.
+    * 
+    * @return   The background color index for the title
+    */
+   @Override
+   public int getCalendarTitleBackgroundColor()
+   {
+      return SysColor.COLOR_ACTIVECAPTION;
+   }
+   
+   /**
+    * Gets the foreground color index for the title of the calendar popup.
+    * 
+    * @return   The foreground color index for the title
+    */
+   @Override
+   public int getCalendarTitleForegroundColor()
+   {
+      return SysColor.COLOR_CAPTIONTEXT;
+   }
+   
+   /**
+    * Gets the background color index for the trailing days around the current month 
+    * of the calendar popup.
+    * 
+    * @return   The background color index for the trailing days
+    */
+   @Override
+   public int getCalendarTrailingBackgroundColor()
+   {
+      return SysColor.COLOR_FILLIN_BG;
+   }
+   
+   /**
+    * Gets the foreground color index for the trailing days around the current month 
+    * of the calendar popup.
+    * 
+    * @return   The foreground color index for the trailing days
+    */
+   @Override
+   public int getCalendarTrailingForegroundColor()
+   {
+      return SysColor.COLOR_INACTIVECAPTIONTEXT;
+   }
+   
+   /**
+    * Gets the background color index for the body of the calendar popup.
+    * 
+    * @return   The background color index for the body
+    */
+   @Override
+   public int getCalendarBodyBackgroundColor()
+   {
+      return SysColor.COLOR_FILLIN_BG;
+   }
+   
+   /**
+    * Gets the foreground color index for the body of the calendar popup.
+    * 
+    * @return   The foreground color index for the body
+    */
+   @Override
+   public int getCalendarBodyForegroundColor()
+   {
+      return SysColor.COLOR_FILLIN_FG;
+   }
+   
+   /**
+    * Prepares the target map of images for up, down and disabled states of a button.
+    * 
+    * @param    enableImage
+    *           The resource name for the enabled button
+    * @param    disableImage
+    *           The resource name for the disabled button
+    * @param    location
+    *           The resource directory
+    * @param    width
+    *           The image width in pixels
+    * @param    height
+    *           The image height in pixels
+    * 
+    * @return   The target images
+    */
+   public static Map<Integer, ImageWrapper> getImages(String enableImage,
+                                                      String disableImage,
+                                                      String location,
+                                                      int width,
+                                                      int height)
+   {
+      ImageWrapper imgButtEn = VectorGraphicsHelper.getScalableImage(enableImage, location,
+               width, height);
+      ImageWrapper imgButtDis = VectorGraphicsHelper.getScalableImage(disableImage, location,
+               width, height);
+
+      Map<Integer, ImageWrapper> images = new HashMap<>(3);
+      
+      images.put(ButtonConfig.IMAGE_DOWN, imgButtEn);
+      images.put(ButtonConfig.IMAGE_UP, imgButtEn);
+      images.put(ButtonConfig.IMAGE_INS, imgButtDis);
+      
+      return images;
+   }
+   
+   /**
+    * Assigns the target images for the given button.
+    * 
+    * @param    button
+    *           The image consumer
+    * @param    enableImage
+    *           The resource name for the enabled button
+    * @param    disableImage
+    *           The resource name for the disabled button
+    * @param    location
+    *           The resource directory
+    * @param    width
+    *           The image width in pixels
+    * @param    height
+    *           The image height in pixels
+    */
+   public static void setButtonImages(ButtonGuiImpl button,
+                                      String enableImage,
+                                      String disableImage,
+                                      String location,
+                                      int width,
+                                      int height)
+   {
+      Map<Integer, ImageWrapper> images = getImages(enableImage, disableImage, location, width, height);
+      
+      button.config().imgDownDef.heightPixels = height;
+      button.config().imgDownDef.widthPixels = width;
+      button.config().imgDownDef.name = enableImage;
+      
+      button.config().imgUpDef.heightPixels = height;
+      button.config().imgUpDef.widthPixels = width;
+      button.config().imgUpDef.name = enableImage;
+      
+      button.config().imgInsDef.heightPixels = height;
+      button.config().imgInsDef.widthPixels = width;
+      button.config().imgInsDef.name = disableImage;
+      
+      
+      button.setImage(ButtonConfig.IMAGE_UP, images.get(ButtonConfig.IMAGE_UP));
+      button.setImage(ButtonConfig.IMAGE_DOWN, images.get(ButtonConfig.IMAGE_DOWN));
+      button.setImage(ButtonConfig.IMAGE_INS, images.get(ButtonConfig.IMAGE_INS));
+   }
 }

=== modified file 'src/com/goldencode/p2j/ui/client/gui/theme/Theme.java'
--- src/com/goldencode/p2j/ui/client/gui/theme/Theme.java	2020-01-29 14:38:53 +0000
+++ src/com/goldencode/p2j/ui/client/gui/theme/Theme.java	2020-02-25 16:40:33 +0000
@@ -2,7 +2,7 @@
 ** Module   : Theme.java
 ** Abstract : the interface for all themes supported by FWD. 
 **
-** Copyright (c) 2017-2019, Golden Code Development Corporation.
+** Copyright (c) 2017-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- ---------------------------------Description---------------------------------
 ** 001 OM  20170308 Initial version with declaration of basic renderers and getters.
@@ -26,6 +26,7 @@
 **                  changes.
 ** 017 MAG 20191011 Added ProgresBar widget draw methods. 
 ** 018 MAG 20191204 Split draw for TabSet control.
+** 019 SBI 20200225 Added new look and feel methods for the Calendar widget.
 */
 
 /*
@@ -576,6 +577,96 @@
                           int height);
    
    /**
+    * Sets DOWN arrow images for the given button.
+    * 
+    * @param    button
+    *           The given button
+    */
+   public void setDownArrowImages(ButtonGuiImpl button);
+   
+   /**
+    * Sets LEFT arrow images for the given button.
+    * 
+    * @param    button
+    *           The given button
+    */
+   public void setLeftArrowImages(ButtonGuiImpl button);
+   
+   /**
+    * Sets RIGHT arrow images for the given button.
+    * 
+    * @param    button
+    *           The given button
+    */
+   public void setRightArrowImages(ButtonGuiImpl button);
+   
+   /**
+    * Sets UP arrow images for the given button.
+    * 
+    * @param    button
+    *           The given button
+    */
+   public void setUpArrowImages(ButtonGuiImpl button);
+   
+   /**
+    * Gets the background color index for the selected day of the calendar popup.
+    * 
+    * @return   The background color index for the selected day
+    */
+   int getSelectedDayBackgroundColor();
+   
+   /**
+    * Gets the foreground color index for the selected day of the calendar popup.
+    * 
+    * @return   The foreground color index for the selected day
+    */
+   int getSelectedDayForegroundColor();
+   
+   /**
+    * Gets the background color index for the title of the calendar popup.
+    * 
+    * @return   The background color index for the title
+    */
+   int getCalendarTitleBackgroundColor();
+   
+   /**
+    * Gets the foreground color index for the title of the calendar popup.
+    * 
+    * @return   The foreground color index for the title
+    */
+   int getCalendarTitleForegroundColor();
+   
+   /**
+    * Gets the background color index for the trailing days around the current month 
+    * of the calendar popup.
+    * 
+    * @return   The background color index for the trailing days
+    */
+   int getCalendarTrailingBackgroundColor();
+   
+   /**
+    * Gets the foreground color index for the trailing days around the current month 
+    * of the calendar popup.
+    * 
+    * @return   The foreground color index for the trailing days
+    */
+   int getCalendarTrailingForegroundColor();
+   
+   /**
+    * Gets the background color index for the body of the calendar popup.
+    * 
+    * @return   The background color index for the body
+    */
+   int getCalendarBodyBackgroundColor();
+   
+   /**
+    * Gets the foreground color index for the body of the calendar popup.
+    * 
+    * @return   The foreground color index for the body
+    */
+   int getCalendarBodyForegroundColor();
+   
+   /**
     * Obtain the padding for alert boxes.
     * 
     * @return  the padding for alert boxes.

=== added file 'src/com/goldencode/p2j/ui/client/layout/GridLayout.java'
--- src/com/goldencode/p2j/ui/client/layout/GridLayout.java	1970-01-01 00:00:00 +0000
+++ src/com/goldencode/p2j/ui/client/layout/GridLayout.java	2020-02-11 08:31:45 +0000
@@ -0,0 +1,193 @@
+/*
+** Module   : GridLayout.java
+** Abstract : Simple grid layout manager. 
+**
+** Copyright (c) 2020, Golden Code Development Corporation.
+**
+** -#- -I- --Date-- ---------------------------------Description----------------------------------
+** 001 SBI 20200210 Created initial version
+*/
+/*
+** 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.client.layout;
+
+import com.goldencode.p2j.ui.*;
+import com.goldencode.p2j.ui.client.*;
+import com.goldencode.p2j.ui.client.gui.*;
+import com.goldencode.p2j.ui.client.widget.*;
+
+/**
+ * Simple implementation of grid layout.
+ */
+public class GridLayout<O extends OutputManager<?>>
+implements LayoutManager<O>
+{
+   /** The number of columns */
+   private final int columns;
+   
+   /** The number of rows */
+   private final int rows;
+   
+   /**
+    * Takes the visible number of columns and rows to align widgets within their container
+    * 
+    * @param    columns
+    *           The number of columns
+    * @param    rows
+    *           The number of rows
+    * 
+    */
+   public GridLayout(int columns, int rows)
+   {
+      this.columns = columns;
+      this.rows = rows;
+   }
+
+   /**
+    * Calculate minimum space required for contained widgets.
+    * 
+    * @param    container
+    *           Container to process.
+    * 
+    * @return   calculated dimension.
+    */
+   @Override
+   public Dimension minimumSize(Container<O> container)
+   {
+      double width = 0;
+      double height = 0;
+
+      Widget<O> innerPane = container.get(0);
+      
+      if (innerPane != null)
+      {
+         Dimension inner = innerPane.minimumSize();
+         height = inner.height;
+         width = inner.width;
+      }
+      
+      Insets insets = container.insets();
+      
+      height = Coordinate.scale(height + insets.heightDelta());
+      width = Coordinate.scale(width + insets.widthDelta());
+
+      return new Dimension(width, height);
+   }
+
+   /**
+    * Process container and place components at required locations.
+    * 
+    * @param    container
+    *           Container to process.
+    */
+   @Override
+   public void doLayout(Container<O> container)
+   {
+      Widget<O>[] widgets = container.widgets();
+      
+      Dimension size = container.dimension();
+      Insets insets = container.insets();
+
+      double availWidth = size.width - insets.widthDelta();
+      double availHeight = size.height - insets.heightDelta();
+      
+      // BorderedPanelGuiImpl already takes care of translating the insets for its children,
+      // so for the children of BorderedPanelGuiImpl the origin is relative to the parent's
+      // origin plus the insets
+      Point loc = container instanceof BorderedPanelGuiImpl ? new Point(0, 0) : 
+                                                              new Point(insets.left, insets.top);
+      
+      double widthCell = availWidth / columns;
+      
+      double heightCell = availHeight / rows;
+      
+      int col = 1;
+      int row = 1;
+      double shiftCol = 0;
+      double shiftRow = 0;
+      int num = widgets.length;
+      
+      for(int i = 0; i < num; i++)
+      {
+         Widget<O> widget = widgets[i];
+         Dimension dim = widget.dimension();
+         
+         double x = loc.x + (widthCell - dim.width) / 2 + shiftCol;
+         
+         double y = loc.y + (heightCell - dim.height) / 2 + shiftRow;
+         
+         widget.setLocation(x, y);
+         
+         if (col == columns)
+         {
+            if (row < rows)
+            {
+               row++;
+               shiftRow += heightCell;
+            }
+            else
+            {
+               break;
+            }
+            col = 1;
+            shiftCol = 0;
+         }
+         else
+         {
+            col++;
+            shiftCol += widthCell;
+         }
+      }
+   }
+}

=== modified file 'src/com/goldencode/p2j/ui/client/widget/AbstractWidget.java'
--- src/com/goldencode/p2j/ui/client/widget/AbstractWidget.java	2019-12-18 22:26:05 +0000
+++ src/com/goldencode/p2j/ui/client/widget/AbstractWidget.java	2020-02-25 16:38:15 +0000
@@ -152,6 +152,7 @@
 **                  For selectable widgets process only key events for navigation keys.
 ** 085 HC  20191210 Improved emulation of setFocus Win32 system function.
 **     HC  20191218 Added the support for a notification when widget ancestor changes.
+** 086 SBI 20200225 Added getTextHeightNative.
 */
 
 /*
@@ -2682,6 +2683,55 @@
    }
    
    /**
+    * Compute the text height using {@link GuiDriver#getTextHeight}.
+    *
+    * @param    txt
+    *           The text.
+    * @param    font
+    *           The font details.
+    *
+    * @return   The text height.
+    */
+   public int getTextHeightNative(String txt, FontDetails font)
+   {
+      TitledWindow<O> topWindow = ancestor();
+      Window<O> window = null;
+      if (topWindow == null)
+      {
+         // check if it's frame-id is set - if so, use that
+         if (config().frameId != -1)
+         {
+            Widget<?> parent = ThinClient.getInstance().getWidget(config().frameId);
+            topWindow = window = (Window<O>) parent.window();
+         }
+         else
+         {
+            // fallback to default-window
+            topWindow = window = WindowManager.getDefaultWindow();
+         }
+      }
+      else if (topWindow instanceof TopLevelWindow)
+      {
+         window = topWindow.window();
+      }
+      else
+      {
+         window = window();
+      }
+      
+      GuiDriver gd = (GuiDriver) screen().getInstanceDriver();
+      gd.selectWindow(UiUtils.getWidgetIdAsInt(topWindow));
+      try
+      {
+         return gd.getTextHeight(txt, font);
+      }
+      finally
+      {
+         gd.releaseWindow();
+      }
+   }
+   
+   /**
     * Compute the text width using {@link FontManager#getTextWidth}.
     * 
     * @param    txt

=== modified file 'src/com/goldencode/p2j/util/HandleCommon.java'
--- src/com/goldencode/p2j/util/HandleCommon.java	2019-11-19 14:11:39 +0000
+++ src/com/goldencode/p2j/util/HandleCommon.java	2020-02-25 16:42:23 +0000
@@ -3,7 +3,7 @@
 ** Abstract : A massive interface which groups together all attributes and methods available
 **            via a handle.
 **
-** Copyright (c) 2013-2019, Golden Code Development Corporation.
+** Copyright (c) 2013-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description----------------------------------
 ** 001 CA  20130112 Created initial version.
@@ -67,6 +67,7 @@
 **     OM  20190721 Added DataRelationInterface, and DataSourceInterface.
 ** 049 HC  20190614 Initial implementation of IMAGELIST widget.
 ** 050 MAG 20190920 Added ProgressBar interface.
+** 051 SBI 20200225 Added Calendar widget support.
 */
 
 /*
@@ -242,6 +243,7 @@
         
         // FWD extension interfaces are listed from this point forward
         CustomFont,
+        Calendar,
         EmailSender,
         FWDTimer,
         HtmlBrowser,

=== modified file 'src/com/goldencode/p2j/util/LegacyResource.java'
--- src/com/goldencode/p2j/util/LegacyResource.java	2020-01-29 14:38:53 +0000
+++ src/com/goldencode/p2j/util/LegacyResource.java	2020-02-25 16:36:49 +0000
@@ -2,7 +2,7 @@
 ** Module   : LegacyResource.java
 ** Abstract : Annotation definition for a legacy resource.
 **
-** Copyright (c) 2013-2019, Golden Code Development Corporation.
+** Copyright (c) 2013-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --------------------------------Description-----------------------------------
 ** 001 CA  20130926 Created initial version.
@@ -21,6 +21,7 @@
 ** 009 OM  20190411 Added constants for DataSet related resources.
 ** 010 HC  20190614 Initial implementation of IMAGELIST widget.
 ** 011 MAG 20190919 Added constants for PROGRESS-BAR widget.
+** 012 SBI 20200225 Added Calendar widget support.
 */
 
 /*
@@ -192,6 +193,9 @@
    /** Constant identifying the DIALOG-BOX resource. */
    public static String DIALOG_BOX = "DIALOG-BOX";
 
+   /** Constant identifying the CALENDAR resource. */
+   public static String CALENDAR = "CALENDAR";
+
    /** Constant identifying the  resource. */
    public static String EDITOR = "EDITOR";
 

=== modified file 'src/com/goldencode/p2j/util/Utils.java'
--- src/com/goldencode/p2j/util/Utils.java	2020-01-30 20:08:18 +0000
+++ src/com/goldencode/p2j/util/Utils.java	2020-02-25 16:35:33 +0000
@@ -144,6 +144,7 @@
 **     CA  20191212          Added findConstructor, to find the first ctor which matches the 
 **                           argument's class or super-class, or any of its super-interfaces.
 ** 069 HC  20200130          Added a method for enumerating nodes.
+** 070 SBI 20200225          Moved isValidValue to this helper class.
 */
 
 /*
@@ -3756,6 +3757,30 @@
    }
    
    /**
+    * Check if a given value is a valid value.
+    * 
+    * @param    value
+    *           Value to check.
+    * @param    validValues
+    *           List of valid values.
+    *          
+    * @return   <code>true</code> if value is in the list of valid values,
+    *           otherwise <code>false</code>.
+    */
+   public static boolean isValidValue(int value, int ... validValues)
+   {
+      for (int v : validValues)
+      {
+         if (value == v)
+         {
+            return true;
+         }
+      }
+      
+      return false;
+   }
+   
+   /**
     * Determines whether two sets have common elements.
     * 
     * @param <T> 

=== modified file 'src/com/goldencode/p2j/util/handle.java'
--- src/com/goldencode/p2j/util/handle.java	2020-01-21 14:39:51 +0000
+++ src/com/goldencode/p2j/util/handle.java	2020-02-25 16:33:41 +0000
@@ -2,7 +2,7 @@
 ** Module   : handle.java
 ** Abstract : Progress 4GL compatible handle object
 **
-** Copyright (c) 2005-2019, Golden Code Development Corporation.
+** Copyright (c) 2005-2020, Golden Code Development Corporation.
 **
 ** -#- -I- --Date-- --JPRM-- -----------------------------Description-----------------------------
 ** 001 GES 20051125   @23505 Created initial version which represents
@@ -207,6 +207,7 @@
 ** 114 HC  20190614          Initial implementation of IMAGELIST widget.
 **                           assigned to a handle output argument.
 ** 115 MAG 20190920          Added PROGRESS-BAR support.
+** 116 SBI 20200225          Added Calendar widget support.
 */
 
 /*
@@ -268,12 +269,15 @@
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.logging.*;
+
 import com.goldencode.p2j.extension.*;
 import com.goldencode.p2j.email.*;
 import com.goldencode.p2j.persist.*;
 import com.goldencode.p2j.reporting.*;
 import com.goldencode.p2j.security.*;
 import com.goldencode.p2j.ui.*;
+/** Calendar must be imported explicitly */ 
+import com.goldencode.p2j.ui.Calendar;
 import com.goldencode.p2j.xml.*;
 import com.goldencode.proxy.*;
 
@@ -2781,6 +2785,16 @@
    }
 
    /**
+    * Unwrap this handle to the calendar widget interface.
+    * 
+    * @return   The calendar widget interface
+    */
+   public Calendar unwrapCalendar()
+   {
+      return unwrapImpl(this, Calendar.class);
+   }
+
+   /**
     * Unwrap this handle to a {@link DeblankInterface} instance. If the
     * handle is not a widget handle or is invalid, an 
     * {@link #invalidAttrAccessProxy dynamic proxy} is returned.
@@ -3293,7 +3307,17 @@
    {
       return unwrapImpl(this, ImageList.class);
    }
-
+   
+   /**
+    * Gets the instance of WidgetExtension.
+    * 
+    * @return   The instance of WidgetExtension
+    */
+   public WidgetExtension unwrapWidgetExtension()
+   {
+      return unwrapImpl(this, WidgetExtension.class);
+   }
+   
    /**
     * Creates a new instance of the same type that represents the 
     * <code>unknown value</code>.

