text: /*
   * This file is part of libdom.
   * Licensed under the MIT License,
   *                http://www.opensource.org/licenses/mit-license.php
   * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
   */
  
  #include <stdlib.h>
  #include <string.h>
  
  #include "events/keyboard_event.h"
  #include "core/document.h"
  
  #include "utils/utils.h"
  
  static void _virtual_dom_keyboard_event_destroy(struct dom_event *evt);
  
  static const struct dom_event_private_vtable _event_vtable = {
  	_virtual_dom_keyboard_event_destroy
  };
  
  /* Constructor */
  dom_exception _dom_keyboard_event_create(struct dom_keyboard_event **evt)
  {
  	*evt = calloc(1, sizeof(dom_keyboard_event));
  	if (*evt == NULL) 
  		return DOM_NO_MEM_ERR;
  	
  	((struct dom_event *) *evt)->vtable = &_event_vtable;
  
  	return _dom_keyboard_event_initialise(*evt);
  }
  
  /* Destructor */
  void _dom_keyboard_event_destroy(struct dom_keyboard_event *evt)
  {
  	_dom_keyboard_event_finalise(evt);
  
  	free(evt);
  }
  
  /* Initialise function */
  dom_exception _dom_keyboard_event_initialise(struct dom_keyboard_event *evt)
  {
  	dom_exception err;
  	dom_string *empty_string;
  
  	err = dom_string_create((const uint8_t *)"", 0, &empty_string);
  	if (err != DOM_NO_ERR) {
  		return err;
  	}
  
  	evt->key = empty_string;
  	evt->code = dom_string_ref(empty_string);
  
  	return _dom_ui_event_initialise(&evt->base);
  }
  
  /* Finalise function */
  void _dom_keyboard_event_finalise(struct dom_keyboard_event *evt)
  {
  	if (evt->key != NULL)
  		dom_string_unref(evt->key);
  	if (evt->code != NULL)
  		dom_string_unref(evt->code);
  
  	_dom_ui_event_finalise(&evt->base);
  }
  
  /* The virtual destroy function */
  void _virtual_dom_keyboard_event_destroy(struct dom_event *evt)
  {
  	_dom_keyboard_event_destroy((dom_keyboard_event *) evt);
  }
  
  /*----------------------------------------------------------------------*/
  /* The public API */
  
  /**
   * Get the key
   *
   * \param evt  The Event object
   * \param key  The returned key
   * \return DOM_NO_ERR.
   */
  dom_exception _dom_keyboard_event_get_key(dom_keyboard_event *evt,
  		dom_string **key)
  {
  	*key = dom_string_ref(evt->key);
  
  	return DOM_NO_ERR;
  }
  
  /**
   * Get the code
   *
   * \param evt   The Event object
   * \param code  The returned code
   * \return DOM_NO_ERR.
   */
  dom_exception _dom_keyboard_event_get_code(dom_keyboard_event *evt,
  		dom_string **code)
  {
  	*code = dom_string_ref(evt->code);
  
  	return DOM_NO_ERR;
  }
  
  /**
   * Get the key location
   *
   * \param evt  The Event object
   * \param location  The returned key location
   * \return DOM_NO_ERR.
   */
  dom_exception _dom_keyboard_event_get_location(dom_keyboard_event *evt,
  		dom_key_location *location)
  {
  	*location = evt->location;
  
  	return DOM_NO_ERR;
  }
  
  /**
   * Get the ctrl key state
   *
   * \param evt  The Event object
   * \param key  Whether the Control key is pressed down
   * \return DOM_NO_ERR.
   */
  dom_exception _dom_keyboard_event_get_ctrl_key(dom_keyboard_event *evt,
  		bool *key)
  {
  	*key = ((evt->modifier_state & DOM_MOD_CTRL) != 0);
  
  	return DOM_NO_ERR;
  }
  
  /**
   * Get the shift key state
   *
   * \param evt  The Event object
   * \param key  Whether the Shift key is pressed down
   * \return DOM_NO_ERR.
   */
  dom_exception _dom_keyboard_event_get_shift_key(dom_keyboard_event *evt,
  		bool *key)
  {
  	*key = ((evt->modifier_state & DOM_MOD_SHIFT) != 0);
  
  	return DOM_NO_ERR;
  }
  
  /**
   * Get the alt key state
   *
   * \param evt  The Event object
   * \param key  Whether the Alt key is pressed down
   * \return DOM_NO_ERR.
   */
  dom_exception _dom_keyboard_event_get_alt_key(dom_keyboard_event *evt,
  		bool *key)
  {
  	*key = ((evt->modifier_state & DOM_MOD_ALT) != 0);
  
  	return DOM_NO_ERR;
  }
  
  /**
   * Get the meta key state
   *
   * \param evt  The Event object
   * \param key  Whether the Meta key is pressed down
   * \return DOM_NO_ERR.
   */
  dom_exception _dom_keyboard_event_get_meta_key(dom_keyboard_event *evt,
  		bool *key)
  {
  	*key = ((evt->modifier_state & DOM_MOD_META) != 0);
  
  	return DOM_NO_ERR;
  }
  
  /**
   * Query the state of a modifier using a key identifier
   *
   * \param evt    The event object
   * \param ml     The modifier identifier, such as "Alt", "Control", "Meta", 
   *               "AltGraph", "CapsLock", "NumLock", "Scroll", "Shift".
   * \param state  Whether the modifier key is pressed
   * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
   *
   * @note: If an application wishes to distinguish between right and left 
   * modifiers, this information could be deduced using keyboard events and 
   * KeyboardEvent.keyLocation.
   */
  dom_exception _dom_keyboard_event_get_modifier_state(dom_keyboard_event *evt,
  		dom_string *m, bool *state)
  {
  	const char *data;
  	size_t len;
  
  	if (m == NULL) {
  		*state = false;
  		return DOM_NO_ERR;
  	}
  
  	data = dom_string_data(m);
  	len = dom_string_byte_length(m);
  
  	if (len == SLEN("AltGraph") && strncmp(data, "AltGraph", len) == 0) {
  		*state = ((evt->modifier_state & DOM_MOD_ALT_GRAPH) != 0);
  	} else if (len == SLEN("Alt") && strncmp(data, "Alt", len) == 0) {
  		*state = ((evt->modifier_state & DOM_MOD_ALT) != 0);
  	} else if (len == SLEN("CapsLock") &&
  			strncmp(data, "CapsLock", len) == 0) {
  		*state = ((evt->modifier_state & DOM_MOD_CAPS_LOCK) != 0);
  	} else if (len == SLEN("Control") &&
  			strncmp(data, "Control", len) == 0) {
  		*state = ((evt->modifier_state & DOM_MOD_CTRL) != 0);
  	} else if (len == SLEN("Meta") && strncmp(data, "Meta", len) == 0) {
  		*state = ((evt->modifier_state & DOM_MOD_META) != 0);
  	} else if (len == SLEN("NumLock") &&
  			strncmp(data, "NumLock", len) == 0) {
  		*state = ((evt->modifier_state & DOM_MOD_NUM_LOCK) != 0);
  	} else if (len == SLEN("Scroll") &&
  			strncmp(data, "Scroll", len) == 0) {
  		*state = ((evt->modifier_state & DOM_MOD_SCROLL) != 0);
  	} else if (len == SLEN("Shift") && strncmp(data, "Shift", len) == 0) {
  		*state = ((evt->modifier_state & DOM_MOD_SHIFT) != 0);
  	}
  
  	return DOM_NO_ERR;
  }
  
  /**
   * Helper to initialise the keyboard event
   *
   * \param evt           The Event object
   * \param view          The AbstractView associated with this event
   * \param key           The key identifier of pressed key
   * \param code          The code identifier of pressed key
   * \param location      The key location of the preesed key
   * \param ctrl_key      Whether the ctrl_key is pressed
   * \param shift_key     Whether the shift_key is pressed
   * \param alt_key       Whether the alt_key is pressed
   * \param meta_key      Whether the ctrl_key is pressed
   * \param repeat        Whether this is a repeat press from a held key
   * \param is_composing  Whether the input is being composed
   */
  static void _dom_keyboard_event_init_helper(
  		dom_keyboard_event *evt,
  		dom_string *key,
  		dom_string *code,
  		dom_key_location location,
  		bool ctrl_key,
  		bool shift_key,
  		bool alt_key,
  		bool meta_key,
  		bool repeat,
  		bool is_composing)
  {
  	if (key != NULL) {
  		dom_string_unref(evt->key);
  		evt->key = dom_string_ref(key);
  	}
  	if (code != NULL) {
  		dom_string_unref(evt->code);
  		evt->code = dom_string_ref(code);
  	}
  
  	evt->location = location;
  
  	if (ctrl_key) {
  		evt->modifier_state |= DOM_MOD_CTRL;
  	}
  	if (shift_key) {
  		evt->modifier_state |= DOM_MOD_CTRL;
  	}
  	if (alt_key) {
  		evt->modifier_state |= DOM_MOD_SHIFT;
  	}
  	if (meta_key) {
  		evt->modifier_state |= DOM_MOD_META;
  	}
  
  	evt->repeat = repeat;
  	evt->is_composing = is_composing;
  }
  
  
  
  /**
   * Initialise the keyboard event with namespace
   *
   * \param evt           The Event object
   * \param type          The event's type
   * \param bubble        Whether this is a bubbling event
   * \param cancelable    Whether this is a cancelable event
   * \param view          The AbstractView associated with this event
   * \param key           The key identifier of pressed key
   * \param code          The code identifier of pressed key
   * \param location      The key location of the preesed key
   * \param ctrl_key      Whether the ctrl_key is pressed
   * \param shift_key     Whether the shift_key is pressed
   * \param alt_key       Whether the alt_key is pressed
   * \param meta_key      Whether the ctrl_key is pressed
   * \param repeat        Whether this is a repeat press from a held key
   * \param is_composing  Whether the input is being composed
   * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
   */
  dom_exception _dom_keyboard_event_init(
  		dom_keyboard_event *evt,
  		dom_string *type,
  		bool bubble,
  		bool cancelable,
  		struct dom_abstract_view *view,
  		dom_string *key,
  		dom_string *code,
  		dom_key_location location,
  		bool ctrl_key,
  		bool shift_key,
  		bool alt_key,
  		bool meta_key,
  		bool repeat,
  		bool is_composing)
  {
  	_dom_keyboard_event_init_helper(evt, key, code, location,
  			ctrl_key, shift_key, alt_key, meta_key,
  			repeat, is_composing);
  
  	return _dom_ui_event_init(&evt->base, type, bubble, cancelable,
  			view, 0);
  }
  
  /**
   * Initialise the keyboard event with namespace
   *
   * \param evt           The Event object
   * \param namespace     The namespace of this event
   * \param type          The event's type
   * \param bubble        Whether this is a bubbling event
   * \param cancelable    Whether this is a cancelable event
   * \param view          The AbstractView associated with this event
   * \param key           The key identifier of pressed key
   * \param code          The code identifier of pressed key
   * \param location      The key location of the preesed key
   * \param ctrl_key      Whether the ctrl_key is pressed
   * \param shift_key     Whether the shift_key is pressed
   * \param alt_key       Whether the alt_key is pressed
   * \param meta_key      Whether the ctrl_key is pressed
   * \param repeat        Whether this is a repeat press from a held key
   * \param is_composing  Whether the input is being composed
   * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
   */
  dom_exception _dom_keyboard_event_init_ns(
  		dom_keyboard_event *evt,
  		dom_string *namespace,
  		dom_string *type,
  		bool bubble,
  		bool cancelable,
  		struct dom_abstract_view *view,
  		dom_string *key,
  		dom_string *code,
  		dom_key_location location,
  		bool ctrl_key,
  		bool shift_key,
  		bool alt_key,
  		bool meta_key,
  		bool repeat,
  		bool is_composing)
  {
  	_dom_keyboard_event_init_helper(evt, key, code, location,
  			ctrl_key, shift_key, alt_key, meta_key,
  			repeat, is_composing);
  
  	return _dom_ui_event_init_ns(&evt->base, namespace, type, bubble,
  			cancelable, view, 0);
  }
  
  
  /*-------------------------------------------------------------------------*/
  
  /**
   * Parse the modifier list string to corresponding bool variable state
   *
   * \param modifier_list   The modifier list string
   * \param modifier_state  The returned state
   * \return DOM_NO_ERR on success, appropriate dom_exception on failure.
   */
  dom_exception _dom_parse_modifier_list(dom_string *modifier_list,
  		uint32_t *modifier_state)
  {
  	const char *data;
  	const char *m;
  	size_t len = 0;
  
  	*modifier_state = 0;
  
  	if (modifier_list == NULL)
  		return DOM_NO_ERR;
  	
  	data = dom_string_data(modifier_list);
  	m = data;
  
  	while (true) {
  		/* If we reach a space or end of the string, we should parse
  		 * the new token. */
  		if (*data == ' ' || *data == '\0') {
  			if (len == SLEN("AltGraph") &&
  					strncmp(m, "AltGraph", len) == 0) {
  				*modifier_state = *modifier_state | 
  						DOM_MOD_ALT_GRAPH;
  			} else if (len == SLEN("Alt") &&
  					strncmp(m, "Alt", len) == 0) {
  				*modifier_state = *modifier_state | 
  						DOM_MOD_ALT;
  			} else if (len == SLEN("CapsLock") &&
  					strncmp(m, "CapsLock", len) == 0) {
  				*modifier_state = *modifier_state | 
  						DOM_MOD_CAPS_LOCK;
  			} else if (len == SLEN("Control") &&
  					strncmp(m, "Control", len) == 0) {
  				*modifier_state = *modifier_state | 
  						DOM_MOD_CTRL;
  			} else if (len == SLEN("Meta") &&
  					strncmp(m, "Meta", len) == 0) {
  				*modifier_state = *modifier_state | 
  						DOM_MOD_META;
  			} else if (len == SLEN("NumLock") &&
  					strncmp(m, "NumLock", len) == 0) {
  				*modifier_state = *modifier_state | 
  						DOM_MOD_NUM_LOCK;
  			} else if (len == SLEN("Scroll") &&
  					strncmp(m, "Scroll", len) == 0) {
  				*modifier_state = *modifier_state | 
  						DOM_MOD_SCROLL;
  			} else if (len == SLEN("Shift") &&
  					strncmp(m, "Shift", len) == 0) {
  				*modifier_state = *modifier_state | 
  						DOM_MOD_SHIFT;
  			}
  
  			while (*data == ' ') {
  				data++;
  			}
  			/* Finished parsing and break */
  			if (*data == '\0')
  				break;
  
  			m = data;
  			len = 0;
  		}
  
  		data++;
  		len++;
  	}
  
  	return DOM_NO_ERR;
  }
  
  