KeyReader.java
/*
** Module : KeyReader.java
** Abstract : helper methods to manage reading a key/char and accessing the
** last key read
**
** Copyright (c) 2005-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- --JPRM-- ----------------------------------Description-----------------------------------
** 001 GES 20051109 @23284 Created initial version, supporting helper
** methods to manage reading a key/char and
** accessing the last key read.
** 002 NVS 20051212 @23679 LogicalTerminal.readKey() is called which was
** previously commented out since LT wasn't
** fully implemented.
** 003 GES 20060115 @23922 Better safety code for stream access.
** 004 GES 20060123 @24020 Moved to context-local storage.
** 005 GES 20060207 @24326 Ensure that lastkey is set to -1 in case
** there is a StopConditionException while
** reading.
** 006 GES 20060207 @24346 Added new readkey signatures and otherwise
** handle new unnamed streams interface.
** 007 NVS 20070725 @34686 Renamed isInOK() method to isIn() for the
** sake of uniformity.
** 008 RK 20080227 @37177 Javadoc has been updated to state the
** different initial values for different
** terminals.
** 009 GES 20080320 @37632 Added some LAST-EVENT system handle support
** (for the FUNCTION attribute).
** 010 GES 20090624 @42884 isIn() usage to determine if the stream represents
** the terminal is switched to isTerm(). This fixes
** a problem where a terminal was incorrectly
** calculated when there was a closed input stream
** (which should fail with an error instead of
** automatically redirecting to the terminal).
** 011 CA 20130112 Added asHandle, getType, isValid and isUnknown
** methods, accessed by the LAST-EVENT handle.
** 012 CA 20130221 Added LABEL support. getType was renamed to getResourceType.
** DATA-TYPE, COLUMN-LABEL and FORMAT attributes are not supported (but
** forced to be defined by CommonField interface) so proper error APIs
** are called.
** 013 OM 20130304 Refactored isValid and isUnknown of WrappedResource to valid
** and unknown.
** 014 CA 20130927 Static proxies need the unknown() API. Resource type is determined
** from LegacyResource constants.
** 015 EVL 20131014 Adding annotation for legacy attributes to be ignored for the class.
** 016 VIG 20131122 Adding support of LAST-EVENT:LABEL and
** LAST-EVENT:EVENT-TYPE attributes.
** 017 GES 20140116 Code formatting and javadoc cleanup.
** 018 CA 20141021 Added support for legacy MOUSE events.
** 019 CA 20141029 A minor improvement for setting the LAST-EVENT attrs, when the key is
** read via READKEY.
** 020 CA 20150502 The resource ID needs to be kept at the resource, so it can be
** accessed after the resource is deleted.
** 021 IAS 20150801 WIDGET-LEAVE/WIDGER-ENTER support.
** 022 OM 20150926 X/Y attributes support.
** 023 CA 20160327 Finished X/Y attribute implementation.
** 024 CA 20171218 Fixed LAST-EVENT:WIDGET-ENTER/LEAVE attributes, in cases when
** ROW-ENTRY/LEAVE events are used.
** 025 HC 20181212 Implemented support for asynchronous key state read emulation.
** 026 CA 20200211 Allow fractional seconds (convert to millis based on the legacy version).
** 027 GES 20200627 Renamed Coordinates interface to be more generic. Added the undocumented
** LAST-EVENT:SET-LASTKEY() method. Added the LAST-EVENT:COLUMN/ROW attributes
** support.
** OM 20201030 Invalid attribute API support for getters/setters.
** OM 20201203 Fixed handling of READ/ONLY attributes.
** 028 RFB 20210512 Initial value of lastkey changed from 401 to -1. Ref #5363.
** VVT 20211206 'state' replaced by 'modifiers' in code related to key modifiers.
** See #5850-26.
** VVT 20211215 New methods added: setX(int), setY(int). See #5899.
** VVT 20221003 CommonHandle.getResourceType() method renamed to resourceType() to prevent conflicts
** with namesakes in DMO. See #6694.
** 029 EVL 20230620 Consider the WINDOW UI mark bit to get legacy lastKey value.
** 030 CA 20230828 'getLastKey' must return 'integer', and not Java 'int'.
** 031 AB 20231107 Modified getWidgetEnter() to handle correctly the entry event when the frame is
** opened.
** 032 SVL 20231124 Fix for ROW-ENTRY event.
** 033 VVT 20240206 setLastKey(key, modifiers) now does not set LAST-EVENT:LABEL. See #8068.
** Code cleanup.
** 034 SVL 20240530 Disabled usage of keyReader.label.
*/
/*
** 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.util;
import com.goldencode.p2j.security.*;
import com.goldencode.p2j.ui.*;
import com.goldencode.p2j.ui.client.event.*;
import com.goldencode.proxy.*;
/**
* Provides Progress compatible helper methods to manage reading a key/char
* and accessing the last key read. This class provides a generic interface
* and uses the {@link Stream} implementation or the
* <code>LogicalTerminal</code> implementation to back the service based
* on runtime state.
* <p>
* <b>The initial last key value is currently set to 401 which is valid for
* only a sub-set of terminal types (e.g xterm/vt100). It is known that for
* vt220/vt320 type terminals this would need to be corrected (they report
* -1 in Progress). For other terminal types, the initial value is not known.
* The solution will require that a terminal specific value be reported
* depending on runtime data. This solution has not yet been defined. Part of
* the problem with the solution is that the server has no knowledge of the
* terminal type nor does it currently have any other terminal type
* dependencies.</b>
*/
public class KeyReader
{
/** LAST-EVENT:EVENT-TYPE for high-level Progress events */
private static final String PROGRESS_EVTYPE = "PROGRESS";
/** LAST-EVENT:EVENT-TYPE for keyboard Progress events */
private static final String KEY_EVTYPE = "KEYPRESS";
/** LAST-EVENT:EVENT-TYPE for mouse Progress events */
private static final String MOUSE_EVTYPE = "MOUSE";
/** Context-local data area for last key state. */
private static ContextContainer workArea = new ContextContainer();
/**
* Get a the instance for the LAST-EVENT system handle. Is obtained using
* a call to the {@link StaticProxy#obtain(Class, Class[])}, using the
* {@link CommonLastEvent} interface and its methods implemented by these
* classes: {@link KeyReader}.
*
* @return See above.
*/
public static handle asHandle()
{
WorkArea wa = workArea.obtain();
Class[] classes = { KeyReader.class };
CommonLastEvent proxy = StaticProxy.obtain(CommonLastEvent.class, classes);
if (wa.id == null)
{
wa.id = handle.resourceId(proxy);
}
return new handle(proxy);
}
/**
* Get this resource's ID.
*
* @return The resource's ID.
*/
public static Long id()
{
WorkArea wa = workArea.obtain();
return wa.id;
}
/**
* Set this resource's ID.
* <p>
* This is a no-op for system handles.
*
* @param id
* The resource's ID.
*/
public static void id(@SuppressWarnings("unused") long id)
{
// no-op
}
/**
* Implementation for the {@link WrappedResource#valid()} API.
*
* @return Always true.
*/
public static boolean valid()
{
return true;
}
/**
* Implementation for the {@link WrappedResource#unknown()} API.
*
* @return Always false.
*/
public static boolean unknown()
{
return false;
}
/**
* API needed to implement read-only attribute assignment (a 4GL "feature").
*
* @param attribute
* The attribute's name.
*
* @see handle#readOnlyError(handle, String)
*/
public static void readOnlyError(String attribute)
{
handle.readOnlyError(asHandle(), attribute);
}
/**
* API needed to implement read-only attribute assignment (a 4GL "feature").
*
* @param attribute
* The attribute's name.
* @param expr
* The value which is attempted to be assigned to the read-only attribute.
*
* @see handle#readOnlyError(handle, String, Object)
*/
public static void readOnlyError(String attribute, @SuppressWarnings("unused") Object expr)
{
handle.readOnlyError(asHandle(), attribute);
}
/**
* Obtains the last key read from any source (stream or terminal).
*
* @return The last key read.
*/
public static integer lastKey()
{
return getLastKey();
}
/**
* Obtains the last key read from any source (stream or terminal).
*
* @return The last key read.
*/
public static integer getLastKey()
{
return new integer(workArea.obtain().lastkey);
}
/**
* Sets the event type and last key read from any source (stream or terminal). This is an undocumented
* method for the LAST-EVENT system handle that was found in customer source code.
*
* @param etype
* The event-type of the last event. This may be 0 (for KEYPRESS), 1 (for MOUSE). If one of
* these valid values is passed, then the method will change the event state and return
* {@code true}. With any other value, the method will not change state and will return
* {@code false}.
* @param kcode
* The key-code of the last event. Any value may be passed but any changes will only be saved
* if the {@code etype} is valid (and the method thus returns {@code true}). Any integer
* between -32768 and 32767 (inclusive) will set the key code to that same number. Any other
* number will be cast into a 16-bit signed integer before assignment (which will drop the most
* significant 48-bits).
*
* @return {@code true} if the {@code etype} is valid and state is changed. {@code false} if
* {@code etype} is NOT valid and state is NOT changed.
*/
public static logical _setLastKey(long etype, long kcode)
{
// TODO: should we be setting the keyFunction, label and lastKeyState?
if (etype == 0 || etype == 1)
{
WorkArea wa = workArea.obtain();
wa.eventType = (etype == 0) ? KEY_EVTYPE : MOUSE_EVTYPE;
wa.lastkey = (short) kcode;
return new logical(true);
}
return new logical(false);
}
/**
* Sets the event type and last key read from any source (stream or terminal). This is an undocumented
* method for the LAST-EVENT system handle that was found in customer source code.
*
* @param etype
* The event-type of the last event. This may be 0 (for KEYPRESS), 1 (for MOUSE). If one of
* these valid values is passed, then the method will change the event state and return
* {@code true}. With any other value, the method will not change state and will return
* {@code false}. Passing unknown value is equivalent to KEYPRESS.
* @param kcode
* The key-code of the last event. Any value may be passed but any changes will only be saved
* if the {@code etype} is valid (and the method thus returns {@code true}). Any integer
* between -32768 and 32767 (inclusive) will set the key code to that same number. Any other
* number will be cast into a 16-bit signed integer before assignment (which will drop the most
* significant 48-bits).
*
* @return {@code true} if the {@code etype} is valid and state is changed. {@code false} if
* {@code etype} is NOT valid and state is NOT changed.
*/
public static logical _setLastKey(NumberType etype, long kcode)
{
return _setLastKey((etype == null || etype.isUnknown()) ? 0L : etype.longValue(), kcode);
}
/**
* Sets the event type and last key read from any source (stream or terminal). This is an undocumented
* method for the LAST-EVENT system handle that was found in customer source code.
*
* @param etype
* The event-type of the last event. This may be 0 (for KEYPRESS), 1 (for MOUSE). If one of
* these valid values is passed, then the method will change the event state and return
* {@code true}. With any other value, the method will not change state and will return
* {@code false}.
* @param kcode
* The key-code of the last event. Any value may be passed but any changes will only be saved
* if the {@code etype} is valid (and the method thus returns {@code true}). Any integer
* between -32768 and 32767 (inclusive) will set the key code to that same number. Any other
* number will be cast into a 16-bit signed integer before assignment (which will drop the most
* significant 48-bits). Passing unknown value is the same as passing 0.
*
* @return {@code true} if the {@code etype} is valid and state is changed. {@code false} if
* {@code etype} is NOT valid and state is NOT changed.
*/
public static logical _setLastKey(long etype, NumberType kcode)
{
return _setLastKey(etype, (kcode == null || kcode.isUnknown()) ? 0L : kcode.longValue());
}
/**
* Sets the event type and last key read from any source (stream or terminal). This is an undocumented
* method for the LAST-EVENT system handle that was found in customer source code.
*
* @param etype
* The event-type of the last event. This may be 0 (for KEYPRESS), 1 (for MOUSE). If one of
* these valid values is passed, then the method will change the event state and return
* {@code true}. With any other value, the method will not change state and will return
* {@code false}. Passing unknown value is equivalent to KEYPRESS.
* @param kcode
* The key-code of the last event. Any value may be passed but any changes will only be saved
* if the {@code etype} is valid (and the method thus returns {@code true}). Any integer
* between -32768 and 32767 (inclusive) will set the key code to that same number. Any other
* number will be cast into a 16-bit signed integer before assignment (which will drop the most
* significant 48-bits). Passing unknown value is the same as passing 0.
*
* @return {@code true} if the {@code etype} is valid and state is changed. {@code false} if
* {@code etype} is NOT valid and state is NOT changed.
*/
public static logical _setLastKey(NumberType etype, NumberType kcode)
{
return _setLastKey((etype == null || etype.isUnknown()) ? 0L : etype.longValue(),
(kcode == null || kcode.isUnknown()) ? 0L : kcode.longValue());
}
/**
* Sets the last key read and main attributes: LABEL, FUNCTION, EVENT-TYPE
*
* @param key
* The last key read (LASTKEY).
*/
public static void setLastKey(int key)
{
setLastKey(key, 0);
}
/**
* Sets the last key read and main attributes: LABEL, FUNCTION, EVENT-TYPE
*
* @param key
* The last key read (LASTKEY).
* @param modifiers
* The last extended keys modifiers (MODIFIERS).
*/
public static void setLastKey(final int key, final int modifiers)
{
WorkArea wa = workArea.obtain();
wa.lastkey = key > 0 ? key & ~Keyboard.WINDOW_UI_EVENT_MASK : key;
wa.lastKeyModifiers = modifiers;
wa.eventType = Keyboard.isPGEvent(key)
? PROGRESS_EVTYPE
: Keyboard.isMouseEvent(key)
? MOUSE_EVTYPE
: KEY_EVTYPE;
wa.keyFunction = Keyboard.keyFunction(key);
}
/**
* Returns the last extended keys state.
*
* @return see above.
*/
public static int getLastKeyModifiers()
{
return workArea.obtain().lastKeyModifiers;
}
/**
* Implementation of the LAST-EVENT system handle's FUNCTION attribute.
*
* @return The key function of the last key event.
*/
public static character getLastFunction()
{
// The FUNCTION attribute read at this step can originate from two locations:
// - value from ClientState, cached in LogicalTerminal or
// - value set explicitly through setLastKey() or readKey()
//
// TODO: double check if this is read/written correctly, as it doesn't look right
int functionKey = LogicalTerminal.getFunctionKey();
if (functionKey != KeyInput.CHAR_UNDEFINED)
{
setKeyFunctionWorker(functionKey);
}
return new character(workArea.obtain().keyFunction);
}
/**
* Used internally by P2J to set the {@link WorkArea#keyFunction}
*
* @param func
* The FUNCTION to be set.
*/
public static void setKeyFunctionWorker(int func)
{
WorkArea wa = workArea.obtain();
wa.keyFunction = Keyboard.eventName(func);
wa.eventType = Keyboard.isPGEvent(func) ? PROGRESS_EVTYPE
: Keyboard.isMouseEvent(func) ? MOUSE_EVTYPE
: KEY_EVTYPE;
}
/**
* Read a character from the unnamed input stream (if open) or from the
* <code>LogicalTerminal</code>, blocking until input is received.
* <p>
* The exact character read will be stored as the lastkey and can be
* accessed via {@link #lastKey}. The lastkey will be set to -1 if there
* is any interruption or error during processing or will be set to -2 at
* <code>EOF</code>.
*
* @return The character read, -1 if there is any interruption or
* error during processing or -2 at <code>EOF</code>.
*
* @throws StopConditionException
* If any interruption occurs during I/O processing.
*/
public static int readKey()
throws StopConditionException
{
return readKey(null, -1);
}
/**
* Read a character from the given stream or from the
* <code>LogicalTerminal</code>, blocking until input is received.
* <p>
* The exact character read will be stored as the lastkey and can be
* accessed via {@link #lastKey}. The lastkey will be set to -1 if there
* is any interruption or error during processing or will be set to -2 at
* <code>EOF</code>.
*
* @param in
* The stream from which to read. If <code>null</code> or if
* it is an instance of {@link StreamWrapper} which has no
* backing stream, then the <code>LogicalTerminal</code> will
* be read.
*
* @return The character read, -1 if there is any interruption or
* error during processing or -2 at <code>EOF</code>.
*
* @throws StopConditionException
* If any interruption occurs during I/O processing.
*/
public static int readKey(Stream in)
throws StopConditionException
{
return readKey(in, -1);
}
/**
* Read a character from the unnamed input stream (if open) or from the
* <code>LogicalTerminal</code>.
* <p>
* The exact character read will be stored as the lastkey and can be
* accessed via {@link #lastKey}. The lastkey will be set to -1 if there
* is any interruption or error during processing or will be set to -2 at
* <code>EOF</code>.
*
* @param pause
* The number of seconds to wait for a character to be available
* for reading. Use 0 to get an immediate response (it will
* return -1 if there is no character immediately available).
* Use -1 to specify an infinite timeout (it will never
* timeout). If set to the <code>unknown value</code>, an
* indefinite wait will occur.
*
* @return The character read, -1 if there is any interruption or
* error during processing or -2 at <code>EOF</code>.
*
* @throws StopConditionException
* If any interruption occurs during I/O processing.
*/
public static int readKey(NumberType pause)
throws StopConditionException
{
return readKey(null, pause);
}
/**
* Read a character from the unnamed input stream (if open) or from the
* <code>LogicalTerminal</code>.
* <p>
* The exact character read will be stored as the lastkey and can be
* accessed via {@link #lastKey}. The lastkey will be set to -1 if there
* is any interruption or error during processing or will be set to -2 at
* <code>EOF</code>.
*
* @param pause
* The number of seconds to wait for a character to be available
* for reading. Use 0 to get an immediate response (it will
* return -1 if there is no character immediately available).
* Use -1 to specify an infinite timeout (it will never
* timeout).
*
* @return The character read, -1 if there is any interruption or
* error during processing or -2 at <code>EOF</code>.
*
* @throws StopConditionException
* If any interruption occurs during I/O processing.
*/
public static int readKey(double pause)
throws StopConditionException
{
return readKey(null, pause);
}
/**
* Read a character from the given stream or from the
* <code>LogicalTerminal</code>.
* <p>
* The exact character read will be stored as the lastkey and can be
* accessed via {@link #lastKey}. The lastkey will be set to -1 if there
* is any interruption, the timeout expires without a character being read
* or there is an error during processing. The lastkey will be set to -2
* at <code>EOF</code>.
*
* @param in
* The stream from which to read. If <code>null</code> or if
* it is an instance of {@link StreamWrapper} which has no
* backing stream, then the <code>LogicalTerminal</code> will
* be read.
* @param pause
* The number of seconds to wait for a character to be available
* for reading. Use 0 to get an immediate response (it will
* return -1 if there is no character immediately available).
* Use -1 to specify an infinite timeout (it will never
* timeout). If set to the <code>unknown value</code>, an
* indefinite wait will occur.
*
* @return The character read, -1 if there is any interruption, timeout
* or error during processing or -2 at <code>EOF</code>.
*
* @throws StopConditionException
* If any interruption occurs during I/O processing.
*/
public static int readKey(Stream in, NumberType pause)
throws StopConditionException
{
return readKey(in, (pause.isUnknown() ? -1 : pause.doubleValue()));
}
/**
* Read a character from the given stream or from the
* <code>LogicalTerminal</code>.
* <p>
* The exact character read will be stored as the lastkey and can be
* accessed via {@link #lastKey}. The lastkey will be set to -1 if there
* is any interruption, the timeout expires without a character being read
* or there is an error during processing. The lastkey will be set to -2
* at <code>EOF</code>.
*
* @param in
* The stream from which to read. If <code>null</code> or if
* it is an instance of {@link StreamWrapper} which has no
* backing stream, then the <code>LogicalTerminal</code> will
* be read.
* @param pause
* The number of seconds to wait for a character to be available
* for reading. Use 0 to get an immediate response (it will
* return -1 if there is no character immediately available).
* Use -1 to specify an infinite timeout (it will never
* timeout).
*
* @return The character read, -1 if there is any interruption, timeout
* or error during processing or -2 at <code>EOF</code>.
*
* @throws StopConditionException
* If any interruption occurs during I/O processing.
*/
public static int readKey(Stream in, double pause)
throws StopConditionException
{
int result = -1;
// default the key to -1 in case we are interrupted
WorkArea wa = workArea.obtain();
wa.lastkey = result;
boolean terminal = false;
if (in == null)
{
in = UnnamedStreams.input();
terminal = (in == null);
}
else
{
if (in instanceof StreamWrapper)
{
terminal = in.isTerm();
}
}
int millis = LogicalTerminal.toMilliseconds(pause);
if (terminal)
{
// read from the terminal
result = LogicalTerminal.readKey(millis);
}
else
{
// read from a named or unnamed stream
result = in.readChar(millis).intValue();
}
// save the key that was read
setLastKey(result);
return result;
}
/**
* Get the type of its associated handle.
*
* @return Always return the PSEUDO-WIDGET value.
*/
public static character resourceType()
{
return new character(LegacyResource.PSEUDO_WIDGET);
}
/**
* Calls {@link handle#invalidAttribute}, as the FORMAT attribute is not supported.
*
* @return unknown value.
*/
@LegacyAttribute(name = "FORMAT", ignore = true)
public static character getFormat()
{
handle.invalidAttribute("FORMAT", asHandle(), false);
return new character();
}
/**
* Calls {@link handle#readOnlyError} as this attribute is not supported.
*
* @param format
* Not used.
*/
@LegacyAttribute(name = "FORMAT", setter = true, ignore = true)
public static void setFormat(@SuppressWarnings("unused") character format)
{
handle.readOnlyError(asHandle(), "FORMAT");
}
/**
* Calls {@link handle#readOnlyError} as this attribute is not supported.
*
* @param format
* Not used.
*/
@LegacyAttribute(name = "FORMAT", setter = true, ignore = true)
public static void setFormat(@SuppressWarnings("unused") String format)
{
handle.readOnlyError(asHandle(), "FORMAT");
}
/**
* Calls {@link handle#invalidAttribute}, as the COLUMN-LABEL attribute is not supported.
*
* @return unknown value.
*/
@LegacyAttribute(name = "COLUMN-LABEL", ignore = true)
public static character getColumnLabel()
{
handle.invalidAttribute("COLUMN-LABEL", asHandle(), false);
return new character();
}
/**
* Calls {@link handle#readOnlyError} as this attribute is not supported.
*
* @param columnLabel
* Not used.
*/
@LegacyAttribute(name = "COLUMN-LABEL", setter = true, ignore = true)
public static void setColumnLabel(@SuppressWarnings("unused") character columnLabel)
{
handle.readOnlyError(asHandle(), "COLUMN-LABEL");
}
/**
* Calls {@link handle#readOnlyError} as this attribute is not supported.
*
* @param columnLabel
* Not used.
*/
@LegacyAttribute(name = "COLUMN-LABEL", setter = true, ignore = true)
public static void setColumnLabel(@SuppressWarnings("unused") String columnLabel)
{
handle.readOnlyError(asHandle(), "COLUMN-LABEL");
}
/**
* Calls {@link handle#invalidAttribute}, as the DATA-TYPE attribute is not supported.
*
* @return unknown value.
*/
@LegacyAttribute(name = "DATA-TYPE", ignore = true)
public static character getDataType()
{
handle.invalidAttribute("DATA-TYPE", asHandle(), false);
return new character();
}
/**
* Calls {@link handle#readOnlyError} as this attribute is not supported.
*
* @param dataType
* Not used.
*/
@LegacyAttribute(name = "DATA-TYPE", setter = true, ignore = true)
public static void setDataType(@SuppressWarnings("unused") String dataType)
{
handle.readOnlyError(asHandle(), "DATA-TYPE");
}
/**
* Calls {@link handle#readOnlyError} as this attribute is not supported.
*
* @param dataType
* Not used.
*/
@LegacyAttribute(name = "DATA-TYPE", setter = true, ignore = true)
public static void setDataType(@SuppressWarnings("unused") character dataType)
{
handle.readOnlyError(asHandle(), "DATA-TYPE");
}
/**
* Returns the value of the LAST-EVENT:WIDGET-LEAVE attribute
*
* @return character
* current value of LAST-EVENT:WIDGET-LEAVE
*/
public static handle getWidgetLeave()
{
EventInfo info = LogicalTerminal.getEventInfo();
if (info == null)
{
return new handle();
}
if ("ENTRY".equals(info.name) || "ROW-ENTRY".equals(info.name))
{
return LogicalTerminal.getWidgetHandleById(info.otherId);
}
else if ("LEAVE".equals(info.name) || "ROW-LEAVE".equals(info.name))
{
return LogicalTerminal.getWidgetHandleById(info.widgetId);
}
return new handle();
}
/**
* Returns the value of the LAST-EVENT:WIDGET-ENTER attribute
*
* @return character
* current value of LAST-EVENT:WIDGET-ENTER
*/
public static handle getWidgetEnter()
{
EventInfo info = LogicalTerminal.getEventInfo();
if (info == null)
{
return new handle();
}
if ("ENTRY".equals(info.name))
{
if (info.otherId == -1)
{
return new handle();
}
return LogicalTerminal.getWidgetHandleById(info.widgetId);
}
else if ("ROW-ENTRY".equals(info.name))
{
return LogicalTerminal.getWidgetHandleById(info.widgetId);
}
else if ("LEAVE".equals(info.name) || "ROW-LEAVE".equals(info.name))
{
return LogicalTerminal.getWidgetHandleById(info.otherId);
}
return new handle();
}
/**
* Returns the value of the LAST-EVENT:EVENT-TYPE attribute
*
* @return character
* current value of LAST-EVENT:EVENT-TYPE
*/
public static character getEventType()
{
return new character(workArea.obtain().eventType);
}
/**
* Returns the value of the LABEL attribute
*
* @return Current value of the LABEL attribute.
*/
public static character getLabel()
{
// The case LABEL=ENTRY at start of procedure when a field got a focus. This case stands
// out from the main line of implementation. The value in this case is coming from
// the ClientState unlike the main scenario when a label is set via setLabel(),
// setLabelWorker(), readKey() methods.
//
// This code is commented because it interferes the main scenario, so the problem is not
// solved. TODO: find solution for this case.
//
// int functionKey = LogicalTerminal.getFunctionKey();
// if (functionKey != -1)
// {
// setLabelWorker(functionKey);
// }
final WorkArea keyReader = workArea.obtain();
//return new character(PROGRESS_EVTYPE.equals(keyReader.eventType) ? keyReader.label
// : LogicalTerminal.getEventLabel());
return new character(LogicalTerminal.getEventLabel());
}
/**
* Set the <code>name</code> attribute of handle.
*
* @param label
* The LABEL to be set.
*/
@LegacyAttribute(name = "LABEL", setter = true, ignore = true)
public static void setLabel(@SuppressWarnings("unused") String label)
{
// setLabel(new character(label));
handle.readOnlyError(asHandle(), "LABEL");
}
/**
* Set the <code>name</code> attribute of handle.
*
* @param label
* The LABEL to be set.
*/
@LegacyAttribute(name = "LABEL", setter = true, ignore = true)
public static void setLabel(@SuppressWarnings("unused") character label)
{
handle.readOnlyError(asHandle(), "LABEL");
}
/**
* Used internally by P2J to set the {@link WorkArea#label} in server-side event cases.
*
* @param label
* The LABEL to be set.
*/
public static void setLabelWorker(String label)
{
WorkArea wa = workArea.obtain();
wa.label = label;
// for all server-side events EVENT-TYPE=PROGRESS
wa.eventType = PROGRESS_EVTYPE;
// TODO: should we set the function here?
}
/**
* Get the COLUMN attribute.
*
* @return The column value in characters.
*/
@LegacyAttribute(name = "COLUMN")
public static decimal getColumn()
{
// TODO: we need testcases to confirm behavior
Double col = LogicalTerminal.getEventColumn();
return col == null ? new decimal() : new decimal(col);
}
/**
* Get the ROW attribute.
*
* @return The row value in characters.
*/
@LegacyAttribute(name = "ROW")
public static decimal getRow()
{
// TODO: we need testcases to confirm behavior
Double row = LogicalTerminal.getEventRow();
return row == null ? new decimal() : new decimal(row);
}
/**
* Implementation of the LAST-EVENT system handle's X attribute.
*
* @return The pixel location of the mouse cursor relative to the left edge of the display.
*/
@LegacyAttribute(name = "X")
public static integer getX()
{
Integer x = LogicalTerminal.getEventX();
return x == null ? new integer() : new integer(x);
}
/**
* Implementation of the LAST-EVENT system handle's Y attribute.
*
* @return The pixel location of the mouse cursor relative to the left edge of the display.
*/
@LegacyAttribute(name = "Y")
public static integer getY()
{
Integer y = LogicalTerminal.getEventY();
return y == null ? new integer() : new integer(y);
}
/**
* Set the X attribute.
*
* @param x
* The X attribute.
*/
@LegacyAttribute(name = "X", setter = true)
public static void setX(@SuppressWarnings("unused") NumberType x)
{
readOnlyError("X");
}
/**
* Set the Y attribute.
*
* @param y
* The Y attribute.
*/
@LegacyAttribute(name = "Y", setter = true)
public static void setY(@SuppressWarnings("unused") NumberType y)
{
readOnlyError("Y");
}
/**
* Set the X attribute.
*
* @param x
* The X attribute.
*/
@LegacyAttribute(name = "X", setter = true)
public static void setX(@SuppressWarnings("unused") int x)
{
readOnlyError("X");
}
/**
* Set the Y attribute.
*
* @param y
* The Y attribute.
*/
@LegacyAttribute(name = "Y", setter = true)
public static void setY(@SuppressWarnings("unused") int y)
{
readOnlyError("Y");
}
/**
* Set the COLUMN attribute.
*
* @param column
* The column value in characters.
*/
@LegacyAttribute(name = "COLUMN", setter = true)
public static void setColumn(@SuppressWarnings("unused") double column)
{
readOnlyError("COLUMN");
}
/**
* Set the COLUMN attribute.
*
* @param column
* The column value in characters.
*/
@LegacyAttribute(name = "COLUMN", setter = true)
public static void setColumn(@SuppressWarnings("unused") NumberType column)
{
readOnlyError("COLUMN");
}
/**
* Set the ROW attribute.
*
* @param row
* The row value in characters.
*/
@LegacyAttribute(name = "ROW", setter = true)
public static void setRow(@SuppressWarnings("unused") double row)
{
readOnlyError("ROW");
}
/**
* Set the ROW attribute.
*
* @param row
* The row value in characters.
*/
@LegacyAttribute(name = "ROW", setter = true)
public static void setRow(@SuppressWarnings("unused") NumberType row)
{
readOnlyError("ROW");
}
/**
* Implementation of the ON-FRAME-BORDER system handle's EVENT-TYPE attribute.
*
* @return {@code YES} if the last event was a mouse event that occurred on a frame border.
*/
@LegacyAttribute(name = "ON-FRAME-BORDER")
public static logical isOnFrameBorder()
{
UnimplementedFeature.missing("LAST-EVENT:ON-FRAME-BORDER attribute not implemented.");
return new logical();
}
/**
* Stores data relating to the state of the current context's key reading.
*/
private static class WorkArea
{
/** The resource's ID. */
private Long id = null;
/** Stores the last key read from any source (stream or terminal). */
private int lastkey = -1;
/** Stores the last extended keys state. */
private int lastKeyModifiers = 0;
/**
* LAST-EVENT:LABEL value that can be set explicitly (setLabelWorker) or implicitly
* ({@link KeyReader#setLastKey(int)}, or in {@link KeyReader#readKey()}). Commonly have
* a name of pressed key, but in some specific cases can have the same value as
* {@link #keyFunction} for example values "CHOOSE" and "ENTRY" - at the start of procedure.
*/
private String label;
/**
* LAST-EVENT:EVENT-TYPE attribute. Can have on of following values:
* <ul>
* <li>"PROGRESS",</li>
* <li>"KEYPRESS",</li>
* <li>"MOUSE".</li>
* </ul>
* It has the "PROGRESS" values for different cases:
* <ul>
* <li>CHOOSE event (button press),</li>
* <li>ENTRY event,</li>
* <li>server events such as CONNECT, PROCEDURE-COMPLETE, READ-RESPONSE,</li>
* <li>VALUE-CHANGED event,</li>
* <li>WINDOW events at OS level</li>
* </ul>
* and "KEYPRESS" in other cases.
*/
private String eventType;
/**
* LAST-EVENT:FUNCTION attribute value that can be set explicitly (setKeyFunctionWorker).
* The high-level name of action based on EVENT-TYPE. For EVENT-TYPE="KEYPRESS" contains
* key function (for example "RETURN"), for EVENT-TYPE="PROGRESS" contains some high-level
* widget event name such as "CHOOSE" or "LEAVE".
*/
private String keyFunction;
}
/**
* Simple container that stores and returns a context-local instance of
* the global work area.
*/
private static class ContextContainer
extends ContextLocal
{
/**
* Obtains the context-local instance of the contained global work
* area.
*
* @return The work area associated with this context.
*/
public WorkArea obtain()
{
return (WorkArea) this.get();
}
/**
* Initializes the work area, the first time it is requested within a
* new context.
*
* @return The newly instantiated work area.
*/
@Override
protected synchronized Object initialValue()
{
return new WorkArea();
}
}
}