/*******************************************************************************
 * Copyright (c) 2000, 2025 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.swt.accessibility;

import java.util.*;
import java.util.List;

import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.*;
import org.eclipse.swt.internal.accessibility.gtk.*;
import org.eclipse.swt.internal.gtk.*;
import org.eclipse.swt.internal.gtk3.*;
import org.eclipse.swt.widgets.*;

class AccessibleObject {
	long atkHandle;
	int index = -1, id = ACC.CHILDID_SELF;
	Accessible accessible;
	AccessibleObject parent;
	AccessibleObject[] children;
	/*
	* a lightweight object does not correspond to a concrete gtk widget, but
	* to a logical child of a widget (eg.- a CTabItem, which is simply drawn)
	*/
	boolean isLightweight = false;

	static long actionNamePtr = -1;
	static long descriptionPtr = -1;
	static long keybindingPtr = -1;
	static long namePtr = -1;
	static final Map<LONG, AccessibleObject> AccessibleObjects = new HashMap<> (9);
	static final boolean DEBUG = Device.DEBUG;

	AccessibleObject (long type, long widget, Accessible accessible, boolean isLightweight) {
		super ();

		if (GTK.GTK4) {
			//TODO: Make use of Accessibility interface of GtkWidget rather than atk object which has been removed
		} else {
			if (type == OS.swt_fixed_get_type()) {
				if (widget != 0 && !isLightweight) {
					atkHandle = GTK3.gtk_widget_get_accessible(widget);
				} else {
					// Lightweight widgets map to no "real" GTK widget, so we
					// just instantiate a new SwtFixedAccessible
					atkHandle = OS.g_object_new (OS.swt_fixed_accessible_get_type(), 0);
				}
				OS.swt_fixed_accessible_register_accessible(atkHandle, false, widget);
			} else {
				// TODO_a11y: accessibility listeners on the Java side have not yet
				// been implemented for native GTK widgets on GTK3.
				atkHandle = GTK3.gtk_widget_get_accessible(widget);
			}
		}

		this.accessible = accessible;
		this.isLightweight = isLightweight;
		AccessibleObjects.put (new LONG (atkHandle), this);
	}

	static void print (String str) {
		System.out.println (str);
	}

	static int size(Collection<?> listeners) {
		return listeners == null ? 0 : listeners.size();
	}

	/**
	 * Fills a Java AtkActionIface struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkActionIface Java object representing the interface struct of atkObject's
	 * parent
	 */
	static AtkActionIface getParentActionIface (long atkObject) {
		long type = OS.swt_fixed_accessible_get_type();
		if (OS.g_type_is_a (OS.g_type_parent (type), ATK.ATK_TYPE_ACTION())) {
			AtkActionIface iface = new AtkActionIface ();
			ATK.memmove (iface, OS.g_type_interface_peek_parent (ATK.ATK_ACTION_GET_IFACE (atkObject)));
			return iface;
		}
		return null;
	}

	/**
	 * Performs the specified action on atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index the action index corresponding to the action to be performed
	 *
	 * @return long int representing whether the action succeeded: 1 for success,
	 * 0 for failure
	 */
	static long atkAction_do_action (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleActionListener> listeners = accessible.accessibleActionListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleActionEvent event = new AccessibleActionEvent(accessible);
				event.index = (int)index;
				for (int i = 0; i < length; i++) {
					AccessibleActionListener listener = listeners.get(i);
					listener.doAction(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkActionIface iface = getParentActionIface (atkObject);
		if (iface != null && iface.do_action != 0) {
			parentResult = ATK.call (iface.do_action, atkObject, index);
		}
		return parentResult;
	}

	/**
	 * Returns the number of accessible actions available on atkObject.
	 * If there are more than one, the first is considered the default
	 * action of the object.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return the number of actions available
	 */
	static long atkAction_get_n_actions (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleActionListener> listeners = accessible.accessibleActionListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleActionEvent event = new AccessibleActionEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleActionListener listener = listeners.get(i);
					listener.getActionCount(event);
				}
				return event.count;
			}
		}
		long parentResult = 0;
		AtkActionIface iface = getParentActionIface (atkObject);
		if (iface != null && iface.get_n_actions != 0) {
			parentResult = ATK.call (iface.get_n_actions, atkObject);
		}
		return parentResult;
	}

	/**
	 * Returns a description of the specified action of the atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index the action index corresponding to the action to be performed
	 *
	 * @return a pointer to the description string
	 */
	static long atkAction_get_description (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleActionListener> listeners = accessible.accessibleActionListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleActionEvent event = new AccessibleActionEvent(accessible);
				event.index = (int)index;
				for (int i = 0; i < length; i++) {
					AccessibleActionListener listener = listeners.get(i);
					listener.getDescription(event);
				}
				if (event.result == null) return 0;
				if (descriptionPtr != -1) OS.g_free (descriptionPtr);
				return descriptionPtr = getStringPtr (event.result);
			}
		}
		long parentResult = 0;
		AtkActionIface iface = getParentActionIface (atkObject);
		if (iface != null && iface.get_description != 0) {
			parentResult = ATK.call (iface.get_description, atkObject, index);
		}
		return parentResult;
	}

	/**
	 * Returns the keybinding which can be used to activate
	 * this atkObject, if one exists. Example: "Ctrl+1"
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index the action index corresponding to the action to be performed
	 *
	 * @return a pointer to the keybinding string
	 */
	static long atkAction_get_keybinding (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkActionIface iface = getParentActionIface (atkObject);
		if (iface != null && iface.get_keybinding != 0) {
			parentResult = ATK.call (iface.get_keybinding, atkObject, index);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleActionListener> listeners = accessible.accessibleActionListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleActionEvent event = new AccessibleActionEvent(accessible);
				event.index = (int)index;
				for (int i = 0; i < length; i++) {
					AccessibleActionListener listener = listeners.get(i);
					listener.getKeyBinding(event);
				}
				if (event.result != null) {
					if (keybindingPtr != -1) OS.g_free (keybindingPtr);
					return keybindingPtr = getStringPtr (event.result);
				}
			}
			List<AccessibleListener> listeners2 = accessible.accessibleListeners;
			length = size(listeners2);
			if (length > 0) {
				AccessibleEvent event = new AccessibleEvent (accessible);
				event.childID = object.id;
				if (parentResult != 0) event.result = getString (parentResult);
				for (int i = 0; i < length; i++) {
					AccessibleListener listener = listeners2.get(i);
					listener.getKeyboardShortcut (event);
				}
				if (event.result != null) {
					if (keybindingPtr != -1) OS.g_free (keybindingPtr);
					return keybindingPtr = getStringPtr (event.result);
				}
			}
		}
		return parentResult;
	}

	/**
	 * Returns the name of the specified action of the atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index the action index corresponding to the action to be performed
	 *
	 * @return a pointer to the name string
	 */
	static long atkAction_get_name (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkActionIface iface = getParentActionIface (atkObject);
		if (iface != null && iface.get_name != 0) {
			parentResult = ATK.call (iface.get_name, atkObject, index);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleActionListener> listeners = accessible.accessibleActionListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleActionEvent event = new AccessibleActionEvent(accessible);
				event.index = (int)index;
				for (int i = 0; i < length; i++) {
					AccessibleActionListener listener = listeners.get(i);
					listener.getName(event);
				}
				if (event.result != null) {
					if (actionNamePtr != -1) OS.g_free (actionNamePtr);
					return actionNamePtr = getStringPtr (event.result);
				}
			}
			if (index == 0) {
				List<AccessibleControlListener> listeners2 = accessible.accessibleControlListeners;
				length = size(listeners2);
				if (length > 0) {
					AccessibleControlEvent event = new AccessibleControlEvent (accessible);
					event.childID = object.id;
					if (parentResult != 0) event.result = getString (parentResult);
					for (int i = 0; i < length; i++) {
						AccessibleControlListener listener = listeners2.get(i);
						listener.getDefaultAction (event);
					}
					if (event.result != null) {
						if (actionNamePtr != -1) OS.g_free (actionNamePtr);
						return actionNamePtr = getStringPtr (event.result);
					}
				}
			}
		}
		return parentResult;
	}

	/**
	 * Fills a Java AtkComponentIface struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkComponentIface Java object representing the interface struct of atkObject's
	 * parent
	 */
	static AtkComponentIface getParentComponentIface (long atkObject) {
		long type = OS.swt_fixed_accessible_get_type();
		if (OS.g_type_is_a (OS.g_type_parent (type), ATK.ATK_TYPE_COMPONENT())) {
			AtkComponentIface iface = new AtkComponentIface ();
			ATK.memmove (iface, OS.g_type_interface_peek_parent (ATK.ATK_COMPONENT_GET_IFACE (atkObject)));
			return iface;
		}
		return null;
	}

	/**
	 * Gets the rectangle which gives the extent of the component.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param x memory address of gint to put x coordinate
	 * @param y memory address of gint to put y coordinate
	 * @param width memory address of gint to put width coordinate
	 * @param height memory address of gint to put height coordinate
	 * @param coord_type specifies whether the coordinates are relative to
	 * the screen or to the components top level window
	 *
	 * @return 0 (this is a void function at the native level)
	 */
	static long atkComponent_get_extents (long atkObject, long x, long y,
			long width, long height, long coord_type) {
		AccessibleObject object = getAccessibleObject (atkObject);
		C.memmove (x, new int[] {0}, 4);
		C.memmove (y, new int[] {0}, 4);
		C.memmove (width, new int[] {0}, 4);
		C.memmove (height, new int[] {0}, 4);
		AtkComponentIface iface = getParentComponentIface (atkObject);
		if (iface != null && iface.get_extents != 0) {
			OS.call (iface.get_extents, atkObject, x, y, width, height, coord_type);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			int length = size(listeners);
			if (length > 0) {
				int[] parentX = new int [1], parentY = new int [1];
				int[] parentWidth = new int [1], parentHeight = new int [1];
				C.memmove (parentX, x, 4);
				C.memmove (parentY, y, 4);
				C.memmove (parentWidth, width, 4);
				C.memmove (parentHeight, height, 4);
				AccessibleControlEvent event = new AccessibleControlEvent (accessible);
				event.childID = object.id;
				event.x = parentX [0]; event.y = parentY [0];
				event.width = parentWidth [0]; event.height = parentHeight [0];
				int[] topWindowX = new int [1], topWindowY = new int [1];
				if (coord_type == ATK.ATK_XY_WINDOW) {
					windowPoint (object, topWindowX, topWindowY);
					event.x += topWindowX [0];
					event.y += topWindowY [0];
				}
				for (int i = 0; i < length; i++) {
					AccessibleControlListener listener = listeners.get (i);
					listener.getLocation (event);
				}
				if (coord_type == ATK.ATK_XY_WINDOW) {
					event.x -= topWindowX [0];
					event.y -= topWindowY [0];
				}
				C.memmove (x, new int[] {event.x}, 4);
				C.memmove (y, new int[] {event.y}, 4);
				C.memmove (width, new int[] {event.width}, 4);
				C.memmove (height, new int[] {event.height}, 4);
			}
		}
		return 0;
	}

	/**
	 * Gets a reference to the accessible child, if one exists,
	 * at the coordinate point specified by x and y.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param x long integer representing the x coordinate
	 * @param y long integer representing the y coordinate
	 * @param coord_type specifies whether the coordinates are relative to
	 * the screen or to the components top level window
	 *
	 * @return a pointer to the accessible child, if one exists
	 */
	static long atkComponent_ref_accessible_at_point (long atkObject, long x,
			long y, long coord_type) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleControlEvent event = new AccessibleControlEvent (accessible);
				event.childID = object.id;
				event.x = (int)x; event.y = (int)y;
				int[] topWindowX = new int [1], topWindowY = new int [1];
				if (coord_type == ATK.ATK_XY_WINDOW) {
					windowPoint (object, topWindowX, topWindowY);
					event.x += topWindowX [0];
					event.y += topWindowY [0];
				}
				for (int i = 0; i < length; i++) {
					AccessibleControlListener listener = listeners.get (i);
					listener.getChildAtPoint (event);
				}
				if (event.childID == object.id) event.childID = ACC.CHILDID_SELF;
				Accessible result = event.accessible;
				AccessibleObject accObj = result != null ? result.getAccessibleObject() : object.getChildByID (event.childID);
				if (accObj != null) {
					return OS.g_object_ref (accObj.atkHandle);
				}
			}
		}
		long parentResult = 0;
		AtkComponentIface iface = getParentComponentIface (atkObject);
		if (iface != null && iface.ref_accessible_at_point != 0) {
			parentResult = OS.call (iface.ref_accessible_at_point, atkObject, x, y, coord_type);
		}
		return parentResult;
	}


	/**
	 * Fills a Java AtkEditableTextIface struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkEdtiableTextIface Java object representing the interface struct of atkObject's
	 * parent
	 */
	static AtkEditableTextIface getParentEditableTextIface (long atkObject) {
		long type = OS.swt_fixed_accessible_get_type();
		if (OS.g_type_is_a (OS.g_type_parent (type), ATK.ATK_TYPE_EDITABLE_TEXT())) {
			AtkEditableTextIface iface = new AtkEditableTextIface ();
			ATK.memmove (iface, OS.g_type_interface_peek_parent (ATK.ATK_EDITABLE_TEXT_GET_IFACE (atkObject)));
			return iface;
		}
		return null;
	}

	/**
	 * Sets the attributes for a specified range.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param attrib_set a pointer to an AtkAttributeSet
	 * @param start_offset start range in which to set attributes
	 * @param end_offset end of range in which to set attributes
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkEditableText_set_run_attributes (long atkObject, long attrib_set,
			long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleEditableTextListener> listeners = accessible.accessibleEditableTextListeners;
			int length = size(listeners);
			if (length > 0) {
				Display display = accessible.control.getDisplay();
				long fontDesc = OS.pango_font_description_new ();
				boolean createFont = false;
				TextStyle style = new TextStyle();
				String [] attributes = new String [0];
				long current = attrib_set;
				int listLength = OS.g_slist_length (attrib_set);
				for (int i = 0; i < listLength; i++) {
					long attrPtr = OS.g_slist_data (current);
					if (attrPtr != 0) {
						AtkAttribute attr = new AtkAttribute();
						ATK.memmove(attr, attrPtr, AtkAttribute.sizeof);
						String name = getString(attr.name);
						String value = getString(attr.value);
						OS.g_free(attrPtr);
						String [] newAttributes = new String [attributes.length + 2];
						System.arraycopy (attributes, 0, newAttributes, 0, attributes.length);
						newAttributes[attributes.length] = name;
						newAttributes[attributes.length + 1] = value;
						attributes = newAttributes;
						try {
							if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_RISE)))) {
								// number of pixels above baseline
								style.rise = Integer.parseInt(value);
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_UNDERLINE)))) {
								// "none", "single", "double", "low", or "error" (also allow "squiggle")
								if (value.equals("single") || value.equals("low")) {
									style.underline = true;
									style.underlineStyle = SWT.UNDERLINE_SINGLE;
								} else if (value.equals("double")) {
									style.underline = true;
									style.underlineStyle = SWT.UNDERLINE_DOUBLE;
								} else if (value.equals("error")) {
									style.underline = true;
									style.underlineStyle = SWT.UNDERLINE_ERROR;
								} else if (value.equals("squiggle")) {
									style.underline = true;
									style.underlineStyle = SWT.UNDERLINE_SQUIGGLE;
								}
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_STRIKETHROUGH)))) {
								// "true" or "false" (also allow "1" and "single")
								if (value.equals("true") || value.equals("1") || value.equals("single")) style.strikeout = true;
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_FAMILY_NAME)))) {
								// font family name
								byte [] buffer = Converter.wcsToMbcs(value, true);
								OS.pango_font_description_set_family(fontDesc, buffer);
								createFont = true;
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_SIZE)))) {
								// size of characters in points (allow fractional points)
								float size = Float.parseFloat(value);
								OS.pango_font_description_set_size(fontDesc, (int)(size * OS.PANGO_SCALE));
								createFont = true;
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_STYLE)))) {
								// "normal", "italic" or "oblique"
								int fontStyle = -1;
								if (value.equals("normal")) fontStyle = OS.PANGO_STYLE_NORMAL;
								else if (value.equals("italic")) fontStyle = OS.PANGO_STYLE_ITALIC;
								else if (value.equals("oblique")) fontStyle = OS.PANGO_STYLE_OBLIQUE;
								if (fontStyle != -1) {
									OS.pango_font_description_set_style(fontDesc, fontStyle);
									createFont = true;
								}
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_VARIANT)))) {
								// "normal" or "small_caps"
								int variant = -1;
								if (value.equals("normal")) variant = OS.PANGO_VARIANT_NORMAL;
								else if (value.equals("small_caps")) variant = OS.PANGO_VARIANT_SMALL_CAPS;
								if (variant != -1) {
									OS.pango_font_description_set_variant(fontDesc, variant);
									createFont = true;
								}
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_STRETCH)))) {
								//"ultra_condensed", "extra_condensed", "condensed", "semi_condensed", "normal", "semi_expanded", "expanded", "extra_expanded" or "ultra_expanded"
								int stretch = -1;
								if (value.equals("ultra_condensed")) stretch = OS.PANGO_STRETCH_ULTRA_CONDENSED;
								else if (value.equals("extra_condensed")) stretch = OS.PANGO_STRETCH_EXTRA_CONDENSED;
								else if (value.equals("condensed")) stretch = OS.PANGO_STRETCH_CONDENSED;
								else if (value.equals("semi_condensed")) stretch = OS.PANGO_STRETCH_SEMI_CONDENSED;
								else if (value.equals("normal")) stretch = OS.PANGO_STRETCH_NORMAL;
								else if (value.equals("semi_expanded")) stretch = OS.PANGO_STRETCH_SEMI_EXPANDED;
								else if (value.equals("expanded")) stretch = OS.PANGO_STRETCH_EXPANDED;
								else if (value.equals("extra_expanded")) stretch = OS.PANGO_STRETCH_EXTRA_EXPANDED;
								else if (value.equals("ultra_expanded")) stretch = OS.PANGO_STRETCH_ULTRA_EXPANDED;
								if (stretch != -1) {
									OS.pango_font_description_set_stretch(fontDesc, stretch);
									createFont = true;
								}
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_WEIGHT)))) {
								// weight of the characters
								int weight = Integer.parseInt(value);
								OS.pango_font_description_set_weight(fontDesc, weight);
								createFont = true;
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_FG_COLOR)))) {
								// RGB value of the format "u,u,u"
								style.foreground = colorFromString(display, value);
							} else if (name.equals(getString(ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_BG_COLOR)))) {
								// RGB value of the format "u,u,u"
								style.background = colorFromString(display, value);
							} else {
								//TODO language and direction
							}
						} catch (NumberFormatException ex) {}
					}
					current = OS.g_slist_next (current);
				}
				if (createFont) {
					style.font = Font.gtk_new(display, fontDesc);
				}

				AccessibleTextAttributeEvent event = new AccessibleTextAttributeEvent(accessible);
				event.start = (int)start_offset;
				event.end = (int)end_offset;
				event.textStyle = style;
				event.attributes = attributes;
				for (int i = 0; i < length; i++) {
					AccessibleEditableTextListener listener = listeners.get(i);
					listener.setTextAttributes(event);
				}
				if (style.font != null) {
					style.font.dispose();
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkEditableTextIface iface = getParentEditableTextIface (atkObject);
		if (iface != null && iface.set_run_attributes != 0) {
			parentResult = OS.call (iface.set_run_attributes, atkObject, attrib_set, start_offset, end_offset);
		}
		return parentResult;
	}

	/*
	 * Return a Color given a string of the form "n,n,n".
	 * @param display must be the display for the accessible's control
	 * @param rgbString must not be null
	 */
	static Color colorFromString(Display display, String rgbString) {
		try {
			int comma1 = rgbString.indexOf(',');
			int comma2 = rgbString.indexOf(',', comma1 + 1);
			int r = Integer.parseInt(rgbString.substring(0, comma1));
			int g = Integer.parseInt(rgbString.substring(comma1 + 1, comma2));
			int b = Integer.parseInt(rgbString.substring(comma2 + 1, rgbString.length()));
			return new Color(r, g, b);
		} catch (NumberFormatException ex) {}
		return null;
	}

	/**
	 * Sets text contents of the atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param string the text to be set
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkEditableText_set_text_contents (long atkObject, long string) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleEditableTextListener> listeners = accessible.accessibleEditableTextListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(accessible);
				event.start = 0;
				String text = object.getText ();
				event.end = text == null ? 0 : text.length ();
				event.string = getString (string);
				for (int i = 0; i < length; i++) {
					AccessibleEditableTextListener listener = listeners.get(i);
					listener.replaceText(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkEditableTextIface iface = getParentEditableTextIface (atkObject);
		if (iface != null && iface.set_text_contents != 0) {
			parentResult = ATK.call (iface.set_text_contents, atkObject, string);
		}
		return parentResult;
	}

	/**
	 * Inserts text at a given position.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param string the text to insert
	 * @param string_length the length of the text to insert, in bytes
	 * @param position the caller initializes this to the position at which to
	 * insert the text. After the call, it points at the position after the
	 * newly inserted text.
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkEditableText_insert_text (long atkObject, long string,
			long string_length, long position) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleEditableTextListener> listeners = accessible.accessibleEditableTextListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(accessible);
				int[] pos = new int [1];
				C.memmove (pos, position, C.PTR_SIZEOF);
				event.start = event.end = pos[0];
				event.string = getString (string);
				for (int i = 0; i < length; i++) {
					AccessibleEditableTextListener listener = listeners.get(i);
					listener.replaceText(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkEditableTextIface iface = getParentEditableTextIface (atkObject);
		if (iface != null && iface.insert_text != 0) {
			parentResult = OS.call (iface.insert_text, atkObject, string, string_length, position);
		}
		return parentResult;
	}

	/**
	 * Copies text from start_pos up to (but not including) end_pos
	 * into the clipboard.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param start_pos the start position of the text to be copied
	 * @param end_pos the end position of the text to be copied
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkEditableText_copy_text(long atkObject, long start_pos, long end_pos) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleEditableTextListener> listeners = accessible.accessibleEditableTextListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(accessible);
				event.start = (int)start_pos;
				event.end = (int)end_pos;
				for (int i = 0; i < length; i++) {
					AccessibleEditableTextListener listener = listeners.get(i);
					listener.copyText(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkEditableTextIface iface = getParentEditableTextIface (atkObject);
		if (iface != null && iface.copy_text != 0) {
			parentResult = ATK.call (iface.copy_text, atkObject, start_pos, end_pos);
		}
		return parentResult;
	}

	/**
	 * Cuts text from start_pos up to (but not including) end_pos
	 * into the clipboard.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param start_pos the start position of the text to be cut
	 * @param end_pos the end position of the text to be cut
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkEditableText_cut_text (long atkObject, long start_pos, long end_pos) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleEditableTextListener> listeners = accessible.accessibleEditableTextListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(accessible);
				event.start = (int)start_pos;
				event.end = (int)end_pos;
				for (int i = 0; i < length; i++) {
					AccessibleEditableTextListener listener = listeners.get(i);
					listener.cutText(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkEditableTextIface iface = getParentEditableTextIface (atkObject);
		if (iface != null && iface.cut_text != 0) {
			parentResult = ATK.call (iface.cut_text, atkObject, start_pos, end_pos);
		}
		return parentResult;
	}

	/**
	 * Delete text start_pos up to (but not including) end_pos.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param start_pos the start position of the text to be deleted
	 * @param end_pos the end position of the text to be deleted
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkEditableText_delete_text (long atkObject, long start_pos, long end_pos) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleEditableTextListener> listeners = accessible.accessibleEditableTextListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(accessible);
				event.start = (int)start_pos;
				event.end = (int)end_pos;
				event.string = "";
				for (int i = 0; i < length; i++) {
					AccessibleEditableTextListener listener = listeners.get(i);
					listener.replaceText(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkEditableTextIface iface = getParentEditableTextIface (atkObject);
		if (iface != null && iface.delete_text != 0) {
			parentResult = ATK.call (iface.delete_text, atkObject, start_pos, end_pos);
		}
		return parentResult;
	}

	/**
	 * Paste text from clipboard to specified position.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param position the position to paste
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkEditableText_paste_text (long atkObject, long position) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleEditableTextListener> listeners = accessible.accessibleEditableTextListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleEditableTextEvent event = new AccessibleEditableTextEvent(accessible);
				event.start = (int)position;
				for (int i = 0; i < length; i++) {
					AccessibleEditableTextListener listener = listeners.get(i);
					listener.pasteText(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkEditableTextIface iface = getParentEditableTextIface (atkObject);
		if (iface != null && iface.paste_text != 0) {
			parentResult = ATK.call (iface.paste_text, atkObject, position);
		}
		return parentResult;
	}

	/**
	 * Fills a Java AtkHypertextIface struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkHypertextIface Java object representing the interface struct of atkObject's
	 * parent
	 */
	static AtkHypertextIface getParentHypertextIface (long atkObject) {
		long type = OS.swt_fixed_accessible_get_type();
		if (OS.g_type_is_a (OS.g_type_parent (type), ATK.ATK_TYPE_HYPERTEXT())) {
			AtkHypertextIface iface = new AtkHypertextIface ();
			ATK.memmove (iface, OS.g_type_interface_peek_parent (ATK.ATK_HYPERTEXT_GET_IFACE (atkObject)));
			return iface;
		}
		return null;
	}

	/**
	 * Gets the link in this hypertext document at index link_index.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param link_index the index of the link
	 *
	 * @return a pointer to the AtkHypertext at link_index in atkObject
	 */
	static long atkHypertext_get_link (long atkObject, long link_index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.index = (int)link_index;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getHyperlink(event);
				}
				Accessible result = event.accessible;
				return result != null ? result.getAccessibleObject().atkHandle : 0;
			}
		}
		long parentResult = 0;
		AtkHypertextIface iface = getParentHypertextIface (atkObject);
		if (iface != null && iface.get_link != 0) {
			parentResult = ATK.call (iface.get_link, atkObject, link_index);
		}
		return parentResult;
	}

	/**
	 * Gets the number of links in this hypertext document.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an integer representing the number of links in the hypertext
	 * in atkObject
	 */
	static long atkHypertext_get_n_links (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getHyperlinkCount(event);
				}
				return event.count;
			}
		}
		long parentResult = 0;
		AtkHypertextIface iface = getParentHypertextIface (atkObject);
		if (iface != null && iface.get_n_links != 0) {
			parentResult = ATK.call (iface.get_n_links, atkObject);
		}
		return parentResult;
	}

	/**
	 * Gets the index into the array of hyperlinks that is
	 * associated with the character specified by char_index.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param char_index a character index
	 *
	 * @return an integer representing the index into the array of
	 * hypertexts
	 */
	static long atkHypertext_get_link_index (long atkObject, long char_index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.offset = (int)char_index;
				event.index = -1;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getHyperlinkIndex(event);
				}
				return event.index;
			}
		}
		long parentResult = 0;
		AtkHypertextIface iface = getParentHypertextIface (atkObject);
		if (iface != null && iface.get_link_index != 0) {
			parentResult = ATK.call (iface.get_link_index, atkObject, char_index);
		}
		return parentResult;
	}

	/**
	 * Fills a Java AtkObjectClass struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkObjectClass object representing the class struct of atkObject's
	 * parent
	 */
	static AtkObjectClass getParentAtkObjectClass () {
		AtkObjectClass objectClass = new AtkObjectClass ();
		long type = OS.swt_fixed_accessible_get_type();
		if (type != 0) {
			long parentType = OS.g_type_parent (type);
			if (parentType != 0) ATK.memmove (objectClass, OS.g_type_class_peek (parentType));
		}
		return objectClass;
	}

	/**
	 * Gets the accessible description of the widget associated with
	 * atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a pointer to the gchar representation of the accessible description
	 */
	static long atkObject_get_description (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.get_description != 0) {
			parentResult = ATK.call (objectClass.get_description, atkObject);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleListener> listeners = accessible.accessibleListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleEvent event = new AccessibleEvent (accessible);
				event.childID = object.id;
				if (parentResult != 0) event.result = getString (parentResult);
				for (int i = 0; i < length; i++) {
					AccessibleListener listener = listeners.get (i);
					listener.getDescription (event);
				}
				if (event.result == null) return parentResult;
				if (descriptionPtr != -1) OS.g_free (descriptionPtr);
				return descriptionPtr = getStringPtr (event.result);
			}
		}
		return parentResult;
	}

	/**
	 * Get a list of properties applied to this object as a whole,
	 * as an AtkAttributeSet consisting of name-value pairs.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a pointer to the AtkAttributeSet consisting of all properties
	 * applied to atkObject (can be empty if no properties are set)
	 */
	static long atkObject_get_attributes (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.get_attributes != 0) {
			parentResult = ATK.call (objectClass.get_attributes, atkObject);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleAttributeListener> listeners = accessible.accessibleAttributeListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleAttributeEvent event = new AccessibleAttributeEvent (accessible);
				event.topMargin = event.bottomMargin = event.leftMargin = event.rightMargin = event.alignment
					= event.indent = event.groupLevel = event.groupCount = event.groupIndex = -1;
				for (int i = 0; i < length; i++) {
					AccessibleAttributeListener listener = listeners.get (i);
					listener.getAttributes (event);
				}
				AtkAttribute attr = new AtkAttribute();
				if (event.leftMargin != -1) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_LEFT_MARGIN));
					attr.value = getStringPtr (String.valueOf(event.leftMargin));
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}
				if (event.rightMargin != -1) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_RIGHT_MARGIN));
					attr.value = getStringPtr (String.valueOf(event.rightMargin));
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}
				if (event.topMargin != -1) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = getStringPtr ("top-margin"); //$NON-NLS-1$
					attr.value = getStringPtr (String.valueOf(event.topMargin));
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}
				if (event.bottomMargin != -1) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = getStringPtr ("bottom-margin"); //$NON-NLS-1$
					attr.value = getStringPtr (String.valueOf(event.bottomMargin));
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}
				if (event.indent != -1) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_INDENT));
					attr.value = getStringPtr (String.valueOf(event.indent));
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}
				if (event.justify) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_JUSTIFICATION));
					attr.value = getStringPtr ("fill"); //$NON-NLS-1$
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				} else if (event.alignment != -1) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_JUSTIFICATION));
					String str = "left"; //$NON-NLS-1$
					switch (event.alignment) {
						case SWT.LEFT: str = "left"; break; //$NON-NLS-1$
						case SWT.RIGHT: str = "right"; break; //$NON-NLS-1$
						case SWT.CENTER: str = "center"; break; //$NON-NLS-1$
					}
					attr.value = getStringPtr (str);
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}
				//TODO - tabStops

				/* Check for group attributes. */
				int level = (event.groupLevel != -1) ? event.groupLevel : 0;
				int setsize = (event.groupCount != -1) ? event.groupCount : 0;
				int posinset = (event.groupIndex != -1) ? event.groupIndex : 0;
				if (setsize == 0 && posinset == 0) {
					/* Determine position and count for radio buttons. */
					Control control = accessible.control;
					if (control instanceof Button && ((control.getStyle() & SWT.RADIO) != 0)) {
						Control [] children = control.getParent().getChildren();
						posinset = 1;
						setsize = 1;
						for (int i = 0; i < children.length; i++) {
							Control child = children[i];
							if (child instanceof Button && ((child.getStyle() & SWT.RADIO) != 0)) {
								if (child == control) posinset = setsize;
								else setsize++;
							}
						}
					}
				}
				if (level != 0) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = getStringPtr ("level"); //$NON-NLS-1$
					attr.value = getStringPtr (String.valueOf(level));
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}
				if (setsize != 0) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = getStringPtr ("setsize"); //$NON-NLS-1$
					attr.value = getStringPtr (String.valueOf(setsize));
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}
				if (posinset != 0) {
					long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
					attr.name = getStringPtr ("posinset"); //$NON-NLS-1$
					attr.value = getStringPtr (String.valueOf(posinset));
					ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
					parentResult = OS.g_slist_append(parentResult, attrPtr);
				}

				if (event.attributes != null) {
					int end = event.attributes.length / 2 * 2;
					for (int i = 0; i < end; i+= 2) {
						long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = getStringPtr (event.attributes[i]);
						attr.value = getStringPtr (event.attributes[i + 1]);
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						parentResult = OS.g_slist_append(parentResult, attrPtr);
					}
				}
			}
		}
		return parentResult;
	}

	/**
	 * Gets the accessible name of the widget associated with
	 * atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a pointer to the gchar representation of the accessible name
	 */
	static long atkObject_get_name (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.get_name != 0) {
			parentResult = ATK.call (objectClass.get_name, atkObject);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleListener> listeners = accessible.accessibleListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleEvent event = new AccessibleEvent (accessible);
				event.childID = object.id;
				if (parentResult != 0) event.result = getString (parentResult);
				for (int i = 0; i < length; i++) {
					AccessibleListener listener = listeners.get (i);
					listener.getName (event);
				}
				if (event.result == null) return parentResult;
				if (namePtr != -1) OS.g_free (namePtr);
				return namePtr = getStringPtr (event.result);
			}
		}
		return parentResult;
	}

	/**
	 * Gets the number of accessible children of the widget associated with
	 * atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an int representing the number of accessible children associated
	 * with atkObject
	 */
	static long atkObject_get_n_children (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.get_n_children != 0) {
			parentResult = ATK.call (objectClass.get_n_children, atkObject);
		}
		if (object != null && object.id == ACC.CHILDID_SELF) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleControlEvent event = new AccessibleControlEvent (accessible);
				event.childID = object.id;
				event.detail = (int)parentResult;
				for (int i = 0; i < length; i++) {
					AccessibleControlListener listener = listeners.get (i);
					listener.getChildCount (event);
				}
				return event.detail;
			}
		}
		return parentResult;
	}

	/**
	 * Gets the 0-based index of the widget (associated with atkObject)
	 * in its parent.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an int which is the index of the accessible atkObject in its
	 * parent
	 */
	static long atkObject_get_index_in_parent (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			AccessibleControlEvent event = new AccessibleControlEvent(accessible);
			event.childID = ACC.CHILDID_CHILD_INDEX;
			event.detail = -1;
			for (int i = 0; i < size(listeners); i++) {
				AccessibleControlListener listener = listeners.get(i);
				listener.getChild(event);
			}
			if (event.detail != -1) {
				return event.detail;
			}
			if (object.index != -1) {
				return object.index;
			}
		}
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.get_index_in_parent == 0) return 0;
		long result = ATK.call (objectClass.get_index_in_parent, atkObject);
		return result;
	}

	/**
	 * Gets the accessible parent of the widget associated with
	 * atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a pointer to the AtkObject representing the accessible parent of
	 * atkObject
	 */
	static long atkObject_get_parent (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			if (object.parent != null) {
				return object.parent.atkHandle;
			}
		}
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.get_parent == 0) return 0;
		long parentResult = ATK.call (objectClass.get_parent, atkObject);
		return parentResult;
	}

	/**
	 * Gets the role of the accessible associated with atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return the AtkRole of atkObject
	 */
	static long atkObject_get_role (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleControlEvent event = new AccessibleControlEvent (accessible);
				event.childID = object.id;
				event.detail = -1;
				for (int i = 0; i < length; i++) {
					AccessibleControlListener listener = listeners.get (i);
					listener.getRole (event);
				}
				if (event.detail != -1) {
					switch (event.detail) {
						/* Convert from win32 role values to atk role values */
						case ACC.ROLE_CHECKBUTTON: return ATK.ATK_ROLE_CHECK_BOX;
						case ACC.ROLE_CLIENT_AREA: return ATK.ATK_ROLE_DRAWING_AREA;
						case ACC.ROLE_COMBOBOX: return ATK.ATK_ROLE_COMBO_BOX;
						case ACC.ROLE_DIALOG: return ATK.ATK_ROLE_DIALOG;
						case ACC.ROLE_LABEL: return ATK.ATK_ROLE_LABEL;
						case ACC.ROLE_LINK: return ATK.ATK_ROLE_TEXT;
						case ACC.ROLE_LIST: return ATK.ATK_ROLE_LIST;
						case ACC.ROLE_LISTITEM: return ATK.ATK_ROLE_LIST_ITEM;
						case ACC.ROLE_MENU: return ATK.ATK_ROLE_MENU;
						case ACC.ROLE_MENUBAR: return ATK.ATK_ROLE_MENU_BAR;
						case ACC.ROLE_MENUITEM: return ATK.ATK_ROLE_MENU_ITEM;
						case ACC.ROLE_PROGRESSBAR: return ATK.ATK_ROLE_PROGRESS_BAR;
						case ACC.ROLE_PUSHBUTTON: return ATK.ATK_ROLE_PUSH_BUTTON;
						case ACC.ROLE_SCROLLBAR: return ATK.ATK_ROLE_SCROLL_BAR;
						case ACC.ROLE_SEPARATOR: return ATK.ATK_ROLE_SEPARATOR;
						case ACC.ROLE_SLIDER: return ATK.ATK_ROLE_SLIDER;
						case ACC.ROLE_TABLE: return ATK.ATK_ROLE_TABLE;
						case ACC.ROLE_TABLECELL: return ATK.ATK_ROLE_TABLE_CELL;
						case ACC.ROLE_TABLECOLUMNHEADER: return ATK.ATK_ROLE_TABLE_COLUMN_HEADER;
						case ACC.ROLE_TABLEROWHEADER: return ATK.ATK_ROLE_TABLE_ROW_HEADER;
						case ACC.ROLE_TABFOLDER: return ATK.ATK_ROLE_PAGE_TAB_LIST;
						case ACC.ROLE_TABITEM: return ATK.ATK_ROLE_PAGE_TAB;
						case ACC.ROLE_TEXT: return ATK.ATK_ROLE_TEXT;
						case ACC.ROLE_TOOLBAR: return ATK.ATK_ROLE_TOOL_BAR;
						case ACC.ROLE_TOOLTIP: return ATK.ATK_ROLE_TOOL_TIP;
						case ACC.ROLE_TREE: return ATK.ATK_ROLE_TREE;
						case ACC.ROLE_TREEITEM: return ATK.ATK_ROLE_LIST_ITEM;
						case ACC.ROLE_RADIOBUTTON: return ATK.ATK_ROLE_RADIO_BUTTON;
						case ACC.ROLE_SPLITBUTTON: return ATK.ATK_ROLE_PUSH_BUTTON;
						case ACC.ROLE_WINDOW: return ATK.ATK_ROLE_WINDOW;
						case ACC.ROLE_ROW: return ATK.ATK_ROLE_TABLE_ROW;
						case ACC.ROLE_COLUMN: return ATK.ATK_ROLE_UNKNOWN; //Column role doesn't exist on Gtk.
						case ACC.ROLE_ALERT: return ATK.ATK_ROLE_ALERT;
						case ACC.ROLE_ANIMATION: return ATK.ATK_ROLE_ANIMATION;
						case ACC.ROLE_CANVAS: return ATK.ATK_ROLE_CANVAS;
						case ACC.ROLE_GROUP: return ATK.ATK_ROLE_PANEL;
						case ACC.ROLE_SPINBUTTON: return ATK.ATK_ROLE_SPIN_BUTTON;
						case ACC.ROLE_STATUSBAR: return ATK.ATK_ROLE_STATUSBAR;
						case ACC.ROLE_CHECKMENUITEM: return ATK.ATK_ROLE_CHECK_MENU_ITEM;
						case ACC.ROLE_RADIOMENUITEM: return ATK.ATK_ROLE_RADIO_MENU_ITEM;
						case ACC.ROLE_CLOCK: return ATK.ATK_ROLE_UNKNOWN;
						case ACC.ROLE_CALENDAR: return ATK.ATK_ROLE_CALENDAR;
						case ACC.ROLE_DATETIME: return ATK.ATK_ROLE_DATE_EDITOR;
						case ACC.ROLE_FOOTER: return ATK.ATK_ROLE_FOOTER;
						case ACC.ROLE_FORM: return ATK.ATK_ROLE_FORM;
						case ACC.ROLE_HEADER: return ATK.ATK_ROLE_HEADER;
						case ACC.ROLE_HEADING: return ATK.ATK_ROLE_HEADING;
						case ACC.ROLE_PAGE: return ATK.ATK_ROLE_PAGE;
						case ACC.ROLE_PARAGRAPH: return ATK.ATK_ROLE_PARAGRAPH;
						case ACC.ROLE_SECTION: return ATK.ATK_ROLE_SECTION;
						case ACC.ROLE_DOCUMENT: return ATK.ATK_ROLE_DOCUMENT_FRAME;
						case ACC.ROLE_GRAPHIC: return ATK.ATK_ROLE_IMAGE;
					}
				}
			}
		}
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.get_role == 0) return 0;
		return ATK.call (objectClass.get_role, atkObject);
	}

	/**
	 * Gets a reference to the accessible child whose parent is atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index the index of the child
	 *
	 * @return a pointer to the AtkObject of the child at the provided index
	 */
	static long atkObject_ref_child (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null && object.id == ACC.CHILDID_SELF) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleControlEvent event = new AccessibleControlEvent(accessible);
				event.childID = ACC.CHILDID_CHILD_AT_INDEX;
				event.detail = (int)index;
				for (int i = 0; i < length; i++) {
					AccessibleControlListener listener = listeners.get(i);
					listener.getChild(event);
				}
				if (event.accessible != null) {
					AccessibleObject accObject = event.accessible.getAccessibleObject();
					if (accObject != null) {
						return OS.g_object_ref (accObject.atkHandle);
					}
				}
			}
			object.updateChildren ();
			AccessibleObject accObject = object.getChildByIndex ((int)index);
			if (accObject != null) {
				return OS.g_object_ref (accObject.atkHandle);
			}
		}
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.ref_child == 0) return 0;
		return ATK.call (objectClass.ref_child, atkObject, index);
	}

	/**
	 * Gets a reference to the state set of the accessible associated with
	 * atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a pointer to the AtkStateSet for the accessible widget atkObject
	 */
	static long atkObject_ref_state_set (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkObjectClass objectClass = getParentAtkObjectClass ();
		if (objectClass.ref_state_set != 0) {
			parentResult = ATK.call (objectClass.ref_state_set, atkObject);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			int length = size(listeners);
			if (length > 0) {
				long set = parentResult;
				AccessibleControlEvent event = new AccessibleControlEvent (accessible);
				event.childID = object.id;
				event.detail = -1;
				for (int i = 0; i < length; i++) {
					AccessibleControlListener listener = listeners.get (i);
					listener.getState (event);
				}
				if (event.detail != -1) {
					/*	Convert from win32 state values to atk state values */
					int state = event.detail;
					if ((state & ACC.STATE_BUSY) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_BUSY);
					if ((state & ACC.STATE_CHECKED) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_CHECKED);
					if ((state & ACC.STATE_EXPANDED) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_EXPANDED);
					if ((state & ACC.STATE_FOCUSABLE) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_FOCUSABLE);
					if ((state & ACC.STATE_FOCUSED) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_FOCUSED);
					if ((state & ACC.STATE_HOTTRACKED) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_ARMED);
					if ((state & ACC.STATE_INVISIBLE) == 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_VISIBLE);
					if ((state & ACC.STATE_MULTISELECTABLE) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_MULTISELECTABLE);
					if ((state & ACC.STATE_OFFSCREEN) == 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_SHOWING);
					if ((state & ACC.STATE_PRESSED) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_PRESSED);
					if ((state & ACC.STATE_READONLY) == 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_EDITABLE);
					if ((state & ACC.STATE_SELECTABLE) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_SELECTABLE);
					if ((state & ACC.STATE_SELECTED) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_SELECTED);
					if ((state & ACC.STATE_SIZEABLE) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_RESIZABLE);
					if ((state & ACC.STATE_DISABLED) == 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_ENABLED);
					if ((state & ACC.STATE_ACTIVE) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_ACTIVE);
					if ((state & ACC.STATE_SINGLELINE) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_SINGLE_LINE);
					if ((state & ACC.STATE_MULTILINE) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_MULTI_LINE);
					if ((state & ACC.STATE_REQUIRED) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_REQUIRED);
					if ((state & ACC.STATE_INVALID_ENTRY) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_INVALID_ENTRY);
					if ((state & ACC.STATE_SUPPORTS_AUTOCOMPLETION) != 0) ATK.atk_state_set_add_state (set, ATK.ATK_STATE_SUPPORTS_AUTOCOMPLETION);
					/* Note: STATE_COLLAPSED, STATE_LINKED and STATE_NORMAL have no ATK equivalents */
				}
				return set;
			}
		}
		return parentResult;
	}

	/**
	 * Fills a Java AtkSelectionIface struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkSelectionIface Java object representing the interface struct of atkObject's
	 * parent
	 */
	static AtkSelectionIface getParentSelectionIface (long atkObject) {
		long type = OS.swt_fixed_accessible_get_type() ;
		if (OS.g_type_is_a (OS.g_type_parent (type), ATK.ATK_TYPE_SELECTION())) {
			AtkSelectionIface iface = new AtkSelectionIface ();
			ATK.memmove (iface, OS.g_type_interface_peek_parent (ATK.ATK_SELECTION_GET_IFACE (atkObject)));
			return iface;
		}
		return null;
	}

	/**
	 * Determines if the current child of this atkObject is selected.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index a long integer representing the index
	 *
	 * @return a long integer where 1 represents TRUE, 0 otherwise
	 */
	static long atkSelection_is_child_selected (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkSelectionIface iface = getParentSelectionIface (atkObject);
		if (iface != null && iface.is_child_selected != 0) {
			parentResult = ATK.call (iface.is_child_selected, atkObject, index);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleControlEvent event = new AccessibleControlEvent (accessible);
				event.childID = object.id;
				for (int i = 0; i < length; i++) {
					AccessibleControlListener listener = listeners.get (i);
					listener.getSelection (event);
				}
				Accessible result = event.accessible;
				AccessibleObject accessibleObject = result != null ? result.getAccessibleObject() : object.getChildByID (event.childID);
				if (accessibleObject != null) {
					return accessibleObject.index == index ? 1 : 0;
				}
			}
		}
		return parentResult;
	}

	/**
	 * Gets a reference to the atkObject representing the specified
	 * selected child of the object.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index a long integer representing the index
	 *
	 * @return a pointer to the AtkObject representing the selected accessible,
	 * or 0
	 */
	static long atkSelection_ref_selection (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkSelectionIface iface = getParentSelectionIface (atkObject);
		if (iface != null && iface.ref_selection != 0) {
			parentResult = ATK.call (iface.ref_selection, atkObject, index);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleControlEvent event = new AccessibleControlEvent (accessible);
				event.childID = object.id;
				for (int i = 0; i < length; i++) {
					AccessibleControlListener listener = listeners.get (i);
					listener.getSelection (event);
				}
				AccessibleObject accObj = object.getChildByID (event.childID);
				if (accObj != null) {
					if (parentResult != 0) OS.g_object_unref (parentResult);
					OS.g_object_ref (accObj.atkHandle);
					return accObj.atkHandle;
				}
			}
		}
		return parentResult;
	}

	/**
	 * Fills a Java AtkTableIface struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkTableIface Java object representing the interface struct of atkObject's
	 * parent
	 */
	static AtkTableIface getParentTableIface (long atkObject) {
		long type =  OS.swt_fixed_accessible_get_type();
		if (OS.g_type_is_a (OS.g_type_parent (type), ATK.ATK_TYPE_TABLE())) {
			AtkTableIface iface = new AtkTableIface ();
			ATK.memmove (iface, OS.g_type_interface_peek_parent (ATK.ATK_TABLE_GET_IFACE (atkObject)));
			return iface;
		}
		return null;
	}

	/**
	 * Get a reference to the table cell at row, column.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing a row in atkObject
	 * @param column a long integer representing a column in atkObject
	 *
	 * @return a pointer to an AtkObject representing the specified table
	 */
	static long atkTable_ref_at (long atkObject, long row, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.row = (int)row;
				event.column = (int)column;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getCell(event);
				}
				Accessible result = event.accessible;
				if (result != null) {
					AccessibleObject accessibleObject = result.getAccessibleObject();
					OS.g_object_ref(accessibleObject.atkHandle);
					return accessibleObject.atkHandle;
				}
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.ref_at != 0) {
			parentResult = ATK.call (iface.ref_at, atkObject, row, column);
		}
		return parentResult;
	}

	/**
	 * Get a reference to the table cell at row, column.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing a row in atkObject
	 * @param column a long integer representing a column in atkObject
	 *
	 * @return a pointer to an AtkObject representing the specified table
	 */
	static long atkTable_get_index_at (long atkObject, long row, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.row = (int)row;
				event.column = (int)column;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getCell(event);
				}
				Accessible result = event.accessible;
				if (result == null) return -1;
				event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getColumnCount(event);
				}
				return row * event.count + column;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_index_at != 0) {
			parentResult = ATK.call (iface.get_index_at, atkObject, row, column);
		}
		return parentResult;
	}

	/**
	 * Gets a gint representing the column at the specified index.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index a long integer representing an index in the table
	 *
	 * @return a long integer representing the column at the specified index, or
	 * -1
	 */
	static long atkTable_get_column_at_index (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getColumnCount(event);
				}
				long result = event.count == 0 ? -1 : index % event.count;
				return result;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_column_at_index != 0) {
			parentResult = ATK.call (iface.get_column_at_index, atkObject, index);
		}
		return parentResult;
	}

	/**
	 * Gets a gint representing the row at the specified index.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param index a long integer representing an index in the table
	 *
	 * @return a long integer representing the row at the specified index, or
	 * -1
	 */
	static long atkTable_get_row_at_index (long atkObject, long index) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getColumnCount(event);
				}
				long result = event.count == 0 ? -1 : index / event.count;
				return result;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_row_at_index != 0) {
			parentResult = ATK.call (iface.get_row_at_index, atkObject, index);
		}
		return parentResult;
	}

	/**
	 * Gets the number of columns in the table.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a long integer representing the number of columns, or
	 * 0
	 */
	static long atkTable_get_n_columns (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_n_columns != 0) {
			parentResult = ATK.call (iface.get_n_columns, atkObject);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.count = (int)parentResult;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getColumnCount(event);
					parentResult = event.count;
				}
			}
		}
		return parentResult;
	}

	/**
	 * Gets the number of rows in the table.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a long integer representing the number of rows, or
	 * 0
	 */
	static long atkTable_get_n_rows (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_n_rows != 0) {
			parentResult = ATK.call (iface.get_n_rows, atkObject);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.count = (int)parentResult;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getRowCount(event);
					parentResult = event.count;
				}
			}
		}
		return parentResult;
	}

	/**
	 * Gets the number of columns occupied at the specified row
	 * and column.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing the row
	 * @param column a long integer representing the column
	 *
	 * @return a long integer representing the column extent at the
	 * specified position, or 0
	 */
	static long atkTable_get_column_extent_at (long atkObject, long row, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_column_extent_at != 0) {
			parentResult = ATK.call (iface.get_column_extent_at, atkObject, row, column);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.row = (int)row;
				event.column = (int)column;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getCell(event);
				}
				Accessible result = event.accessible;
				if (result != null) {
					List<AccessibleTableCellListener> listeners2 = result.accessibleTableCellListeners;
					length = size(listeners2);
					if (length > 0) {
						AccessibleTableCellEvent cellEvent = new AccessibleTableCellEvent(result);
						cellEvent.count = (int)parentResult;
						for (int i = 0; i < length; i++) {
							AccessibleTableCellListener listener = listeners2.get(i);
							listener.getColumnSpan(cellEvent);
						}
						return cellEvent.count;
					}
				}
			}
		}
		return parentResult;
	}

	/**
	 * Gets the number of rows occupied at the specified row
	 * and column.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing the row
	 * @param column a long integer representing the column
	 *
	 * @return a long integer representing the row extent at the specified
	 * position, or 0
	 */
	static long atkTable_get_row_extent_at (long atkObject, long row, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_row_extent_at != 0) {
			parentResult = ATK.call (iface.get_row_extent_at, atkObject, row, column);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.row = (int)row;
				event.column = (int)column;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getCell(event);
				}
				Accessible result = event.accessible;
				if (result != null) {
					List<AccessibleTableCellListener> listeners2 = result.accessibleTableCellListeners;
					length = size(listeners2);
					if (length > 0) {
						AccessibleTableCellEvent cellEvent = new AccessibleTableCellEvent(result);
						cellEvent.count = (int)parentResult;
						for (int i = 0; i < length; i++) {
							AccessibleTableCellListener listener = listeners2.get(i);
							listener.getRowSpan(cellEvent);
						}
						return cellEvent.count;
					}
				}
			}
		}
		return parentResult;
	}

	/**
	 * Gets the caption for the table.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a pointer to the AtkObject representing the caption, or 0
	 */
	static long atkTable_get_caption (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getCaption(event);
				}
				Accessible result = event.accessible;
				if (result != null) return result.getAccessibleObject().atkHandle;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_caption != 0) {
			parentResult = ATK.call (iface.get_caption, atkObject);
		}
		return parentResult;
	}

	/**
	 * Gets the summary for the table.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a pointer to the AtkObject representing the summary, or 0
	 */
	static long atkTable_get_summary (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getSummary(event);
				}
				Accessible result = event.accessible;
				if (result != null) return result.getAccessibleObject().atkHandle;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_summary != 0) {
			parentResult = ATK.call (iface.get_summary, atkObject);
		}
		return parentResult;
	}

	/**
	 * Gets the description text of the specified column.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param column a long integer representing the specified column
	 *
	 * @return a pointer to the gchar representation of the column description,
	 * or 0
	 */
	static long atkTable_get_column_description (long atkObject, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_column_description != 0) {
			parentResult = ATK.call (iface.get_column_description, atkObject, column);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.column = (int)column;
				if (parentResult != 0) event.result = getString (parentResult);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getColumnDescription(event);
				}
				if (event.result == null) return parentResult;
				if (descriptionPtr != -1) OS.g_free (descriptionPtr);
				return descriptionPtr = getStringPtr (event.result);
			}
		}
		return parentResult;
	}

	/**
	 * Gets the column header of a specified column.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param column a long integer representing the specified column
	 *
	 * @return a pointer to the AtkObject representing the specified column
	 * header, or 0
	 */
	static long atkTable_get_column_header (long atkObject, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getRowHeaderCells(event);
				}
				Accessible[] accessibles = event.accessibles;
				if (accessibles != null) {
					if (0 <= column && column < accessibles.length) {
						return accessibles[(int)column].getAccessibleObject().atkHandle;
					}
				}
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_column_header != 0) {
			parentResult = ATK.call (iface.get_column_header, atkObject, column);
		}
		return parentResult;
	}

	/**
	 * Gets the description text of the specified row.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing the specified row
	 *
	 * @return a pointer to the gchar representation of the row description,
	 * or 0
	 */
	static long atkTable_get_row_description (long atkObject, long row) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_row_description != 0) {
			parentResult = ATK.call (iface.get_row_description, atkObject, row);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.row = (int)row;
				if (parentResult != 0) event.result = getString (parentResult);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getRowDescription(event);
				}
				if (event.result == null) return parentResult;
				if (descriptionPtr != -1) OS.g_free (descriptionPtr);
				return descriptionPtr = getStringPtr (event.result);
			}
		}
		return parentResult;
	}

	/**
	 * Gets the column header of a specified row.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing the specified row
	 *
	 * @return a pointer to the AtkObject representing the specified row
	 * header, or 0
	 */
	static long atkTable_get_row_header (long atkObject, long row) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getRowHeaderCells(event);
				}
				Accessible[] accessibles = event.accessibles;
				if (accessibles != null) {
					if (0 <= row && row < accessibles.length) {
						return accessibles[(int)row].getAccessibleObject().atkHandle;
					}
				}
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_row_header != 0) {
			parentResult = ATK.call (iface.get_row_header, atkObject, row);
		}
		return parentResult;
	}

	/**
	 * Gets the selected columns of the table.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param selected an array of integer pointers that is to contain the
	 * selected column numbers
	 *
	 * @return a long integer representing the number of columns selected,
	 * or 0
	 */
	static long atkTable_get_selected_columns (long atkObject, long selected) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getSelectedColumns(event);
				}
				int count = event.selected != null ? event.selected.length : 0;
				long result = OS.g_malloc(count * 4);
				if (event.selected != null) C.memmove(result, event.selected, count * 4);
				if (selected != 0) C.memmove(selected, new long []{result}, C.PTR_SIZEOF);
				return count;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_selected_columns != 0) {
			parentResult = ATK.call (iface.get_selected_columns, atkObject, selected);
		}
		return parentResult;
	}

	/**
	 * Gets the selected rows of the table.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param selected an array of integer pointers that is to contain the
	 * selected row numbers
	 *
	 * @return a long integer representing the number of rows selected,
	 * or 0
	 */
	static long atkTable_get_selected_rows (long atkObject, long selected) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getSelectedRows(event);
				}
				int count = event.selected != null ? event.selected.length : 0;
				long result = OS.g_malloc(count * 4);
				if (event.selected != null) C.memmove(result, event.selected, count * 4);
				if (selected != 0) C.memmove(selected, new long []{result}, C.PTR_SIZEOF);
				return count;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.get_selected_rows != 0) {
			parentResult = ATK.call (iface.get_selected_rows, atkObject, selected);
		}
		return parentResult;
	}

	/**
	 * Determines if the specified column is selected.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param column a long integer representing the column
	 *
	 * @return a long integer where 1 represents TRUE, 0 otherwise
	 */
	static long atkTable_is_column_selected (long atkObject, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.is_column_selected != 0) {
			parentResult = ATK.call (iface.is_column_selected, atkObject, column);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.isSelected = parentResult != 0;
				event.column = (int)column;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.isColumnSelected(event);
				}
				return event.isSelected ? 1 : 0;
			}
		}
		return parentResult;
	}

	/**
	 * Determines if the specified row is selected.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing the row
	 *
	 * @return a long integer where 1 represents TRUE, 0 otherwise
	 */
	static long atkTable_is_row_selected (long atkObject, long row) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.is_row_selected != 0) {
			parentResult = ATK.call (iface.is_row_selected, atkObject, row);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.isSelected = parentResult != 0;
				event.row = (int)row;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.isRowSelected(event);
				}
				return event.isSelected ? 1 : 0;
			}
		}
		return parentResult;
	}

	/**
	 * Determines if the AtkObject at the specified
	 * column and row is selected.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing the row
	 * @param column a long integer representing the column
	 *
	 * @return a long integer where 1 represents TRUE, 0 otherwise
	 */
	static long atkTable_is_selected (long atkObject, long row, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.is_selected != 0) {
			parentResult = ATK.call (iface.is_selected, atkObject, row, column);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.row = (int)row;
				event.column = (int)column;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.getCell(event);
				}
				Accessible result = event.accessible;
				if (result != null) {
					List<AccessibleTableCellListener> listeners2 = result.accessibleTableCellListeners;
					length = size(listeners2);
					if (length > 0) {
						AccessibleTableCellEvent cellEvent = new AccessibleTableCellEvent(result);
						cellEvent.isSelected = parentResult != 0;
						for (int i = 0; i < length; i++) {
							AccessibleTableCellListener listener = listeners2.get(i);
							listener.isSelected(cellEvent);
						}
						return cellEvent.isSelected ? 1 : 0;
					}
				}
			}
		}
		return parentResult;
	}

	/**
	 * Adds the specified row to the selection.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing the row
	 *
	 * @return a long int representing whether the action succeeded: 1 for success,
	 * 0 for failure
	 */
	static long atkTable_add_row_selection (long atkObject, long row) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.row = (int)row;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.selectRow(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.add_row_selection != 0) {
			parentResult = ATK.call (iface.add_row_selection, atkObject, row);
		}
		return parentResult;
	}

	/**
	 * Removes the specified row to the selection.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param row a long integer representing the row
	 *
	 * @return a long int representing whether the action succeeded: 1 for success,
	 * 0 for failure
	 */
	static long atkTable_remove_row_selection (long atkObject, long row) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.row = (int)row;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.deselectRow(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.remove_row_selection != 0) {
			parentResult = ATK.call (iface.remove_row_selection, atkObject, row);
		}
		return parentResult;
	}

	/**
	 * Adds the specified column to the selection.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param column a long integer representing the column
	 *
	 * @return a long int representing whether the action succeeded: 1 for success,
	 * 0 for failure
	 */
	static long atkTable_add_column_selection (long atkObject, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.column = (int)column;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.selectColumn(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.add_column_selection != 0) {
			parentResult = ATK.call (iface.add_column_selection, atkObject, column);
		}
		return parentResult;
	}

	/**
	 * Removes the specified column to the selection.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param column a long integer representing the column
	 *
	 * @return a long int representing whether the action succeeded: 1 for success,
	 * 0 for failure
	 */
	static long atkTable_remove_column_selection (long atkObject, long column) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTableListener> listeners = accessible.accessibleTableListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTableEvent event = new AccessibleTableEvent(accessible);
				event.column = (int)column;
				for (int i = 0; i < length; i++) {
					AccessibleTableListener listener = listeners.get(i);
					listener.deselectColumn(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkTableIface iface = getParentTableIface (atkObject);
		if (iface != null && iface.remove_column_selection != 0) {
			parentResult = ATK.call (iface.remove_column_selection, atkObject, column);
		}
		return parentResult;
	}

	/**
	 * Fills a Java AtkTextIface struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkTextIface Java object representing the interface struct of atkObject's
	 * parent
	 */
	static AtkTextIface getParentTextIface (long atkObject) {
		long type = OS.swt_fixed_accessible_get_type();
		if (OS.g_type_is_a (OS.g_type_parent (type), ATK.ATK_TYPE_TEXT())) {
			AtkTextIface iface = new AtkTextIface ();
			ATK.memmove (iface, OS.g_type_interface_peek_parent (ATK.ATK_TEXT_GET_IFACE (atkObject)));
			return iface;
		}
		return null;
	}

	static String getString (long strPtr) {
		int length = C.strlen (strPtr);
		byte [] buffer = new byte [length];
		C.memmove (buffer, strPtr, length);
		return new String (Converter.mbcsToWcs (buffer));
	}

	static long getStringPtr (String str) {
		byte [] buffer = Converter.wcsToMbcs(str != null ? str : "", true);
		long ptr = OS.g_malloc(buffer.length);
		C.memmove(ptr, buffer, buffer.length);
		return ptr;
	}

	/**
	 * Get the bounding box containing the glyph representing the character
	 * at a particular text offset.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param offset the offset of the text character for which bounding info
	 * is required
	 * @param x the pointer for the x coordinate of the bounding box
	 * @param y the pointer for the y coordinate of the bounding box
	 * @param width the pointer for the width of the bounding box
	 * @param height the pointer for the height of the bounding box
	 * @param coords long int representing the AtkCoordType for the coordinates
	 *
	 * @return a long int representation of 0 indicating that the method completed
	 * successfully
	 */
	static long atkText_get_character_extents (long atkObject, long offset,
			long x, long y, long width, long height, long coords) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.start = (int)offset;
				event.end = (int)(offset + 1);
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getTextBounds(event);
				}
				int[] topWindowX = new int [1], topWindowY = new int [1];
				if (coords == ATK.ATK_XY_WINDOW) {
					windowPoint (object, topWindowX, topWindowY);
					event.x -= topWindowX [0];
					event.y -= topWindowY [0];
				}
				C.memmove (x, new int[]{event.x}, 4);
				C.memmove (y, new int[]{event.y}, 4);
				C.memmove (width, new int[]{event.width}, 4);
				C.memmove (height, new int[]{event.height}, 4);
				return 0;
			}
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_character_extents != 0) {
			OS.call (iface.get_character_extents, atkObject, offset, x, y, width, height, coords);
		}
		return 0;
	}

	/**
	 * Get the bounding box for text within the specified range.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param start_offset the offset of the first text character for which
	 * bounding info is required
	 * @param end_offset the offset of the last text character after the last
	 * character for which boundary info is required
	 * @param coord_type long int representing the AtkCoordType for the coordinates
	 * @param rect a pointer to the AtkTextRectangle which is filled by this function
	 *
	 * @return a long int representation of 0 indicating that the method completed
	 * successfully
	 */
	static long atkText_get_range_extents (long atkObject, long start_offset,
			long end_offset, long coord_type, long rect) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.start = (int)start_offset;
				event.end = (int)end_offset;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getTextBounds(event);
				}
				int[] topWindowX = new int [1], topWindowY = new int [1];
				if (coord_type == ATK.ATK_XY_WINDOW) {
					windowPoint (object, topWindowX, topWindowY);
					event.x -= topWindowX [0];
					event.y -= topWindowY [0];
				}
				AtkTextRectangle atkRect = new AtkTextRectangle();
				atkRect.x = event.x;
				atkRect.y = event.y;
				atkRect.width = event.width;
				atkRect.height = event.height;
				ATK.memmove (rect, atkRect, AtkTextRectangle.sizeof);
				return 0;
			}
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_range_extents != 0) {
			ATK.call (iface.get_range_extents, atkObject, start_offset, end_offset, coord_type, rect);
		}
		return 0;
	}

	/**
	 * Creates an AtkAttributeSet which consists of the attributes explicitly
	 * set at the position offset in the text.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param offset the offset at which to get the attributes
	 * @param start_offset the address to put the start offset of the range
	 * @param end_offset the address to put the end offset of the range
	 *
	 * @return a pointer to the AtkAttributeSet created
	 */
	static long atkText_get_run_attributes (long atkObject, long offset,
			long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleAttributeListener> listeners = accessible.accessibleAttributeListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextAttributeEvent event = new AccessibleTextAttributeEvent(accessible);
				event.offset = (int)offset;
				for (int i = 0; i < length; i++) {
					AccessibleAttributeListener listener = listeners.get(i);
					listener.getTextAttributes(event);
				}
				C.memmove (start_offset, new int []{event.start}, 4);
				C.memmove (end_offset, new int []{event.end}, 4);
				TextStyle style = event.textStyle;
				long result = 0;
				AtkAttribute attr = new AtkAttribute();
				if (style != null) {
					if (style.rise != 0) {
						long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_RISE));
						attr.value = getStringPtr (String.valueOf(style.rise));
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);
					}
					if (style.underline) {
						long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_UNDERLINE));
						String str = "none"; //$NON-NLS-1$
						switch (style.underlineStyle) {
							case SWT.UNDERLINE_DOUBLE: str = "double"; break; //$NON-NLS-1$
							case SWT.UNDERLINE_SINGLE: str = "single"; break; //$NON-NLS-1$
							case SWT.UNDERLINE_ERROR: str = "error"; break; //$NON-NLS-1$
							case SWT.UNDERLINE_SQUIGGLE: str = "squiggle"; break; //$NON-NLS-1$
						}
						attr.value = getStringPtr (str);
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);
					}
					if (style.strikeout) {
						long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_STRIKETHROUGH));
						attr.value = getStringPtr ("1");
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);
					}
					Font font = style.font;
					if (font != null && !font.isDisposed()) {
						//TODO language and direction
						long attrPtr;
						attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_FAMILY_NAME));
						attr.value = OS.g_strdup (OS.pango_font_description_get_family (font.handle));
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);

						attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_SIZE));
						attr.value = getStringPtr (String.valueOf (OS.pango_font_description_get_size(font.handle) / OS.PANGO_SCALE));
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);

						attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_STYLE));
						attr.value = OS.g_strdup (ATK.atk_text_attribute_get_value(ATK.ATK_TEXT_ATTR_STYLE, OS.pango_font_description_get_style(font.handle)));
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);

						attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_VARIANT));
						attr.value = OS.g_strdup (ATK.atk_text_attribute_get_value(ATK.ATK_TEXT_ATTR_VARIANT, OS.pango_font_description_get_variant(font.handle)));
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);

						attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_STRETCH));
						attr.value = OS.g_strdup (ATK.atk_text_attribute_get_value(ATK.ATK_TEXT_ATTR_STRETCH, OS.pango_font_description_get_stretch(font.handle)));
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);

						attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_WEIGHT));
						attr.value = getStringPtr (String.valueOf (OS.pango_font_description_get_weight(font.handle)));
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);
					}
					Color color = style.foreground;
					if (color != null && !color.isDisposed()) {
						long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_FG_COLOR));
						attr.value = getStringPtr ((color.handle.red * 255) + "," + (color.handle.green * 255) + "," + (color.handle.blue * 255)); //$NON-NLS-1$ //$NON-NLS-2$
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);
					}
					color = style.background;
					if (color != null && !color.isDisposed()) {
						long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = OS.g_strdup (ATK.atk_text_attribute_get_name(ATK.ATK_TEXT_ATTR_BG_COLOR));
						attr.value = getStringPtr ((color.handle.red * 255) + "," + (color.handle.green * 255) + "," + (color.handle.blue * 255)); //$NON-NLS-1$ //$NON-NLS-2$
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);
					}
				}
				if (event.attributes != null) {
					int end = event.attributes.length / 2 * 2;
					for (int i = 0; i < end; i+= 2) {
						long attrPtr = OS.g_malloc(AtkAttribute.sizeof);
						attr.name = getStringPtr (event.attributes[i]);
						attr.value = getStringPtr (event.attributes[i + 1]);
						ATK.memmove(attrPtr, attr, AtkAttribute.sizeof);
						result = OS.g_slist_append(result, attrPtr);
					}
				}
				return result;
			}
		}
		long parentResult = 0;
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_run_attributes != 0) {
			parentResult = OS.call (iface.get_run_attributes, atkObject, offset, start_offset, end_offset);
		}
		return parentResult;
	}

	/**
	 * Gets the offset of the character located at coordinates x and y.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param x screen x-position of the character
	 * @param y screen y-position of the character
	 * @param coords long int representing the AtkCoordType for the coordinates
	 *
	 * @return the offset to the character which is located at the specified
	 * x and y coordinates
	 */
	static long atkText_get_offset_at_point (long atkObject, long x,
			long y, long coords) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.x = (int)x;
				event.y = (int)y;
				int[] topWindowX = new int [1], topWindowY = new int [1];
				if (coords == ATK.ATK_XY_WINDOW) {
					windowPoint (object, topWindowX, topWindowY);
					event.x += topWindowX [0];
					event.y += topWindowY [0];
				}
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getOffsetAtPoint(event);
				}
				return event.offset;
			}
		}
		long parentResult = 0;
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_offset_at_point != 0) {
			parentResult = OS.call (iface.get_offset_at_point, atkObject, x, y, coords);
		}
		return parentResult;
	}

	/**
	 * Adds a selection bounded by the specified offsets.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param start_offset the start position of the selected region
	 * @param end_offset the offset of the first character after the selected region
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkText_add_selection (long atkObject, long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.start = (int)start_offset;
				event.end = (int)end_offset;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.addSelection(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.add_selection != 0) {
			parentResult = ATK.call (iface.add_selection, atkObject, start_offset, end_offset);
		}
		return parentResult;
	}

	/**
	 * Removes the specified selection.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param selection_num the selection number.
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkText_remove_selection (long atkObject, long selection_num) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.index = (int)selection_num;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.removeSelection(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.remove_selection != 0) {
			parentResult = ATK.call (iface.remove_selection, atkObject, selection_num);
		}
		return parentResult;
	}

	/**
	 * Sets the caret (cursor) position to the specified offset.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param offset the position
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkText_set_caret_offset (long atkObject, long offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.offset = (int)offset;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.setCaretOffset(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.set_caret_offset != 0) {
			return ATK.call (iface.set_caret_offset, atkObject, offset);
		}
		return 0;
	}

	/**
	 * Changes the start and end offset of the specified selection.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param selection_num the selection number
	 * @param start_offset the new start position of the selection
	 * @param end_offset the new end position of the selection
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkText_set_selection (long atkObject, long selection_num,
			long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.index = (int)selection_num;
				event.start = (int)start_offset;
				event.end = (int)end_offset;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.setSelection(event);
				}
				return ACC.OK.equals(event.result) ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.set_selection != 0) {
			parentResult = OS.call (iface.set_selection, atkObject, selection_num, start_offset, end_offset);
		}
		return parentResult;
	}

	/**
	 * Gets the offset position of the caret (cursor).
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return a long int representation of the offset position of the caret (cursor)
	 */
	static long atkText_get_caret_offset (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_caret_offset != 0) {
			parentResult = ATK.call (iface.get_caret_offset, atkObject);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getCaretOffset (event);
				}
				return event.offset;
			}
			List<AccessibleTextListener> listeners2 = accessible.accessibleTextListeners;
			length = size(listeners2);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent (object.accessible);
				event.childID = object.id;
				event.offset = (int)parentResult;
				for (int i = 0; i < length; i++) {
					AccessibleTextListener listener = listeners2.get(i);
					listener.getCaretOffset (event);
				}
				return event.offset;
			}
		}
		return parentResult;
	}

	/**
	 * Get the ranges of text in the specified bounding box.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param rect the AtkTextRectangle giving the dimensions of the bounding box
	 * @param coord_type long int representing the AtkCoordType for the coordinates
	 * @param x_clip_type a long int representing the AtkTextClipType of the
	 * horizontal clip
	 * @param y_clip_type a long int representing the AtkTextClipType of the
	 * vertical clip
	 *
	 * @return a pointer to the array of AtkTextRanges
	 */
	static long atkText_get_bounded_ranges (long atkObject, long rect,
			long coord_type, long x_clip_type, long y_clip_type) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				AtkTextRectangle atkRect = new AtkTextRectangle();
				ATK.memmove (atkRect, rect, AtkTextRectangle.sizeof);
				event.x = atkRect.x;
				event.y = atkRect.y;
				event.width = atkRect.width;
				event.height = atkRect.height;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getRanges (event);
				}
				int [] ranges = event.ranges;
				int size = ranges == null ? 1 : ranges.length / 2;
				long result = C.malloc(size * AtkTextRange.sizeof);
				AtkTextRange range = new AtkTextRange();
				for (int j = 0, end = (ranges != null ? ranges.length / 2 : 1); j < end; j++) {
					if (ranges != null) {
						int index = j * 2;
						event.start = ranges[index];
						event.end = ranges[index+1];
					}
					event.count = 0;
					event.type = ACC.TEXT_BOUNDARY_ALL;
					for (int i = 0; i < length; i++) {
						AccessibleTextExtendedListener listener = listeners.get(i);
						listener.getText(event);
					}
					range.start_offset = event.start;
					range.end_offset = event.end;
					range.content = getStringPtr (event.result);
					event.result = null;
					event.count = event.type = event.x = event.y = event.width = event.height = 0;
					for (int i = 0; i < length; i++) {
						AccessibleTextExtendedListener listener = listeners.get(i);
						listener.getTextBounds(event);
					}
					range.bounds.x = event.x;
					range.bounds.y = event.y;
					range.bounds.width = event.width;
					range.bounds.height = event.height;
					ATK.memmove(result + j * AtkTextRange.sizeof, range, AtkTextRange.sizeof);
				}
				return result;
			}
		}
		long parentResult = 0;
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_bounded_ranges != 0) {
			parentResult = ATK.call (iface.get_bounded_ranges, atkObject);
		}
		return parentResult;
	}

	/**
	 * Gets the specified text.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param offset position
	 *
	 * @return a long int representing the character at offset
	 */
	static long atkText_get_character_at_offset (long atkObject, long offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.start = (int)offset;
				event.end = (int)(offset + 1);
				event.type = ACC.TEXT_BOUNDARY_CHAR;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getText(event);
				}
				String text = event.result;
				return text != null && text.length() > 0 ? text.charAt(0) : 0;
			}
			String text = object.getText ();
			if (text != null && text.length() > offset) return text.charAt ((int)offset);
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_character_at_offset != 0) {
			return ATK.call (iface.get_character_at_offset, atkObject, offset);
		}
		return 0;
	}

	/**
	 * Gets the character count.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return the number of characters
	 */
	static long atkText_get_character_count (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getCharacterCount(event);
				}
				return event.count;
			}
			String text = object.getText ();
			if (text != null) return text.length ();
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_character_count != 0) {
			return ATK.call (iface.get_character_count, atkObject);
		}
		return 0;
	}

	/**
	 * Gets the number of selected regions.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return the number of selected regions, or -1 if a failure occurred
	 */
	static long atkText_get_n_selections (long atkObject) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getSelectionCount (event);
				}
				return event.count;
			}
			List<AccessibleTextListener> listeners2 = accessible.accessibleTextListeners;
			length = size(listeners2);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent (object.accessible);
				event.childID = object.id;
				for (int i = 0; i < length; i++) {
					AccessibleTextListener listener = listeners2.get(i);
					listener.getSelectionRange (event);
				}
				if (event.length > 0) return 1;
			}
		}
		long parentResult = 0;
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_n_selections != 0) {
			parentResult = ATK.call (iface.get_n_selections, atkObject);
		}
		return parentResult;
	}

	/**
	 * Gets the text from the specified selection.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param selection_num the selection number
	 * @param start_offset passes back the start position of the selected region
	 * @param end_offset passes back the end position of the selected region
	 *
	 * @return a pointer to the newly allocated string containing the selected text
	 */
	static long atkText_get_selection (long atkObject, long selection_num,
			long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		long parentResult = 0;
		C.memmove (start_offset, new int[] {0}, 4);
		C.memmove (end_offset, new int[] {0}, 4);
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_selection != 0) {
			parentResult = OS.call (iface.get_selection, atkObject, selection_num, start_offset, end_offset);
		}
		if (object != null) {
			int[] parentStart = new int [1];
			int[] parentEnd = new int [1];
			C.memmove (parentStart, start_offset, 4);
			C.memmove (parentEnd, end_offset, 4);
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.index = (int)selection_num;
				event.start = parentStart[0];
				event.end = parentEnd[0];
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getSelection (event);
				}
				parentStart [0] = event.start;
				parentEnd [0] = event.end;
				C.memmove (start_offset, parentStart, 4);
				C.memmove (end_offset, parentEnd, 4);
				event.count = event.index = 0;
				event.type = ACC.TEXT_BOUNDARY_ALL;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getText(event);
				}
				if (parentResult != 0) OS.g_free(parentResult);
				return getStringPtr (event.result);
			}
			if (selection_num == 0) {
				List<AccessibleTextListener> listeners2 = accessible.accessibleTextListeners;
				length = size(listeners2);
				if (length > 0) {
					AccessibleTextEvent event = new AccessibleTextEvent (accessible);
					event.childID = object.id;
					event.offset = parentStart [0];
					event.length = parentEnd [0] - parentStart [0];
					for (int i = 0; i < length; i++) {
						AccessibleTextListener listener = listeners2.get(i);
						listener.getSelectionRange (event);
					}
					C.memmove (start_offset, new int[] {event.offset}, 4);
					C.memmove (end_offset, new int[] {event.offset + event.length}, 4);
					if (parentResult != 0) OS.g_free(parentResult);
					String text = object.getText();
					if (text != null && text.length () > event.offset && text.length() >= event.offset + event.length) {
						return getStringPtr (text.substring(event.offset, event.offset + event.length));
					}
					if (iface != null && iface.get_text != 0) {
						return ATK.call (iface.get_text, atkObject, event.offset, event.offset + event.length);
					}
					return 0;
				}
			}
		}
		return parentResult;
	}

	/**
	 * Gets the specified text.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param start_offset start position
	 * @param end_offset end position, or -1 for the end of the string
	 *
	 * @return a pointer to the newly allocated string containing the text
	 * from start_offset up to (but not including) end_offset
	 */
	static long atkText_get_text (long atkObject, long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.start = (int)start_offset;
				event.end = (int)end_offset;
				event.type = ACC.TEXT_BOUNDARY_ALL;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getText(event);
				}
				return getStringPtr (event.result);
			}
			String text = object.getText ();
			if (text != null && text.length () > 0) {
				if (end_offset == -1) {
					end_offset = text.length ();
				} else {
					end_offset = Math.min (end_offset, text.length ());
				}
				start_offset = Math.min (start_offset, end_offset);
				text = text.substring ((int)start_offset, (int)end_offset);
				return getStringPtr (text);
			}
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_text != 0) {
			return ATK.call (iface.get_text, atkObject, start_offset, end_offset);
		}
		return 0;
	}

	/**
	 * Gets the specified text.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param offset position
	 * @param boundary_type the AtkTextBoundary
	 * @param start_offset the start offset of the returned string
	 * @param end_offset the end_offset of the first character after the returned string
	 *
	 * @return a pointer to the newly allocated string containing
	 * the text after offset bounded by the specified boundary_type
	 */
	static long atkText_get_text_after_offset (long atkObject, long offset_value,
			long boundary_type, long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				long charCount = atkText_get_character_count (atkObject);
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.start = event.end = (int)offset_value;
				event.count = 1;
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_CHAR: event.type = ACC.TEXT_BOUNDARY_CHAR; break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_START: event.type = ACC.TEXT_BOUNDARY_WORD; break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_END: event.type = ACC.TEXT_BOUNDARY_WORD; break;
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
					case ATK.ATK_TEXT_BOUNDARY_LINE_START: event.type = ACC.TEXT_BOUNDARY_LINE; break;
					case ATK.ATK_TEXT_BOUNDARY_LINE_END: event.type = ACC.TEXT_BOUNDARY_LINE; break;
				}
				int eventStart = event.start;
				int eventEnd = event.end;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getText(event);
				}
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_WORD_START:
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START:
					case ATK.ATK_TEXT_BOUNDARY_LINE_START:
						if (event.end < charCount) {
							int start = event.start;
							event.start = eventStart;
							event.end = eventEnd;
							event.count = 2;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
							event.end = event.start;
							event.start = start;
							event.type = ACC.TEXT_BOUNDARY_ALL;
							event.count = 0;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
						}
						break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_END:
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END:
					case ATK.ATK_TEXT_BOUNDARY_LINE_END:
						if (0 < event.start) {
							int end = event.end;
							event.start = eventStart;
							event.end = eventEnd;
							event.count = 0;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
							event.start = event.end;
							event.end = end;
							event.type = ACC.TEXT_BOUNDARY_ALL;
							event.count = 0;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
						}
						break;
				}
				C.memmove (start_offset, new int[] {event.start}, 4);
				C.memmove (end_offset, new int[] {event.end}, 4);
				return getStringPtr (event.result);
			}
			int offset = (int)offset_value;
			String text = object.getText ();
			if (text != null && text.length () > 0) {
				length = text.length ();
				offset = Math.min (offset, length - 1);
				int startBounds = offset;
				int endBounds = offset;
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_CHAR: {
						if (length > offset) endBounds++;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_WORD_START: {
						int wordStart1 = nextIndexOfChar (text, " !?.\n", offset - 1);
						if (wordStart1 == -1) {
							startBounds = endBounds = length;
							break;
						}
						wordStart1 = nextIndexOfNotChar (text, " !?.\n", wordStart1);
						if (wordStart1 == length) {
							startBounds = endBounds = length;
							break;
						}
						startBounds = wordStart1;
						int wordStart2 = nextIndexOfChar (text, " !?.\n", wordStart1);
						if (wordStart2 == -1) {
							endBounds = length;
							break;
						}
						endBounds = nextIndexOfNotChar (text, " !?.\n", wordStart2);
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_WORD_END: {
						int previousWordEnd = previousIndexOfNotChar (text, " \n", offset);
						if (previousWordEnd == -1 || previousWordEnd != offset - 1) {
							offset = nextIndexOfNotChar (text, " \n", offset);
						}
						if (offset == -1) {
							startBounds = endBounds = length;
							break;
						}
						int wordEnd1 = nextIndexOfChar (text, " !?.\n", (int)offset);
						if (wordEnd1 == -1) {
							startBounds = endBounds = length;
							break;
						}
						wordEnd1 = nextIndexOfNotChar (text, "!?.", wordEnd1);
						if (wordEnd1 == length) {
							startBounds = endBounds = length;
							break;
						}
						startBounds = wordEnd1;
						int wordEnd2 = nextIndexOfNotChar (text, " \n", wordEnd1);
						if (wordEnd2 == length) {
							startBounds = endBounds = length;
							break;
						}
						wordEnd2 = nextIndexOfChar (text, " !?.\n", wordEnd2);
						if (wordEnd2 == -1) {
							endBounds = length;
							break;
						}
						endBounds = nextIndexOfNotChar (text, "!?.", wordEnd2);
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: {
						int previousSentenceEnd = previousIndexOfChar (text, "!?.", offset);
						int previousText = previousIndexOfNotChar (text, " !?.\n", offset);
						int sentenceStart1 = 0;
						if (previousSentenceEnd >= previousText) {
							sentenceStart1 = nextIndexOfNotChar (text, " !?.\n", offset);
						} else {
							sentenceStart1 = nextIndexOfChar (text, "!?.", offset);
							if (sentenceStart1 == -1) {
								startBounds = endBounds = length;
								break;
							}
							sentenceStart1 = nextIndexOfNotChar (text, " !?.\n", sentenceStart1);
						}
						if (sentenceStart1 == length) {
							startBounds = endBounds = length;
							break;
						}
						startBounds = sentenceStart1;
						int sentenceStart2 = nextIndexOfChar (text, "!?.", sentenceStart1);
						if (sentenceStart2 == -1) {
							endBounds = length;
							break;
						}
						endBounds = nextIndexOfNotChar (text, " !?.\n", sentenceStart2);
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: {
						int sentenceEnd1 = nextIndexOfChar (text, "!?.", offset);
						if (sentenceEnd1 == -1) {
							startBounds = endBounds = length;
							break;
						}
						sentenceEnd1 = nextIndexOfNotChar (text, "!?.", sentenceEnd1);
						if (sentenceEnd1 == length) {
							startBounds = endBounds = length;
							break;
						}
						startBounds = sentenceEnd1;
						int sentenceEnd2 = nextIndexOfNotChar (text, " \n", sentenceEnd1);
						if (sentenceEnd2 == length) {
							startBounds = endBounds = length;
							break;
						}
						sentenceEnd2 = nextIndexOfChar (text, "!?.", sentenceEnd2);
						if (sentenceEnd2 == -1) {
							endBounds = length;
							break;
						}
						endBounds = nextIndexOfNotChar (text, "!?.", sentenceEnd2);
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_LINE_START: {
						int lineStart1 = text.indexOf ('\n', offset - 1);
						if (lineStart1 == -1) {
							startBounds = endBounds = length;
							break;
						}
						lineStart1 = nextIndexOfNotChar (text, "\n", lineStart1);
						if (lineStart1 == length) {
							startBounds = endBounds = length;
							break;
						}
						startBounds = lineStart1;
						int lineStart2 = text.indexOf ('\n', lineStart1);
						if (lineStart2 == -1) {
							endBounds = length;
							break;
						}
						lineStart2 = nextIndexOfNotChar (text, "\n", lineStart2);
						endBounds = lineStart2;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_LINE_END: {
						int lineEnd1 = nextIndexOfChar (text, "\n", offset);
						if (lineEnd1 == -1) {
							startBounds = endBounds = length;
							break;
						}
						startBounds = lineEnd1;
						if (startBounds == length) {
							endBounds = length;
							break;
						}
						int lineEnd2 = nextIndexOfChar (text, "\n", lineEnd1 + 1);
						if (lineEnd2 == -1) {
							endBounds = length;
							break;
						}
						endBounds = lineEnd2;
						break;
					}
				}
				C.memmove (start_offset, new int[] {startBounds}, 4);
				C.memmove (end_offset, new int[] {endBounds}, 4);
				text = text.substring (startBounds, endBounds);
				return getStringPtr (text);
			}
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_text_after_offset != 0) {
			return ATK.call (iface.get_text_after_offset, atkObject, offset_value, boundary_type, start_offset, end_offset);
		}
		return 0;
	}

	/**
	 * Gets the specified text.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param offset position
	 * @param boundary_type the AtkTextBoundary
	 * @param start_offset the start offset of the returned string
	 * @param end_offset the end_offset of the first character after the returned string
	 *
	 * @return a pointer to the newly allocated string containing the
	 * text at offset bounded by the specified boundary_type
	 */
	static long atkText_get_text_at_offset (long atkObject, long offset_value,
			long boundary_type, long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				long charCount = atkText_get_character_count (atkObject);
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.start = event.end = (int)offset_value;
				event.count = 0;
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_CHAR: event.type = ACC.TEXT_BOUNDARY_CHAR; break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_START: event.type = ACC.TEXT_BOUNDARY_WORD; break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_END: event.type = ACC.TEXT_BOUNDARY_WORD; break;
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
					case ATK.ATK_TEXT_BOUNDARY_LINE_START: event.type = ACC.TEXT_BOUNDARY_LINE; break;
					case ATK.ATK_TEXT_BOUNDARY_LINE_END: event.type = ACC.TEXT_BOUNDARY_LINE; break;
				}
				int eventStart = event.start;
				int eventEnd = event.end;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getText(event);
				}
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_WORD_START:
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START:
					case ATK.ATK_TEXT_BOUNDARY_LINE_START:
						if (event.end < charCount) {
							int start = event.start;
							event.start = eventStart;
							event.end = eventEnd;
							event.count = 1;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
							event.end = event.start;
							event.start = start;
							event.type = ACC.TEXT_BOUNDARY_ALL;
							event.count = 0;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
						}
						break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_END:
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END:
					case ATK.ATK_TEXT_BOUNDARY_LINE_END:
						if (0 < event.start) {
							int end = event.end;
							event.start = eventStart;
							event.end = eventEnd;
							event.count = -1;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
							event.start = event.end;
							event.end = end;
							event.type = ACC.TEXT_BOUNDARY_ALL;
							event.count = 0;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
						}
						break;
				}
				C.memmove (start_offset, new int[] {event.start}, 4);
				C.memmove (end_offset, new int[] {event.end}, 4);
				return getStringPtr (event.result);
			}
			int offset = (int)offset_value;
			String text = object.getText ();
			if (text != null && text.length () > 0) {
				length = text.length ();
				offset = Math.min (offset, length - 1);
				int startBounds = offset;
				int endBounds = offset;
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_CHAR: {
						if (length > offset) endBounds++;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_WORD_START: {
						int wordStart1 = previousIndexOfNotChar (text, " !?.\n", offset);
						if (wordStart1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						wordStart1 = previousIndexOfChar (text, " !?.\n", wordStart1) + 1;
						if (wordStart1 == -1) {
							startBounds = 0;
							break;
						}
						startBounds = wordStart1;
						int wordStart2 = nextIndexOfChar (text, " !?.\n", wordStart1);
						endBounds = nextIndexOfNotChar (text, " !?.\n", wordStart2);
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_WORD_END: {
						int wordEnd1 = previousIndexOfNotChar (text, "!?.", offset + 1);
						wordEnd1 = previousIndexOfChar (text, " !?.\n", wordEnd1);
						wordEnd1 = previousIndexOfNotChar (text, " \n", wordEnd1 + 1);
						if (wordEnd1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						startBounds = wordEnd1 + 1;
						int wordEnd2 = nextIndexOfNotChar (text, " \n", startBounds);
						if (wordEnd2 == length) {
							endBounds = startBounds;
							break;
						}
						wordEnd2 = nextIndexOfChar (text, " !?.\n", wordEnd2);
						if (wordEnd2 == -1) {
							endBounds = startBounds;
							break;
						}
						endBounds = nextIndexOfNotChar (text, "!?.", wordEnd2);
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: {
						int sentenceStart1 = previousIndexOfNotChar (text, " !?.\n", offset + 1);
						if (sentenceStart1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						sentenceStart1 = previousIndexOfChar (text, "!?.", sentenceStart1) + 1;
						startBounds = nextIndexOfNotChar (text, " \n", sentenceStart1);
						int sentenceStart2 = nextIndexOfChar (text, "!?.", startBounds);
						endBounds = nextIndexOfNotChar (text, " !?.\n", sentenceStart2);
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: {
						int sentenceEnd1 = previousIndexOfNotChar (text, "!?.", offset + 1);
						sentenceEnd1 = previousIndexOfChar (text, "!?.", sentenceEnd1);
						sentenceEnd1 = previousIndexOfNotChar (text, " \n", sentenceEnd1 + 1);
						if (sentenceEnd1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						startBounds = sentenceEnd1 + 1;
						int sentenceEnd2 = nextIndexOfNotChar (text, " \n", startBounds);
						if (sentenceEnd2 == length) {
							endBounds = startBounds;
							break;
						}
						sentenceEnd2 = nextIndexOfChar (text, "!?.", sentenceEnd2);
						if (sentenceEnd2 == -1) {
							endBounds = startBounds;
							break;
						}
						endBounds = nextIndexOfNotChar (text, "!?.", sentenceEnd2);
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_LINE_START: {
						startBounds = previousIndexOfChar (text, "\n", offset) + 1;
						int lineEnd2 = nextIndexOfChar (text, "\n", startBounds);
						if (lineEnd2 < length) lineEnd2++;
						endBounds = lineEnd2;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_LINE_END: {
						int lineEnd1 = previousIndexOfChar (text, "\n", offset);
						if (lineEnd1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						startBounds = lineEnd1;
						endBounds = nextIndexOfChar (text, "\n", lineEnd1 + 1);
					}
				}
				C.memmove (start_offset, new int[] {startBounds}, 4);
				C.memmove (end_offset, new int[] {endBounds}, 4);
				text = text.substring (startBounds, endBounds);
				return getStringPtr (text);
			}
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_text_at_offset != 0) {
			return ATK.call (iface.get_text_at_offset, atkObject, offset_value, boundary_type, start_offset, end_offset);
		}
		return 0;
	}

	/**
	 * Gets the specified text.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param offset position
	 * @param boundary_type the AtkTextBoundary
	 * @param start_offset the start offset of the returned string
	 * @param end_offset the end_offset of the first character after the returned string
	 *
	 * @return a pointer to the newly allocated string containing
	 * the text before offset bounded by the specified boundary_type
	 */
	static long atkText_get_text_before_offset (long atkObject, long offset_value,
			long boundary_type, long start_offset, long end_offset) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
			int length = size(listeners);
			if (length > 0) {
				long charCount = atkText_get_character_count (atkObject);
				AccessibleTextEvent event = new AccessibleTextEvent(accessible);
				event.start = event.end = (int)offset_value;
				event.count = -1;
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_CHAR: event.type = ACC.TEXT_BOUNDARY_CHAR; break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_START: event.type = ACC.TEXT_BOUNDARY_WORD; break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_END: event.type = ACC.TEXT_BOUNDARY_WORD; break;
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: event.type = ACC.TEXT_BOUNDARY_SENTENCE; break;
					case ATK.ATK_TEXT_BOUNDARY_LINE_START: event.type = ACC.TEXT_BOUNDARY_LINE; break;
					case ATK.ATK_TEXT_BOUNDARY_LINE_END: event.type = ACC.TEXT_BOUNDARY_LINE; break;
				}
				int eventStart = event.start;
				int eventEnd = event.end;
				for (int i = 0; i < length; i++) {
					AccessibleTextExtendedListener listener = listeners.get(i);
					listener.getText(event);
				}
				C.memmove (start_offset, new int[] {event.start}, 4);
				C.memmove (end_offset, new int[] {event.end}, 4);
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_WORD_START:
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START:
					case ATK.ATK_TEXT_BOUNDARY_LINE_START:
						if (event.end < charCount) {
							int start = event.start;
							event.start = eventStart;
							event.end = eventEnd;
							event.count = 0;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
							event.end = event.start;
							event.start = start;
							event.type = ACC.TEXT_BOUNDARY_ALL;
							event.count = 0;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
						}
						break;
					case ATK.ATK_TEXT_BOUNDARY_WORD_END:
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END:
					case ATK.ATK_TEXT_BOUNDARY_LINE_END:
						if (0 < event.start) {
							int end = event.end;
							event.start = eventStart;
							event.end = eventEnd;
							event.count = -2;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
							event.start = event.end;
							event.end = end;
							event.type = ACC.TEXT_BOUNDARY_ALL;
							event.count = 0;
							for (int i = 0; i < length; i++) {
								AccessibleTextExtendedListener listener = listeners.get(i);
								listener.getText(event);
							}
						}
						break;
				}
				return getStringPtr (event.result);
			}
			int offset = (int)offset_value;
			String text = object.getText ();
			if (text != null && text.length () > 0) {
				length = text.length ();
				offset = Math.min (offset, length - 1);
				int startBounds = offset;
				int endBounds = offset;
				switch ((int)boundary_type) {
					case ATK.ATK_TEXT_BOUNDARY_CHAR: {
						if (length >= offset && offset > 0) startBounds--;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_WORD_START: {
						int wordStart1 = previousIndexOfChar (text, " !?.\n", offset - 1);
						if (wordStart1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						int wordStart2 = previousIndexOfNotChar (text, " !?.\n", wordStart1);
						if (wordStart2 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						endBounds = wordStart1 + 1;
						startBounds = previousIndexOfChar (text, " !?.\n", wordStart2) + 1;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_WORD_END: {
						int wordEnd1 =previousIndexOfChar (text, " !?.\n", offset);
						if (wordEnd1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						wordEnd1 = previousIndexOfNotChar (text, " \n", wordEnd1 + 1);
						if (wordEnd1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						endBounds = wordEnd1 + 1;
						int wordEnd2 = previousIndexOfNotChar (text, " !?.\n", endBounds);
						wordEnd2 = previousIndexOfChar (text, " !?.\n", wordEnd2);
						if (wordEnd2 == -1) {
							startBounds = 0;
							break;
						}
						startBounds = previousIndexOfNotChar (text, " \n", wordEnd2 + 1) + 1;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_START: {
						int sentenceStart1 = previousIndexOfChar (text, "!?.", offset);
						if (sentenceStart1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						int sentenceStart2 = previousIndexOfNotChar (text, "!?.", sentenceStart1);
						if (sentenceStart2 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						endBounds = sentenceStart1 + 1;
						startBounds = previousIndexOfChar (text, "!?.", sentenceStart2) + 1;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_SENTENCE_END: {
						int sentenceEnd1 = previousIndexOfChar (text, "!?.", offset);
						if (sentenceEnd1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						sentenceEnd1 = previousIndexOfNotChar (text, " \n", sentenceEnd1 + 1);
						if (sentenceEnd1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						endBounds = sentenceEnd1 + 1;
						int sentenceEnd2 = previousIndexOfNotChar (text, "!?.", endBounds);
						sentenceEnd2 = previousIndexOfChar (text, "!?.", sentenceEnd2);
						if (sentenceEnd2 == -1) {
							startBounds = 0;
							break;
						}
						startBounds = previousIndexOfNotChar (text, " \n", sentenceEnd2 + 1) + 1;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_LINE_START: {
						int lineStart1 = previousIndexOfChar (text, "\n", offset);
						if (lineStart1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						endBounds = lineStart1 + 1;
						startBounds = previousIndexOfChar (text, "\n", lineStart1) + 1;
						break;
					}
					case ATK.ATK_TEXT_BOUNDARY_LINE_END: {
						int lineEnd1 = previousIndexOfChar (text, "\n", offset);
						if (lineEnd1 == -1) {
							startBounds = endBounds = 0;
							break;
						}
						endBounds = lineEnd1;
						startBounds = previousIndexOfChar (text, "\n", lineEnd1);
						if (startBounds == -1) startBounds = 0;
						break;
					}
				}
				C.memmove (start_offset, new int[] {startBounds}, 4);
				C.memmove (end_offset, new int[] {endBounds}, 4);
				text = text.substring (startBounds, endBounds);
				return getStringPtr (text);
			}
		}
		AtkTextIface iface = getParentTextIface (atkObject);
		if (iface != null && iface.get_text_before_offset != 0) {
			return ATK.call (iface.get_text_before_offset, atkObject, offset_value, boundary_type, start_offset, end_offset);
		}
		return 0;
	}

	static void setGValue (long value, Number number) {
		if (number == null) return;
		if (OS.G_VALUE_TYPE(value) != 0) OS.g_value_unset(value);
		if (number instanceof Double) {
			OS.g_value_init(value, OS.G_TYPE_DOUBLE());
			OS.g_value_set_double(value, number.doubleValue());
		} else if (number instanceof Float) {
			OS.g_value_init(value, OS.G_TYPE_FLOAT());
			OS.g_value_set_float(value, number.floatValue());
		} else if (number instanceof Long) {
			OS.g_value_init(value, OS.G_TYPE_INT64());
			OS.g_value_set_int64(value, number.longValue());
		} else {
			OS.g_value_init(value, OS.G_TYPE_INT());
			OS.g_value_set_int(value, number.intValue());
		}
	}

	static Number getGValue (long value) {
		long type = OS.G_VALUE_TYPE(value);
		if (type == 0) return null;
		if (type == OS.G_TYPE_DOUBLE()) return Double.valueOf(OS.g_value_get_double(value));
		if (type == OS.G_TYPE_FLOAT()) return Float.valueOf(OS.g_value_get_float(value));
		if (type == OS.G_TYPE_INT64()) return Long.valueOf(OS.g_value_get_int64(value));
		return Integer.valueOf(OS.g_value_get_int(value));
	}

	/**
	 * Fills a Java AtkValueIface struct with that of the parent class.
	 * This is a Java implementation of what is referred to in GObject as "chaining up".
	 * See: https://developer.gnome.org/gobject/stable/howto-gobject-chainup.html
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AtkValueIface Java object representing the interface struct of atkObject's
	 * parent
	 */
	static AtkValueIface getParentValueIface (long atkObject) {
		long type = OS.swt_fixed_accessible_get_type();
		if (OS.g_type_is_a (OS.g_type_parent (type), ATK.ATK_TYPE_VALUE())) {
			AtkValueIface iface = new AtkValueIface ();
			ATK.memmove (iface, OS.g_type_interface_peek_parent (ATK.ATK_VALUE_GET_IFACE (atkObject)));
			return iface;
		}
		return null;
	}

	/**
	 * Gets the value of this atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param value a pointer to a GValue that represents the current value
	 *
	 * @return 0, this is a void function -- the value is stored in the parameter
	 */
	static long atkValue_get_current_value (long atkObject, long value) {
		AccessibleObject object = getAccessibleObject (atkObject);
		AtkValueIface iface = getParentValueIface (atkObject);
		if (iface != null && iface.get_current_value != 0) {
			ATK.call (iface.get_current_value, atkObject, value);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleValueListener> listeners = accessible.accessibleValueListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleValueEvent event = new AccessibleValueEvent(accessible);
				event.value = getGValue(value);
				for (int i = 0; i < length; i++) {
					AccessibleValueListener listener = listeners.get(i);
					listener.getCurrentValue(event);
				}
				setGValue(value, event.value);
			}
		}
		return 0;
	}

	/**
	 * Gets the maximum value of this atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param value a pointer to a GValue that represents the current maximum
	 * value
	 *
	 * @return 0, this is a void function -- the value is stored in the parameter
	 */
	static long atkValue_get_maximum_value (long atkObject, long value) {
		AccessibleObject object = getAccessibleObject (atkObject);
		AtkValueIface iface = getParentValueIface (atkObject);
		if (iface != null && iface.get_maximum_value != 0) {
			ATK.call (iface.get_maximum_value, atkObject, value);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleValueListener> listeners = accessible.accessibleValueListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleValueEvent event = new AccessibleValueEvent(accessible);
				event.value = getGValue(value);
				for (int i = 0; i < length; i++) {
					AccessibleValueListener listener = listeners.get(i);
					listener.getMaximumValue(event);
				}
				setGValue(value, event.value);
			}
		}
		return 0;
	}

	/**
	 * Gets the minimum value of this atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param value a pointer to a GValue that represents the current minimum
	 * value
	 *
	 * @return 0, this is a void function -- the value is stored in the parameter
	 */
	static long atkValue_get_minimum_value (long atkObject, long value) {
		AccessibleObject object = getAccessibleObject (atkObject);
		AtkValueIface iface = getParentValueIface (atkObject);
		if (iface != null && iface.get_minimum_value != 0) {
			ATK.call (iface.get_minimum_value, atkObject, value);
		}
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleValueListener> listeners = accessible.accessibleValueListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleValueEvent event = new AccessibleValueEvent(accessible);
				event.value = getGValue(value);
				for (int i = 0; i < length; i++) {
					AccessibleValueListener listener = listeners.get(i);
					listener.getMinimumValue(event);
				}
				setGValue(value, event.value);
			}
		}
		return 0;
	}

	/**
	 * Sets the value of this atkObject.
	 *
	 * This is the implementation of an ATK function which
	 * queries the Accessible listeners at the Java level. On GTK3 the ATK
	 * interfaces are implemented in os_custom.c and access this method via
	 * JNI.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 * @param value a pointer to the new GValue to be set
	 *
	 * @return a long int representation of 1 for success, 0 otherwise
	 */
	static long atkValue_set_current_value (long atkObject, long value) {
		AccessibleObject object = getAccessibleObject (atkObject);
		if (object != null) {
			Accessible accessible = object.accessible;
			List<AccessibleValueListener> listeners = accessible.accessibleValueListeners;
			int length = size(listeners);
			if (length > 0) {
				AccessibleValueEvent event = new AccessibleValueEvent(accessible);
				event.value = getGValue(value);
				for (int i = 0; i < length; i++) {
					AccessibleValueListener listener = listeners.get(i);
					listener.setCurrentValue(event);
				}
				return event.value != null ? 1 : 0;
			}
		}
		long parentResult = 0;
		AtkValueIface iface = getParentValueIface (atkObject);
		if (iface != null && iface.set_current_value != 0) {
			parentResult = ATK.call (iface.set_current_value, atkObject, value);
		}
		return parentResult;
	}

	/**
	 * Gets the AccessibleObject associated with the AtkObject handle
	 * from the Java map of AccessibleObjects.
	 *
	 * @param atkObject a pointer to the current AtkObject
	 *
	 * @return an AccessibleObject associated with the provided AtkObject pointer
	 */
	static AccessibleObject getAccessibleObject (long atkObject) {
		AccessibleObject object = AccessibleObjects.get (new LONG (atkObject));
		if (object == null) return null;
		if (object.accessible == null) return null;
		Control control = object.accessible.control;
		if (control == null || control.isDisposed()) return null;
		return object;
	}

	AccessibleObject getChildByID (int childId) {
		if (childId == ACC.CHILDID_SELF) return this;
		if (childId == ACC.CHILDID_NONE || childId == ACC.CHILDID_MULTIPLE) return null;
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				AccessibleObject child = children[i];
				if (child != null && child.id == childId) return child;
			}
		}
		return null;
	}

	AccessibleObject getChildByIndex (int childIndex) {
		if (children != null && childIndex < children.length) return children [childIndex];
		return null;
	}

	String getText () {
		List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
		int length = size(listeners);
		if (length > 0) {
			String parentText = "";	//$NON-NLS-1$
			AtkTextIface iface = getParentTextIface (atkHandle);
			if (iface != null && iface.get_character_count != 0) {
				long characterCount = ATK.call (iface.get_character_count, atkHandle);
				if (characterCount > 0 && iface.get_text != 0) {
					long parentResult = ATK.call (iface.get_text, atkHandle, 0, characterCount);
					if (parentResult != 0) {
						parentText = getString (parentResult);
						OS.g_free(parentResult);
					}
				}
			}
			AccessibleControlEvent event = new AccessibleControlEvent (accessible);
			event.childID = id;
			event.result = parentText;
			for (int i = 0; i < length; i++) {
				AccessibleControlListener listener = listeners.get (i);
				listener.getValue (event);
			}
			return event.result;
		}
		return null;
	}

	static long gObjectClass_finalize (long atkObject) {
		/*
		 * GObject destruction is handled in os_custom.c in GTK3.
		 * AccessibleObject has to be removed from the map of AccessibleObjects, though.
		 */
		AccessibleObject object = AccessibleObjects.get (new LONG (atkObject));
		if (object != null) {
			AccessibleObjects.remove (new LONG (atkObject));
		}
		return 0;
	}

	static int toATKRelation (int relation) {
		switch (relation) {
			case ACC.RELATION_CONTROLLED_BY: return ATK.ATK_RELATION_CONTROLLED_BY;
			case ACC.RELATION_CONTROLLER_FOR: return ATK.ATK_RELATION_CONTROLLER_FOR;
			case ACC.RELATION_DESCRIBED_BY: return ATK.ATK_RELATION_DESCRIBED_BY;
			case ACC.RELATION_DESCRIPTION_FOR: return ATK.ATK_RELATION_DESCRIPTION_FOR;
			case ACC.RELATION_EMBEDDED_BY: return ATK.ATK_RELATION_EMBEDDED_BY;
			case ACC.RELATION_EMBEDS: return ATK.ATK_RELATION_EMBEDS;
			case ACC.RELATION_FLOWS_FROM: return ATK.ATK_RELATION_FLOWS_FROM;
			case ACC.RELATION_FLOWS_TO: return ATK.ATK_RELATION_FLOWS_TO;
			case ACC.RELATION_LABEL_FOR: return ATK.ATK_RELATION_LABEL_FOR;
			case ACC.RELATION_LABELLED_BY: return ATK.ATK_RELATION_LABELLED_BY;
			case ACC.RELATION_MEMBER_OF: return ATK.ATK_RELATION_MEMBER_OF;
			case ACC.RELATION_NODE_CHILD_OF: return ATK.ATK_RELATION_NODE_CHILD_OF;
			case ACC.RELATION_PARENT_WINDOW_OF: return ATK.ATK_RELATION_PARENT_WINDOW_OF;
			case ACC.RELATION_POPUP_FOR: return ATK.ATK_RELATION_POPUP_FOR;
			case ACC.RELATION_SUBWINDOW_OF: return ATK.ATK_RELATION_SUBWINDOW_OF;
		}
		return 0;
	}

	/**
	 * Static toDisplay implementation for accessibility purposes. This function
	 * is called from os_custom.c via JNI.
	 *
	 * @param gdkResource the GdkWindow (GTK3) or GdkSurface (GTK4)
	 * @param x a pointer to an integer which represents the x coordinate
	 * @param y a pointer to an integer which represents the y coordinate
	 * @return 0
	 */
	static long toDisplay (long gdkResource, long x, long y) {
		int [] origin_x = new int [1], origin_y = new int [1];
		if (gdkResource == 0) {
			// memmove anyways to prevent garbage data in the pointers
			C.memmove (x, new int[] {0}, 4);
			C.memmove (y, new int[] {0}, 4);
			return 0;
		}
		if (GTK.GTK4) {
			//TODO: GTK4 no gdk_surface_get_origin
		} else {
			GDK.gdk_window_get_origin (gdkResource, origin_x, origin_y);
		}
		C.memmove (x, new int[] {origin_x [0]}, 4);
		C.memmove (y, new int[] {origin_y [0]}, 4);
		return 0;
	}

	static void windowPoint (AccessibleObject object, int [] x, int [] y) {
		long widget = GTK3.gtk_accessible_get_widget(object.atkHandle);
		while (widget == 0 && object.parent != null) {
			object = object.parent;
			widget = GTK3.gtk_accessible_get_widget(object.atkHandle);
		}
		if (widget == 0) return;

		if (GTK.GTK4) {
			//TODO: GTK4 no gdk_surface_get_origin
		} else {
			long topLevel = GTK3.gtk_widget_get_toplevel (widget);
			long window = GTK3.gtk_widget_get_window (topLevel);
			GDK.gdk_window_get_origin (window, x, y);
		}
	}

	static int nextIndexOfChar (String string, String searchChars, int startIndex) {
		int result = string.length ();
		for (int i = 0; i < searchChars.length (); i++) {
			char current = searchChars.charAt (i);
			int index = string.indexOf (current, startIndex);
			if (index != -1) result = Math.min (result, index);
		}
		return result;
	}

	static int nextIndexOfNotChar (String string, String searchChars, int startIndex) {
		int length = string.length ();
		int index = startIndex;
		while (index < length) {
			char current = string.charAt (index);
			if (searchChars.indexOf (current) == -1) break;
			index++;
		}
		return index;
	}

	static int previousIndexOfChar (String string, String searchChars, int startIndex) {
		int result = -1;
		if (startIndex < 0) return result;
		string = string.substring (0, startIndex);
		for (int i = 0; i < searchChars.length (); i++) {
			char current = searchChars.charAt (i);
			int index = string.lastIndexOf (current);
			if (index != -1) result = Math.max (result, index);
		}
		return result;
	}

	static int previousIndexOfNotChar (String string, String searchChars, int startIndex) {
		if (startIndex < 0) return -1;
		int index = startIndex - 1;
		while (index >= 0) {
			char current = string.charAt (index);
			if (searchChars.indexOf (current) == -1) break;
			index--;
		}
		return index;
	}

	void addRelation (int type, Accessible target) {
		if(GTK.GTK4) return; //TODO investigate proper way for GTK 4.x
		AccessibleObject targetAccessibleObject = target.getAccessibleObject();
		if (targetAccessibleObject != null) {
			/*
			 * FIXME: Workaround for https://bugs.eclipse.org/312451
			 *
			 * This null check is conceptually wrong and will probably cause
			 * inconsistent behavior, but since we don't know in what
			 * circumstances the target doesn't have an accessibleObject, that's
			 * the best way we know to avoid throwing an NPE.
			 */
			ATK.atk_object_add_relationship(atkHandle, toATKRelation(type), targetAccessibleObject.atkHandle);
		}
	}

	void release () {
		accessible = null;
		/*
		 * GObject destruction is implemented in os_custom.c for GTK3:
		 * only unref lightweight widgets and children.
		 */
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				AccessibleObject child = children [i];
				if (child != null) OS.g_object_unref(child.atkHandle);
			}
			children = null;
		}
		if (isLightweight) {
			OS.g_object_unref(atkHandle);
		}
	}

	void removeRelation (int type, Accessible target) {
		if(GTK.GTK4) return; //TODO investigate proper way for GTK 4.x
		AccessibleObject targetAccessibleObject = target.getAccessibleObject();
		if (targetAccessibleObject != null) {
			/*
			 * FIXME: Workaround for https://bugs.eclipse.org/312451
			 *
			 * This null check is conceptually wrong and will probably cause
			 * inconsistent behavior, but since we don't know in what
			 * circumstances the target doesn't have an accessibleObject, that's
			 * the best way we know to avoid throwing an NPE.
			 */
			ATK.atk_object_remove_relationship (atkHandle, toATKRelation(type), targetAccessibleObject.atkHandle);
		}
	}

	void selectionChanged () {
		if(GTK.GTK4) return; //TODO investigate proper way for GTK 4.x
		OS.g_signal_emit_by_name (atkHandle, ATK.selection_changed);
	}

	void sendEvent(int event, Object eventData) {
		if(GTK.GTK4) return; //TODO reenable for GTK 4.x
		switch (event) {
			case ACC.EVENT_SELECTION_CHANGED:
				OS.g_signal_emit_by_name (atkHandle, ATK.selection_changed);
				break;
			case ACC.EVENT_TEXT_SELECTION_CHANGED:
				OS.g_signal_emit_by_name (atkHandle, ATK.text_selection_changed);
				break;
			case ACC.EVENT_STATE_CHANGED: {
				if (!(eventData instanceof int[])) break;
				int[] array = (int[])eventData;
				int state =  array[0];
				int value = array[1];
				int atkState = -1;
				switch (state) {
					case ACC.STATE_SELECTED: atkState = ATK.ATK_STATE_SELECTED; break;
					case ACC.STATE_SELECTABLE: atkState = ATK.ATK_STATE_SELECTABLE; break;
					case ACC.STATE_MULTISELECTABLE: atkState = ATK.ATK_STATE_MULTISELECTABLE; break;
					case ACC.STATE_FOCUSED: atkState = ATK.ATK_STATE_FOCUSED; break;
					case ACC.STATE_FOCUSABLE: atkState = ATK.ATK_STATE_FOCUSABLE; break;
					case ACC.STATE_PRESSED: atkState = ATK.ATK_STATE_PRESSED; break;
					case ACC.STATE_CHECKED: atkState = ATK.ATK_STATE_CHECKED; break;
					case ACC.STATE_EXPANDED: atkState = ATK.ATK_STATE_EXPANDED; break;
					case ACC.STATE_COLLAPSED: atkState = ATK.ATK_STATE_EXPANDED; break;
					case ACC.STATE_HOTTRACKED: atkState = ATK.ATK_STATE_ARMED; break;
					case ACC.STATE_BUSY: atkState = ATK.ATK_STATE_BUSY; break;
					case ACC.STATE_READONLY: atkState = ATK.ATK_STATE_EDITABLE; break;
					case ACC.STATE_INVISIBLE: atkState = ATK.ATK_STATE_VISIBLE; break;
					case ACC.STATE_OFFSCREEN: atkState = ATK.ATK_STATE_SHOWING; break;
					case ACC.STATE_SIZEABLE: atkState = ATK.ATK_STATE_RESIZABLE; break;
					case ACC.STATE_LINKED: break;
					case ACC.STATE_DISABLED: atkState = ATK.ATK_STATE_ENABLED; break;
					case ACC.STATE_ACTIVE: atkState = ATK.ATK_STATE_ACTIVE; break;
					case ACC.STATE_SINGLELINE: atkState = ATK.ATK_STATE_SINGLE_LINE; break;
					case ACC.STATE_MULTILINE: atkState = ATK.ATK_STATE_MULTI_LINE; break;
					case ACC.STATE_REQUIRED: atkState = ATK.ATK_STATE_REQUIRED; break;
					case ACC.STATE_INVALID_ENTRY: atkState = ATK.ATK_STATE_INVALID_ENTRY; break;
					case ACC.STATE_SUPPORTS_AUTOCOMPLETION: atkState = ATK.ATK_STATE_SUPPORTS_AUTOCOMPLETION; break;
				}
				if (atkState == -1) break;
				ATK.atk_object_notify_state_change(atkHandle, atkState, value != 0);
				break;
			}
			case ACC.EVENT_LOCATION_CHANGED: {
				List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
				int length = size(listeners);
				GdkRectangle rect = new GdkRectangle();
				if (length > 0) {
					AccessibleControlEvent e = new AccessibleControlEvent (accessible);
					e.childID = id;
					for (int i = 0; i < length; i++) {
						AccessibleControlListener listener = listeners.get (i);
						listener.getLocation (e);
					}
					rect.x = e.x;
					rect.y = e.y;
					rect.width = e.width;
					rect.height = e.height;
				}
				OS.g_signal_emit_by_name (atkHandle, ATK.bounds_changed, rect);
				break;
			}
			case ACC.EVENT_NAME_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_name);
				break;
			case ACC.EVENT_DESCRIPTION_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_description);
				break;
			case ACC.EVENT_VALUE_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_value);
				break;
			case ACC.EVENT_DOCUMENT_LOAD_COMPLETE:
				OS.g_signal_emit_by_name (atkHandle, ATK.load_complete);
				break;
			case ACC.EVENT_DOCUMENT_LOAD_STOPPED:
				OS.g_signal_emit_by_name (atkHandle, ATK.load_stopped);
				break;
			case ACC.EVENT_DOCUMENT_RELOAD:
				OS.g_signal_emit_by_name (atkHandle, ATK.reload);
				break;
			case ACC.EVENT_PAGE_CHANGED:
				break;
			case ACC.EVENT_SECTION_CHANGED:
				break;
			case ACC.EVENT_ACTION_CHANGED:
				break;
			case ACC.EVENT_HYPERLINK_END_INDEX_CHANGED:
				OS.g_object_notify(atkHandle, ATK.end_index);
				break;
			case ACC.EVENT_HYPERLINK_ANCHOR_COUNT_CHANGED:
				OS.g_object_notify(atkHandle, ATK.number_of_anchors);
				break;
			case ACC.EVENT_HYPERLINK_SELECTED_LINK_CHANGED:
				OS.g_object_notify(atkHandle, ATK.selected_link);
				break;
			case ACC.EVENT_HYPERLINK_START_INDEX_CHANGED:
				OS.g_object_notify(atkHandle, ATK.start_index);
				break;
			case ACC.EVENT_HYPERLINK_ACTIVATED:
				OS.g_signal_emit_by_name (atkHandle, ATK.link_activated);
				break;
			case ACC.EVENT_HYPERTEXT_LINK_SELECTED:
				if (!(eventData instanceof Integer)) break;
				int index =  ((Integer)eventData).intValue();
				OS.g_signal_emit_by_name (atkHandle, ATK.link_selected, index);
				break;
			case ACC.EVENT_HYPERTEXT_LINK_COUNT_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_hypertext_nlinks);
				break;
			case ACC.EVENT_ATTRIBUTE_CHANGED:
				long gType = OS.G_OBJECT_TYPE(atkHandle);
				if (gType == GTK.GTK_TYPE_TEXT_VIEW_ACCESSIBLE()) break;
				OS.g_signal_emit_by_name (atkHandle, ATK.attributes_changed);
				break;
			case ACC.EVENT_TABLE_CAPTION_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_table_caption_object);
				break;
			case ACC.EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_table_column_description);
				break;
			case ACC.EVENT_TABLE_COLUMN_HEADER_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_table_column_header);
				break;
			case ACC.EVENT_TABLE_CHANGED: {
				if (!(eventData instanceof int[])) break;
				int[] array = (int[])eventData;
				int type =  array[0];
				int rowStart = array[1];
				int rowCount = array[2];
				int columnStart = array[3];
				int columnCount = array[4];
				switch (type) {
					case ACC.DELETE:
						if (rowCount > 0) OS.g_signal_emit_by_name (atkHandle, ATK.row_deleted, rowStart, rowCount);
						if (columnCount > 0) OS.g_signal_emit_by_name (atkHandle, ATK.column_deleted, columnStart, columnCount);
						break;
					case ACC.INSERT:
						if (rowCount > 0) OS.g_signal_emit_by_name (atkHandle, ATK.row_inserted, rowStart, rowCount);
						if (columnCount > 0) OS.g_signal_emit_by_name (atkHandle, ATK.column_inserted, columnStart, columnCount);
						break;
				}
				break;
			}
			case ACC.EVENT_TABLE_ROW_DESCRIPTION_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_table_row_description);
				break;
			case ACC.EVENT_TABLE_ROW_HEADER_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_table_row_header);
				break;
			case ACC.EVENT_TABLE_SUMMARY_CHANGED:
				OS.g_object_notify(atkHandle, ATK.accessible_table_summary);
				break;
			case ACC.EVENT_TEXT_ATTRIBUTE_CHANGED:
				OS.g_signal_emit_by_name (atkHandle, ATK.text_attributes_changed);
				break;
			case ACC.EVENT_TEXT_CARET_MOVED:
			case ACC.EVENT_TEXT_COLUMN_CHANGED: {
				int offset = 0;
				List<AccessibleTextExtendedListener> listeners = accessible.accessibleTextExtendedListeners;
				int length = size(listeners);
				AccessibleTextEvent e = new AccessibleTextEvent (accessible);
				if (length > 0) {
					for (int i = 0; i < length; i++) {
						AccessibleTextListener listener = listeners.get(i);
						listener.getCaretOffset (e);
					}
				} else {
					List<AccessibleTextListener> listeners2 = accessible.accessibleTextListeners;
					length = size(listeners2);
					if (length > 0) {
						e.childID = id;
						for (int i = 0; i < length; i++) {
							AccessibleTextListener listener = listeners2.get(i);
							listener.getCaretOffset (e);
						}
					}
				}
				offset = e.offset;
				OS.g_signal_emit_by_name (atkHandle, ATK.text_caret_moved, offset);
				break;
			}
			case ACC.EVENT_TEXT_CHANGED: {
				if (!(eventData instanceof Object[])) break;
				Object[] data = (Object[])eventData;
				int type = ((Integer)data[0]).intValue();
				int start = ((Integer)data[1]).intValue();
				int end = ((Integer)data[2]).intValue();
				switch (type) {
					case ACC.DELETE:
						OS.g_signal_emit_by_name (atkHandle, ATK.text_changed_delete, start, end -start);
						break;
					case ACC.INSERT:
						OS.g_signal_emit_by_name (atkHandle, ATK.text_changed_insert, start, end -start);
						break;
				}
				break;
			}
		}
	}

	void sendEvent(int event, Object eventData, int childID) {
		updateChildren ();
		AccessibleObject accObject = getChildByID (childID);
		if (accObject != null) {
			accObject.sendEvent(event, eventData);
		}
	}

	void setFocus (int childID) {
		if(GTK.GTK4) return; //TODO investigate proper way for GTK 4.x
		updateChildren ();
		AccessibleObject accObject = getChildByID (childID);
		if (accObject != null) {
			OS.g_signal_emit_by_name (accObject.atkHandle, ATK.focus_event, 1, 0);
			ATK.atk_object_notify_state_change(accObject.atkHandle, ATK.ATK_STATE_FOCUSED, true);
		}
	}

	void textCaretMoved(int index) {
		if(GTK.GTK4) return; //TODO investigate proper way for GTK 4.x
		OS.g_signal_emit_by_name (atkHandle, ATK.text_caret_moved, index);
	}

	void textChanged(int type, int startIndex, int length) {
		if(GTK.GTK4) return; //TODO investigate proper way for GTK 4.x
		if (type == ACC.TEXT_DELETE) {
			OS.g_signal_emit_by_name (atkHandle, ATK.text_changed_delete, startIndex, length);
		} else {
			OS.g_signal_emit_by_name (atkHandle, ATK.text_changed_insert, startIndex, length);
		}
	}

	void textSelectionChanged() {
		if(GTK.GTK4) return; //TODO investigate proper way for GTK 4.x
		OS.g_signal_emit_by_name (atkHandle, ATK.text_selection_changed);
	}

	void updateChildren () {
		List<AccessibleControlListener> listeners = accessible.accessibleControlListeners;
		int length = size(listeners);
		AccessibleControlEvent event = new AccessibleControlEvent (accessible);
		event.childID = id;
		for (int i = 0; i < length; i++) {
			AccessibleControlListener listener = listeners.get (i);
			listener.getChildren (event);
		}
		Object[] children = event.children;
		AccessibleObject[] oldChildren = this.children;
		int count = children != null ? children.length : 0;
		AccessibleObject[] newChildren = new AccessibleObject[count];
		for (int i = 0; i < count; i++) {
			Object child = children [i];
			AccessibleObject object = null;
			// Widgets where the children are Integers: CTable, BarChart, and CTabFolder
			if (child instanceof Integer) {
				int id = ((Integer)child).intValue();
				object = oldChildren != null && i < oldChildren.length ? oldChildren [i] : null;
				if (object == null || object.id != id) {
					event = new AccessibleControlEvent (accessible);
					event.childID = id;
					for (int j = 0; j < length; j++) {
						AccessibleControlListener listener = listeners.get (j);
						listener.getChild (event);
					}
					if (event.accessible != null) {
						object = event.accessible.getAccessibleObject();
						if (object != null)	OS.g_object_ref(object.atkHandle);
					} else {
						long type = OS.G_OBJECT_TYPE (accessible.getControlHandle());
						long widget = accessible.getControlHandle();
						object = new AccessibleObject(type, widget, accessible, true);
					}
					object.id = id;
				} else {
					OS.g_object_ref(object.atkHandle);
				}
			} else if (child instanceof Accessible) {
				object = ((Accessible)child).getAccessibleObject();
				if (object != null)	OS.g_object_ref(object.atkHandle);
			}
			if (object != null) {
				object.index = i;
				object.parent = this;
				newChildren[i] = object;
			}
		}
		if (oldChildren != null) {
			for (int i = 0; i < oldChildren.length; i++) {
				AccessibleObject object = oldChildren [i];
				if (object != null) OS.g_object_unref(object.atkHandle);
			}
		}
		this.children = newChildren;
	}

}
